将包含多种字符串日期格式的列转换为 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/yyyy
和dd/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())