Сегодня расскажем о способах работы с датами в Apache Spark. В этой статье вы узнаете: как создать DataFrame с датами; как преобразовать столбец в корректное отображение даты или изменить его формат; как получить год, месяц, день и квартал; как вычитать и складывать даты в Apache PySpark.
Инициализация DataFrame с датами
Самым простым способом создания таблицы с датами является представление записей этой самой даты в виде строки YYYY-MM-DD
. В данном случае порядок: год, месяц, день — важен, иначе функции для работы с датами не будут работать.
DataFrame с датами может выглядеть так:
df = spark.createDataFrame([ ('Anton', '1985-03-12'), ('Valen', '2003-07-25'), ('Andry', '1993-03-12'), ('Alexy', '1979-12-01')], ['name', 'birth'] )
В PySpark тип столбца с датами будет идентифицироваться как строка, но тем не менее рассматриваемые функции можно спокойно применять.
Можно быть более педантичным и указать типы столбцов явно через DateType
. В этом случае нужно использовать объект date
из модуля datetime
. В этот объект нужно передать год, месяц и день в виде чисел.
Так, таблицы через DateType
создает следующим образом:
from pyspark.sql.types import StructType, StructField, StringType, DateType from datetime import date schema = StructType([ StructField('name', StringType()), StructField('birth', DateType()) ]) data = [ ('Anton', date(1985, 3, 12)), ('Valen', date(2003, 7, 25)), ('Andry', date(1993, 3, 12)), ('Alexy', date(1979, 12, 29)) ] df = spark.createDataFrame(data, schema)
В большинстве случаев таблицы не создаются вот так явно, а читаются из файлов. При чтении файла в DataFrame также можно указать дополнительным параметром dateFormat
и надеяться, что даты будут правильно интерпретированы. Однако в PySpark, в отличие от Pandas, нет возможности указать, какой именно столбец содержит даты, поэтому такой трюк, скорее всего, не пройдет. Тем не менее, вы также можете преобразовать строки в даты функцией to_date
, если они не имеют правильного порядка.
Как преобразовать строки в корректные даты в Spark
Допустим, в исходной таблице порядок представления дат не тот. В этом случае можно воспользоваться SQL-функцией to_date
. Формат представления, который передается вторым параметром, должен быть ровно таким, каким представлен в документации. Unix’овые форматы с %
не сработают.
Код курса
MLSP
Ближайшая дата курса
по запросу
Продолжительность
ак.часов
Стоимость обучения
0 руб.
Итак, имеется таблица, в которой сначала идет месяц, год, день, затем какие-то непонятные символы RST
:
df = spark.createDataFrame([ ('Anton', '03/1985/12 RST'), ('Valen', '07/2003/25 RST'), ('Andry', '03/1993/12 RST'), ('Alexy', '12/1979/01 RST')], ['name', 'birth'] )
Чтобы преобразовать данный столбец в корректное представление даты, нужно вызвать функцию to_date
:
df.withColumn('birth', F.to_date("birth", "MM/yyyy/dd 'RST")) """ +-----+----------+ | name| birth| +-----+----------+ |Anton|1985-03-12| |Valen|2003-07-25| |Andry|1993-03-12| |Alexy|1979-12-01| +-----+----------+ """
Здесь одиночный апостроф '
обозначает, что после него идут символы, которые не нужно интерпретировать.
Извлечение года, месяца или дня в Apache Spark
Возможно вам понадобятся конкретный год, месяц или день из столбца с датами. Тогда воспользуйтесь функциями year
, month
, dayofmonth
(день месяца), dayofweek
(день недели), dayofyear
(день из всего года).
df.withColumn('year', F.year('birth')) \ .withColumn('month', F.month('birth')) \ .withColumn('dmont', F.dayofmonth('birth')) \ .withColumn('dweek', F.dayofweek('birth')) \ .withColumn('dyear', F.dayofyear('birth')) \ .show() """ +-----+----------+----+-----+-----+-----+-----+ | name| birth|year|month|dmont|dweek|dyear| +-----+----------+----+-----+-----+-----+-----+ |Anton|1985-03-12|1985| 3| 12| 3| 71| |Valen|2003-07-25|2003| 7| 25| 6| 206| |Andry|1993-03-12|1993| 3| 12| 6| 71| |Alexy|1979-12-29|1979| 12| 29| 7| 363| +-----+----------+----+-----+-----+-----+-----+ """
Также есть дополнительные функции без параметров:
quarter
возвращает квартал года (1, 2, 3 или 4);weekofyear
возвращает номер недели в году (от 1 до 52);last_day
возвращает последний день месяца (от 28 до 31);
Прибавляем, отнимаем даты
Чтобы прибавить заданное количество месяцев или дней, используются функции add_months
или date_add
. Вторым параметром передается количество месяцев или дней, которые нужно прибавить.
df.withColumn('add_month', F.add_months('birth', 12)) \ .withColumn('add_days', F.date_add('birth', 31)) """ +-----+----------+----------+----------+ | name| birth| add_month| add_days| +-----+----------+----------+----------+ |Anton|1985-03-12|1986-03-12|1985-04-12| |Valen|2003-07-25|2004-07-25|2003-08-25| |Andry|1993-03-12|1994-03-12|1993-04-12| |Alexy|1979-12-01|1980-12-01|1980-01-01| +-----+----------+----------+----------+ """
Функции для прибавление года нет, поэтому можно использовать add_months
со значением кратным 12.
Чтобы отнять дату, используются функции date_sub
или datediff
. Первая функция отнимает заданное количество дней. Вторая отнимает выполняет вычитание дат, в т.ч. двух столбцов. Обе функции возвращают количество дней.
Допустим, требуется узнать сколько лет человеку, отсчитывая от сегодняшнего дня. Тогда мы можем воспользоваться еще функцией current_date
для получения текущего времени:
n_days = 365 years = F.datediff(F.current_date(), 'birth') / n_days df.withColumn('years', years.cast('int')) """ +-----+----------+-----+ | name| birth|years| +-----+----------+-----+ |Anton|1985-03-12| 36| |Valen|2003-07-25| 18| |Andry|1993-03-12| 28| |Alexy|1979-12-01| 42| +-----+----------+-----+ """
Мы использовали явное преобразование в int
, иначе результатом было бы число с плавающей точкой, а округление до целого привело бы к неправильному году.
Есть еще функция months_between
, но она возвращает разницу в месяцах. Эта функция также имеет третий параметр roundOff
, по умолчанию равный True
, который округляет до 8 знаков после запятой, если не передано значение False
.
Еще различные функции для работы с датами в Apache Spark
Если вам понадобилось преобразовать даты в какой-то другой формат, то используйте функцию date_format
. Например, требуется ограничить порядки точкой, а не дефисом:
df.withColumn('res', F.date_format('birth', 'dd.MM.yyyy')) """ +-----+----------+----------+ | name| birth| res| +-----+----------+----------+ |Anton|1985-03-12|12.03.1985| |Valen|2003-07-25|25.07.2003| |Andry|1993-03-12|12.03.1993| |Alexy|1979-12-01|01.12.1979| +-----+----------+----------+ """
Функция date_trunc
возвращает даты, округленные до заданного формата. Она может понадобится, когда нужно округлить дату до начало года, месяца, недели или квартала. Эта функция немного странная в том, что она принимает имя столбца вторым параметром и возвращает объект timestamp
(время также будет включено).
Например, получим даты с начала квартала:
df.withColumn('res', F.date_trunc('quarter', 'birth')) """ +-----+----------+-------------------+ | name| birth| res| +-----+----------+-------------------+ |Anton|1985-03-12|1985-01-01 00:00:00| |Valen|2003-07-25|2003-07-01 00:00:00| |Andry|1993-03-12|1993-01-01 00:00:00| |Alexy|1979-12-01|1979-10-01 00:00:00| +-----+----------+-------------------+ """
А вот функция trunc
, наоборот, принимает первым параметром сам столбец и возвращает объект DateType
. А делает все то же самое:
df.withColumn('res', F.trunc('birth', 'month')).show() """ +-----+----------+----------+ | name| birth| res| +-----+----------+----------+ |Anton|1985-03-12|1985-03-01| |Valen|2003-07-25|2003-07-01| |Andry|1993-03-12|1993-03-01| |Alexy|1979-12-01|1979-12-01| +-----+----------+----------+ """
Напоследок упомянем функцию next_day
, которая возвращает ближайшую следующую дату заданного дня недели. Например, next_day('2022-01-30', 'Fri')
вернет значение 2022-02-04
, поскольку ближайшая следующая пятница именно этого числа.
Еще больше подробностей о преобразовании и анализе данных вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве: