##はじめに
SparkDfで、DFのあるカラムついて横持ちから縦持ちにする場合、ちょっと手間取ったので備忘録をかねて記録しておきます。
一応、ソースコードはScalaですがPythonとかでもそんなに変わらないはずです。
##データフレーム操作
###テストDataFrameの作成
val testDf = Seq(
(1,"a",30,20),
(2,"a",40,30),
(3,"c",50,40),
(4,"d",60,50),
(2,"d",40,70),
(2,"d",20,10),
(1,"a",60,90)
)
.toDF("Column1", "Column2", "Column3","Column4")
/*
+-------+-------+-------+-------+
|Column1|Column2|Column3|Column4|
+-------+-------+-------+-------+
| 1| a| 30| 20|
| 2| a| 40| 30|
| 3| c| 50| 40|
| 4| d| 60| 50|
| 2| d| 40| 70|
| 2| d| 20| 10|
| 1| a| 60| 90|
+-------+-------+-------+-------+
*/
##縦持ち → 横持ち
縦持ち→横持ちは簡単で、pivot使えば簡単にできます。
使う際は「df.groupby().pivot().count()」のように3つのメソッドを用いる必要があります。
val test2Df = testDf
.groupBy("Column1")
.pivot("Column2") //横持ちにしたいカラム
.count() //集計関数
.na.fill(0) //ゼロ埋め
/*
+-------+---+---+---+
|Column1| a| c| d|
+-------+---+---+---+
| 1| 2| 0| 0|
| 3| 0| 1| 0|
| 4| 0| 0| 1|
| 2| 1| 0| 2|
+-------+---+---+---+
*/
##横持ち → 縦持ち
本題で、test2Dfがあったとして、何かの事情で縦持ちにしたい場合。
横持ち → 縦持ち はpivotのように一発でできる機能はなくて、ちょっと一手間?加えます。
使うものは、「selectExpr()」と「stack」です。
ソースコードは以下の通り。
val test3Df = test2Df
.selectExpr(
"Column1",//縦持ちの基準となる対象列
"stack(3,'a',a,'c',c,'d',d)".//縦持ちとなる元
)
/*
+-------+----+----+
|Column1|col0|col1|
+-------+----+----+
| 1| a| 2|
| 1| c| 0|
| 1| d| 0|
| 3| a| 0|
| 3| c| 1|
| 3| d| 0|
| 4| a| 0|
| 4| c| 0|
| 4| d| 1|
| 2| a| 1|
| 2| c| 0|
| 2| d| 2|
+-------+----+----+
*/
という風に変形できます。最初に、縦持ちの基準となる対象列を指定してあげて、次にstack()で、縦持ちとなる元を指定します。上の場合だと、Column1に対して、a,c,dを縦持ち化しています。stack()の3は縦持ちの個数(この場合a,c,dの3つ)、"a" "c" "d"は名前、a c dは、元のその列名の数値を参照しています(単純に"name1"とか文字列を入れるみたいなこともできます)。また、列名がDefaultではcol0とかになってますので、変更したい場合は、**.withColumnRenamed("col0","〇〇")**とかを使うと変更できます。
あとは、joinをうまく使ってやると、縦持ちから横持ちへの変更ができるんじゃないかなと思います。