author: aaaanwz gravatar

BigQueryにおけるGA4データの扱いづらさを解決する

背景 Google AnalyticsをBigQueryに連携してSQLで分析しようという時、event_params や user_properties が 1 2 3 4 5 6 7 8 9 10 11 ARRAY< STRUCT< key STRING, value STRUCT< string_value STRING, int_value INT64, float_value FLOAT64, double_value FLOAT64 > > > という大変扱い辛い型のため、以下のようにサブクエリを多用したSQLを度々書いていく必要があります。 1 2 3 4 5 6 7 8 9 SELECT TIMESTAMP_MICROS(event_timestamp) event_timestamp, (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location, (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_title') AS page_title, ... FROM `analytics_123456789.events_*` WHERE _TABLE_SUFFIX BETWEEN '20240101' AND '20240131' このSQLの結果をデータマートにすれば解決かと思いきや、event_paramsにプロパティを追加する度にデータマートをメンテナンスする必要があり運用が非常に面倒です。...

May 6, 2024

グロースハックのための施策テスト設計

PDCAサイクルにおいて、バイアスを除去した効果検証(Check)を行うためにはどのようなPlanningを行う必要があるかをまとめる。 要約 以下のいずれかに該当するようにテストを設計する 施策の対象者の選択(介入)ロジック 指標の予測可否 介入条件の予測可否 介入群と同様なトレンドの群がある 介入を連続値の閾値で決定している 効果検証手法 完全ランダム - - - - 無作為化比較試験 人為的 可 - - - 回帰分析 人為的 不可 可 - - 傾向スコアマッチング 人為的 不可 不可 はい - Causal Impact 人為的 不可 不可 いいえ はい 回帰不連続デザイン a. 無作為化比較試験(A/Bテスト) 適用ケース 無作為に適用される施策であること ex.トップページのデザイン変更 ユーザーアカウントがテスト/コントロール群のどちらに割り振られたかがデータからわかること 特定の条件を満たしたユーザーにのみ影響する施策は選択バイアスを考慮する必要があるためこの手法は使えない。 ex. アンケートに回答したユーザーにクーポンを送付、クーポンの売上への貢献度を調べたい場合など。 アンケートに回答している時点で購入意欲が高く、クーポン有/無のグループの性質が異なっているため比較ができない。 このケースでA/Bテストを適用したければ アンケートに回答したユーザーからランダムでクーポン配布 とすれば選択バイアスを打ち消せるが、サンプル数確保、ユーザー体験、ビジネス機会損失などを考慮する必要がある。 検証手順 施策によって改善される指標を決定 (PdM) ex. 訪問頻度、滞在時間 テスト群の割合、テスト期間の決定(PdM, DS) 施策が適用される人数 (ex. テスト期間中における新トップページのアクセスUU数) が1000人を超えるように見積もる A/Bテストの実施 (Dev) LaunchDarklyなどを用いる 統計的仮説検定 (DS) p-hacking回避のため、2で決めたテスト期間の終了時に一度だけ検定を行う。...

March 4, 2024

特徴量重要度(SHAP)をDataframeで取得する

SHAP を用いて shap.summary_plot() で特徴量重要度のグラフを出力できるが、定量データとして取得したい場合がある。 以下のようにすればDataFrameにできる。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import lightgbm as lgb from sklearn import datasets import shap X = train_df.drop('target', axis=1) y = train_df['target'] model = lgb.train( params={}, train_set=lgb.Dataset(X, y) ) explainer = shap.TreeExplainer(model) shap_values = explainer(X) pd.DataFrame(shap_values.values[0], columns=["feature_importance"], index=X.columns).sort_values( by="feature_importance", ascending=False) feature_importance age 0.045195 gender 0.001254 … …

December 25, 2023

Apache Airflowのstandalone環境でHTTP接続が無期限にハングする問題

概要 Mac OS環境にて airflow standalone でAirflowを立ち上げると、DAGでHTTP接続を行おうとした際に無期限にハングする。 原因 OSXのPythonのバグ 回避策 Airflowを立ち上げる際に NO_PROXY="*" をセットする 1 2 3 $ export NO_PROXY="*" $ export AIRFLOW_HOME=/path/to/airflow/home $ airflow standalone ref: https://github.com/apache/airflow/discussions/35903

December 22, 2023

4社のデータ基盤開発に携わって得たベストプラクティス

最近はデータサイエンティストとして働いているため、データエンジニア時代に得た知見を備忘録として残す。 データ流入量が1TB/日以下、DWHの利用者が1000人以下の現場を想定している。それ以上の大規模組織には以下の話は当てはまらない場合が多そう。 1. DWHにはBigQueryを使う 2023年時点だとDWHの選択肢はBigQuery,RedShift,Snowflakeの3択だと思う。 が、機能、コスト、運用の簡単さ、分析ツールのエコシステムの充実具合など、どの面を見てもBigQuery以外を選択する余地は今のところないように感じる。 詳細な比較に関してはいろいろな人が言っているので割愛するが、マルチクラウド構成にしてまでもBigQueryを選定する価値はある。 アプリケーションがAWSだからという理由でRedShiftやSnowflake on AWSを選定し、結局後から大変な手間をかけてBigQueryに移行したというのはよく聞く。 2. ETLよりELT コスト的な理想としてはETLで必要十分なデータマートを用意していければいいが、絶対といっていいほどうまくいかない。 分析のニーズは事業の変化に応じて変わっていくし、データソースとなるプロダクトも成長に伴って変化していく。 ETLの問題点は弾力性が無く、障害やカラム追加依頼などの度に過去データを入れ直したりと運用コストが高すぎる点にある。 ペタバイト単位のデータでもない限りはBigQueryに課金してELTにしたほうが良い。人的コストに比べれば圧倒的に安い。 3. データマートの前にまずはビューで提供する 前述の通りデータソースもデータマートの要求仕様も刻々と変わる。そのためデータマートを作る前にまずは変更コストの低いビューでリリースしたほうがいい。 クエリコスト的に厳しければマテリアライズドビューで中間テーブルを作り、データを圧縮できるか試す。 データマートは最終手段と捉えておいたほうが良い。 4. 分析で生データを直接触らない RDBから転送したデータをSQLで加工し、BIツールでダッシュボード化、というのはよくある使い方だ。 ではRDBにALTER TABLEがかかるとどうなるかというと、このテーブルを参照しているのはどいつなんだという影響調査が必要になる。 きちんと用途別にServiceAccountを使い分けていればINFORMATION_SCHEMAで大体の特定は可能だが、いずれにしろかなりの量のSQLの修正が必要になる。 適切なモデリングを行ったビューを用意しておき、そのビューだけを分析に使うという運用にするとビューが腐敗防止層として働いてくれる。 データソース側の仕様変更があってもビューで隠蔽すれば分析側に影響せず、圧倒的にトータルの運用コストが下がる。ビューの設計が適切であれば分析の難易度も格段に下がる。 生データを格納するデータセットのアクセス権はデータエンジニアだけが持っているというレベルでいいと思う。 5. データマート/ビューの規約を決める につながる話として、元データの仕様がバラバラであっても腐敗防止層があればインターフェースの仕様は統一できる。せっかくなのでテーブルの統一仕様を決めたほうが良い。 例として以下のようなルールを厳守するようにしておくと後々幸せになれる (日本企業の場合) JSTの時刻データはDATETIME型にする。それ以外のタイムゾーンはUTCに変換してTIMESTAMP型にする 6. データマート/ビューは最小限にする 分析を簡単にするという目的でビジネスロジックを盛り込んだデータマート/ビューを用意していくと、結局分析サイドは仕様を把握しきれずデータカタログやドキュメントをもっと整備してほしいという話になってくる。 こうなってくるといくら人手があっても足りないので、分析用に提供するデータマート/ビューはビジネスで取り扱うエンティティに対応したマスターデータ・トランザクションデータだけにしたほうが良い。 非正規化にはこだわらずにアトミックにしたほうがよい。多少クエリコストがかかろうとも結局それが分析側の学習コストを下げ、分析コードの可読性は上がり、トータルで見るとコスト抑制になる。 7. データ基盤を凝りすぎたアーキテクチャにしない データ基盤は社内システムであり、データエンジニアは間接部門に属する事が多い。 そのためふんだんにコストが投入されることは珍しく、基本的にはどこも常に人手不足だと思う。 更に大学にデータサイエンス学部などが創設されてデータサイエンティストは増え続ける一方だが、データエンジニアを目指す人はレアであり需要増に対して供給が全く足りていない。 新規開発時にはイメージされづらいが何年か経つと少数のデータエンジニアで大量の依頼を捌いていくことになり、分析PJは「データ連携待ち」「データエンジニア調査待ち」のステータスで溜まっていく。 なので組織のデータエンジニアのレベルが高かったとしても基盤はできるだけ学習コストが低い作りにして、業務委託などでスケールできるようにしておいたほうがよい。 最近はBigQuery周りの色々が進化しまくっているので、大体の場合は以下の構築パターンでいいんじゃないかと思う。 RDBデータ連携 Datastream スピードレイヤー Pub/Subに入れてBigQueryサブスクリプション(docs) バッチレイヤー GCS → Data Transfer データマート生成 SQLで完結するならDataflow しないものはワークフローエンジン行き ワークフローエンジンの選定に関しては一概に言えない。(大体の場合マネージドAirflowで良いとは思う) 8. データ品質の監視をする バッチジョブのエラーやマシンリソースの監視は大体どこもやっているだろうが、データの品質についての監視を行っている現場は意外と多くないのではと思う。 ちょっとMLOpsにはみ出た話ではあるが、重複・欠損・異常値などデータの中身を監視する仕組みを作ったほうがいい。 別部門が入力してきたデータをDWHに流すだけ、利用者に指摘されてから調査する、という後手後手の姿勢では無駄なリカバリ作業が増えるし、文化的にもどんどん社内下請けに成り下がってしまう。 9. コストの可視化は早めに始めたほうがいい Bizサイドが委託したアナリストが激重SQLをRedashのスケジュールで毎朝回し、しかもBizサイドはダッシュボードをあまり見ていない。使っていないなら止めろとデータエンジニアから連絡するも対応されない。 なんて光景をよく見てきた。...

November 3, 2023

Ubuntu desktop 23.04 環境構築メモ

GPUを使う開発は自作PC+Linuxに限る、ということでゲーム用Windowsと開発用Ubuntuのデュアルブートにした。 OS関係 Windowsをnvme0, Ubuntuをsata0に入れてfirst boot driveをsata0に設定、GNU GRUBでOSを選択する。 こうしておくとブートローダーを吹っ飛ばしてもsata0を外せばきれいなWindowsが起動する。(はず) ハードウェアクロックをローカルタイムにする 1 sudo hwclock -D --systohc --localtime この設定をしないとWindows側の時刻がずれる 日本語ディレクトリを英語化 1 LANG=C xdg-user-dirs-gtk-update GPU関係 GPUドライバのインストール 1 $ sudo ubuntu-drivers autoinstall CUDAセットアップ https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_network 1 2 3 4 $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.0-1_all.deb $ sudo dpkg -i cuda-keyring_1.0-1_all.deb $ sudo apt update $ sudo apt install cuda ~/.bashrc 1 2 3 ... export PATH="/usr/local/cuda/bin:$PATH" export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH" 動作確認 1 2 3 4 5 6 $ nvcc -V nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2023 NVIDIA Corporation Built on Mon_Apr__3_17:16:06_PDT_2023 Cuda compilation tools, release 12....

June 20, 2023

AutoMLライブラリ MLJARを使う

MLJARとは MLJARは、自動機械学習(AutoML)フレームワークであり、データの前処理、特徴量生成、モデルの選択、ハイパーパラメータの最適化、アンサンブル学習など、機械学習の一連の流れを自動化することができます。 auto-sklearn、H2O、AutoGluonといった競合に対して高いスコアを誇り、更にモデルの解釈性を高めるための機能も備えています。 触ってみたところ雑にデータを突っ込むだけで分析コンペでもある程度戦えるモデルができてしまいました。 ChatGPTに使い方を聞いても微妙に間違っていたりするのでここにまとめておきます。 インストール 1 pip install mljar-supervised 学習 1 2 3 4 5 6 7 8 9 10 11 import pandas as pd from supervised.automl import AutoML train_df = pd.read_csv("train.csv") X = train_df.drop('target', axis=1) y = train_df['target'] automl = AutoML(ml_task="classification", eval_metric="accuracy", mode="Compete") automl.fit(X_train, y_train) print("score:", automl.score(X, y)) ml_task タスクの種類 auto binary_classification multiclass_classification regression のいずれか eval_metric 評価指標 binary_classification:logloss, auc, f1, average_precision, accuracy multiclass_classification: logloss,f1,accuracy regression: rmse, mse, mae, r2,mape,sperman,pearson mode Explain 解釈性を重視したモデルを選択する Perform バランス重視 Compete 分析コンペ向け Optuna 学習時間を度外視したスコア特化 モデルの解釈 1 automl....

April 26, 2023

JSONLファイルからBigQueryスキーマJSONを生成する

script.sh 1 2 3 4 5 6 7 8 #!/bin/bash set -eu PROJECT_ID=myproject DATASET_NAME=temp TABLE_NAME=$(basename $1 | sed 's/\.[^\.]*$//') bq load --autodetect --source_format=NEWLINE_DELIMITED_JSON ${PROJECT_ID}:${DATASET_NAME}.${TABLE_NAME} $1 bq show --schema --format=prettyjson ${PROJECT_ID}:${DATASET_NAME}.${TABLE_NAME} > ${TABLE_NAME}.json bq rm -f -t ${PROJECT_ID}:${DATASET_NAME}.${TABLE_NAME} 1 $ ./script.sh /path/to/mydata.jsonl 以下のファイルが出力される ./mydata.json 1 2 3 4 5 6 7 8 [ { "mode": "NULLABLE", "name": "id", "type": "INTEGER" } ... ] もっといい方法ありそう

January 17, 2023

Pythonデータ分析チートシート

データのロード CSVファイルのロード 1 2 import pandas as pd pd.read_csv('./data.csv') BigQueryクエリ結果のロード 1 2 3 4 5 6 7 8 9 10 import pydata_google_auth import pydata_google_auth.cache from google.cloud.bigquery import Client credentials = pydata_google_auth.get_user_credentials( scopes = ['https://www.googleapis.com/auth/bigquery'], ) client = Client(project="myprojectname", credentials=credentials) job = client.query("SELECT * FROM mytable;") df = job.to_dataframe() データ仕様の把握 データの中身を見る 1 2 df.head() # 先頭 df.tail() # 末端 スキーマ情報を見る 1 df.info() 1 2 3 4 5 6 7 8 9 <class 'pandas....

December 28, 2022

NextCloudのパーミッションエラー回避

こんな感じでNextCloudをk8sにデプロイするとします。 nextcloud.yaml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 apiVersion: apps/v1 kind: Deployment metadata: name: nextcloud namespace: default spec: replicas: 1 selector: matchLabels: app: nextcloud template: metadata: labels: app: nextcloud spec: containers: - name: nextcloud image: nextcloud env: - name: NEXTCLOUD_DATA_DIR value: /data ports: - containerPort: 80 protocol: TCP volumeMounts: - name: myvolume subPath: config mountPath: /var/www/html - name: myvolume subPath: data mountPath: /data volumes: - name: myvolume persistentVolumeClaim: claimName: myvolume securityContext: fsGroup: 33 --- apiVersion: v1 kind: Service metadata: name: nextcloud namespace: default spec: ports: - name: http port: 80 protocol: TCP selector: app: nextcloud clusterIP: None すると初回ログイン後に以下のようなエラーが出てきます。...

November 29, 2022