【问题标题】:Cast column containing multiple string date formats to DateTime in Spark将包含多种字符串日期格式的列转换为 Spark 中的 DateTime
【发布时间】:2023-03-13 04:25:01
【问题描述】:

我的 Spark DataDrame 中有一个包含多种字符串格式的日期列。我想将这些转换为 DateTime。

我的专栏中的两种格式是:

  • mm/dd/yyyy;和
  • yyyy-mm-dd

到目前为止,我的解决方案是使用 UDF 更改第一个日期格式以匹配第二个日期格式,如下所示:

import re

def parseDate(dateString):
    if re.match('\d{1,2}\/\d{1,2}\/\d{4}', dateString) is not None:
        return datetime.strptime(dateString, '%M/%d/%Y').strftime('%Y-%M-%d')
    else:
        return dateString

# Create Spark UDF based on above function
dateUdf = udf(parseDate)

df = (df.select(to_date(dateUdf(raw_transactions_df['trans_dt']))))

这可行,但并不是那么容错。我特别担心:

  • 我还没有遇到过日期格式。
  • 区分mm/dd/yyyydd/mm/yyyy(我使用的正则表达式目前显然不这样做)。

有没有更好的方法来做到这一点?

【问题讨论】:

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


    【解决方案1】:

    我个人建议直接使用 SQL 函数,而不需要昂贵且低效的重新格式化:

    from pyspark.sql.functions import coalesce, to_date
    
    def to_date_(col, formats=("MM/dd/yyyy", "yyyy-MM-dd")):
        # Spark 2.2 or later syntax, for < 2.2 use unix_timestamp and cast
        return coalesce(*[to_date(col, f) for f in formats])
    

    这将选择第一种格式,它可以成功解析输入字符串。

    用法:

    df = spark.createDataFrame([(1, "01/22/2010"), (2, "2018-12-01")], ("id", "dt"))
    df.withColumn("pdt", to_date_("dt")).show()
    
    +---+----------+----------+
    | id|        dt|       pdt|
    +---+----------+----------+
    |  1|01/22/2010|2010-01-22|
    |  2|2018-12-01|2018-12-01|
    +---+----------+----------+
    

    会比udf快,而且添加新格式只需要调整formats参数即可。

    但是,它不会帮助您解决格式不明确的问题。在一般情况下,如果没有人工干预和与外部数据的交叉引用,可能无法做到这一点。

    同样的事情当然可以在 Scala 中完成:

    import org.apache.spark.sql.Column
    import org.apache.spark.sql.functions.{coalesce, to_date}
    
    def to_date_(col: Column, 
                 formats: Seq[String] = Seq("MM/dd/yyyy", "yyyy-MM-dd")) = {
      coalesce(formats.map(f => to_date(col, f)): _*)
    }
    

    【讨论】:

      【解决方案2】:

      您可以在 100% sql 中执行此操作,如下所示:

      create database delete_me;
      use delete_me;
      create table test (enc_date string);
      
      insert into test values ('10/28/2019');
      insert into test values ('2020-03-31 00:00:00.000');
      insert into test values ('2019-10-18');
      insert into test values ('gobledie-gook');
      insert into test values ('');
      insert into test values (null);
      insert into test values ('NULL');
      
      -- you might need the following line depending on your version of spark
      -- set spark.sql.legacy.timeParserPolicy = LEGACY;
      select enc_date, coalesce(to_date(enc_date, "yyyy-MM-dd"), to_date(enc_date, "MM/dd/yyyy")) as date from test;
      
      
      enc_date                    date
      --------                    ----
      2020-03-31 00:00:00.000     2020-03-31
      2019-10-18                  2019-10-18
      null                        null
      10/28/2019                  2019-10-28
      gobledie-gook               null
      NULL                        null
                                  null
      

      【讨论】:

        【解决方案3】:

        使用 to_timestamp(),我认为问题出在时间格式规则上,例如您的数据如下:

        请注意“dd/MM/yyyy HH:mm:ss”、“dd:MM:yyyy HH:mm:ss”等差异,请参见下面的比较:

        【讨论】:

        • spark 版本是 3.0.1
        猜你喜欢
        • 2015-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-11
        • 1970-01-01
        • 2016-12-27
        相关资源
        最近更新 更多