将包含多种字符串日期格式的列转换为 Spark 中的 DateTime

Posted

技术标签:

【中文标题】将包含多种字符串日期格式的列转换为 Spark 中的 DateTime【英文标题】:Cast column containing multiple string date formats to DateTime in Spark 【发布时间】:2017-10-05 21:03:06 【问题描述】:

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

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

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

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

import re

def parseDate(dateString):
    if re.match('\d1,2\/\d1,2\/\d4', 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(我使用的正则表达式目前显然不这样做)。

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

【问题讨论】:

【参考方案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

以上是关于将包含多种字符串日期格式的列转换为 Spark 中的 DateTime的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Java 将 unix 纪元的列转换为 Apache spark DataFrame 中的日期?

Spark - 将包含 JSON 字符串的列从 StringType 转换为 Array Type(StringType())

BigQuery 将字符串转换为日期

将字符串转换为日期时间

将具有日期格式的列中的所有行转换为文本格式

使用 PySpark 将日期和时间字符串转换为时间戳时如何保留毫秒?