【问题标题】:Creating dictionary from Pyspark dataframe showing OutOfMemoryError: Java heap space从显示 OutOfMemoryError:Java 堆空间的 Pyspark 数据帧创建字典
【发布时间】:2020-11-16 01:47:28
【问题描述】:

我已经看到并尝试了许多关于此问题的 existing StackOverflow 帖子,但没有一个有效。我想我的 JAVA 堆空间没有我的大型数据集预期的那么大,我的数据集包含 650 万行。我的 Linux 实例包含 4 核的 64GB 内存。根据suggestion,我需要修复我的代码,但我认为从 pyspark 数据帧制作字典的成本应该不会很高。如果有其他方法可以计算,请告诉我。

我只想从我的 pyspark 数据框制作一个 python 字典,这是我的 pyspark 数据框的内容,

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 作为 key 和 id 作为 列表 值的字典。

预期输出

{
  "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 笔记本输出

上显示此错误
ERROR:py4j.java_gateway:An error occurred while trying to connect to the Java server (127.0.0.1:33097)

【问题讨论】:

  • 不确定它最终是否有帮助,但作为第一步,您可以尝试将列表集合分发为df.groupby("hash_of_cc").agg(collect_list("id")).show()。然后再仔细看看,决定是否真的需要将完整的数据帧收集到驱动程序。

标签: java python apache-spark pyspark


【解决方案1】:

从 pyspark 数据框制作字典应该不会很昂贵

就运行时间而言确实如此,但这很容易占用大量空间。特别是如果您正在执行property_sql_df.collect(),此时您正在将整个数据帧加载到驱动程序内存中。在 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 }

【讨论】:

  • 感谢先生的详尽回答。
  • 只是一个简单的问题,当我尝试使用 df.toPandas() 将我的 pyspark 数据帧转换为 pandas 时发生了同样的错误,在这种情况下我该怎么办?
  • df.toPandas() 在占用空间方面甚至比df.collect() 还要糟糕。相反,要做什么取决于您在此之后要做什么。如果说,关键是要有一个包含这些映射的 csv 文件,那么您可以直接从数据帧(使用 collect_list/collect_set 之后)并通过 df.coalesce(1).write.option('sep',';').csv(path) 将其保存到文件中。跨度>
  • 没有运气同样的问题发生
【解决方案2】:

从链接的帖子中添加已接受的答案以供后代使用。答案通过利用write.json 方法解决了问题,并在此处防止向驱动程序收集过大的数据集:

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

【讨论】:

    【解决方案3】:

    以下是如何使用您的数据制作示例 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 函数来创建字典。

    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 数据帧制作字典的成本应该不会很高”仅适用于非常小的数据帧。从 PySpark DataFrame 制作字典实际上非常昂贵。

    PySpark 是一个集群计算框架,它得益于将数据分布在集群中的节点上。当您调用collect 时,所有数据都将移动到驱动程序节点,而工作程序节点无济于事。每当您尝试将过多数据移动到驱动程序节点时,都会出现 OutOfMemory 异常。

    最好完全避免使用字典并找出解决问题的不同方法。好问题。

    【讨论】:

    • 感谢先生的详尽回答。
    • 只是一个简单的问题,当我尝试使用 df.toPandas() 将我的 pyspark 数据帧转换为 pandas 时发生了同样的错误,在这种情况下我该怎么办?
    • @AlwaysSunny - 是的,toPandas 还会收集驱动节点上的所有数据,除非数据集很小,否则应避免使用。
    • 没有运气同样的问题发生
    • @AlwaysSunny - 是的,这并不奇怪,当您尝试收集大型数据集时,这是一个常见错误。随意问另一个问题,解释你想用字典完成什么,我可以看看是否有另一种方法可以用另一种方法解决你的问题。
    【解决方案4】:

    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,请使用另一个带有 collect_list 的 agg。

    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"]}|
    #+---------------------------------------------------------------------------------------------------------+
    

    【讨论】:

    • 感谢先生的详尽回答。
    • 我使用的是 spark 3.0,所以我可以使用 collect_list
    • 只是一个简单的问题,当我尝试使用 df.toPandas() 将我的 pyspark 数据帧转换为 pandas 时发生了同样的错误,在这种情况下我该怎么办?
    • @AlwaysSunny,尽量避免转换为 pandas 数据帧,如果可能的话,在内置函数中使用 spark 来获得预期的输出。 pandas 收集数据并导致驱动程序故障。 stackoverflow.com/questions/39811054/…
    • 没有运气同样的问题发生
    猜你喜欢
    • 2020-11-16
    • 2013-01-24
    • 2019-03-06
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 2021-02-13
    相关资源
    最近更新 更多