繁体   English   中英

OOM 使用 Spark window function 间隔 30 天

[英]OOM using Spark window function with 30 days interval

我有这个数据框:

df = (
spark
.createDataFrame([
    [20210101, 'A', 103, "abc"], 
    [20210101, 'A', 102, "def"], 
    [20210101, 'A', 101, "def"], 
    [20210102, 'A', 34, "ghu"], 
    [20210101, 'B', 180, "xyz"], 
    [20210102, 'B', 123, "kqt"]
]
    ).toDF("txn_date", "txn_type", "txn_amount", "other_attributes")
)

每个日期都有多个不同类型的交易。 我的任务是计算每条记录的金额标准差(对于相同的类型并返回 30 天)。

最明显的方法(我尝试过)是根据类型创建一个 window 并包括可以追溯到过去 30 天的记录。

days = lambda i: i * 86400
win = Window.partitionBy("txn_type").orderBy(F.col("txn_date").cast(LongType())).rangeBetween(-days(30), 0)
df = df.withColumn("stddev_last_30days", F.stddev(F.col("txn_amount")).over(win))

由于某些交易类型每天有数百万笔交易,这会导致 OOM。

我尝试分部分进行(一次只为每个日期记录少量记录),但这会导致计算容易出错,因为标准偏差不是相加的。

我还为交易类型和日期的所有记录尝试了“collect_set”(因此所有金额都以数组形式出现在一个列中),但这也会遇到 OOM。

我尝试一次处理一个月(我需要至少 2 个月的数据,因为我需要 go 回到 1 个月)但即使这样也让我的执行者不知所措。

解决此问题的可扩展方法是什么?

笔记:

  • 在原始数据中,列txn_date以“yyyyMMdd”格式存储。

  • 数据框中的其他列对于每个日期和类型可能相同也可能不同。 为简单起见,我没有将它们包含在示例代码中。

过滤

删除不需要的数据总是好的。 你说你只需要最后 60 天,所以你可以filter掉不需要的东西。
此行将仅保留日期不早于最后几天(直到今天)的行:

df = df.filter(F.to_date('txn_date', 'yyyyMMdd').between(F.current_date()-61, F.current_date()))

我现在不会使用它来说明其他问题。

Window

第一个简单的事情,如果它已经是长格式,你不需要再次转换为 long,所以我们可以删除.cast(LongType())

另一件大事是你的窗口的下限是错误的。 看,让我们在输入中再添加一行:

[19990101, 'B', 9999999, "xxxxxxx"],

该行表示 1999 年的日期。添加该行后,运行代码,我们得到:

# +--------+--------+----------+----------------+------------------+
# |txn_date|txn_type|txn_amount|other_attributes|stddev_last_30days|
# +--------+--------+----------+----------------+------------------+
# |20210101|       A|       103|             abc|               1.0|
# |20210101|       A|       102|             def|               1.0|
# |20210101|       A|       101|             def|               1.0|
# |20210102|       A|        34|             ghu|34.009802508492555|
# |19990101|       B|   9999999|         xxxxxxx|              null|
# |20210101|       B|       180|             xyz|  7070939.82553808|
# |20210102|       B|       123|             kqt|  5773414.64605055|
# +--------+--------+----------+----------------+------------------+

您可以看到 2021 年行的 stddev 也受到了影响,因此 30 天 window 不起作用,您的 window 实际上可以获取所有数据。 我们可以检查日期20210101的下限是多少:

print(20210101-days(30))  # Returns 17618101 - I doubt you wanted this date as lower bound

可能这是你最大的问题。 你永远不应该试图超越日期和时间。 始终使用专门用于日期和时间的函数。

您可以使用这个 window:

days = lambda i: i * 86400
w = Window.partitionBy('txn_type').orderBy(F.unix_timestamp(F.col('txn_date').cast('string'), 'yyyyMMdd')).rangeBetween(-days(30), 0)
df = df.withColumn('stddev_last_30days', F.stddev('txn_amount').over(w))

df.show()
# +--------+--------+----------+----------------+------------------+
# |txn_date|txn_type|txn_amount|other_attributes|stddev_last_30days|
# +--------+--------+----------+----------------+------------------+
# |20210101|       A|       103|             abc|               1.0|
# |20210101|       A|       102|             def|               1.0|
# |20210101|       A|       101|             def|               1.0|
# |20210102|       A|        34|             ghu|34.009802508492555|
# |19990101|       B|   9999999|         xxxxxxx|              null|
# |20210101|       B|       180|             xyz|              null|
# |20210102|       B|       123|             kqt| 40.30508652763321|
# +--------+--------+----------+----------------+------------------+

unix_timestamp可以将您的 'yyyyMMdd' 格式转换为适当的长格式数字(UNIX 时间以秒为单位)。 从此,现在您可以减去秒数(相当于 30 天的秒数)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM