はじめに
私はもともと理学療法士として働いていて現在はWebアプリ開発のITエンジニアをしています。
理学療法士の頃も病院のデータ集計や、治療データをまとめて学会で発表するのが好きでしたので、今回そういった興味でNishikaのコンペに参加してみたので、記念にデータ分析の流れについて記録しようと思います。
Webアプリ開発ではフロントエンドを主に担当しておりデータ分析に関しては完全に初心者ですが、蓄積された膨大な情報を使って新しいアイディアや解釈を得る方法について興味があります。
※本記事は主に日記ですがどこか間違っていたらすみません。
※何も知らない私のような初心者でもデータ分析を体験できるという事が他の人の後押しになればと思います。
Nishika登録からデータ分析の流れ
1. Nishikaに登録する
Nishika:https://competition.nishika.com/
2. トレーニングコンペに参加する
私は学習サイトやUdemuyの動画を参考に中古マンションの価格予測に参加しました。
Udemy動画:https://www.udemy.com/course/python-data
学習サイト:https://toukei-lab.com/achademy
3. CSVデータを取得する
参加したコンペページのデータタブでCSV形式の膨大なデータを取得します。
4. Pythonの実行環境を用意する
私は以下の方法で行っています。
-
Google Colaboratory
Python JupyterLabがブラウザ上で実行できる。 早く簡単に始めたい場合はこちらがいいと思います。
今回は分析対象データの容量が多くGoogle Driveを圧迫するため使用しない。 -
デスクトップ版のアプリ
他の言語でもそうですが私はこの方法で環境を用意すると管理できず自分のPC環境が散らかってしまいます。 -
Docker
個人的にはDockerで用意するとコンテナごと破壊したり作り直したりできるため管理がし易いです。私は今回もDockerを使いました。
親切な方がたくさん記事を出してくれます。
Google検索
私が使ったDockerFileのPythonに関する設定
FROM python:3.9.7-buster
# JupyterLab関連のパッケージ
RUN python3 -m pip install --upgrade pip \
&& pip install --no-cache-dir \
black \
jupyterlab \
jupyterlab_code_formatter \
jupyterlab-git \
lckr-jupyterlab-variableinspector \
jupyterlab_widgets \
ipywidgets \
import-ipynb
# 基本パッケージ
RUN pip install --no-cache-dir \
numpy \
pandas \
scipy \
scikit-learn \
pycaret \
matplotlib \
japanize_matplotlib \
mlxtend \
seaborn \
plotly \
requests \
beautifulsoup4 \
Pillow \
opencv-python
# 追加パッケージ
RUN pip install --no-cache-dir \
pydeps \
graphviz \
pandas_profiling \
shap \
umap \
xgboost \
lightgbm
親切な方が色々なライブラリを準備してくれていましたが、今回の分析で使用したのは下記になります。
- pandas
- numpy
- matplotlib
- seaborn
- lightgbm
5. 分析する
コンペの目的を思い出す
[Training]中古マンション価格予測・評価方法
目的変数は中古マンションの取引価格について常用対数を取った数値です。
コンペでは何かの情報からマンションの取引価格を予測しその精度を競うようです。
なぜ取引価格について10を底とした対数をとるのか謎は深まるばかりです。
分析のための材料を見る
Dockerコンテナを起動し、Jupyterを使用して取得したサンプルデータの中身を確認する。
Pythonライブラリ読み込み、サンプルデータ読み込み、結合、データフレーム化した後に…
# 欠損データ数とデータタイプの表示
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 637351 entries, 1060685 to 47003572
Data columns (total 27 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 種類 637351 non-null object
1 地域 0 non-null float64
2 市区町村コード 637351 non-null int64
3 都道府県名 637351 non-null object
4 市区町村名 637351 non-null object
5 地区名 637060 non-null object
6 最寄駅:名称 634732 non-null object
7 最寄駅:距離(分) 614306 non-null object
8 間取り 615609 non-null object
9 面積(㎡) 637351 non-null object
10 土地の形状 0 non-null float64
11 間口 0 non-null float64
12 延床面積(㎡) 0 non-null float64
13 建築年 619117 non-null object
14 建物の構造 623423 non-null object
15 用途 591214 non-null object
16 今後の利用目的 275091 non-null object
17 前面道路:方位 0 non-null float64
18 前面道路:種類 0 non-null float64
19 前面道路:幅員(m) 0 non-null float64
20 都市計画 618786 non-null object
21 建ぺい率(%) 614848 non-null float64
22 容積率(%) 614848 non-null float64
23 取引時点 637351 non-null object
24 改装 580431 non-null object
25 取引の事情等 18275 non-null object
26 取引価格(総額)_log 637351 non-null float64
dtypes: float64(10), int64(1), object(16)
memory usage: 136.2+ MB
サンプルデータにはマンションについての各種情報と、予測目標である取引価格が入っているようです。
何をしたらいいか整理する
目的と材料が分かってきましたが何をすれば予測ができるのか分かりません。
Udemy動画や学習サイトではCRISP-DMというデータ分析プロセスが紹介されています。
【5分で分かる】「CRISP-DM」の流れをデータサイエンティストが徹底解説!
※この辺でこのサイトが気に入り学習プラン(980円/月)に登録しました。
Cross-industry standard process for data mining
-
Business Understanding:ビジネス理解
※コンペなので何かのビジネス課題の解決方法としてマンション価格予測が必要となった訳ではないです。
しかしデータ分析を本来の目的である事業での価値創出に結びつけるにはそもそものビジネス理解が必要だと思われます。 -
Data Understanding:データ理解
-
Data Preparation:前加工・前処理
-
Modeling:モデル構築
※今回勉強したのはPythonでデータ理解と前処理を繰り返し最後にモデル構築を行うというものです。 -
Evaluation:モデル評価
※Nishikaが提出された予測値を評価して投稿者に順位を付けてくれます。
コンペのページをよく見ると評価方法にMAEと記載されてます。
(MAE:Mean Absolute Error/平均絶対値誤差 真の値と予測値の差の絶対値の平均)
本来はモデル評価で答え合わせをするためのデータも必要なのではと感じました。
-
Deployment:ビジネスへの落とし込み
※本来の目的はデータを何かに生かす事なので一番大切な気がします。
データ理解・前処理
前処理には以下の工程があるらしい
- 欠損値処理
- ダミー変数化
- 外れ値除去
- テーブルデータの結合
- 特徴量作成
今回はUdemyの動画の先生に言われるがままデータ項目の性質を考えた上で下記を行いました。
- 全てが欠損値であったり、データ種類が1つしかないカラムを削除した
- データの意味が重複しているカラムを削除した
→市町村名と市町村コードがあったので一方を削除
※多重共線性
重回帰分析という複数の説明変数から数値を予測する回帰という方法を利用する際、説明変数同士に相関が高い組み合わせが存在すると良くないらしい - 和暦や最寄り駅までの時間など本来数値型のものを変換した
1.5h→90や、平成3年→1991など
統計量の作成やデータの視覚化
統計量を見るとそのデータの性質が見えてくるらしいですが私には分かりません。
Pandasで算出しました。
統計量
mean: 算術平均
std: 標準偏差
min: 最小値
max: 最大値
Min-Max:四分位数
df.describe()
市区町村コード 最寄駅:距離(分) 面積(㎡) 建築年
count 637351.000000 614306.000000 637351.000000 619117.000000
mean 18513.985300 11.731487 58.663570 25.959221
std 9596.722442 12.197090 26.712019 11.431670
min 1101.000000 0.000000 10.000000 1.000000
25% 13106.000000 5.000000 45.000000 17.000000
50% 14104.000000 8.000000 65.000000 25.000000
75% 27114.000000 14.000000 75.000000 34.000000
max 47213.000000 120.000000 2000.000000 75.000000
建ぺい率(%) 容積率(%) 取引時点 取引価格(総額)_log
count 614848.000000 614848.000000 637351.000000 637351.000000
mean 67.601944 301.601876 2013.633153 7.217424
std 10.402295 148.105400 3.884546 0.353935
min 30.000000 50.000000 2005.750000 2.653213
25% 60.000000 200.000000 2010.500000 7.000000
50% 60.000000 200.000000 2013.750000 7.255273
75% 80.000000 400.000000 2016.990000 7.447158
max 80.000000 1300.000000 2019.990000 9.934498
ヒストグラム
データの分布をヒストグラムで視覚化すると、データの偏りやばらつきが見えるらしいです。
不思議な話ですが社会現象あるいは自然現象の中に現れるばらつきの多くは正規分布と呼ばれる釣鐘型の分布になるそうです。
正規分布の分かりやすいまとめ
普通に考えて駅近、広い、新しいは高額マンションの条件のように思えます。
この3つについて分布を確認しました。
# matplotlibでヒストグラムを表示
fig, axes = plt.subplots(2, 2, figsize=(20, 10))
# binsで区画数を指定,set_xlimでx最大/最小値を設定
axes[0][0].hist(df["最寄駅:距離(分)"], bins=20)
axes[0][0].set_xlabel('最寄駅:距離(分)')
axes[0][1].hist(df["面積(㎡)"], bins=200)
axes[0][1].set_xlabel('面積(㎡)')
axes[0][1].set_xlim(0, 250)
axes[1][0].hist(df["建築年"], bins=20)
axes[1][0].set_xlabel('建築年')
axes[1][1].hist(df["取引価格(総額)_log"], bins=20)
axes[1][1].set_xlabel('取引価格(総額)_log')
plt.show()
最寄り駅については以外とほとんどの物件が徒歩10~15分圏内でした。
今さらですがこれは本当に全国のマンション情報でしょうか。私の実家はマンションではないですが駅から徒歩20分はあります。
pd.set_option('display.max_rows', 40)
df["市区町村名"].value_counts()
マンションがある市区町村を物件数順に並べるとトップ5が全て東京のようです。
多分トップ5以下を含めても全体的に東京の環境に偏ったデータになってそうです。
大田区 12269
江東区 11591
世田谷区 10747
港区 10580
新宿区 10055
...
田川郡糸田町 1
行橋市 1
大川市 1
粕屋郡宇美町 1
安芸郡坂町 1
Name: 市区町村名, Length: 618, dtype: int64
fig, axes = plt.subplots(2, 1, figsize=(20, 10))
sns.countplot(x="取引年", data=df.sort_values("取引年"), ax=axes[0])
sns.countplot(x="取引時点", data=df, ax=axes[1])
この辺で取引時点というパラメータを取引年に変換しました。
2020年第一期→2020.25→2020のように文字型データから数値に変換して最後は年数の少数部分を丸めました。
散布図
取引価格に対してそれぞれ最寄り駅距離、面積、築年数を指定して散布図を作成しました。
駅近、広い、新しいが取引価格と相関があるか気になります。
fig, axes = plt.subplots(3, 1, figsize=(10, 15))
axes[0].scatter(df["最寄駅:距離(分)"], df["取引価格(総額)_log"], alpha=0.1)
axes[0].set_xlabel("最寄駅:距離(分)")
axes[0].set_ylabel("取引価格(総額)_log")
axes[1].scatter(df["面積(㎡)"], df["取引価格(総額)_log"], alpha=0.1)
axes[1].set_xlabel("面積(㎡)")
axes[2].scatter(df["建築年"], df["取引価格(総額)_log"], alpha=0.1)
axes[2].set_xlabel("建築年")
plt.show()
面積は正の相関がありそう
築年数は負の相関がありそう
最寄駅:距離(分)は負の相関がありそうな気がしますがよく分からないです
相関係数
df[["取引価格(総額)_log", "最寄駅:距離(分)","面積(㎡)","建築年"]].corr()
取引価格(総額)_log 最寄駅:距離(分) 面積(㎡) 建築年
取引価格(総額)_log 1.000000 -0.215520 0.382755 -0.541508
最寄駅:距離(分) -0.215520 1.000000 0.151880 0.118339
面積(㎡) 0.382755 0.151880 1.000000 -0.067965
建築年 -0.541508 0.118339 -0.067965 1.000000
取引価格に対して
- 建築年は弱いが負の相関がある
- 面積はもっと弱いが正の相関がある
- 最寄駅:距離(分)との相関はほとんどない
また
- 変数同士に強い相関関係は見られない
結局、取引価格に明らかに相関が高い変数はなかったと感じました。
しかし勉強サイトの続きを見ると機械学習用のライブラリがこれらの情報から適格な予測をしている事が伺えます。
count 637351.000000
mean 7.217424
std 0.353935
min 2.653213
25% 7.000000
50% 7.255273
75% 7.447158
max 9.934498
Name: 取引価格(総額)_log, dtype: float64
実際投稿したデータのMAE評価結果が0.0783なので、 平均7.217424の取引価格_logにおいて正解データとの誤差平均が0.0783という事になりかなり正確に予測しているのではないかと思います(本当?)
まとめ
Data Understanding:データ理解
Data Preparation:前加工・前処理
という工程でデータの構造を視覚化したり適切なデータ型に変換したりしました。
私はUdemyの動画の先生のマネをしただけですが、データ分析ができる人はこの工程でデータ項目の不足を補ったり、目的変数に影響を与える要因を見抜いたりして、次のモデル構築に有利になるようにデータを加工していくのだと思います。
次回モデル作成
続きは加工したデータをもとに上記のマンションの取引価格を予測するためのモデルを作成する工程になります。
pythonの機械学習用ライブラリのLightGBMがデータをすごい勢いで分析してくれるのですが、何が行われているか私には分かりません。
こうしてデータ分析の流れを体験しながら理論についても勉強を進めたいです。
機械学習と統計学の違いについて