データ分析 19/04/08
分析手法の紹介
目次
- 基本統計量、データ分布の確認
- 相関行列
- 見かけ上の相関の調査(偏相関係数)
- ラグ計算(相互相関関数)
- 因子分析
- Feature_importanceによる機械学習モデルを通じた特徴量評価
基本統計量、データ分布の確認
基本統計量
import pandas as pd
all_data = pd.read_csv("csvのファイルパス")
all_data.describe()
PandasのDataFrameに対してdescribe()
を実行するとデータ数、各カラムの平均、中央値等が表示されます。
これを眺めることで、データの大まかな傾向を理解します。
- D時間はほとんど変化してないな
- マイナスを取るデータもあるんだな
- 整数しかとらないカラムもあるな
- 第三四分位数(75%)とmaxの値が飛んでいるデータがいくつかあるので、外れ値があるかもしれないな
などなど、ざっくりと傾向を掴む程度で良いかと思います。
個人的なポイントとしてはmax
,min
を確認して、データの規模がある程度揃っているか、というのも確認します。
例えば、0~1の範囲を取るデータとmaxが100000ぐらいのデータが混在していると値が比べづらいですし、グラフにプロットした時にも見づらくなります。
また、機械学習のデータとして利用する場合もデータ規模が違いすぎる場合はスケーリングした方が精度が上がる場合がありますので、必要に応じて正規化等が必要です。
データ分布の確認
import matplotlib.pyplot as plt
def num_vis(data):
data.hist(figsize=(5, 4), color='darkblue', alpha=.7)
mean = data.mean()
median = data.median()
ymax = pd.cut(data, 10).value_counts().max()
plt.vlines(x=mean, ymin=0, ymax=ymax, colors='red', linestyles='--', lw=.7)# 平均値の直線追加
plt.annotate('Mean: {}'.format(round(mean, 2)),xy=(mean, ymax), color='red')
plt.vlines(x=median, ymin=0, ymax=ymax, colors='orange', linestyles='--', lw=.7)# 中央値の直線追加
plt.annotate('Median: {}'.format(round(median, 2)),xy=(mean, ymax*0.8), color='orange')
plt.title(data.name)
plt.show()
num_vis(all_data['A時間'])
上ではデータ全体を見るため、数字でざっと確認しましたが、今度はグラフにしてみてデータの形を確認します。
例えば「A時間」の場合、基本的には綺麗な山型の分布(正規分布)になっていますが、少数の300~500あたりの値に引っ張られて右に裾が伸びているのがわかります。
このように各データグラフ化することで、データの傾向を確認・比較しやすくなります。
今回の場合は数値データしかありませんでしたが、カテゴリデータについてもグラフ化すると表記揺れ(ex.「男性」と「男」という表記が混ざっている)の確認等にもつながります。
相関行列
all_data.corr()
縦横にそれぞれカラムを取り、それぞれのマスで交わったカラム同士の相関係数が入ったマトリクス。
相関係数は変数同士の関係を知る上で重要な値になりますので、これを一覧確認します。
seaborn
というパッケージを使うとモダンなグラフ表示ができてさらに見やすくなります。
(Excelの「条件付き書式」でも同じように色分けできるので、それでも良いと思います)
import seaborn as sns
plt.figure(figsize=(60, 60))
sns.heatmap(all_data.corr(), annot=True, cmap='plasma', linewidths=.3)
追記:seabornの日本語表示について
seabornやmatplotlibは日本語に弱いため、こちらの記事の対応をすることできれいに日本語対応させることができました。
ピアソンの相関とスピアマンの相関
相関係数の計算アルゴリズムには大きく分けて2種類あります。
-
ピアソンの積率相関
これは「比例の関係にあるか」を数値に表します。(線形の関係にあるか) 先ほどのcorr()
はデフォルトではピアソンの相関を計算します。 -
スピアマンの順位相関
順位相関とはデータを小さい順に並べたときの「順位」が相関関係にあるかを見るということです。
変化量が一定でなくても「増えた」「減った」という傾向があっていれば相関が出やすくなります。 以下のようにパラメータを渡せば、スピアマンの相関行列が取れます。all_data.corr(method='spearman')
どういう時に違いがでるか見てみます。 下の図は横軸にX、縦軸にYの変数を取ってプロットしたグラフです。
この2変数の相関をピアソンで取ると0.88になりますが、スピアマンで計算すると1になります。
スピアマンは変化量は無視して順位だけを見るので、「Xが大きくなるほどYも大きくなる」という傾向があれば相関関係に表れます。
基本的にはスピアマンの方が相関が出やすくなります。
この二つは特にどちらが良いということはなく計算方法が違うだけです。 ピアソンの相関とスピアマンの相関に大きく差がある場合は、一見変化の仕方は違うがデータの傾向は似ているということが言える可能性があります。
見かけ上の相関の確認(偏相関係数)
相関係数が高いほど、基本的には変数同士に強い関係があるのですが、「見かけ上の相関」 というものが存在します。
【例】
小学校の学力を調査したところ、「体重」と「覚えている漢字の数」の相関係数が0.8でした。
これは体重が増えれば、漢字をおぼえるということでしょうか??
実際には「学年」という第三の要素があり、体重も漢字の数も1年生より6年生の方が大きくなる傾向にあるので
体重と覚えている漢字の数は「見かけ上相関がある」ように見えます。
そこで、「学年の影響を取り除いた上で、体重と覚えている漢字の数の相関係数を計算したい」という時に使うのが偏相関係数です。
偏相関係数の計算方法自体は数学の公式なので、そのままPythonであてはめれば計算できます。
import numpy as np
import math
# 3つの相関係数を引数として渡す
def culc_p_corrcoef(target_corr, corr_ac, corr_bc):
p_corr = float(target_corr-corr_ac*corr_bc)/float(math.sqrt(1-corr_ac*corr_ac)*math.sqrt(1-corr_bc*corr_bc))
return p_corr
target_corr = 0.8
corr_ac = 0.9
corr_bc = 0.8
culc_p_corrcoef(target_corr, corr_ac, corr_bc)
-0.12401215819029748
図の場合を計算してみると、偏相関係数は-0.124なので実際にはほとんど相関がないことがわかりました。
例のようにわかりやすいデータであれば見かけ上の相関に騙されることはないと思いますが、今回のTESCOのように知識がない領域のデータだと各データが何を表しているかほとんどわからない場合もあります。
そういった時、重要な変数を選定する際に確認作業として、偏相関係数を計算します。
ラグの計算(相互相関関数)
相関係数は、データ同士にタイムラグがあり、因果関係がずれて発生していると低くなってしまう可能性があります。
下の画像は、全く同じ5000行のデータを用意し、片方だけ500行ずらしたデータで相関を取った結果です。
相関係数が0.18になりほとんど相関がなくなってしまっています。\
TESCOのプロジェクトだと、例えば「ダンパを開けてから炉内温度が上昇するまでにタイムラグがある」という話がありました。
そのラグを検出したい時に使用したのが相互相関関数です。
相互相関関数は二つの変数の類似性を評価する関数ですが、その中でラグを検出することができます。
※内部的には折り畳み積分という計算が行われており、その計算結果が最大になる箇所を探すことでラグを算出します
def xcorr(df, target, data_names):
total_result = pd.DataFrame()
target_list = []
name_list = []
oldcor_list = []
delay_list = []
newcor_list = []
improvement_list = []
for name in data_names:
result = []
target_list.append(target)
name_list.append(name)
# 処理前の相関係数
tmp = df.loc[:, [target, name]]
cornum = tmp.corr().iloc[1, 0]
oldcor_list.append(cornum)
print(cornum)
# 平均0に平準化
tmp[target] = tmp[target] - tmp[target].mean()
tmp[name] = tmp[name] - tmp[name].mean()
# 相互相関関数処理
corr = np.correlate(tmp[target], tmp[name], 'full')
estimated_delay = corr.argmax() - (len(tmp[name] - 1))
delay_list.append(estimated_delay)
print(estimated_delay)
# ラグ分をずらして再度相関係数計算
tmp[name] = tmp[name].shift(estimated_delay)
new_cornum = tmp.corr().dropna().iloc[1, 0]
newcor_list.append(new_cornum)
# 相関の改善値
improvement = abs(new_cornum) - abs(cornum)
improvement_list.append(improvement)
target_list = pd.Series(target_list)
name_list = pd.Series(name_list)
oldcor_list = pd.Series(oldcor_list)
delay_list = pd.Series(delay_list)
newcor_list = pd.Series(newcor_list)
improvement_list = pd.Series(improvement_list)
total_result = pd.concat([target_list, name_list, oldcor_list, delay_list
, newcor_list, improvement_list], axis=1)
total_result.columns = ['target', 'partner', 'old_corr', 'estimated_delay', 'new_corr', 'improvement']
return total_result
temp = pd.read_csv("C:/Users/NSW00_907535/Documents/TESCO/追加データ/20190319/20190319.csv", engine='python')
target = '燃焼室出口温度PV'
data_names = ['燃焼ストーカダンパSV']
x = xcorr(temp1, target, data_names)
画像の「estimated_delay」のところをみると14行(14分)のラグがあることがわかります。
これを矯正することでわずか(0.0137)ではありますが、相関係数も改善が見られます。
ただし、このラグが問題はデータ量が増えてくれば気にする必要がなくなる場合も多いです。 今回は14行のラグがありましたが、データ全体で100行しかないない時と、1000000行ある時ではラグの割合が違うため、当然ラグの影響度も変わります。データ数が増えれば増えるほど、ラグの問題は無視できるようになるのが一般的です。
因子分析
factor_analyzer
パッケージを使うことで因子分析を行うことができます。
因子分析とは各変数に影響を与える「共通因子」を探す作業です。(クラスタリングに近い処理だと思います)
例えば、31アイスクリームのアンケートを取った時、チョコアイスが好きな人はチョコミントが好きな可能性も高いですよね。
これは「チョコ」という共通因子があるためです。
そうやって、共通因子を見つけて変数同士をグルーピングしていく作業を因子分析といいます。
from factor_analyzer import FactorAnalyzer
fa = FactorAnalyzer()
fa.analyze(all_data, 2, rotation='varimax')
Factor1,2というのがパッケージが設定した共通因子です。
ただし、「ただしこれがどういう因子なのかは」数値を見て推測する必要があります。
Factor1を見ると風量や圧力に関係が強いことがわかります。逆に時間のデータは関係性が低そうです。
その他に独自性(uniqueness)や因子自体の寄与率の取得が可能です。
fa.get_uniqueness()
fa.get_factor_variance()
モデルの評価(feature_importance)
feature_importance
はscikit-learnで作成したモデルが持つ属性で、「どの特徴量が予測に貢献したか(重要だったか)」を表す数値です。
これによって、機械学習で作成したモデルで使用する特徴量の選択に活かすことができます。
import lightgbm as lgb
params={'learning_rate': 0.05,
'objective':'mae',
'metric':'mae',
'num_leaves': 7,
'verbose': 0,
'bagging_fraction': 0.7,
'feature_fraction': 0.7
}
reg = lgb.LGBMRegressor(**params, n_estimators=500)
reg.fit(x, y)
# feature_importanceの描画
import matplotlib.pyplot as plt
feature_importance = reg.feature_importances_
feature_importance = 100.0 * (feature_importance / feature_importance.max())
sorted_idx = np.argsort(feature_importance)
pos = np.arange(sorted_idx.shape[0]) + .5
plt.figure(figsize=(12,6))
plt.barh(pos, feature_importance[sorted_idx], align='center')
plt.yticks(pos, x.columns[sorted_idx])
plt.xlabel('Relative Importance')
plt.title('Variable Importance')
plt.savefig('kansoutai.png')
ただし、feature_importanceは「そのモデルの精度に対してどれだけ重要か」なので、精度が40%のモデルにおけるfeature_importance=0.9と、精度が95%のモデルにおけるfeature_importance=0.9では意味合いが全然異なります。
絶対的に評価できる相関係数とは、そのあたりの扱い方が違うので注意。