【问题标题】:PySpark - to_date format from columnPySpark - 列中的 to_date 格式
【发布时间】:2018-09-10 07:44:25
【问题描述】:

我目前正试图弄清楚,如何通过列参数将字符串格式参数传递给 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")))

这里抛出一个错误“Column object not callable”。

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

【问题讨论】:

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


    【解决方案1】:

    您可以使用 spark-sql 语法在没有 udf 的情况下使用 use a column value as a parameter

    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 不支持在 to_date 函数中使用 format 参数,因此您必须使用 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() 
    

    【讨论】:

    • 太棒了!但是为什么我们不能只使用 DataFrame API 呢?
    • @IhorKonovalenko 这是 API。
    【解决方案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|
    +--------+----------+--------+----------+
    

    【讨论】:

      【解决方案3】:

      您也不需要格式列。您可以使用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 日。

      只是一种替代方法!!!

      【讨论】:

        猜你喜欢
        • 2022-01-02
        • 1970-01-01
        • 1970-01-01
        • 2015-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-20
        • 2021-04-25
        相关资源
        最近更新 更多