私はこのようなRDDを持っています:
1 2 3
4 5 6
7 8 9
マトリックスです。ここで、RDDを次のように転置したいと思います。
1 4 7
2 5 8
3 6 9
これどうやってするの?
N×M行列があるとします。
NとMの両方が小さすぎてメモリにN×Mのアイテムを保持できる場合、RDDを使用しても意味がありません。しかし、移調は簡単です。
val rdd = sc.parallelize(Seq(Seq(1, 2, 3), Seq(4, 5, 6), Seq(7, 8, 9)))
val transposed = sc.parallelize(rdd.collect.toSeq.transpose)
NまたはMが大きすぎてメモリにNまたはMエントリを保持できない場合は、このサイズのRDD行を使用できません。この場合、元の行列または転置された行列のいずれかを表すことは不可能です。
NとMは中程度のサイズである可能性があります。メモリにNまたはMエントリを保持できますが、N×Mエントリを保持することはできません。この場合、マトリックスを爆破し、再び組み立てる必要があります。
val rdd = sc.parallelize(Seq(Seq(1, 2, 3), Seq(4, 5, 6), Seq(7, 8, 9)))
// Split the matrix into one number per line.
val byColumnAndRow = rdd.zipWithIndex.flatMap {
case (row, rowIndex) => row.zipWithIndex.map {
case (number, columnIndex) => columnIndex -> (rowIndex, number)
}
}
// Build up the transposed matrix. Group and sort by column index first.
val byColumn = byColumnAndRow.groupByKey.sortByKey().values
// Then sort by row index.
val transposed = byColumn.map {
indexedRow => indexedRow.toSeq.sortBy(_._1).map(_._2)
}
Collect()を使用しない最初のドラフト。すべてがワーカー側で実行され、ドライバーでは何も実行されません。
val rdd = sc.parallelize(Seq(Seq(1, 2, 3), Seq(4, 5, 6), Seq(7, 8, 9)))
rdd.flatMap(row => (row.map(col => (col, row.indexOf(col))))) // flatMap by keeping the column position
.map(v => (v._2, v._1)) // key by column position
.groupByKey.sortByKey // regroup on column position, thus all elements from the first column will be in the first row
.map(_._2) // discard the key, keep only value
このソリューションの問題は、操作が分散システムで実行されると、転置された行列の列がシャッフルされることです。改善されたバージョンを考えます
私の考えでは、マトリックスの各要素に「列番号」を付けるだけでなく、「行番号」も付けます。したがって、例のように列の位置でキーを設定し、キーで再グループ化することはできますが、行番号の各行を並べ替えて、結果から行/列の番号を取り除くことができます。ファイルをRDDにインポートするときに、行番号を知る方法がありません。
各行列要素に列と行の番号を付けるのは重いと思うかもしれませんが、それは入力をチャンクとして分散して処理し、巨大な行列を処理する可能性があるために支払う代償だと思います。
注文の問題の解決策を見つけたときに答えを更新します。
Spark 1.6以降、データの実際の形状に応じて、DataFrameで ピボット操作 を使用できます。あなたはそれをDFあなたは列を行にピボットすることができます、あなたはそれが説明するように次の databricksブログ は非常に役に立ちますコード例を含むいくつかの重要なユースケースの詳細