web-dev-qa-db-fra.com

Comment sélectionner une série d'éléments dans Spark RDD?

J'aimerais sélectionner une gamme d'éléments dans un RDD Spark. Par exemple, j'ai un RDD avec une centaine d'éléments et je dois sélectionner des éléments compris entre 60 et 80. Comment procéder?

Je vois que RDD a une méthode take (i: int), qui retourne les premiers éléments. Mais il n'y a pas de méthode correspondante pour prendre les i derniers éléments, ou i du milieu à partir d'un certain index.

24
PlinyTheElder

Je ne pense pas qu'il existe une méthode efficace pour le faire pour le moment. Mais le moyen le plus simple consiste à utiliser filter(), disons que vous avez un RDD, pairs avec des paires valeur/clé et que vous ne voulez que des éléments de 60 à 80 inclus. 

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

Je pense qu'il est possible que cela soit fait plus efficacement à l'avenir, en utilisant sortByKey et en sauvegardant des informations sur la plage de valeurs mappées sur chaque partition. Gardez à l'esprit que cette approche ne permettrait d'économiser rien si vous envisagiez d'interroger la plage plusieurs fois, car le tri est évidemment coûteux. 

En regardant la source d’étincelles, il serait certainement possible de faire des requêtes de plage efficaces en utilisant RangePartitioner:

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

Ceci est un membre privé de RangePartitioner avec la connaissance de toutes les limites supérieures des partitions, il serait facile de n’interroger que les partitions nécessaires. Il semblerait que cela suscite l’incitation des utilisateurs à l’avenir: SPARK-911

UPDATE: Meilleure réponse, basée sur la demande de tirage que j'écris pour SPARK-911. Il fonctionnera efficacement si le RDD est trié et que vous l’interrogez à plusieurs reprises.

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")

Si toute la partition en mémoire est acceptable, vous pouvez même faire quelque chose comme ceci.
val glommedAndCached = sorted.glom()cache(); glommedAndCached.map(a => a.slice(a.search(lower),a.search(upper)+1)).collect()

search n'est pas un membre BTW Je viens de créer une classe implicite qui possède une fonction de recherche binaire, non illustrée ici

12
aaronman

Quelle est la taille de votre ensemble de données? Vous pourrez peut-être faire ce dont vous avez besoin avec:

data.take(80).drop(59)

Cela semble inefficace, mais devrait fonctionner pour les données de taille petite à moyenne.

Est-il possible de résoudre ce problème d'une autre manière? Quel est le cas pour choisir exactement une certaine plage du milieu de vos données? takeSample vous servirait-il mieux?

6
DPM

La suite devrait pouvoir obtenir la gamme. Notez que le cache vous évitera une surcharge, car en interne, zipWithIndex doit analyser la partition RDD pour obtenir le nombre d'éléments dans chaque partition.

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
5
zhang zhan

Pour ceux qui tombent sur cette question à la recherche d’une réponse compatible avec Spark 2.x, vous pouvez utiliser filterByRange

0
jrook