web-dev-qa-db-ja.com

Spark SQLで複数の列をピボットする方法は?

Pysparkデータフレームで複数の列をピボットする必要があります。サンプルデータフレーム、

 >>> d = [(100,1,23,10),(100,2,45,11),(100,3,67,12),(100,4,78,13),(101,1,23,10),(101,2,45,13),(101,3,67,14),(101,4,78,15),(102,1,23,10),(102,2,45,11),(102,3,67,16),(102,4,78,18)]
>>> mydf = spark.createDataFrame(d,['id','day','price','units'])
>>> mydf.show()
+---+---+-----+-----+
| id|day|price|units|
+---+---+-----+-----+
|100|  1|   23|   10|
|100|  2|   45|   11|
|100|  3|   67|   12|
|100|  4|   78|   13|
|101|  1|   23|   10|
|101|  2|   45|   13|
|101|  3|   67|   14|
|101|  4|   78|   15|
|102|  1|   23|   10|
|102|  2|   45|   11|
|102|  3|   67|   16|
|102|  4|   78|   18|
+---+---+-----+-----+

今、私は日に基づいて各idの行に価格列を取得する必要がある場合、ピボットメソッドを使用できます。

>>> pvtdf = mydf.withColumn('combcol',F.concat(F.lit('price_'),mydf['day'])).groupby('id').pivot('combcol').agg(F.first('price'))
>>> pvtdf.show()
+---+-------+-------+-------+-------+
| id|price_1|price_2|price_3|price_4|
+---+-------+-------+-------+-------+
|100|     23|     45|     67|     78|
|101|     23|     45|     67|     78|
|102|     23|     45|     67|     78|
+---+-------+-------+-------+-------+

したがって、ユニット列も価格として転置する必要がある場合は、上記のようにユニット用のデータフレームをもう1つ作成してから、idを使用して両方を結合する必要があります。 、

>>> def pivot_udf(df,*cols):
...     mydf = df.select('id').drop_duplicates()
...     for c in cols:
...        mydf = mydf.join(df.withColumn('combcol',F.concat(F.lit('{}_'.format(c)),df['day'])).groupby('id').pivot('combcol').agg(F.first(c)),'id')
...     return mydf
...
>>> pivot_udf(mydf,'price','units').show()
+---+-------+-------+-------+-------+-------+-------+-------+-------+
| id|price_1|price_2|price_3|price_4|units_1|units_2|units_3|units_4|
+---+-------+-------+-------+-------+-------+-------+-------+-------+
|100|     23|     45|     67|     78|     10|     11|     12|     13|
|101|     23|     45|     67|     78|     10|     13|     14|     15|
|102|     23|     45|     67|     78|     10|     11|     16|     18|
+---+-------+-------+-------+-------+-------+-------+-------+-------+

それを行うのが良い習慣であり、それを行う他のより良い方法があるかどうかについての提案が必要です。前もって感謝します!

17
Suresh

問題の解決策は私が得ることができる最高のものです。唯一の改善は、ダブルスキャンを回避するために入力データセットをcacheすることです。

mydf.cache
pivot_udf(mydf,'price','units').show()
3
Jacek Laskowski

これは、単一のピボットを含む非UDFの方法です(したがって、すべての一意の日付を識別するための単一の列スキャンのみ)。

mydf.groupBy('id').pivot('day').agg(F.first('price').alias('price'),F.first('units').alias('unit'))

結果は次のとおりです(順序と名前が一致しないことをお詫びします)。

+---+-------+------+-------+------+-------+------+-------+------+               
| id|1_price|1_unit|2_price|2_unit|3_price|3_unit|4_price|4_unit|
+---+-------+------+-------+------+-------+------+-------+------+
|100|     23|    10|     45|    11|     67|    12|     78|    13|
|101|     23|    10|     45|    13|     67|    14|     78|    15|
|102|     23|    10|     45|    11|     67|    16|     78|    18|
+---+-------+------+-------+------+-------+------+-------+------+

当日ピボットした後、price列とunit列の両方を集計するだけです。

3
Jedi

spark 1.6バージョンのように、ピボットは1列のみを取り、コードの実行を高速化するその列の個別の値を渡すことができる2番目の属性値があるため、これが唯一の方法だと思いますそれ以外の場合はsparkで実行する必要があるため、そうするのが正しい方法です。

2