複数の時系列データの波形の類似度を BigQuery のクエリ一発で調べる方法をご紹介します。
だって、ググると CCF やら DTW やらが出てきますが、わざわざ Python で書くのは正直しんどい時とか、あるじゃないですか。
出典:http://www.thothchildren.czzzzzzzom/chapter/59b495f775704408bd43002d
CCF(相互相関関数):2 つの時系列データの各サンプル値間の相関を取る、時間のズレも計算に入れられる
DTW(動的時間伸縮法):CCFに加え周期が伸縮するような時系列データの類似度も計算できる
タイミングのズレはなく、周期の伸縮も考慮しないなら BigQuery の CORR 関数だけで類似度を調べることができます。さらに今回は、複数の比較対象を一発で見比べたい、という(私の)要望に応えるため BigQuery Scripting でループさせました。
時系列データの波形が似ている=相関が高い
いろいろあってビールの家計支出について東京と似た波形(平行トレンド)の地域ってどこ? を知りたくなった、というのが今回の調査の経緯です。その辺は別記事にしたのでよかったら覗いてってください!
まずは結果から。
東京都の波形と相関の高いもの、低いものそれぞれ2つずつピックアップしてみました。
東京と最も相関が高いのは大阪(堺市)というのはなんとなく想像通りでした。
BigQuery のクエリ一発で時系列データの相関係数を一覧出力する方法
BigQuery で FOR/WHILEループや IF文 が書けるのはご存じでしょうか。BigQuery Scripting と言って、クエリ内にスクリプトが書けるという便利機能です。
今回、時系列データの相関係数を出したいということで、日毎の金額について、東京との差が小さい(=相関が高い)かどうかを CORR 関数(ピアソン相関)で調べます。
つまり、一度のクエリで一つの地域しかできません。
なので、クエリ一発で全地域の相関係数を一覧で出力するために、ループ処理の力を借りることにしました。以下がクエリになります。
クエリ全体
# 変数の宣言
DECLARE areas ARRAY<STRING>; # areas という配列を作ってループを回します
DECLARE x INT64 DEFAULT 1; # areas で使う引数
DECLARE t STRING DEFAULT ”; # 比較のareas名を格納
DECLARE cor FLOAT64 DEFAULT 0; # 相関係数
# 変数への代入
# ARRAYの値を自動的に作りたかったので、ARRAY_AGGで作成
SET areas = (
SELECT ARRAY_AGG(area_code) as list
FROM (SELECT area_code FROM `プロジェクトID.データセット名.テーブル名` GROUP BY area_code ORDER BY area_code)
);
# 結果格納用の一時テーブル
CREATE OR REPLACE TEMP TABLE _SESSION.tmp(
t STRING,
cor FLOAT64
);
# ループ処理
# areasの長さまで繰り返し
WHILE x <= array_length(areas) DO
# 比較対象の都道府県コードを格納
SET t = areas [ORDINAL(x)];
# 相関係数を格納
SET cor = (
WITH area1 as (
SELECT
area_code, area_name,
CAST(CONCAT(
NORMALIZE(SUBSTR(year_month,0,INSTR(year_month,’年’) – 1),NFKC),
‘-‘,
NORMALIZE(LPAD(TRANSLATE(SUBSTR(year_month,INSTR(year_month,’年’) + 1, 2),’月’,”),2,’0′),NFKC),
‘-01’) as DATE) AS year_month,
CAST(value AS FLOAT64) AS value
FROM `プロジェクトID.データセット名.テーブル名`
WHERE cat01_code = ‘011100030’ –ビール
AND cat02_code = ’03’ — 二人以上の世帯(2000年〜)
AND area_code = ‘13003’ –東京都区部
), area2 as (
SELECT
area_code, area_name,
CAST(CONCAT(
NORMALIZE(SUBSTR(year_month,0,INSTR(year_month,’年’) – 1),NFKC),
‘-‘,
NORMALIZE(LPAD(TRANSLATE(SUBSTR(year_month,INSTR(year_month,’年’) + 1, 2),’月’,”),2,’0′),NFKC),
‘-01’) as DATE) AS year_month,
CAST(value AS FLOAT64) AS value
FROM `プロジェクトID.データセット名.テーブル名`
WHERE cat01_code = ‘011100030’ –ビール
AND cat02_code = ’03’ — 二人以上の世帯(2000年〜)
AND area_code = areas [ORDINAL(x)] # areasのX番目の値を取り出す
)
SELECT
CORR(value1,value2) AS correlation
FROM
(SELECT
area1.value AS value1,
area2.value AS value2,
FROM area1 LEFT JOIN area2 ON area1.year_month = area2.year_month
)
);
# 対象の地域コードと、東京との相関係数を一時テーブルにINSERT
INSERT _SESSION.tmp (
SELECT
t,
cor
);
SET x = x + 1;
END WHILE;
# ループ処理終了
# 時系列データの相関係数一覧で出力
SELECT
c.t,
m.area_name,
c.cor
FROM _SESSION.tmp LEFT JOIN (
SELECT area_code,area_name
FROM `プロジェクトID.データセット名.テーブル名`
GROUP BY area_code,area_name
ORDER BY area_code
) AS m ON c.t = m.area_code
ORDER BY c.cor
出力結果(2019年の月別ビールへの支出額に関する、東京との相関係数)
東京自身は相関が1、次に相関が高いのは全国ですが、全国には東京のデータも含まれているので除外し、最も相関の高い地域は堺市(大阪)となりました。
BigQuery Scripting 使ってみた感想
実は、BigQuery Scripting を書いてみたのは今回が初めてでしたが、使ってみてわかりました。めちゃくちゃ便利ですね!
これまでは BigQuery 一発でできない処理は、Vertex AI のワークベンチでノートブック(jupyter notebook的なやつ)を立ち上げて、Python で BigQuery からデータロードしてから処理する、ということをやっていました。
でも、今回のようなちょっとした検証やデータ確認だったら、BigQuery 単体でできる方が素敵ですね。もっと早くに出会いたかった。
まとめ
- 類似度=相関が高い、ということで CORR 関数が使える
- ただし、ズレや伸縮は考慮しない
- 複数の比較対象がある場合は BigQuery Scripting でループさせると便利
API ARIMA AutoML Bard BigQuery Bing ChatGPT Cloud Endpoints Cloud Storage DWH EBPM GAS Generative AI Google Apps Script Google Cloud Google Form Google Workspace IT組織 Outlook PaLM PDF Python ReportLab selenium Statsmodels STL VertexAI Vertex Forecast スクラッチ セミナー ソトミル トレンド分析 トレーニング バッチ予測 世界は女性とデジタルが救う 女性活躍 技術 時系列データ分析 業務効率化 機械学習 特徴量エンジニアリング 生成AI 自動化 評価指標 需要予測