Apache Spark RDD фильтр на два RDDs
Мне нужно разделить RDD на 2 части:
1 часть, которая удовлетворяет условию; другая часть, которая не. Я могу сделать filter
дважды на исходном RDD, но это кажется неэффективным. Есть ли способ сделать то, что мне нужно? Я ничего не могу найти ни в API, ни в литературе.
5 ответов
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.
дело в том, что вы не хотите делать фильтр, а карту.
(T) -> (Boolean, T)
Извините, я неэффективен в синтаксисе Scala. Но идея заключается в том, что вы разделяете свой набор ответов, сопоставляя его с парами ключ/значение. Ключ может быть логическим, указывающим на увядание или нет, он передавал предикат "Filter".
Spark RDD не имеет такого api.
вот версия, основанная на запрос pull для 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)
Если вы в порядке с T
вместо RDD[T]
, можно этого. В противном случае, вы могли бы сделать что-то вроде этого:
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]]])
и, тогда вам, вероятно, нужно будет уменьшить это, чтобы объединить списки, когда вы идете выполнять действие
можно использовать subtract function
(Если операция фильтра слишком дорога).
код PySpark:
rdd1 = data.filter(filterFunction)
rdd2 = data.subtract(rdd1)