The 4th Big Data Analysis Contest 予測部門 チュートリアル
本チュートリアルでは、モデルの構築ではなく、データの理解のための探索的分析、可視化を中心とした内容としています。
実装にはpython(versionは3.6.3)を使用。構成は以下のとおりです。
- 必要なライブラリ
- 必要なデータの用意
- 実装
- データの読み込み
- データの理解
- 投稿ファイルの作成
- まとめ
The 4th Big Data Analysis Contest 予測部門 チュートリアル¶
概要¶
「The 4th Big Data Analysis Contest」( https://signate.jp/competitions/136 )の実装例です。
本チュートリアルでは、モデルの構築ではなく、データの理解のための探索的分析、可視化を中心とした内容としています。
実装にはpython(versionは3.6.3)を使用。構成は以下のとおりです。
- 必要なライブラリ
- 必要なデータの用意
- 実装
- データの読み込み
- データの理解
- 投稿ファイルの作成
- まとめ
必要なライブラリ¶
日付の処理にdatetime、ファイルの処理にpandas、基本的な数値計算にnumpy、可視化にmatplotlib, seabornを使用します。
必要なデータの用意¶
https://signate.jp/competitions/136 へアクセスし, 「データ」タブを押し, 以下のファイルをダウンロードします。
データは、dataset フォルダへ纏めておきます。
- 軌道検測_首都圏路線A (track_A.csv)
- 軌道検測_首都圏路線B (track_B.csv)
- 軌道検測_地方幹線C (track_C.csv)
- 軌道検測_地方路線D (track_D.csv)
- 設備台帳_首都圏路線A (equipment_A.csv)
- 設備台帳_首都圏路線B (equipment_B.csv)
- 設備台帳_地方幹線C (equipment_C.csv)
- 設備台帳_地方路線D (equipment_D.csv)
実装¶
まず必要なライブラリをインポートします。
import datetime as dt
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
1. データの読み込み¶
4種類の軌道検測データを、路線名A,B,C,Dをキーとしたディクショナリに、項目"date"をタイムスタンプ型とした上で、データフレームとして読み込みます。
設備台帳データも同様に読み込みます。
# データの読み込み
tracks={}
for no in ['A','B','C','D']:
tracks[no] = pd.read_csv("dataset/track_" + no + ".csv", parse_dates=["date"])
equipments={}
for no in ['A','B','C','D']:
equipments[no] = pd.read_csv("dataset/equipment_" + no + ".csv")
tracks["A"].head()
equipments["A"].head()
2. データの理解¶
まずは各路線の日付や計測地点の数、目的変数である"高低左"の欠損の割合を確認してみます。
for no, track in tracks.items():
n_date = track["date"].unique().size
n_kiro = track["キロ程"].unique().size
n_data = len(track[track["高低左"].notnull()])
print('{:*^16}'.format(no))
print("期間 :{} - {} ({}days)".format(track["date"].min().date(), track["date"].max().date(), n_date))
print("計測地点数:{:,}".format(n_kiro))
print("有効データ数 / 理論データ数:{:,} / {:,} ({:.2%})".format(n_data, len(track), n_data / len(track)))
路線によって開始日が若干異なっていること、理論データ数の半数近くが欠損していることが分かります。
次にどの日付、計測地点に欠損が生じているのかを可視化してみます。
fig, axes = plt.subplots(2,2,figsize=(10,10), sharey=True)
for k, (no, track) in enumerate(tracks.items()):
i = int(k / 2)
j = k % 2
mat = track.groupby(["date","キロ程"]).max()["高低左"].unstack().notnull()
mat.index = mat.index.format()
sns.heatmap(mat, cbar=False, ax=axes[i, j])
axes[i, j].set_title(no)
plt.subplots_adjust(left=None, bottom=None, top=None, hspace=0.4)
黒い箇所が欠損を表しますが、帯状に伸びている箇所も見られます。欠損の補完方法も鍵となりそうです。
次にtrack_Dのいくつかの計測地点の"高低左"の日別推移を可視化してみます。
track = tracks["D"][["date", "キロ程","高低左"]]
# 全計測地点から10箇所をランダムサンプリング
rnd = np.random.choice(track["キロ程"].unique(), size=10, replace=False, p=None)
# サンプリングした計測地点の高低左変位を描画(Y軸のレンジを平均値±3で統一)
fig, axes = plt.subplots(10,1,figsize=(15, 20) ,sharex=True)#, sharey=True)
for i, k in enumerate(rnd):
data = track[track["キロ程"]==k]["高低左"]
data.plot(ax=axes[i], title="キロ程:" + str(k), marker=".", linewidth=0)
m = track[track["キロ程"]==k]["高低左"].mean()
axes[i].set_ylim(m-3,m+3)
様々な形状が確認できますが、中には補修作業があったと推測できるものも見てとれます。補修作業後の動き方の特徴も詳しく分析する必要がありそうです。
次に、track_Dの計測地点を始点30地点、終点30地点に絞り、"高低左","高低右"の分布を箱ひげ図を用いて可視化してみます。
track = tracks["D"]
fig, axes = plt.subplots(2,1,figsize=(15,8))
start30_kiros = track["キロ程"].unique()[:30]
end30_kiros = track["キロ程"].unique()[-30:]
track1 = track[track["キロ程"].isin(start30_kiros)]
track2 = track[track["キロ程"].isin(end30_kiros)]
# 左は赤、右は青
sns.boxplot(x = "キロ程", y="高低左",data=track1, color="r", ax=axes[0])
sns.boxplot(x = "キロ程", y="高低右",data=track1, color="b", ax=axes[0])
axes[0].set_title("開始30地点")
axes[0].set_xticklabels(start30_kiros, rotation='vertical')
axes[0].set_ylabel("")
sns.boxplot(x = "キロ程", y="高低左",data=track2, color="r", ax=axes[1])
sns.boxplot(x = "キロ程", y="高低右",data=track2, color="b", ax=axes[1])
axes[1].set_title("終了30地点")
axes[1].set_xticklabels(end30_kiros, rotation='vertical')
axes[1].set_ylabel("")
plt.subplots_adjust(left=None, bottom=None, top=None, wspace=0, hspace=0.4)
左右で若干の違いはありますが、滑らかに推移していることが分かります。予測対象の計測地点だけでなく、隣接地点の変位も参考になるかもしれません。
次に構造物の特徴に応じた"高低左"の分布の違いを見てみます。
# 軌道検測データと設備台帳データを統合
dfs={}
for no in ['A','B','C','D']:
dfs[no] = pd.merge(tracks[no], equipments[no], on="キロ程")
# 踏切と踏切以外での"高低左"の分布の違いを確認
fig, axes = plt.subplots(1, 4, figsize=(18, 3), sharex=True, sharey=True)
for i, (no, df) in enumerate(dfs.items()):
for f in [1, 0]: # 1=踏切, 0=踏切以外
tmp = df[df["踏切"]==f]["高低左"].dropna() # 欠損があるとエラーとなるため削除
tmp = tmp[(tmp < 10) & (tmp > -10)] # 範囲を限定
sns.distplot(tmp, kde=True, rug=False, ax=axes[i])
axes[i].legend(["踏切","踏切以外"])
axes[i].set_title(no)
踏切以外はどの路線も-5.0 ~ 5.0 範囲にほぼ正規分布の形状で分布、踏切はやや歪な形をしていることが分かります。構造物の考慮も予測精度に影響すると考えれます。
3. 投稿ファイルの作成¶
ここでは、単純に計測地点(キロ程)毎の平均値を予測値として、投稿ファイルを作成してみます。
index_master を確認すると、"路線","date","キロ程"の優先順に昇順でソートしたものに、idが連番で付与されているため、この構成に合わせて予測値を作成します。
p = []
for no, track in tracks.items():
mean_by_kiros = track.groupby("キロ程").mean()["高低左"] #キロ程毎の平均値
mean_by_kiros91 = np.array(list(mean_by_kiros.values) * 91) # 評価期間の91日分に拡張
p.extend(mean_by_kiros91) # 配列に追加
submit = pd.DataFrame(p)
submit = submit.fillna(0) #欠損値は0で補完
submit =submit.apply(lambda x:x.round(2)) #サイズが大きいため予測値を桁落とし
submit.to_csv("submit.csv", header=None)
投稿の結果、スコアは 0.91930 になりました。
まとめ¶
データの読み込み→データの理解→投稿ファイルの作成までの実装を一通り説明しました。
モデルの構築は行っていませんが、特徴量の選択・設計や、モデル選択、パラメータチューニング等、工夫の余地は多いと考えられます。
皆さんのご応募をお待ちしております.。