如何在Spark RDD中选择一系列元素?

时间:2014-07-10 12:57:37

标签: apache-spark rdd

我想在Spark RDD中选择一系列元素。例如,我有一个带有一百个元素的RDD,我需要选择60到80之间的元素。我该怎么做?

我看到RDD有一个take(i:int)方法,它返回第一个i元素。但是没有相应的方法可以采用最后的i元素,或者从某个索引开始的中间元素。

4 个答案:

答案 0 :(得分:12)

我认为还没有一种有效的方法可以做到这一点。但是简单的方法是使用filter(),假设你有一个RDD,pairs具有键值对,你只需要60到80之间的元素。

val 60to80 = pairs.filter {
    _ match {
        case (k,v) => k >= 60 && k <= 80
        case _ => false //incase of invalid input
    }
}

我认为通过使用sortByKey并保存有关映射到每个分区的值范围的信息,将来可能会更有效地完成此操作。请记住,如果您计划多次查询范围,这种方法只会保存任何内容,因为排序显然很昂贵。

通过查看火花源,肯定可以使用RangePartitioner进行有效的范围查询:

// An array of upper bounds for the first (partitions - 1) partitions
  private val rangeBounds: Array[K] = {

这是RangePartitioner的私有成员,了解分区的所有上限,只需查询必要的分区即可。看起来这是火花用户未来可能会看到的内容:SPARK-911

更新:基于我正在为SPARK-911写的拉取请求,更好的答案。如果RDD已排序并且您多次查询它,它将有效运行。

val sorted = sc.parallelize((1 to 100).map(x => (x, x))).sortByKey().cache()
val p: RangePartitioner[Int, Int] = sorted.partitioner.get.asInstanceOf[RangePartitioner[Int, Int]];
val (lower, upper) = (10, 20)
val range = p.getPartition(lower) to p.getPartition(upper)
println(range)
val rangeFilter = (i: Int, iter: Iterator[(Int, Int)]) => {
  if (range.contains(i))
    for ((k, v) <- iter if k >= lower && k <= upper) yield (k, v)
  else
    Iterator.empty
}
for((k,v) <- sorted.mapPartitionsWithIndex(rangeFilter, preservesPartitioning = true).collect()) println(s"$k, $v")

如果内存中的整个分区都可以接受,你甚至可以这样做。
val glommedAndCached = sorted.glom()cache(); glommedAndCached.map(a => a.slice(a.search(lower),a.search(upper)+1)).collect()

search不是BTW的成员我刚刚创建了一个具有二进制搜索功能的隐式类,这里没有显示

答案 1 :(得分:6)

您的数据集有多大?您可以通过以下方式完成所需:

data.take(80).drop(59)

这似乎效率低下,但对于中小型数据,应该可行。

有可能以另一种方式解决这个问题吗?从数据中间选择一个特定范围的情况是什么? takeSample会更好地为您服务吗?

答案 2 :(得分:5)

以下应该可以获得范围。请注意,缓存将为您节省一些开销,因为内部zipWithIndex需要扫描RDD分区以获取每个分区中的元素数量。

scala>val r1 = sc.parallelize(List("a", "b", "c", "d", "e", "f", "g"), 3).cache
scala>val r2 = r1.zipWithIndex
scala>val r3 = r2.filter(x=> {x._2>2 && x._2 < 4}).map(x=>x._1)
scala>r3.foreach(println)
d

答案 3 :(得分:0)

对于那些偶然发现这个问题寻找Spark 2.x兼容答案的人,可以使用filterByRange