2018/11/10/更新
ピボット(PIVOT)は、Apache Spark 1.6 から DataFrame 機能として実装されています。一意の値を1 つの列から複数の個々の列に変換し、テーブル値式を回転させる機 能です。
Apache Spark 2.4 リリースでは、この便利なピボット機能を SQL でも利用できるようになりました。このブログでは、気温のデータを対象に、SQL の PIVOT で複雑なデータ変換を容易に行う方法を解説します。UNION ALL や Spark SQL とはどんなものなのかもあわせてご紹介しています。
ピボットで夏の気温を比較する
シアトルではこの夏は普段とは異なり、7月に 9日間、最高気温が 80℉(約27℃)から 90℉ (約32℃)に達し、不快なレベルにまで上昇しました。
日付 | 気温 (°F) |
---|---|
7/22/2018 | 86 |
7/23/2018 | 90 |
7/24/2018 | 91 |
7/25/2018 | 92 |
7/26/2018 | 92 |
7/27/2018 | 88 |
7/28/2018 | 85 |
7/29/2018 | 94 |
7/30/2018 | 89 |
まず、過去に同様の気温上昇傾向があったかどうかを調べてみましょう。この場合、数値を調べて提示する直感的な方法の 1 つは、列に月を指定、行に年を指定して、各月の平均最高気温を表示させることです。そうすることで、隣り合う月の温度が横に、異なる年の温度が縦になり、両方向での比較が容易になります。
現在、データブリックスの Spark SQL (Spark SQL とは、構造化データ処理のための Spark モジュールのことです。DataFrames と呼ばれるプログラミングの抽象化が 可能で、分散型 SQL クエリエンジンとしても機能します。)ではPIVOT
構文をサポートしているので、次の SQL クエリでこれを実現することができます。
上記のクエリは次のような結果を生成します:
YEAR | JAN | FEB | MAR | APR | MAY | JUNE | JULY | AUG | SEPT | OCT | NOV | DEC |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2018 | 49.7 | 45.8 | 54.0 | 58.6 | 70.8 | 71.9 | 82.8 | 79.1 | NULL | NULL | NULL | NULL |
2017 | 43.7 | 46.6 | 51.6 | 57.3 | 67.0 | 72.1 | 78.3 | 81.5 | 73.8 | 61.1 | 51.3 | 45.6 |
2016 | 49.1 | 53.6 | 56.4 | 65.9 | 68.8 | 73.1 | 76.0 | 79.5 | 69.6 | 60.6 | 56.0 | 41.9 |
2015 | 50.3 | 54.5 | 57.9 | 59.9 | 68.0 | 78.9 | 82.6 | 79.0 | 68.5 | 63.6 | 49.4 | 47.1 |
気温上昇が大きかった年とそうではない年があることがわかります。2016年は、どちらかというと省エネルギーの年だったようです。
SQL でのピボット(PIVOT)テーブル
このクエリを詳しくみて機能についての理解を深めましょう。まず、ピボットの入力である FROM
句、つまりピボットを実行するベースとなるテーブルまたはサブクエリを指定する必要があります。わたしたちは、年、月、および高い気温を調べたいので、これらがサブクエリに表示されるフィールドになります。
次に、クエリのもう 1 つの重要な部分である PIVOT
句について考えてみます。PIVOT
句の最初の引数は、集計関数と集計される列です。次に、FOR
サブ句のピボット列を 2 番目の引数として指定し、最後の引数としてピボット列の値を含む IN
演算子を指定します。
ピボット列はテーブルを回転するポイントで、ピボット列の値は出力テーブルの列に転置されます。また、IN
句を使用すると、ピボット値ごとにエイリアスも指定でき、より意味のある列名を容易に生成できます。
SQL ピボットに関する重要な考え方は、ピボット列と共に暗黙的なgroup-by
列のリストをベースにグループ化された集計を実行することです。暗黙的なgroup-by
列は、どの集計関数にもピボット列としても表示されないFROM
句の列です。
上記のクエリでは、ピボット列が month列、暗黙的な group-by
列が year 列である場合、式avg(temp
) は、(year, month)
の各個別値ペアに集計され、ここでは、monthは指定されたピボット列の値の 1 つに等しくなります。その結果、このそれぞれの集計値は、year
行と month列
の対応するセルにマップされます。
注意すべきは、この暗黙的なgroup-by
が原因で、ピボット出力の一部にしたくない列をFROM
句から省くことを確認する必要があることで、そうしないとクエ リが不要な結果を生成してしまいます。
複数の集計式の指定
上記の例では、PIVOT
句で使用されている集計式は 1 つだけ示されていますが、実際には、ユーザーは必要に応じて複数の集計式を指定できます。繰り返しますが、上記の気象データでは、6月から9月までの高温の平均(avg)と併せて最高気温(max)を一覧表示することができます。
複数の集計式の場合、列はピボット列の値のデカルト積と集計式で、名前は<value>_<aggExpr>
となります。
year | JUN_avg | JUN_max | JUL_avg | JUL_max | AUG_avg | AUG_max | SEP_avg | SEP_max |
---|---|---|---|---|---|---|---|---|
2018 | 71.9 | 88 | 82.8 | 94 | 79.1 | 94 | NULL | NULL |
2017 | 72.1 | 96 | 78.3 | 87 | 81.5 | 94 | 73.8 | 90 |
2016 | 73.1 | 93 | 76.0 | 89 | 79.5 | 95 | 69.6 | 78 |
2015 | 78.9 | 92 | 82.6 | 95 | 79.0 | 92 | 68.5 | 81 |
列のグループ化とピボット列の比較
次に、この毎日の低い気温の表から気温傾向の調査に低い気温を含めます。
日付 | 気温 (°F) |
---|---|
… | … |
8/1/2018 | 59 |
8/2/2018 | 58 |
8/3/2018 | 59 |
8/4/2018 | 58 |
8/5/2018 | 59 |
8/6/2018 | 59 |
… | … |
このテーブルを前に作成した高い気温の表と組み合わせるには、「日付」列でこれらの 2 つのテーブルを結合します。とはいえ、日付にグループ化を実行するピボットを使用するので、単にUNION ALL
(UNION ALL とは、2 つの表やテーブルを縦に結合させる操作のことで、重複行についてはそのまま全て表示します。)を使用して 2 つのテーブルを連結するだけです。後でわかりますが、このアプローチで互換性も向上します。
それでは、新しい結合テーブルを使用してピボットクエリを試してみましょう。
その結果、過去4年間の各月の高い気温の平均と低い気温の平均が1つのテーブルに表示されます。ピボットクエリに flag
列を含める必要があることに注意しないと、avg(temp)
式は高い気温と低い気温の混合がベースになってしまいます。
year | H/L | JUN | JUL | AUG | SEP |
---|---|---|---|---|---|
2018 | H | 71.9 | 82.8 | 79.1 | NULL |
2018 | L | 53.4 | 58.5 | 58.5 | NULL |
2017 | H | 72.1 | 78.3 | 81.5 | 73.8 |
2017 | L | 53.7 | 56.3 | 59.0 | 55.6 |
2016 | H | 73.1 | 76.0 | 79.5 | 69.9 |
2016 | L | 53.9 | 57.6 | 59.9 | 52.9 |
2015 | H | 78.9 | 82.6 | 79.0 | 68.5 |
2015 | L | 56.4 | 59.9 | 58.5 | 52.5 |
お気づきのとおり、各年に行が2つ作成され、気温の高い行(H)と低い行(L)に分かれています。これは、ピボット入力にもう 1つの列、flag
を追加したためです。この列は元のyear
列に加えて別の暗黙的なグループ化列になります。
もう 1つの方法として、グループ化列ではなく、flag
をピボット列として使用することもできます。これで、month
、flag
の2つのピボット列ができました。
このクエリでは、各年は 1 行ですが、各月では高(hi)/低(lo)2 列ずつ表示され、同じデータの異なるレイアウトを表示できます。
year | JUN_hi | JUN_lo | JUL_hi | JUL_lo | AUG_hi | AUG_lo | SEP_hi | SEP_lo |
---|---|---|---|---|---|---|---|---|
2018 | 71.9 | 53.4 | 82.8 | 58.5 | 79.1 | 58.5 | NULL | NULL |
2017 | 72.1 | 53.7 | 78.3 | 56.3 | 81.5 | 59.0 | 73.8 | 55.6 |
2016 | 73.1 | 53.9 | 76.0 | 57.6 | 79.5 | 57.9 | 69.6 | 52.9 |
2015 | 78.9 | 56.4 | 82.6 | 59.9 | 79.0 | 58.5 | 68.5 | 52.5 |
次のステップ
このブログで使用されているクエリの例を実行するには、この付属 Notebook のピボット SQL のサンプルを参照してください。
SQL のピボット機能の使い方、UNION ALL や Spark SQL とはどんなものなのか知りたい方にとって参考になれば幸いです。
Apache Spark コミュニティに貢献していただいている皆様に感謝します。