Dive into hoge

データ分析関連の備忘録

論文の読み方・書き方

グラム先生のチュートリアル資料「国際会議論文の読み方・書き方」
http://www.phontron.com/slides/neubig15nlptutorial.pdf

「論文の読み方・書き方」改め ArXiv時代の論文の読み方 こう書くと落ちる論文の書き方

www.slideshare.net

集計の軸を探す方法

下記資料から抜粋。
speakerdeck.com



仮説と集計結果が異なる場合、取るアプローチ。

エキスパートジャッジメントで探す

(深いドメイン知識が必要)でいい感じのパラメタ探す

因果推論で探す

共変量の影響を除外して目的変数への影響を知りたい。
傾向スコア等を用いて観測できない集団を推定する。
前提に置く仮定が強すぎて実務上使いにくい気がする。

機械学習で探す

 - feature importance, permutation importance
  機械学習が予測に重要だと思っている変数はなにか
  importanceの高い変数に注目して集計したり
 - Partial Dependence Plot
  ある変数の違いが予測結果にどんな影響を及ぼすか

BigQueryでのGA360データ利用

やりたいこと

  1. GA360と連携されたBigQuery(以下BQ)でカスタムディメンションの集計
  2. 対象テーブルを動的にする

(平日のみ実行。月曜は金土日を対象、それ以外の平日は前日を対象として抽出)

前提

このエントリで説明しないこと。

  1. GA360とBQの連携の仕方
  2. カスタムディメンション設定
  3. BQは標準SQLに設定

さらにそもそもってレベルだと、

  1. GCPアカウントの用意
  2. プロジェクト作成済みであること
  3. プロジェクトでBQを有効にしていること

。。。とか他にもあるかもしれないけど、BQ使えるのが前提ってことです。

まずカスタムディメンション集計

LondonCycleHelmetのサンプルテーブルを使います。
ネストされたデータであるカスタムディメンションをフラットにして、インデックス1と2の組み合わせをセッション集計したいとき。

SELECT 
   PARSE_DATE('%Y%m%d', date) AS date
  ,(SELECT MAX(IF(index=1, value, NULL)) FROM UNNEST(hits.customDimensions)) AS dimension1
  ,(SELECT MAX(IF(index=2, value, NULL)) FROM UNNEST(hits.customDimensions)) AS dimension2
  ,COUNT(DISTINCT CONCAT(fullVisitorId, SAFE_CAST(visitNumber AS STRING))) as sessions
FROM 
  `google.com:analytics-bigquery.LondonCycleHelmet.ga_sessions_20130910`,  
   UNNEST(hits) AS hits
GROUP BY 1, 2, 3

カスタムディメンションはhitsをUNNESTしたうえで、SELECTでもサブクエリ書くんですね。
BQ使ってなかったから知らんかった。

さらにテーブルを動的に変える場合

このサンプルデータは2013年9月10日テーブルしかないけど、GA360連携していれば日々、日次テーブルが追加されていく。
例えば、クエリ実行の曜日によって対象テーブルを動的に変えたいときなどがある。

  • 平日のみ実行
  • 月曜は金土日、それ以外は前日を対象にしたい

※下記のクエリはテーブルが1日分しかないので動きません

SELECT 
   PARSE_DATE('%Y%m%d', date) AS date
  ,(SELECT MAX(IF(index=1, value, NULL)) FROM UNNEST(hits.customDimensions)) AS dimension1
  ,(SELECT MAX(IF(index=2, value, NULL)) FROM UNNEST(hits.customDimensions)) AS dimension2
  ,COUNT(DISTINCT CONCAT(fullVisitorId, SAFE_CAST(visitNumber AS STRING))) as sessions
FROM 
  `google.com:analytics-bigquery.LondonCycleHelmet.ga_sessions_*`,  
   UNNEST(hits) AS hits
WHERE
   _TABLE_SUFFIX BETWEEN 
   REPLACE(CAST(DATE_SUB(CURRENT_DATE, INTERVAL IF(EXTRACT(DAYOFWEEK FROM CURRENT_DATE)=2, 3, 1) day) AS string), '-', '')
   AND REPLACE(CAST(DATE_SUB(CURRENT_DATE, INTERVAL 1 day) AS string), '-', '')
GROUP BY 1, 2, 3
ORDER BY 1

FROMで参照するテーブルの日付を*にして、WHEREでその条件を書く。

参考サイト

エクスチュアのグレートなテックブログ。
エクスチュアCTOの権さん、しゅごい…
https://ex-ture.com/blog/2017/12/13/unnest-ga360-bq-data-with-standardsql/
ex-ture.com

pipとconda

pipとcondaとはなにか?

pipはPython標準のパッケージ管理ツールでPyPIからインストールする。
condaはAnacondaのパッケージ管理ツールでAnaconda社が提供するレポジトリからパッケージをインストールする(だからPyPIにはあるけどAnacondaにはない場合がある)。

pip intallとconda installの違いはなにか?

conda installとpip installではbuildする場所が違う。

  • conda installではすでにどこかでbuildされたpackageをfetchするだけ。
  • pip installはlocalの実行環境と互換のあるwheel(Python のパッケージング形式。実態はzip形式のアーカイブPEP427で定義。)を取得した後、localの実行環境でsetup.pyを実行してbuildしている。

buildするタイミングでlocalの実行環境に合うようライブラリが最適化される。つまり、

  • conda installされたライブラリはlocalの実行環境で最適化されているとは言えない。
  • pip installされたライブラリはlocalの実行環境で高性能を発揮できるよう最適化されている。

つまりpipのほうが実行時間が短くなる場合がある。

どっち使えばいいの?

どちらでもいい。
ただし、併用は厳禁らしい。
なぜならパッケージの仕組みが全くことなり、condaは複数の仮想環境下でディスクの使用量を節約するため、ハードリンクを駆使している。
これによりcondaパッケージをpipパッケージで上書きした場合に環境まるごと壊れる可能性がでてくる。
onoz000.hatenablog.com

なんでこんなことになったのか?

pipとcondaの経緯はこの記事が詳しい。
ymotongpoo.hatenablog.com

pandas操作⑧:queryに変数を指定する場合

queryで条件指定にリストを使いたくて、生のリストではなく変数を入れたい場合。

# tmpはリスト
何らかのデータフレーム.query('カラム名==tmp')

そのまま変数入れるとエラーになる。

UndefinedVariableError: name 'tmp' is not defined

下記のように変数の前に@を入れて回避できる。

# tmpはリスト
何らかのデータフレーム.query('カラム名==@tmp')

<参考>
nekoyukimmm.hatenablog.com

xgboost:エラー編

RandomForestのグリッドサーチと同じようにxgboostでもやろうとした。

下記はmax_depthとmin_child_weightのみを使ったグリッドサーチ。
X_train, X_test, y_train, y_testはpandas。
GridSearchCVのパラメーターは今回の話に関係ないから無視してよい。

from xgboost.sklearn import XGBClassifier
from sklearn.grid_search import GridSearchCV
from sklearn.cross_validation import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
      variables, targets, random_state=666)

param_test2 = {
    'max_depth':[5,7,9],
    'min_child_weight':[1,3,5]
}
gsearch2 = GridSearchCV(estimator = XGBClassifier(learning_rate=0.1, n_estimators=1000, max_depth=5,
                                        min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8,
                                        objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), 
                       param_grid = param_test2, scoring='roc_auc',n_jobs=-1,iid=False, cv=5)
gsearch2.fit(X_train,y_train)

するとgsearch2.fit(X_train,y_train)部分で下記エラー。

IndexError: too many indices for array

どうもy_trainの次元が多いようだ。
ということはpandasでダメってことだろうから、一次元配列にすればいいのだろうなというのがわかるので、下記のように変えてると動く。

gsearch2.fit(X_train,y_train.as_matrix().reshape(-1,))

というか、なんでRandom Forestで動いてるのにxgboostで動かないのだ。
実装違うのかよ、と思ったが、

from xgboost.sklearn import XGBClassifier

とあるので勘違いしていたが、そもそもxgboostはsklearnではないので当然実装は違う。


tree系はターゲットが一次元配列でなくてもよいようだ。
https://github.com/scikit-learn/scikit-learn/blob/f0ab589f/sklearn/ensemble/forest.py#L230

y : array-like, shape = [n_samples] or [n_samples, n_outputs]

一方、xgboostはソースの記述だけではわからないw
https://github.com/dmlc/xgboost/blob/master/python-package/xgboost/sklearn.py#L500

y : array_like

pandas操作⑦:列表示

列が省略されるときに全表示させたい

デフォルトは20らしいので好きな数値に変更する。

# pandasの表示列数設定
pd.set_option('display.max_columns',65)

レコード毎にmaxの列番号を抽出したい

最大値の列番号をmax列に突っ込む。

# DataFrameでレコード毎に最大値の列番号を抽出
# dfは使いたいデータフレームという前提
df['max'] = df.idxmax(axis = 1)

任意のカラムでカウント(Group byでカウントのイメージ)

# 上記の最大値の列番号をmax列に入れたときのカウント
df["max"].value_counts()

列の名前を変更したい

上記で作ったmax列の名前をcluster_idという名称に変更したい。

# 元のデータフレームに変更を反映しない場合
df = df.rename(columns = {'max':'cluster_id'})

# 元のデータフレームに変更を反映する場合
df = df.rename(columns = {'max':'cluster_id'}, inplace = True)