尽管重新分区,但只能在每个分区中压缩具有相同数量元素的RDD

时间:2016-03-17 09:33:24

标签: scala apache-spark rdd

我加载数据集

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

如果可能的话,如何按分区修改元素数?

2 个答案:

答案 0 :(得分:8)

为什么它不起作用?

zip()州的文档:

  

将此RDD与另一个RDD一起切换,返回键值对,每个RDD中的第一个元素,每个RDD中的第二个元素等。假设两个RDD具有相同数量的分区每个分区中相同数量的元素(例如,一个是通过另一个地图制作的。)

所以我们需要确保我们满足两个条件:

  • 两个RDD具有相同数量的分区
  • 这些RDD中的各个分区具有完全相同的大小

您确保与repartition()具有相同数量的分区,但Spark并不保证每个RDD的每个分区都具有相同的分布。

为什么?

因为有不同类型的RDD,并且大多数都有不同的分区策略!例如:

  • 当您使用sc.parallelize(collection)并行化集合时,会创建ParallelCollectionRDD,它将查看应该有多少分区,将检查集合的大小并计算step大小。即你在列表中有15个元素,想要4个分区,前3个将有4个连续的元素,最后一个将剩下的3个。
  • HadoopRDD如果我没记错的话,每个文件块一个分区。即使您在内部使用本地文件,当您读取本地文件然后映射该RDD时,Spark首先创建这种RDD,因为该RDD是<Long, Text>的RDD对,您只需要String : - )
  • etc.etc。

在您的示例中,Spark内部会在进行重新分区时创建不同类型的RDD(CoalescedRDDShuffledRDD),但我认为您已经了解了不同的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会造成随机播放。