pd.mergeで同じカラムがあるとcol_x, col_y...と別カラムが生える件
概要
pandasのmergeを繰り返し行うと意図せずcolumn_x
, column_y
...と、元々あったcolumn
に_x
, _y
とついてきて困ったことがある人向けです。
結果的に綺麗な解決策ではないですが、結合に使用するカラム名は揃えることは徹底した方が良さそうです。
どうしたの
pythonで機械学習の前準備をする際、特徴量生成のためにいくつかのcsvをマージすることはよくあると思います。
例えば、以下のようなデータがあったとします。
ds
が日付、y
がその日の値を表現します。
これにそれぞれ別のソースから獲得されるflag_1
とflag_2
というフラグを用意してmergeさせてみます(というケースを仮定します)。
データ生成コードはこちら。
feature_1 = pd.DataFrame({ "target_ds": df["ds"], "flag_1": np.random.binomial(1, 0.25, len(df)) }) feature_2 = pd.DataFrame({ "target_ds": df["ds"], "flag_2": np.random.binomial(1, 0.1, len(df)) }) flag_df_list = [feature_1, feature_2] })
これをいざマージして中身を見てみると、元々あったtarget_ds
というカラムがなくなり、代わりにtarget_ds_x
とtarget_ds_y
の、意図しないカラムが作られてしまっています。
for _df_flag in flag_df_list: df = pd.merge( left=df, right=_df_flag, how="left", left_on="ds", right_on="target_ds" ) df.head()
すでにds
というカラムがあるので、
どうするの
mergeするkeyとなるカラムの名称を全て揃えておけば問題なさそう。
以下のように、feature_1などの日付カラム名もds
に統一したところうまくいった。
feature_1 = pd.DataFrame({ "ds": df["ds"], "flag_1": np.random.binomial(1, 0.25, len(df)) }) feature_2 = pd.DataFrame({ "ds": df["ds"], "flag_2": np.random.binomial(1, 0.1, len(df)) }) flag_df_list = [feature_1, feature_2] for _df_flag in flag_df_list: df = pd.merge( left=df, right=_df_flag, how="left", ) df.head()
pd.merge
する時にleft_on
とright_on
の両方を使おうとした際は気をつけた方が良さそうです。
業務で詰まったポイントを再現するためこんな話の展開になっているけど、まあ普通に考えてfeature_1のカラム名をわざわざtarget_ds
にするなんて有り得ないよなというself-ツッコミで終わり。
参考
(一応コード) github.com
AAAI-22 斜め読みメモ
トップ会議に採択された研究を眺めるだけで、1mmくらいはAI/ML系のトレンドをキャッチできるのではないかと思い、 AAAI-22の採択論文の中からいくつかピックアップして斜め読みしてみました。 内容に不備や間違いがあれば"優しく"コメントいただけると嬉しいです。
以下に個人的に面白いなと思った研究の内容を、簡単にメモ書きします。
STDEN - Towards Physics-Guided Neural Networks for Traffic Flow Prediction
- 交通予測に対して、物理的な法則と、NNのアプローチを融合させた手法を提案
- 位置エネルギーと運動エネルギーの関係性から定式化したものを、Neural ODEの入力にして時系列予測を実施
- 3つの交通データセットで実験を行い、既存手法よりも良い予測精度が得られた
- 今回読んだ論文の中で、個人的に最も面白いアプローチだと感じた
Graph Neural Controlled Differential Equations for Traffic Forecasting
- controlled Differential Equation(微分方程式の一種)を活用したneural CDEとGNNを組み合わせて、交通予測タスクを実施。
- ノードの交通量を時系列予測
- RNNとGraph Convolutional Networkの組み合わせは多いが、上記の組み合わせは初
- Neural CDEが時系列上のイレギュラーに対して頑健らしく、欠損値があってもモデルが動く
DiffSinger - Singing Voice Synthesis via Shallow Diffusion Mechanism
- 音声合成の研究。
- マルコフ連鎖をベースにしたモデルを提案
- 単純な損失の最小化問題や、GANによる音声合成だと、学習が安定しない、不自然な合成になるなどの問題があった
- 推論を高速化するため、イテレーションの初めの方でground-truthとのメルスペクトログラムの差を学習に活用する手法を導入
- 音声合成のタスクにおいて、Feed Forward TransforerやGANベースの既存手法に比べて、MOSやMCDの精度が向上
Out of Distribution Data Detection Using Dropout Bayesian Neural Networks
- dropoutを使用したBNNで、分布から外れたデータ(OOD)の検出手法を提案
- 中間層のembeddingの距離をユークリッドではなくcosine距離で定義した
- 画像、自然言語、マルウェア検出の3つのタスクで、OODを検出できたかAUCとAccuracyで比較
Data-Driven Real-Time Strategic Placement of Mobile Vaccine Distribution Sites
- コロナワクチン接種率が低いデモグラフィックに接種してもらうために、週次でワクチン接種会場のレコメンドを行うシステムを開発
Market Design for Drone Traffic Management
- ドローンを社会実装していく上で、問題となっている要素などをまとめて、今後の研究テーマなどに活用することを目的としたサーベイ論文のようなもの
- ドローンを社会実装していく上で重要な観点は次の5つ
- efficiency
- 何を持って効率性とするか、どのように測定するのか
- 先入先出法のような手法では、緊急事態や、直近に生じたニーズに対応できないというか課題
- fairness
- 金、順番など、どの要素を持って公平性を担保するか、政府などが決定する必要あり
- simplicity
- incentives
- 悪意のある行動に、他のユーザーの行動が萎縮してしまう場合もある
- scalability
- 規模を大きくするには、システムを自動化していく必要があるが、NP困難などの計算コスト的問題が発生する
- efficiency
FairFoody - Bringing in Fairness in Food Delivery
- フードデリバリー従事者(ギグワーカー含め)の給与が平等ではない問題がある
- 地域によって収入に差があることが分かった
- これらの問題を解決するため、エージェントを機会平等になるよう配置する手法を提案
- あまり理解できず読むのをやめてしまった...理解したいので分かる方いたらコメントください
終わりに
特にまとめとかもないのですが、個人的には、物理法則を機械学習に導入している最初の2つの研究は(ベースとなっている既存研究含めて)面白いなと思いました。 特に手法が交通などのドメインに特化しているならば、それらの動きを特徴量やモデル内部のフローに導入するだけで、大きく表現力が上がりそうです。この辺りはAuto MLでも難しく、それゆえ人間による工夫の余地があって楽しそうです。
状態空間モデルの周期変動オプションを色々試す
概要
pythonの状態空間モデルのライブラリといえば、statsmodelsのUnobservedComponents*1だと思います。
このページを読んでいくと、特に周期変動(seasonal)のモデリングの方法がたくさんありそうだぞということが分かってきます。 柔軟に様々な表現をライブラリ内で簡単にカスタマイズできることはありがたいことです。 しかしそれぞれが一体どんな挙動になるのか、ドキュメントを読むだけではイメージしにくかったので、 実験してみたというのが今回の記事の内容になります。
状態空間モデルってなんだっけ
時系列データの分析や予測に使用される手法です。 下図のように、観測値が生成されるプロセスと、実際には観測されない状態が生成されるプロセスを合わせて記述します。
状態を生成する過程を記述する変数として、時間経過によって緩やかに上昇/下降することを表現するトレンドや、 曜日変動などを表現する周期変動、 外部要因のイレギュラーな影響などを組み込むことができます。 外部要因に対する回帰の重みを時間軸方向に共通化しなければ、外部要因の観測値や状態に与える影響が時間経過によって、どのように変化するかを評価できるみたいです。 例えば、CMを打った直後から終了までの売り上げに与える影響が、時間が経過すると変化することをモデル上で表現できます。
また、この記事では対象ではないですが、 観測値の生成分布なども柔軟に設定することもできます。 例えば、観測値が正の値のみの場合、対数正規分布から生成されるように設定することが可能です。 もし変数の推定を綺麗に導出できない場合も、MCMCが良い感じに推定してくれます。
このように時系列の生成過程を、人間の直感や知見を柔軟に組み込むことで記述できることが状態空間モデルの利点です。
UnobservedComponentsの周期変動オプションについて
今回対象とする状態空間モデルのライブラリであるUnobservedComponentsには、大きく3つの(私の考える)周期変動を表現するオプションがあります。
(数式的なところは、書いても良いのですがただのコピペになるのでやめました。 https://www.statsmodels.org/dev/generated/statsmodels.tsa.statespace.structural.UnobservedComponents.html を見てください。)
1. Seasonal
周期性の見られる期間-1個のパラメータを足し合わせることで、周期性を表現します。
stochastic_seasonal=False
にすることで、周期変動を表現する式から誤差項がなくなります。
2.Frequency-domain Seasonal
1と大きく異なるのは、三角関数を使って表現する点でしょうか。
期間period
の他に、harmonics
(フーリエ級数展開の三角関数項の数とほぼ同等と考えています)を設定する必要があります。
Seasonalと違い、月の周期性と週の周期性というような、いくつかの周期性を同時に表現できるみたいです。
3.Cycle
推定する変数が一気に増加した印象があります。 学習データが1.5年から12年までの長さであるとうまくいくみたいです。
周期変動を状態空間モデルで表現したい場合、これだけオプションがあるとどれを使ったら良いか分からなくなります(筆者の気持ち)。 Seasonalの方が最も推定コストは低そうですが、Cycleを実際に適用した場合、どのくらいの表現力を持つのかも気になるところです。 次の章では、各オプションの挙動について簡単に検証してみます。
実験
使用データ
seabornライブラリから読み込める旅客機乗客数に関するデータを使用しました。 データは月毎の乗客数を表現しています。
試しにトレンドや周期性があるかどうかをざっと確認してみると、 (オープンデータゆえに綺麗に修正されているからなのか)とても綺麗に時系列を分解できました。 トレンドがはっきりと右肩上がりになっているので、モデリングする際はトレンド項を考慮した方が良さそうです。
上図を見ると1年ごとに周期性が確認できます。
念の為周期性成分のみで自己相関を可視化すると、lag=12
のところでコレログラムのピークを確認できるため、12ヶ月で1周期とみなして良さそうです。
設定
状態空間モデルのモデリングにはstatsmodelsのUnobservedComponentsを使用しました。
トレンド成分があるデータであることは確認済みなので、今回はlevel='local linear trend'
と設定することにしました。
データは1949年1月から1960年12月まであるので、1959年12月までを学習期間として、1960年の1年を予測期間として検証に使用しました。 検証のため、予測期間の誤差をMAE(絶対誤差)で測定しました。
比較手法は次の3つです。
- model_1:
seasonal=7
に設定。seasonalの挙動を知るため。 - model_2:
freq_seasonal=[{'period':12, 'harmonics':6}]
に設定。frequency-domain seasonalの挙動を知るため。 - model_3:
cycle=True
に設定。cycleの挙動を知るため。
この3つの手法でそれぞれ学習と予測を実行します。 例として、model_1の実行コードを以下に貼ります。
import numpy as np import pandas as pd import statsmodels as sm from statsmodels.tsa.statespace.structural import UnobservedComponents from statsmodels.datasets import elec_equip from statsmodels.tsa.seasonal import STL, seasonal_decompose from statsmodels.graphics.tsaplots import plot_acf, plot_pacf %matplotlib inline import matplotlib.pyplot as plt # pre-processing df = sns.load_dataset('flights') df.index = pd.date_range(start='1949-01-01', freq='MS', periods=len(df)) df["mode"] = df["year"].map(lambda x: "train" if x < 1960 else "test") # 1. seasonal model_1 = UnobservedComponents( df.loc[df["mode"] == "train", "passengers"], level="local linear trend", seasonal=12 ) res_1 = model_1.fit() print(res_1.summary()) # prediction y_pred = res_1.predict(start='1949-01', end='1960-12') df["pred_1"] = y_pred mae_1 = np.mean(np.abs(df.loc[df["mode"]=="test", "passengers"] - df.loc[df["mode"]=="test", "pred_1"])) print("MAE :", mae_1)
この後の結果も含めた、実際に実行したコードはこちらです。
結果
3つの学習と予測の結果を貼ります。 まずはmodel_1(seasonal検証モデル)。
Unobserved Components Results ===================================================================================== Dep. Variable: passengers No. Observations: 132 Model: local linear trend Log Likelihood -501.715 + stochastic seasonal(12) AIC 1011.430 Date: Sun, 27 Feb 2022 BIC 1022.546 Time: 07:39:05 HQIC 1015.944 Sample: 01-01-1949 - 12-01-1959 Covariance Type: opg ==================================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------------ sigma2.irregular 4.639e-08 20.916 2.22e-09 1.000 -40.995 40.995 sigma2.level 1.536e-07 51.519 2.98e-09 1.000 -100.976 100.976 sigma2.trend 56.5406 19.161 2.951 0.003 18.985 94.096 sigma2.seasonal 18.7901 4.137 4.542 0.000 10.682 26.898 =================================================================================== Ljung-Box (L1) (Q): 7.80 Jarque-Bera (JB): 64.85 Prob(Q): 0.01 Prob(JB): 0.00 Heteroskedasticity (H): 4.01 Skew: -1.04 Prob(H) (two-sided): 0.00 Kurtosis: 5.96 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step). MAE : 78.7839030141792
続いてmodel_2(frequency domain seasonal検証モデル)。
Unobserved Components Results ============================================================================================= Dep. Variable: passengers No. Observations: 132 Model: local linear trend Log Likelihood -458.354 + stochastic freq_seasonal(12(6)) AIC 924.707 Date: Sun, 27 Feb 2022 BIC 935.790 Time: 07:31:00 HQIC 929.207 Sample: 01-01-1949 - 12-01-1959 Covariance Type: opg ============================================================================================== coef std err z P>|z| [0.025 0.975] ---------------------------------------------------------------------------------------------- sigma2.irregular 1.596e-08 9.677 1.65e-09 1.000 -18.967 18.967 sigma2.level 15.4431 5.667 2.725 0.006 4.335 26.551 sigma2.trend 0.0154 0.033 0.473 0.637 -0.049 0.080 sigma2.freq_seasonal_12(6) 1.0049 0.221 4.540 0.000 0.571 1.439 =================================================================================== Ljung-Box (L1) (Q): 19.75 Jarque-Bera (JB): 1.92 Prob(Q): 0.00 Prob(JB): 0.38 Heteroskedasticity (H): 2.28 Skew: 0.17 Prob(H) (two-sided): 0.01 Kurtosis: 2.47 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step). MAE : 14.404733712999388
最後にmodel_3(cycle検証モデル)。
Unobserved Components Results ============================================================================== Dep. Variable: passengers No. Observations: 132 Model: local linear trend Log Likelihood -629.951 + cycle AIC 1267.902 Date: Sun, 27 Feb 2022 BIC 1279.310 Time: 07:29:31 HQIC 1272.537 Sample: 01-01-1949 - 12-01-1959 Covariance Type: opg ==================================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------------ sigma2.irregular 8.27e-06 96.626 8.56e-08 1.000 -189.384 189.384 sigma2.level 1013.6807 187.769 5.399 0.000 645.660 1381.702 sigma2.trend 3.243e-07 6.161 5.26e-08 1.000 -12.075 12.075 frequency.cycle 0.0436 0.055 0.788 0.431 -0.065 0.152 =================================================================================== Ljung-Box (L1) (Q): 10.74 Jarque-Bera (JB): 5.67 Prob(Q): 0.00 Prob(JB): 0.06 Heteroskedasticity (H): 7.91 Skew: -0.37 Prob(H) (two-sided): 0.00 Kurtosis: 3.72 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step). MAE : 70.1899506705816
学習期間のAICやBIC、予測期間のMAEのそれぞれにおいてmodel_2が最も良い値になりました。 model_1を単純に複雑化したのがmodel_2ですが、パラメータ数と学習期間における推論能力のトレードオフのような指標であるAIC*2やBICもmodel_2の方が良かったので、モデルのパラメータが増えたことによる過学習の可能性は低そうだと考えられます。 model_3は学習期間のLog likelihoodも最も悪い値になっているので、うまく学習できていないか、今回のタスクに合っていない可能性があります。
数字だけ見ても分からない部分があるので、実際の値とそれぞれの予測値を可視化しました。
AICやMAEなどが最も良かったmodel_2は、予測期間(点線よりも右側)でもうまく予測できているように見えます。 今回のタスクにおいてはfrequency domain seasonalを活用して周期性をモデリングした方が良かったと考えられます。 model_2に比べると、model_1は周期変動を表現しようとはしているものの、予測期間においてトレンドをうまく合わせられていません。 またmodel_3は周期変動のようなものをうまく表現できていません。 model_1, model_3が予測に失敗した原因を調査するため、成分ごとに可視化してみました。
model_2とmodel_1を比較すると、model_1の方は1960年の直前でトレンドの値がマイナスになっており、これが予測期間において実際の値よりも小さい値を予測した原因と考えられます。 model_2の方は周期性の表現力が増したおかげなのか、時間の経過とともに周期変動成分(= seasonal component)の振幅が大きくなっています。 それゆえトレンドに上下の動きのようなノイズが少なくなるので、緩やかな上昇を表現できるようになります。結果としてmodel_2の予測精度は良くなっています。 このことから、周期変動の表現力が不足していると他の成分で表現しようとするあまり、予測に失敗する可能性があることが分かりました。
model_3は予測値でも周期変動をキャッチできていませんでしたが、cycle成分もほぼ横ばいになっていることがわかります。 代わりに状態(=Level component)に周期性のようなものが見えてしまっています。 ドキュメントには
The cyclical component is intended to capture cyclical effects at time frames much longer than captured by the seasonal component.
と書かれていたのですが、今回はデータが1ヶ月おきに記録されているものだったので、cycleの良さを活かせていない可能性があります。 データの記録頻度が日ごとのもので実験するとまた変わるかもしれません*3。
まとめ
状態空間モデルの周期変動を表現するオプションをそれぞれ試しました。 超簡単なデータセットでの実験でしたが、周期変動の表現力が不足していると他の成分の表現に悪影響を与える恐れがあり、それゆえ予測に失敗する可能性があるということが分かりました。 実際にどのオプションが良いかはデータによると思いますが、 周期変動を表現したい場合はseasonalの設定とfrequency domain seasonalの設定の両方を試し、AICやクロスバリデーションでどちらを採用するか決めるのが良いかなと考えます。
オープンデータセットって見つけるの意外と大変ですね...。ダウンロードしてみると非数ばかりのデータや、周期変動の全くないデータなどが多かったです。
参考文献
(使用したコードを再掲)
プロジェクトメンバーが1人の時こそGithubを使い倒す
概要
この記事は新人エンジニア1がいきなり開発を1人で任された際に、Github使ったら、タスク管理もコード開発もスムーズだったという体験談です。
チームメンバーの数に対して仕事量や依頼数が多いと、どうしてもPJT当たりのメンバーは減って、最悪1人という事態になります。 1人だと単純に作業量が膨大という点に加えて、「今やるべきタスクは?」「将来的に問題になりそうなところは?」といったことを管理する必要があります。 行き当たりばったりで作業すると、1時間に一度は迷える子羊になって手が止まるか、数日作業した後に根本的にコード構成を変更する必要に気がつくかのどちらかで、良いことありません。 PJT管理のために、本来チーム開発でよく活用される2Githubを、作業者が1人のPJTに導入してみました。 結果的にまあまあ良い感じでPJT管理できたからオススメだよというテーマです。
どうやって使っているか
簡単に、実際にどのような感じで利用しているのか書きます。 基本的には、チーム開発でよくあるGithubの使い方だと思います(なのでこのセクション要らない説)。 さすがに仕事で使っている画面は載せられないのと、単にGithubに関する説明は他の文献に譲りたいので、サラッといきます。
まず、課題をissues上で言語化/体系化します。 プロジェクトの目的を達成するために何が必要か、考えていくと課題がたくさん見つかると思います。 頭の中で湧いてきた課題を、ひたすらissueに書きます。
その後、issue内で完結できるものに関してはissue、コードのマージが必要な場合はPull request(PR)を作成します。 個人的には、PRは空コミットしてdraft PRを作成すると、タスクが可視化されてなお良いと思います。 作業者が1人でも、もし他メンバーのレビューはもらえるという場合は、n人以上のapproveでマージ可能みたいな設定をしておけば、コードの治安も保たれると思います。作業者が1人だと、属人性の高いコードになっていき、後で見返した時に手を入れられなくなる可能性大なので、1人の時こそ、別途レビュワーが必要だと考えます。チーム状況によりますが。
参考: qiita.com
その他、issueにするほどまとまっていないけれど気になる部分や、将来的に実装したいことがあれば、最近できたdiscussion機能とかも使えそうです。
良かったこと
先述の通りにGithubを使うと何が嬉しかったか述べます。
並行して複数の問題について考えられた
マルチタスクは良くない!!というのはその通りですが、どうしてもissueが完全に独立していない限り、issueやPRの作業中に別のissueの方針を変更したくなったり、新たにissueが頭の中で生えたりします。 この時、気軽に該当issueにメモ書きできるのは結構便利です。
他には急に上司や別部署から、対処中のissueとは別のタスクに対処するよう急かされる場合があります。 この場合も、作業中のタスクを一旦終わらせてから...となる現象は比較的抑えられるのではないかと思います。
このように、複数のタスクをツール上(issues, PR)に並べておけるので、頭の中で常に複数のタスクを考慮する必要はありません。脳のメモリに余裕を持たせるという意味で良かったと思っています。
コメント欄で進捗を周知できる
issuesにもPRにもコメント欄があります。 ここをうまく活用すると、もしかしたら良いことがあるかもしれません。 例えば、状況を逐一コメントに記入していけば、上司などが進捗を把握することができます。 さらにそこそこ時間を溶かしていたり、悩んでいたりする内容も簡単に実況すれば、心優しいメンバーが運よくコメントを見て、アドバイスくれます。 あと自分への備忘録にもなります。
この頃はテレワークが多くなっています。オフィスでともに働いていた時の些細なコミュニケーションが今の社会全体の課題だと思いますが、それはissuesやPRのコメントである程度代替可能だと思います。なのでコメントは独り言欄として使うと良いです。
他のPJT管理ツールじゃダメなの?
プロジェクトを管理するツールは他にもあります。 多くの管理ツールで、カンバン方式でタスクが管理できたり、タスクごとにコメントも可能です。
ただGithubは予めissues, PRという流れがなんとなく決まっている、良い意味で自由度が低いツールだと思います。 そういった意味で、ルールが属人化しにくいです。 まあコードをgit管理できるという部分が結構大きいですが...
個人的には、ガントチャートがないのが残念です。
終わりに
まだまだ私もgitもGithubも勉強中ですので、ベストプラクティスはコロコロ変わる可能性があります。 他のプロジェクト管理ツール含め、良い使い方あればご教示ください。 これが初の技術系ブログでした(コード何も書いてないけど)。疲れますねこれ...