web-dev-qa-db-fra.com

Agrégat RDD dans spark

Je suis un Apache Spark apprenant et je suis tombé sur une action RDDaggregate dont je n'ai aucune idée de son fonctionnement. Quelqu'un peut-il épeler et expliquer en détail étape par étape comment sommes-nous arrivés au résultat ci-dessous pour le code ici

RDD input = {1,2,3,3}

RDD Aggregate function :

rdd.aggregate((0, 0))
((x, y) =>
(x._1 + y, x._2 + 1),
(x, y) =>
(x._1 + y._1, x._2 + y._2))

output : {9,4}

Merci

15
Lijju Mathew

Si vous n'êtes pas sûr de ce qui se passe, il est préférable de suivre les types. En omettant ClassTag implicite par souci de concision, nous commençons par quelque chose comme ça

abstract class RDD[T] extends Serializable with Logging 

def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U): U 

Si vous ignorez tous les paramètres supplémentaires, vous verrez que aggregate est une fonction qui mappe de RDD[T] À U. Cela signifie que le type des valeurs dans l'entrée RDD ne doit pas nécessairement être le même que le type de la valeur de sortie. C'est donc clairement différent de par exemple reduce:

def reduce(func: (T, T) ⇒ T): T 

ou fold:

def fold(zeroValue: T)(op: (T, T) => T): T

Comme fold, aggregate nécessite un zeroValue. Comment le choisir? Il doit s'agir d'un élément d'identité (neutre) par rapport à combOp.

Vous devez également fournir deux fonctions:

  • seqOp qui mappe de (U, T) à U
  • combOp qui mappe de (U, U) à U

Sur la base de ces signatures, vous devriez déjà voir que seul seqOp peut accéder aux données brutes. Il prend une valeur de type U une autre de type T et renvoie une valeur de type U. Dans votre cas, c'est une fonction avec la signature suivante

((Int, Int), Int) => (Int, Int) 

À ce stade, vous pensez probablement qu'il est utilisé pour une sorte d'opération de type pli.

La deuxième fonction prend deux arguments de type U et retourne une valeur de type U. Comme indiqué précédemment, il doit être clair qu'il ne touche pas aux données d'origine et ne peut fonctionner que sur les valeurs déjà traitées par le seqOp. Dans votre cas, cette fonction a une signature comme suit:

((Int, Int), (Int, Int)) => (Int, Int) 

Alors, comment pouvons-nous réunir tout cela?

  1. Tout d'abord, chaque partition est agrégée à l'aide de la norme Iterator.aggregate avec zeroValue, seqOp et combOp passée comme z, seqop et combop respectivement. Puisque InterruptibleIterator utilisé en interne ne remplace pas aggregate il doit être exécuté comme un simple foldLeft(zeroValue)(seqOp)

  2. Les résultats partiels suivants collectés à partir de chaque partition sont agrégés à l'aide de combOp

Supposons que l'entrée RDD possède trois partitions avec la distribution de valeurs suivante:

  • Iterator(1, 2)
  • Iterator(2, 3)
  • Iterator()

Vous pouvez vous attendre à ce que l'exécution, en ignorant l'ordre absolu, soit équivalente à quelque chose comme ceci:

val seqOp = (x: (Int, Int), y: Int) => (x._1 + y, x._2 + 1)
val combOp = (x: (Int, Int), y: (Int, Int)) => (x._1 + y._1, x._2 + y._2)

Seq(Iterator(1, 2), Iterator(3, 3), Iterator())
  .map(_.foldLeft((0, 0))(seqOp))
  .reduce(combOp)

foldLeft pour une seule partition peut ressembler à ceci:

Iterator(1, 2).foldLeft((0, 0))(seqOp)
Iterator(2).foldLeft((1, 1))(seqOp)
(3, 2)

et sur toutes les partitions

Seq((3,2), (6,2), (0,0))

qui combiné vous donnera le résultat observé:

(3 + 6 + 0, 2 + 2 + 0)
(9, 4)

En général, c'est un modèle courant que vous trouverez partout Spark où vous passez une valeur neutre, une fonction utilisée pour traiter les valeurs par partition et une fonction utilisée pour fusionner des agrégats partiels de différentes partitions. les exemples comprennent:

  • aggregateByKey
  • Fonctions d'agrégation définies par l'utilisateur
  • Aggregators on Spark Datasets.
23
zero323

Voici ma compréhension pour votre référence:

Imaginez que vous avez deux nœuds, l'un prend l'entrée des deux premiers éléments de la liste {1,2} et l'autre prend {3, 3}. (La partition ici est uniquement pour plus de commodité)

Au premier nœud: "(x, y> (x._1 + y, x._2 + 1)) ==", le premier x est (0,0) comme indiqué, et y est votre premier élément 1, et vous aurez la sortie (0 + 1, 0 + 1), puis vient votre deuxième élément y = 2, et la sortie (1 + 2, 1 + 1), qui est (3, 2)

Au deuxième nœud, la même procédure se déroule en parallèle, et vous aurez (6, 2).

"(x, y> (x._1 + y._1, x._2 + y._2)) ==", vous indique de fusionner deux nœuds, et vous obtiendrez (9,4 )


une chose à noter est que (0,0) est en fait ajouté à la longueur du résultat (rdd) +1 fois.

" scala> rdd.aggregate ((1,1)) ((x, y) => (x._1 + y, x._2 + 1), (x, y) = > (x._1 + y._1, x._2 + y._2)) res1: (Int, Int) = (14,9) "

4
Yuanxu Xu