Pyspark - формат to_date из столбца

В настоящее время я пытаюсь понять, как передать аргумент String - format функции pyspark to_date через параметр столбца.

В частности, у меня следующая установка:

sc = SparkContext.getOrCreate()
df = sc.parallelize([('a','2018-01-01','yyyy-MM-dd'),
                      ('b','2018-02-02','yyyy-MM-dd'),
                      ('c','02-02-2018','dd-MM-yyyy')]).toDF(
                    ["col_name","value","format"])

В настоящее время я пытаюсь добавить новый столбец, в котором каждая из дат из столбца F.col («значение»), который является строковым значением, анализируется до даты.

Отдельно для каждого формата это можно сделать с помощью

df = df.withColumn("test1",F.to_date(F.col("value"),"yyyy-MM-dd")).\
        withColumn("test2",F.to_date(F.col("value"),"dd-MM-yyyy"))

Однако это дает мне 2 новых столбца, но я хочу иметь 1 столбец, содержащий оба результата, но вызов столбца с помощью функции to_date не представляется возможным:

df = df.withColumn("test3",F.to_date(F.col("value"),F.col("format")))

Здесь выдается ошибка «Объект столбца не вызывается».

Можно ли использовать общий подход для всех возможных форматов (чтобы мне не приходилось вручную добавлять новые столбцы для каждого формата)?

6
0
38 737
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Насколько мне известно, ваша проблема требует, чтобы некоторые udf (пользовательские функции) применяли правильный формат. Но тогда внутри udf вы не можете напрямую использовать искровые функции, такие как to_date. Поэтому я создал небольшое обходное решение. Сначала udf берет преобразование даты Python с соответствующим форматом из столбца и преобразует его в iso-формат. Затем другой withColumn преобразует ISO-дату в правильный формат в столбце test3. Однако вам необходимо адаптировать формат в исходном столбце, чтобы он соответствовал строкам формата даты Python, например гггг ->% Y, MM ->% m, ...

test_df = spark.createDataFrame([
('a','2018-01-01','%Y-%m-%d'),
                  ('b','2018-02-02','%Y-%m-%d'),
                  ('c','02-02-2018','%d-%m-%Y')
], ("col_name","value","format"))

def map_to_date(s,format):
    return datetime.datetime.strptime(s,format).isoformat()

myudf = udf(map_to_date)

test_df.withColumn("test3",myudf(col("value"),col("format")))\ 
   .withColumn("test3",to_date("test3")).show(truncate=False)

Результат:

+--------+----------+--------+----------+
|col_name|value     |format  |test3     |
+--------+----------+--------+----------+
|a       |2018-01-01|%Y-%m-%d|2018-01-01|
|b       |2018-02-02|%Y-%m-%d|2018-02-02|
|c       |02-02-2018|%d-%m-%Y|2018-02-02|
+--------+----------+--------+----------+
Ответ принят как подходящий

Вы можете использовать значение столбца в качестве параметра без udf, используя синтаксис spark-sql:

Spark версии 2.2 и выше

from pyspark.sql.functions import expr
df.withColumn("test3",expr("to_date(value, format)")).show()
#+--------+----------+----------+----------+
#|col_name|     value|    format|     test3|
#+--------+----------+----------+----------+
#|       a|2018-01-01|yyyy-MM-dd|2018-01-01|
#|       b|2018-02-02|yyyy-MM-dd|2018-02-02|
#|       c|02-02-2018|dd-MM-yyyy|2018-02-02|
#+--------+----------+----------+----------+

Или, что то же самое, с помощью pyspark-sql:

df.createOrReplaceTempView("df")
spark.sql("select *, to_date(value, format) as test3 from df").show() 

Spark версии 1.5 и выше

Старые версии Spark не поддерживают наличие аргумента format для функции to_date, поэтому вам придется использовать unix_timestamp и from_unixtime:

from pyspark.sql.functions import expr
df.withColumn(
    "test3",
    expr("from_unixtime(unix_timestamp(value,format))").cast("date")
).show()

Или, что то же самое, с помощью pyspark-sql:

df.createOrReplaceTempView("df")
spark.sql(
    "select *, cast(from_unixtime(unix_timestamp(value,format)) as date) as test3 from df"
).show() 

Большой! Но почему мы не можем сделать это только с помощью DataFrame API?

Ihor Konovalenko 22.05.2019 13:37

@IhorKonovalenko это с API.

pault 22.05.2019 17:18

Вам также не нужен столбец формата. Вы можете использовать coalesce, чтобы проверить все возможные варианты

def get_right_date_format(date_string):
      from pyspark.sql import functions as F
      return F.coalesce(
                    F.to_date(date_string, 'yyyy-MM-dd'),
                    F.to_date(date_string, 'dd-MM-yyyy'),
                    F.to_date(date_string, 'yyyy-dd-MM')
      )

df = sc.parallelize([('a','2018-01-01'),
                      ('b','2018-02-02'),
                      ('c','2018-21-02'),
                      ('d','02-02-2018')]).toDF(
                    ["col_name","value"])

df = df.withColumn("formatted_data",get_right_date_format(df.value, 'dd-MM-yyyy'))

Проблема с этим подходом заключается в том, что такая дата, как 2020-02-01, будет рассматриваться как 1 февраля 2020 года, когда вполне вероятно, что 2 января 2020 года также возможно.

Просто альтернативный подход !!!

Другие вопросы по теме