私はApache Spark学習者であり、RDD
action aggregate
に遭遇しました。ここでコードの以下の結果にどのように到達したのか、ステップごとに詳細に
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}
ありがとう
何が起こっているのかわからない場合は、タイプに従うのが最善です。簡潔にするために暗黙のClassTag
を省略すると、次のようなものから始めます。
_abstract class RDD[T] extends Serializable with Logging
def aggregate[U](zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U): U
_
追加のパラメーターをすべて無視すると、aggregate
は_RDD[T]
_からU
にマップされる関数であることがわかります。これは、入力RDD
の値のタイプが出力値のタイプと同じである必要がないことを意味します。したがって、たとえばreduce
とは明らかに異なります。
_def reduce(func: (T, T) ⇒ T): T
_
またはfold
:
_def fold(zeroValue: T)(op: (T, T) => T): T
_
fold
と同様に、aggregate
にはzeroValue
が必要です。どうやって選ぶの? combOp
に関してアイデンティティ(中立)要素でなければなりません。
また、2つの機能を提供する必要があります。
(U, T)
_からseqOp
にマップするU
(U, U)
_からcombOp
にマップするU
この署名に基づいて、seqOp
のみが未加工データにアクセスできることがすでにわかるはずです。タイプU
の値を取り、タイプT
の別の値を取り、タイプU
の値を返します。あなたの場合、それは次の署名を持つ関数です
_((Int, Int), Int) => (Int, Int)
_
この時点で、おそらく何らかの折り畳み操作に使用されていると思われます。
2番目の関数は、U
型の2つの引数を取り、U
型の値を返します。前に述べたように、元のデータには影響せず、seqOp
によって既に処理された値に対してのみ動作できることは明らかです。あなたの場合、この関数には次のようなシグネチャがあります。
_((Int, Int), (Int, Int)) => (Int, Int)
_
それでは、どうすればそれらすべてをまとめることができますか?
最初に、各パーティションは標準の _Iterator.aggregate
_ を使用して集約され、それぞれzeroValue
、seqOp
およびcombOp
がz
、seqop
およびcombop
として渡されます。内部で使用される InterruptibleIterator
はaggregate
をオーバーライドしないため、単純な foldLeft(zeroValue)(seqOp)
として実行する必要があります。
各パーティションから収集された次の部分的な結果は、combOp
を使用して集約されます
入力RDDに次の値の分布を持つ3つのパーティションがあると仮定します。
Iterator(1, 2)
Iterator(2, 3)
Iterator()
絶対順序を無視した実行は、次のようなものと同等になると期待できます。
_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
は次のようになります。
_Iterator(1, 2).foldLeft((0, 0))(seqOp)
Iterator(2).foldLeft((1, 1))(seqOp)
(3, 2)
_
すべてのパーティションに
_Seq((3,2), (6,2), (0,0))
_
組み合わせた結果は次のとおりです。
_(3 + 6 + 0, 2 + 2 + 0)
(9, 4)
_
一般的に、これは一般的なパターンであり、Spark中立値、パーティションごとの値を処理するために使用される関数、異なるパーティションからの部分集約をマージするために使用される関数。例は次のとおりです。
aggregateByKey
Aggregators
on Spark Datasets
。あなたの参照のための私の理解はここにあります:
2つのノードがあるとします。1つは最初の2つのリスト要素{1,2}の入力を受け取り、もう1つは{3、3}を受け取ります。 (ここのパーティションは便宜上のものです)
最初のノード: "(x、y>(x._1 + y、x._2 + 1))=="、最初のxは与えられた(0,0)であり、yは最初の要素1、出力(0 + 1、0 + 1)があり、2番目の要素y = 2、出力(1 + 2、1 + 1)、つまり(3、2)
2番目のノードでは、同じ手順が並行して行われ、(6、2)が得られます。
"(x、y>(x._1 + y._1、x._2 + y._2))=="、2つのノードをマージするように指示すると、(9,4 )
注目に値するものの1つは、(0,0)が実際に結果の長さ(rdd)+1回に追加されることです。
"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) "