Apache Spark RDD过滤成两个RDD

时间:2015-04-09 19:24:00

标签: apache-spark rdd

我需要将RDD分成两部分:

满足条件的1部分;另一部分没有。我可以在原始RDD上执行filter两次但看起来效率低下。有没有办法可以做我以后的事情?我无法在API和文献中找到任何内容。

5 个答案:

答案 0 :(得分:19)

Spark默认不支持此功能。如果您事先对其进行缓存,则对相同数据进行两次过滤并不是那么糟糕,并且过滤本身很快。

如果它只是两种不同的类型,您可以使用辅助方法:

implicit class RDDOps[T](rdd: RDD[T]) {
  def partitionBy(f: T => Boolean): (RDD[T], RDD[T]) = {
    val passes = rdd.filter(f)
    val fails = rdd.filter(e => !f(e)) // Spark doesn't have filterNot
    (passes, fails)
  }
}

val (matches, matchesNot) = sc.parallelize(1 to 100).cache().partitionBy(_ % 2 == 0)

但是只要您有多种类型的数据,只需将过滤分配给新的val。

答案 1 :(得分:3)

Spark RDD没有这样的api。

以下是基于pull request for rdd.span的版本,该版本应该有效:

import scala.reflect.ClassTag
import org.apache.spark.rdd._

def split[T:ClassTag](rdd: RDD[T], p: T => Boolean): (RDD[T], RDD[T]) = {

    val splits = rdd.mapPartitions { iter =>
        val (left, right) = iter.partition(p)
        val iterSeq = Seq(left, right)
        iterSeq.iterator
    }

    val left = splits.mapPartitions { iter => iter.next().toIterator}

    val right = splits.mapPartitions { iter => 
        iter.next()
        iter.next().toIterator
    }
    (left, right)
}

val rdd = sc.parallelize(0 to 10, 2)

val (first, second) = split[Int](rdd, _ % 2 == 0 )

first.collect
// Array[Int] = Array(0, 2, 4, 6, 8, 10)

答案 2 :(得分:3)

重点是,你不想做一个过滤器,而是一张地图。

(T) -> (Boolean, T)

抱歉,我在Scala语法中效率低下。但这个想法是你通过将答案集映射到键/值对来拆分答案集。 Key可以是一个布尔值,表示它是否正在传递'Filter'谓词。

答案 3 :(得分:0)

如果您使用T而不是RDD[T],那么您可以do this。否则,你可能会做这样的事情:

val data = sc.parallelize(1 to 100)
val splitData = data.mapPartitions{iter => {
    val splitList = (iter.toList).partition(_%2 == 0)
    Tuple1(splitList).productIterator
  }
}.map(_.asInstanceOf[Tuple2[List[Int],List[Int]]])

然后,当你去执行一个动作时,你可能需要减少这个以合并列表

答案 4 :(得分:0)

您可以使用subtract function(如果过滤操作过于昂贵)。

PySpark代码:

rdd1 = data.filter(filterFunction)

rdd2 = data.subtract(rdd1)