在 PySpark Dataframe 中向数组内的元素添加天数

Add days to element inside array in PySpark Dataframe

我有一个包含三列的 PySpark 数据框。前两列以数组作为元素,最后一列给出最后一列数组的长度。以下是 PySpark 数据框:

+---------------------+---------------------+-----+
|                   c1|                   c2|lenc2|
+---------------------+---------------------+-----+
|[2017-02-14 00:00:00]|[2017-02-24 00:00:00]|    1|
|[2017-01-16 00:00:00]|                   []|    0|
+---------------------+---------------------+-----+

数组包含时间戳数据类型。 lenc2 列表示 c1 列中数组的长度。对于 lenc2==0 所在的所有行,列 c1 只有一个(时间戳)元素。

对于 lenc2==0 的所有行,我想从列 c1 中的数组中获取时间戳,向其添加 5 天并将其放入行中的数组中c2。我该怎么做?

这是预期输出的示例:

+---------------------+---------------------+-----+
|                   c1|                   c2|lenc2|
+---------------------+---------------------+-----+
|[2017-02-14 00:00:00]|[2017-02-24 00:00:00]|    1|
|[2017-01-16 00:00:00]|[2017-01-21 00:00:00]|    0|
+---------------------+---------------------+-----+

以下是我到目前为止所尝试的:

df2 = df1.withColumn(
    "c2",
    F.when(F.col("lenc2") == 0, F.array_union(F.col("c1"), F.col("c2"))).otherwise(
        F.col("c2")
    ),
)

你的 when(…).otherwise(…) 已经正确了。

鉴于您似乎对亚秒精度不感兴趣,您可以将时间戳转换为自 Unix 纪元以来的秒数,并添加 5 天的秒数,然后转换回时间戳:

from datetime import datetime

from pyspark.sql.functions import *

one_sec_before_leap_time = datetime(2016, 12, 31, 23, 59, 59)
seconds_in_a_day = 24 * 3600

df = spark.createDataFrame([
    ([one_sec_before_leap_time], [datetime.now()], 1),
    ([one_sec_before_leap_time], [], 0),
],
    schema=("c1", "c2", "lenc2"))


def add_seconds_to_timestamp(ts_col, seconds_col):
    return to_timestamp(unix_timestamp(ts_col) + seconds_col)


df2 = df.withColumn("c2",
                    when(col("lenc2") == 0,
                         array(
                             add_seconds_to_timestamp(
                                 col("c1").getItem(0),
                                 lit(5 * seconds_in_a_day))))
                    .otherwise(col("c2")))
df2.show(truncate=False)
# +---------------------+----------------------------+-----+                      
# |c1                   |c2                          |lenc2|
# +---------------------+----------------------------+-----+
# |[2016-12-31 23:59:59]|[2019-12-07 16:58:32.864176]|1    |
# |[2016-12-31 23:59:59]|[2017-01-05 23:59:59]       |0    |
# +---------------------+----------------------------+-----+

请注意,当您必须考虑夏令时时,这很可能会给您带来奇怪的结果。最好用 UTC 表示所有内容,并且仅在输入和输出处将 UTC 时间戳正确转换为以本地时区表示的时间。基本上类似于Unicode三明治。

此外,这没有考虑 leap seconds,如上图所示(2016 年还有一秒,使 2016-12-31T12:59:60Z 在技术上有效)。然而,闰秒是出了名的难,因为没有确切的公式(但是 - 谁知道呢,也许有一天我们可以模拟地质和气候事件?)。