我加载数据集
val data = sc.textFile("/home/kybe/Documents/datasets/img.csv",defp)
我想在这个数据上加上索引
val nb = data.count.toInt
val tozip = sc.parallelize(1 to nb).repartition(data.getNumPartitions)
val res = tozip.zip(data)
不幸的是我有以下错误
Can only zip RDDs with same number of elements in each partition
如果可能的话,如何按分区修改元素数?
答案 0 :(得分:8)
zip()州的文档:
将此RDD与另一个RDD一起切换,返回键值对,每个RDD中的第一个元素,每个RDD中的第二个元素等。假设两个RDD具有相同数量的分区和每个分区中相同数量的元素(例如,一个是通过另一个地图制作的。)
所以我们需要确保我们满足两个条件:
您确保与repartition()
具有相同数量的分区,但Spark并不保证每个RDD的每个分区都具有相同的分布。
因为有不同类型的RDD,并且大多数都有不同的分区策略!例如:
sc.parallelize(collection)
并行化集合时,会创建ParallelCollectionRDD,它将查看应该有多少分区,将检查集合的大小并计算step
大小。即你在列表中有15个元素,想要4个分区,前3个将有4个连续的元素,最后一个将剩下的3个。<Long, Text>
的RDD对,您只需要String
: - )在您的示例中,Spark内部会在进行重新分区时创建不同类型的RDD(CoalescedRDD
和ShuffledRDD
),但我认为您已经了解了不同的RDD具有不同的分区策略< / strong>: - )
请注意,zip()
doc的最后一部分提到了map()
操作。此操作不会重新分区,因为它是窄转换数据,因此 可以保证这两种条件。
在这个简单的例子中,你可以简单地data.zipWithIndex
来做。如果您需要更复杂的内容,则应使用zip()
创建map()
的新RDD,如上所述。
答案 1 :(得分:0)
我通过创建一个像这样的隐式助手来解决了这个问题
implicit class RichContext[T](rdd: RDD[T]) {
def zipShuffle[A](other: RDD[A])(implicit kt: ClassTag[T], vt: ClassTag[A]): RDD[(T, A)] = {
val otherKeyd: RDD[(Long, A)] = other.zipWithIndex().map { case (n, i) => i -> n }
val thisKeyed: RDD[(Long, T)] = rdd.zipWithIndex().map { case (n, i) => i -> n }
val joined = new PairRDDFunctions(thisKeyed).join(otherKeyd).map(_._2)
joined
}
}
然后可以像这样使用
val rdd1 = sc.parallelize(Seq(1,2,3))
val rdd2 = sc.parallelize(Seq(2,4,6))
val zipped = rdd1.zipShuffle(rdd2) // Seq((1,2),(2,4),(3,6))
注意::请记住,join
会造成随机播放。