我有一个带有架构的数据框如下:
root
|-- category_id: string (nullable = true)
|-- article_title: string (nullable = true)
看起来像这样的数据:
+-----------+--------------------+
|category_id| articletitle |
+-----------+--------------------+
| 1000|HP EliteOne 800 G...|
| 1000|ASUS EB1501P ATM...|
| 1000|HP EliteOne 800 G...|
| 1|ASUS R557LA-XO119...|
| 1|HP EliteOne 800 G...|
+-----------+--------------------+
只有两个不同的category_id
1000和1.
我希望在每个分区上按category_id
和mapPartition
进行重新分区。
p_df = df.repartition(2, "category_id")
p_df.rdd.mapPartitionsWithIndex(some_func)
但是数据没有正确分区,预期的结果是每个mappartition只有一个category_id
的数据。但实际结果是一个分区得到0个记录而另一个得到所有记录。
为什么会发生这种情况以及如何解决这个问题?
关于spark partitioner的工作原理已经有question。我的问题是不同的,因为答案只包含有关分区工作原理的解释,但我的问题是为什么会发生这种情况(已经回答)以及如何解决它。
答案 0 :(得分:4)
您已正确使用repartition
和mapPartitionsWithIndex
功能。
如果您将explain
功能应用为
df.repartition(2, "category_id").explain()
您将看到以下输出,清楚地表明它已重新分区为两个分区。
== Physical Plan ==
Exchange hashpartitioning(category_id#0L, 2)
+- Scan ExistingRDD[category_id#0L,articletitle#1L]
现在真正的罪魁祸首是hashPartitioning,它将1,10,1000,100000 ......视为与分区号 2相同的哈希
解决方案是将分区数更改为3或更多,
或
将category_id
1000更改为其他内容。
答案 1 :(得分:0)
@Ramesh Maharjan在上面的答案中解释了为什么重新分区将所有数据放在一个分区中的原因。有关散列分区的更多信息https://www.gnu.org/software/wget/manual/html_node/Wgetrc-Commands.html
我可以使用自定义分区程序将数据转到不同的分区程序。我将rdd变成了pairRdd格式(category_id,row),并使用了partitionBy方法给出了分区数和custom_partitioner。
categories = input_df.select("category_id").distinct().rdd.map(lambda r: r.category_id).collect()
cat_idx = dict([(cat, idx) for idx, cat in enumerate(categories)])
def category_partitioner(cid):
return cat_idx[cid]