簡體   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