如何在每个分区中不同数量元素的两个RDD上执行类似zip的操作?

时间:2014-09-27 15:39:45

标签: java apache-spark

我正在使用Spark 1.1.0。

我有2个类型为firstSample的RDD secondSampleJavaRDD<IndividualBean>。这些RDD的内容如下:

[
IndividualBean [params={...}], 
IndividualBean [params={...}], 
IndividualBean [params={...}]
]

[
IndividualBean [params={...}], 
IndividualBean [params={...}], 
IndividualBean [params={...}]
]

当我尝试zip他们在一起时,我收到以下错误:

  

只能在每个分区中压缩具有相同元素数量的RDD

我想这是因为我的RDD没有相同数量的分区,或者每个分区的元素数量相同。

我想对这些RDD执行操作,这会给我与zip相同的结果。

现在,我找到了以下解决方案(totalSize变量的大小只有firstSample.union(secondSample)):

JavaPairRDD<IndividualBean, IndividualBean> zipped = firstSample.union(secondSample).zipWithIndex().mapToPair(
            new PairFunction<Tuple2<IndividualBean,Long>, Long, IndividualBean>() {
                @Override
                public Tuple2<Long, IndividualBean> call(
                        Tuple2<IndividualBean, Long> tuple) throws Exception {
                    return new Tuple2<Long, IndividualBean>(tuple._2, tuple._1);
                }
    }).groupBy(new Function<Tuple2<Long,IndividualBean>, Long>() {
        @Override
        public Long call(Tuple2<Long, IndividualBean> tuple) throws Exception {
            long index = tuple._1.longValue();
            if(index < totalSize/2){
                return index+totalSize/2;
            }
            return index;
        }
    }).values().mapToPair(new PairFunction<Iterable<Tuple2<Long, IndividualBean>>, IndividualBean, IndividualBean>() {
        @Override
        public Tuple2<IndividualBean, IndividualBean> call(
                Iterable<Tuple2<Long, IndividualBean>> iterable) throws Exception {
            Iterator<Tuple2<Long, IndividualBean>> it = iterable.iterator();
            IndividualBean firstBean = it.next()._2;
            IndividualBean secondBean = it.next()._2;
            return new Tuple2<IndividualBean, IndividualBean>(firstBean, secondBean);
        }
    });

但它很贵,因为它涉及改组。

这可能是一个更好的方法吗?

1 个答案:

答案 0 :(得分:0)

Scala中的一个解决方案,因为我的所有Spark编程都是如此。

此解决方案的关键是始终保持相同的分区方案,然后将各个分区压缩在一起。为了实现这一点,解决方案通过采样快速而松散地进行特别是,与每个随机选择的点配对的数据点是:

  1. 从同一分区中选择
  2. 不是随意选择的(实际上往往会在原始RDD中紧挨着它)
  3. 这些简化中的第一个对于解决方案至关重要。可以通过向下面定义的zipFunc添加一些代码来删除第二个,以重新排序zip的一侧。

    了解zipFunc的作用非常重要:我将样本及其补码压缩在一起,这些甚至不是相同的大小。我简单地压缩了两个RDD中相应分区的内容,即使它们没有相同数量的样本:当我在zip的一侧用完样本时,我只是放下另一侧的样本。

    val testRDD = sc.parallelize(1 to 1000, 4)
    
    val firstSample = testRDD.sample(false, 0.4)
    val remaining = testRDD.subtract(firstSample)
    
    def zipFunc(l: Iterator[Int], r: Iterator[Int]) : Iterator[(Int,Int)] = {
      val res = new ListBuffer[(Int, Int)]
      // exercise for the reader: suck either l or r into a container before iterating 
      // and consume it in random order to achieve more random pairing if desired
      while (l.hasNext && r.hasNext) {
        res += ((l.next(), r.next()))
      }
      res.iterator
    }
    // notice the `true` to make sure partitioning is preserved
    val pairs:RDD[(Int,Int)] = firstSample.zipPartitions(remaining, true)(zipFunc)
    

    据我所知,这不需要跨分区通信。它取决于您的样品 在整个分区中相当均匀地绘制,根据我的经验,sample()方法在这方面并不算太差。