【このページは現在作成中です】

第4回:探索的データ分析,教師なし学習

4.1 探索的データ分析

探索的データ分析 (Exploratory Data Analysis)

EDAって何?

データとお友達になる

EDAでやるべきこと

例題:シェアサイクルの需要予測問題

4_bike_sharing_pic.png
図1: 【参考】神戸コミュニティサイクル「コベリン」

準備

  1. Google Colabを開き,新規ノートブックを作成
  2. ノートブックの名前 Untitled.ipynb を bike-eda.ipynb に変更する
#準備(すべてに共通)
# PandasとNumpyをインポート
import pandas as pd
import numpy as np

# 日本語化Matplotlibもインポート
import matplotlib.pyplot as plt
!pip install japanize-matplotlib
import japanize_matplotlib

# Seabornもインポート
import seaborn as sns

# pickleをインポート(モデルの保存用)
import pickle

# データの取得
data = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/bike-sharing-day.csv")
data

(A) データの確認と整形

データ項目の確認

4_bike_data.png
図2: シェアサイクルのデータ

表データの確認

データが構造化された表にちゃんと収まっているかを確認する

表データの整形

分析用のデータセットの作成に向けて,データの型やインデクスを整えていく

シェアサイクルのデータを整形する

#先頭数行の値を確認
data.head()
#型を確認
data.info()
4_bike_dtypes.png
図3: 各変数の型
#オリジナルデータをコピーして作業
df = data.copy()
#dtedayを日付型に変換
df["dteday"] = pd.to_datetime(df["dteday"])
#確認
df.info()
df
#season, holiday, weekday, workingday, weathersitをカテゴリ型に変換
for col in ["season", "holiday", "weekday", "workingday", "weathersit"]:
  df[col] = df[col].astype("category")
#確認
df.info()
df
#dtedayをインデクスに設定して時系列データにする
df.set_index("dteday", inplace=True)
#確認
df.info()
df
#instant, yr, mnth: 冗長なので削除
df.drop(columns=["instant", "yr", "mnth"], inplace=True)
#確認
df.info()
df

(B) データを眺める

様々な観点からデータを探索(要約・可視化)して,データが持つ性質を理解する

可視化にはpythonのライブラリを使う

1変数を眺める

シェアサイクルの1変数を眺める

#describe()は基本的に数値列のみを要約する
df.describe()
#カテゴリの列を要約
for col in ["season", "holiday", "weekday", "workingday", "weathersit"]:
  print(f"\n【{col}の要約】")
  print(df[col].value_counts())
#気象データの分布を箱ひげ図で可視化
sns.boxplot(df[["temp", "atemp", "hum", "windspeed"]]) 
#利用者データの分布を箱ひげ図で可視化
sns.boxplot(df[["casual", "registered", "cnt"]])
#各変数のヒストグラムを描いてみる.サブプロットを使って,1枚の図に並べる
fig, axes= plt.subplots(nrows=4, ncols=2, tight_layout=True, squeeze=False)

for i, col in enumerate(["temp", "atemp", "hum", "windspeed", "cnt", "casual", "registered"]):
  sns.histplot(df[col], ax=axes[i//2, i%2], bins=40)
#利用者データの推移を見てみる
fig, axes = plt.subplots(nrows=2, ncols=1, tight_layout=True, squeeze=False, figsize=(6,8))

#利用者データの推移
df[["casual", "registered", "cnt"]].plot(ax=axes[0,0], title="利用者データの推移(日次)")
#利用者データの月ごとの推移を棒グラフで(登録・都度の内訳)
df[["casual", "registered"]].resample("M").sum().plot.bar(stacked=True, ax=axes[1,0], title="利用者データの推移(月次)")

2変数間の関係を眺める

シェアサイクルの2変数間の関係を眺める

#相関係数を求める
df.corr()
#カテゴリ変数も含めたければ,get_dummies()を行う
pd.get_dummies(df).corr()
#ヒートマップで可視化
sns.heatmap(df.corr(), annot=True)
#気温と総利用者数の関係.季節で色分け
sns.scatterplot(df, x="temp", y="cnt", hue="season")
#ペアプロット
sns.pairplot(df)

 

グループに分けて比較する

シェアサイクルのデータをグループに分けて比較する

【比較の観点】

#統計量の要約
df.groupby(df.index.year).describe("cnt") 
df.groupby("season").describe("cnt") 
#以下同様
# :

#箱ひげ図で比較する
fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(18,18))
sns.boxplot(df, y="cnt", x=df.index.year, ax=axes[0,0])
sns.boxplot(df, y="cnt", x="season", ax=axes[0,1])
sns.boxplot(df, y="cnt", x=df.index.month, ax=axes[1,0])
sns.boxplot(df, y="cnt", x="weekday", ax=axes[1,1])
sns.boxplot(df, y="cnt", x="holiday", ax=axes[2,0])
sns.boxplot(df, y="cnt", x="workingday", ax=axes[2,1])
sns.boxplot(df, y="cnt", x="weathersit", ax=axes[3,0])
#バイオリンプロットでもやってみてください
# 略

(C) データを修正する【前処理】

欠損値 (missing value) の処理

外れ値 (Outlier) の処理

重複行 (Duplicates) の処理

シェアサイクルのデータでやってみる

#クリーニングされていないシェアサイクルのデータをロード
data = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/bike-sharing-unclean.csv")
#コピーしておく
df = data.copy()
#欠損値の個数を数える
df.isnull().sum()
#tempが欠損している行を抜き出す
df[df["temp"].isnull()]
#前後を見てみる
df[185:195]

#行ごと削除する (dfに代入していないので,dfはそのまま)
df.dropna(subset=["temp"])[185:195]
#定数0で埋める (dfに代入していないので,dfはそのまま)
df.fillna(0)[185:195]
#中央値で埋める (dfに代入していないので,dfはそのまま)
df.fillna(df.median())[185:195]
#線形補間で埋める (dfに代入していないので,dfはそのまま)
df.interpolate()[185:195]
#すべての欠損値を線形補間で埋める
df = df.interpolate()
#欠損値を数える
df.isnull().sum()
#要約統計量を求める
df.describe()
#怪しそうな変数を表示する
df[["casual", "registered", "cnt"]].plot.box()

#条件で特定してみる
df[df["cnt"] > 80000]
#周辺のデータを見てみる
df[510:520] 
#修正する
df.loc[517, "casual"] = 533
df.loc[517, "cnt"] = 4127
#再度箱ひげ図で確認
df[["casual", "registered", "cnt"]].plot.box()
#重複行を見つける
df[df.duplicated(keep=False)]
#重複行を削除する
df = df.drop_duplicates()
#重複行を再度確認
df[df.duplicated(keep=False)]

(D) データを変換する 【前処理】

カテゴリデータのダミー変数化 (one-hot encoding)

数値データのスケーリング

4_scaling.png
図9: 単位・値域の違う変数

データの標準化 (Standardization)

4_standardization.png
図10: データの標準化
#元データを適当に作る
df = pd.DataFrame(data={"物件No.":[1,2,3,4,5],
                         "駅からの距離":[0.2,0.8,1.5,3.4,4.8],
                         "築年数":[30, 25, 5, 20, 50], 
                         "部屋数":[1,3,3,4,6], 
                         "家賃":[55000,83000,64000,72000, 100000],
                         "管理費":[5000, 10000, 10000, 20000, 30000]},
                   ).set_index("物件No.")
df
from sklearn.preprocessing import StandardScaler
#標準化のためのスケーラー
sc = StandardScaler()

#各列にフィットさせる
sc.fit(df) 
#スケール変換(sc.transform(df))して,データフレームに入れなおす
df_sc = pd.DataFrame(sc.transform(df), index=df.index,columns=df.columns)
#確認
df_sc

データの正規化 (Normalization)

4_normalization.png
図11: データの正規化
from sklearn.preprocessing import MinMaxScaler
#正規化のためのスケーラー
sc = MinMaxScaler()

#各列にフィットさせる
sc.fit(df) 
#スケール変換(sc.transform(df))して,データフレームに入れなおす
df_sc = pd.DataFrame(sc.transform(df), index=df.index,columns=df.columns)
#確認
df_sc

データ変換のTips

シェアサイクルの機械学習モデルの構築

4.2 教師なし学習:クラスタリング

教師なし学習 (unsupervised learning)

4_unsupervised_learning.png
図12: 教師あり学習と教師なし学習のイメージ

クラスタリング (clustering)

基本的な考え方

距離関数

4_distance.png
図1X 距離関数

階層的クラスタリング

4_dendrogram.png
図1Xデンドログラム

非階層的クラスタリング

4.3 教師なし学習:次元削減

次元の呪い

次元削減

主成分分析


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS