如何在加入操作之前转换DataFrame?

时间:2018-01-27 21:14:43

标签: scala apache-spark spark-dataframe

以下代码用于从列products中提取排名。排名是每对[...]中的第二个数字。例如,在给定示例[[222,66],[333,55]]中,对于具有PK 6655的产品,排名为222333。 但是当df_products大约800 Mb时,Spark 2.2中的代码运行得非常慢:

df_products.createOrReplaceTempView("df_products")

val result = df.as("df2")
               .join(spark.sql("SELECT * FROM df_products")
               .select($"product_PK", explode($"products").as("products"))
               .withColumnRenamed("product_PK","product_PK_temp").as("df1"),$"df2.product               _PK" === $"df1.product_PK_temp" and $"df2.rec_product_PK" === $"df1.products.product_PK", "left")
               .drop($"df1.product_PK_temp")
               .select($"product_PK", $"rec_product_PK", coalesce($"df1.products.col2", lit(0.0)).as("rank_product"))

这是df_productsdf的一小部分样本:

df_products =

+----------+--------------------+
|product_PK|            products|
+----------+--------------------+
|       111|[[222,66],[333,55...|
|       222|[[333,24],[444,77...|
...
+----------+--------------------+

df =

+----------+-----------------+                 
|product_PK|   rec_product_PK|
+----------+-----------------+
|       111|              222|
|       222|              888|
+----------+-----------------+

products的每一行中的数组包含少量元素时,上面给出的代码很有效。但是当每行[[..],[..],...]的数组中有很多元素时,代码似乎会卡住并且不会前进。

如何优化代码?任何帮助都非常感谢。

例如,可以在加入之前将df_products转换为以下DataFrame吗?

df_products =

+----------+--------------------+------+
|product_PK|      rec_product_PK|  rank|
+----------+--------------------+------+
|       111|                 222|    66|
|       111|                 333|    55|
|       222|                 333|    24|
|       222|                 444|    77|
...
+----------+--------------------+------+

1 个答案:

答案 0 :(得分:1)

根据我的回答here,您可以使用以下内容转换df_products:

import org.apache.spark.sql.functions.explode
df1 = df.withColumn("array_elem", explode(df("products"))
df2 = df1.select("product_PK", "array_elem.*")

这假定产品是结构数组。如果products是数组数组,则可以使用以下代码:

df2 = df1.withColumn("rank", df2("products").getItem(1))