Как на Spark получить возраст в бинах по дате рождения на определённую дату
Что проверяют
Умеете ли вы считать возраст правильно, а не делением дней на 365, и знаете ли встроенные функции Spark вместо Python UDF. Следом обычно спрашивают, куда попадают граничные значения и строки без даты рождения.
Как отвечать
Каркас решения на DataFrame API, без UDF:
- дата отсчёта: параметр задачи,
F.to_date(F.lit("2026-09-01")), или колонка с датой события, если возраст нужен на момент каждой записи. Сcurrent_date()результат зависит от дня запуска, и перезапуск вчерашней задачи даст другие цифры; - полные годы:
F.floor(F.months_between(ref, F.col("birth_date")) / 12).months_betweenвозвращает целое число месяцев, когда совпадают числа или обе даты последние в месяце, поэтому граница дня рождения не съезжает. Делениеdatediffна 365.25 ошибается как раз около дня рождения; - бины: полуоткрытые интервалы цепочкой
F.when(age < 18, "0-17").when(age < 30, "18-29"), где каждая граница проверяется один раз. Если нужен номер бина, подойдётBucketizerизpyspark.ml.featureсоsplits=[0, 18, 30, 45, 60, float("inf")]; - грязные данные: пустая дата рождения, дата позже даты отсчёта, возраст за 120 лет уходят в отдельный бин
unknown, а не в последний; - агрегация:
groupBy("age_bin").count()один раз по всем данным. Порядок бинов на выходе задавайте по нижней границе, а не по строке подписи.
Встроенные функции оптимизирует Catalyst, и выполняются они в JVM. Python UDF гоняет каждую строку через Python-процесс, на больших таблицах это в разы медленнее. Если своя логика неизбежна, берите pandas UDF.
Где спотыкаются
Используют between для соседних бинов. Он включает обе границы, поэтому 18 попадает в первый бин, а 30 во второй, хотя по подписи должен в третий.
Отправляют всё, что не подошло под условия, в otherwise("60+"). Туда же уезжают null и отрицательный возраст, и распределение незаметно искажается.
При дозагрузке объединяют через union уже посчитанные агрегаты. Бины дублируются, и нужна повторная агрегация, а проще пересчитать по объединённым сырым данным.