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

wnavrhmk  于 2022-09-21  发布在  Spark
关注(0)|答案(3)|浏览(121)

我的Spark DataDrame中有一个包含多种字符串格式的Date列。我想把这些扔给Date Time。

我的专栏中有两种格式:

  • 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(我使用的regex目前显然不能做到这一点)。

有没有更好的方法来做这件事?

yqhsw0fo

yqhsw0fo1#

就我个人而言,我建议直接使用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)): _*)
}
yrefmtwq

yrefmtwq2#

您可以用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
hof1towb

hof1towb3#

使用TO_TIMESTAMP(),我认为问题来自时间格式规则,例如您的数据如下:

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

相关问题