PySpark-列的to_date格式

时间:2018-09-10 07:44:25

标签: apache-spark pyspark apache-spark-sql

我目前正在尝试找出如何通过列参数将String-format参数传递给to_date pyspark函数。

具体地说,我有以下设置:

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(“ value”)列中的每个日期(它是一个字符串值)解析为一个日期。

对于每种格式,可以分别使用

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")))

在这里抛出错误“列对象不可调用”。

是否可以对所有可能的格式采用通用方法(这样我就不必为每种格式手动添加新列)?

3 个答案:

答案 0 :(得分:3)

您可以使用spark-sql语法use a column value as a parameter,而无需使用udf

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_timestampfrom_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() 

答案 1 :(得分:2)

据我所知,您的问题需要一些udf(用户定义的函数)才能应用正确的格式。但是在udf内部,您不能直接使用to_date之类的spark函数。因此,我在解决方案中创建了一些解决方法。首先udf从列中以适当的格式进行python日期转换,并将其转换为iso格式。然后另一个withColumn将iso-date转换为test3列中的正确格式。但是,您必须调整原始列中的格式以匹配python dateformat字符串,例如yyyy->%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|
+--------+----------+--------+----------+

答案 2 :(得分:0)

您也不需要format列。您可以使用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视为2020年2月1日,而很可能也可能是2020年1月2日。

只是一种替代方法!