簡體   English   中英

從 Pyspark dataframe 創建字典,顯示 OutOfMemoryError: Java 堆空間

[英]Creating dictionary from Pyspark dataframe showing OutOfMemoryError: Java heap space

我已經看到並嘗試了許多關於此問題的現有StackOverflow 帖子,但沒有一個有效。 我猜我的 JAVA 堆空間沒有我的大型數據集預期的那么大,我的數據集包含 650 萬行。 我的 Linux 實例包含 64GB Ram with 4 cores 根據這個建議,我需要修復我的代碼,但我認為從 pyspark dataframe 制作字典應該不會很昂貴。 如果有其他方法可以計算,請告訴我。

I just want to make a python dictionary from my pyspark dataframe, this is the content of my pyspark dataframe,

property_sql_df.show()顯示,

+--------------+------------+--------------------+--------------------+
|            id|country_code|       name|          hash_of_cc_pn_li|
+--------------+------------+--------------------+--------------------+
|  BOND-9129450|          US|Scotron Home w/Ga...|90cb0946cf4139e12...|
|  BOND-1742850|          US|Sited in the Mead...|d5c301f00e9966483...|
|  BOND-3211356|          US|NEW LISTING - Com...|811fa26e240d726ec...|
|  BOND-7630290|          US|EC277- 9 Bedroom ...|d5c301f00e9966483...|
|  BOND-7175508|          US|East Hampton Retr...|90cb0946cf4139e12...|
+--------------+------------+--------------------+--------------------+

我想要的是制作一個字典,其中 hash_of_cc_pn_li 作為,id 作為列表值。

預計 Output

{
  "90cb0946cf4139e12": ["BOND-9129450", "BOND-7175508"]
  "d5c301f00e9966483": ["BOND-1742850","BOND-7630290"]
}

到目前為止我所嘗試的,

%%time
duplicate_property_list = {}
for ind in property_sql_df.collect(): 
     hashed_value = ind.hash_of_cc_pn_li
     property_id = ind.id
     if hashed_value in duplicate_property_list:
         duplicate_property_list[hashed_value].append(property_id) 
     else:
         duplicate_property_list[hashed_value] = [property_id] 

我現在在控制台上得到什么:

java.lang.OutOfMemoryError: Java 堆空間

並在Jupyter 筆記本 output上顯示此錯誤

ERROR:py4j.java_gateway:An error occurred while trying to connect to the Java server (127.0.0.1:33097)

從 pyspark dataframe 制作字典應該不會很昂貴

就運行時間而言確實如此,但這很容易占用大量空間。 特別是如果您正在執行property_sql_df.collect() ,此時您正在將整個 dataframe 加載到驅動程序 memory 中。 在 650 萬行時,如果每行有 10KB 或 10K 個字符,您就已經達到了 65GB,而我們甚至還沒有找到字典。

首先,您可以只收集您需要的列(例如,不是name )。 其次,您可以在 Spark 中進行上游聚合,這將節省一些空間,具體取決於每個hash_of_cc_pn_li有多少id

rows = property_sql_df.groupBy("hash_of_cc_pn_li") \
  .agg(collect_set("id").alias("ids")) \
  .collect()

duplicate_property_list = { row.hash_of_cc_pn_li: row.ids for row in rows }

以下是使用您的數據制作示例 DataFrame 的方法:

data = [
    ("BOND-9129450", "90cb"),
    ("BOND-1742850", "d5c3"),
    ("BOND-3211356", "811f"),
    ("BOND-7630290", "d5c3"),
    ("BOND-7175508", "90cb"),
]
df = spark.createDataFrame(data, ["id", "hash_of_cc_pn_li"])

讓我們在 Spark DataFrame 中聚合數據,以限制在驅動程序節點上收集的行數。 我們將使用quinn中定義的two_columns_to_dictionary function 來創建字典。

agg_df = df.groupBy("hash_of_cc_pn_li").agg(F.max("hash_of_cc_pn_li").alias("hash"), F.collect_list("id").alias("id"))
res = quinn.two_columns_to_dictionary(agg_df, "hash", "id")
print(res) # => {'811f': ['BOND-3211356'], 'd5c3': ['BOND-1742850', 'BOND-7630290'], '90cb': ['BOND-9129450', 'BOND-7175508']}

這可能適用於相對較小的 650 萬行數據集,但不適用於大型數據集。 “我認為從 pyspark dataframe 制作字典的成本應該不會很高”僅適用於非常小的 DataFrame。 從 PySpark DataFrame 制作字典實際上非常昂貴。

PySpark 是一個集群計算框架,它受益於將數據分布在集群中的節點上。 當您調用collect時,所有數據都將移動到驅動程序節點,而工作程序節點則無濟於事。 每當您嘗試將過多數據移動到驅動程序節點時,您都會收到 OutOfMemory 異常。

最好完全避免使用字典並找出解決問題的不同方法。 好問題。

Spark-2.4開始,我們可以使用groupBy,collect_list,map_from_arrays,to_json內置函數來解決這種情況。

Example:

df.show()
#+------------+-----------------+
#|          id| hash_of_cc_pn_li|
#+------------+-----------------+
#|BOND-9129450|90cb0946cf4139e12|
#|BOND-7175508|90cb0946cf4139e12|
#|BOND-1742850|d5c301f00e9966483|
#|BOND-7630290|d5c301f00e9966483|
#+------------+-----------------+
df.groupBy(col("hash_of_cc_pn_li")).\
agg(collect_list(col("id")).alias("id")).\
selectExpr("to_json(map_from_arrays(array(hash_of_cc_pn_li),array(id))) as output").\
show(10,False)
#+-----------------------------------------------------+
#|output                                               |
#+-----------------------------------------------------+
#|{"90cb0946cf4139e12":["BOND-9129450","BOND-7175508"]}|
#|{"d5c301f00e9966483":["BOND-1742850","BOND-7630290"]}|
#+-----------------------------------------------------+

要獲得one dict使用另一個 agg 和collect_list

df.groupBy(col("hash_of_cc_pn_li")).\
agg(collect_list(col("id")).alias("id")).\
agg(to_json(map_from_arrays(collect_list(col("hash_of_cc_pn_li")),collect_list(col("id")))).alias("output")).\
show(10,False)
#+---------------------------------------------------------------------------------------------------------+
#|output                                                                                                   |
#+---------------------------------------------------------------------------------------------------------+
#|{"90cb0946cf4139e12":["BOND-9129450","BOND-7175508"],"d5c301f00e9966483":["BOND-1742850","BOND-7630290"]}|
#+---------------------------------------------------------------------------------------------------------+

從鏈接的帖子中添加已接受的答案以供后代使用。 答案通過利用write.json方法解決了問題,並在此處防止向驅動程序收集過大的數據集:

https://stackoverflow.com/a/63111765/12378881

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM