Tips

AthenaでネストされたJSONデータをクロス集計する

S3に配置したJSON形式のアンケート回答データサンプルを、Athenaから参照してクロス集計するSQLを例示します。複数の設問を軸にして、全設問をクロス集計してみます。

クロス集計とは、こんな感じの表のことです。(集計結果は適当に私が入力したものなので参考にしないでください!)
この表でいう軸の設問は、未婚既婚、性別、年代の3つとなります。

あなたはTVを見ますか? 未婚 既婚
男性 女性 男性 女性
20代 30代 40代 20代 30代 40代 20代 30代 40代 20代 30代 40代
全体(人) 130 128 132 126 134 130 127 130 132 126 130 128
よく見る 45% 63% 58% 53% 60% 49%  40%  55%  50%  60%  58%  47%
たまに見る 30% 24% 25% 40% 38% 36%  45%  38%  37%  28%  30%  43%
あまり見ない 25% 13% 17% 8% 2% 15%  15%  7%  13%  12%  12% 10% 

本記事のポイント

  1. Athenaでサブクエリをつかって、Array形式の列を縦持ちに分解する方法
  2. 上記クエリの中で、軸の情報を各設問と紐づける方法

前提(JSON構造)

JSONは以下のような構成になっていて、1行1回答者のレコードとなります。1行の中に10問の設問への回答がARRAY(配列)として格納されています。ここでは軸1=Q1,軸2=Q2,軸3=Q3とします。

{
"id":"654",
"answers":
	[
		{	"question":"Q1",	"choiced":1	},
		{	"question":"Q2",	"choiced":1	},
		{	"question":"Q3",	"choiced":3	},
		{	"question":"Q4",	"choiced":3	},
		{	"question":"Q5",	"choiced":1	},
		{	"question":"Q6",	"choiced":3	},
		{	"question":"Q7",	"choiced":1	},
		{	"question":"Q8",	"choiced":8	},
		{	"question":"Q9",	"choiced":2	},
		{	"question":"Q10",	"choiced":9	}
	]
}

idが回答者番号(ユニークKeyとなる)、answersが設問と回答の配列です。
questionが設問番号で、choicedが選択した選択肢番号です。

Athena側のテーブル定義

上記をGlueで読み込んで作られた定義です。(PartitionはS3のディレクトリに紐づけています)

surveys

#
Column name
Data type
Partition key
Comment
1 id string
2 answers array
3 survey string Partition (1)

Athenaで作成したSQL

まず1行1回答者のレコードを、設問ごとの盾持ち構造にしたうえで、軸となる設問を対象の設問に紐づくようにサブクエリで構成します。そのうえで、軸毎にGroupingします。

WITH exploded_answers AS (
--answersを分解
	SELECT
		id,
		e.question,
		e.choiced,
		MAX(CASE WHEN e.question= 'Q1' THEN e.choiced END) OVER (PARTITION BY id) AS axis1,
		MAX(CASE WHEN e.question= 'Q2' THEN e.choiced END) OVER (PARTITION BY id) AS axis2,
		MAX(CASE WHEN e.question= 'Q3' THEN e.choiced END) OVER (PARTITION BY id) AS axis3
	FROM
		surveys
		CROSS JOIN UNNEST(answers) AS t(e)
	WHERE
		survey = 'survey0001'
)
--軸1,軸2,軸3,設問毎の集計
SELECT
	question, choiced, axis1, axis2, axis3, COUNT(*) AS count
FROM
	exploded_answers
GROUP BY
	axis1, axis2, axis3, question, choiced
ORDER BY
	question, choiced,axis1, axis2, axis3

 

解説

Array形式の項目をSQL内で取扱いできるようにするには「UNNEST」でeに代入します。

CROSS JOIN UNNEST(answers) AS t(e)

 

サブクエリでは、idというユニークキーをパーティションとして軸設問を抽出し、各設問と紐付けます。

MAX(CASE WHEN e.question= 'Q1' THEN e.choiced END) OVER (PARTITION BY id) AS axis1,
MAX(CASE WHEN e.question= 'Q2' THEN e.choiced END) OVER (PARTITION BY id) AS axis2,
MAX(CASE WHEN e.question= 'Q3' THEN e.choiced END) OVER (PARTITION BY id) AS axis3

 

id 設問(question) 選択肢(choiced) 軸1(axis1) 軸2(axis2) 軸3(axis3)
654 Q1 1 1 1 3
654 Q2 1 1 1 3
654 Q3 3 1 1 3
654 Q4 3 1 1 3
654 Q5 1 1 1 3
654 Q6 3 1 1 3
: : : : :

あとはCountし、軸を含めてGroup Byで集計します。(組み合わせが存在する場合に集計した結果行が取得できます。)

設問(question) 選択肢(choiced) 軸1(axis1) 軸2(axis2) 軸3(axis3) count(集計)
Q1 1 1 1 1 78
Q1 1 1 1 2 3
Q1 1 1 1 3 45
Q1 1 1 2 1 34
Q1 1 1 2 2 24
Q1 1 1 3 1 9
Q1 1 1 3 2 30
Q1 1 2 1 1 10
: : : : : :

最後に

AthenaのSQLで得た結果を配列で処理し、ああしてこうしてこねくりまわして冒頭のクロス集計表に編集します。

reiko suzuki
OLD SKOOLシステムエンジニア。ねこを撫でながら働いています。