Numpyだけで回帰分析その7。自動車の燃費を重回帰分析する。
Numpyだけで回帰分析その7。
自動車の燃費の重回帰分析を行う。
実行環境
Androidスマホ
termux
Python3.8
JupyterNotebook
- Pythonライブラリ
- Pandas
- Numpy
目次
- 実行環境
- 目次
- データの入手
- 入手したデータの中身・項目
- Pandasで読み込み
- Numpy linalg.lstsqで重回帰分析
- エラーが出ている原因と対処
- 清書する
- mpg算出値の確認 predict
- おまけ 馬力と燃費の単回帰分析
- おわりに
データの入手
8 cylinders all mine !!
データはカリフォルニア大学アーバイン校の機械学習リポジトリーのページからauto-mpg.data
なるファイルをダウンロードした。
UCI Machine Learning Repository: Auto MPG Data Set
ここには他にも機械学習用データセットが488個もある。
ホームページ
データセットのリンクをクリックすると「Associated Tasks」と書いてある所がある。Classification
とかRegression
とか、どういう分析向きのデータかが分かるようになっている。
入手したデータの中身・項目
auto-mpg.data
という名前のファイルをダウンロードしました。データの項目は以下。
- mpg: continuous
- cylinders: multi-valued discrete
- displacement: continuous
- horsepower: continuous
- weight: continuous
- acceleration: continuous
- model year: multi-valued discrete
- origin: multi-valued discrete
- car name: string (unique for each instance)
mpg
というのはmile per gallon
の事で燃料1ガロンあたりにつき何マイル走るかの燃費を表している。
シリンダーの数や馬力などのデータが入ってるようです。
originは整数の1,2,3が入っていて、 (origin == 1)はアメ車、(origin == 2)は欧州車、(origin == 3)は日本車とのこと。
Pandasで読み込み
ダウンロードしたファイル名は「auto-mpg.data」
というものですが、read_csv()で開ける。
こちらを参照ください。
Notebook :: Anaconda Cloud
Fatih Sarigoz - python machine_learning auto mpg dataset
一番上のAnacondaのページが適切かな。
import pandas as pd data_file = "auto-mpg.data" # 項目名の設定 column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight', 'Acceleration', 'Model Year', 'Origin', 'CarName'] # ファイルの読み込み df = pd.read_csv( data_file, delim_whitespace=True, #空白区切り names=column_names ) # 上5行表示 df.head()
MPG | Cylinders | Displacement | Horsepower | Weight | Acceleration | Model Year | Origin | CarName | |
---|---|---|---|---|---|---|---|---|---|
0 | 18.0 | 8 | 307.0 | 130.0 | 3504.0 | 12.0 | 70 | 1 | chevrolet chevelle malibu |
1 | 15.0 | 8 | 350.0 | 165.0 | 3693.0 | 11.5 | 70 | 1 | buick skylark 320 |
2 | 18.0 | 8 | 318.0 | 150.0 | 3436.0 | 11.0 | 70 | 1 | plymouth satellite |
3 | 16.0 | 8 | 304.0 | 150.0 | 3433.0 | 12.0 | 70 | 1 | amc rebel sst |
4 | 17.0 | 8 | 302.0 | 140.0 | 3449.0 | 10.5 | 70 | 1 | ford torino |
情報の確認
無事に開けたので適当に情報を確認します。
# 基本情報
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 398 entries, 0 to 397
Data columns (total 9 columns):
MPG 398 non-null float64
Cylinders 398 non-null int64
Displacement 398 non-null float64
Horsepower 398 non-null object
Weight 398 non-null float64
Acceleration 398 non-null float64
Model Year 398 non-null int64
Origin 398 non-null int64
CarName 398 non-null object
dtypes: float64(4), int64(3), object(2)
memory usage: 28.1+ KB
# 欠損値個数
df.isnull().sum()
MPG 0
Cylinders 0
Displacement 0
Horsepower 0
Weight 0
Acceleration 0
Model Year 0
Origin 0
CarName 0
dtype: int64
欠損値は無く、特に問題はないように思われるが、あとで「Horsepower」に泣かされる事にこの時はまだ気付いて居なかった。
素通りする。
# 統計的情報
df.describe()
MPG | Cylinders | Displacement | Weight | Acceleration | Model Year | Origin | |
---|---|---|---|---|---|---|---|
count | 398.000000 | 398.000000 | 398.000000 | 398.000000 | 398.000000 | 398.000000 | 398.000000 |
mean | 23.514573 | 5.454774 | 193.425879 | 2970.424623 | 15.568090 | 76.010050 | 1.572864 |
std | 7.815984 | 1.701004 | 104.269838 | 846.841774 | 2.757689 | 3.697627 | 0.802055 |
min | 9.000000 | 3.000000 | 68.000000 | 1613.000000 | 8.000000 | 70.000000 | 1.000000 |
25% | 17.500000 | 4.000000 | 104.250000 | 2223.750000 | 13.825000 | 73.000000 | 1.000000 |
50% | 23.000000 | 4.000000 | 148.500000 | 2803.500000 | 15.500000 | 76.000000 | 1.000000 |
75% | 29.000000 | 8.000000 | 262.000000 | 3608.000000 | 17.175000 | 79.000000 | 2.000000 |
max | 46.600000 | 8.000000 | 455.000000 | 5140.000000 | 24.800000 | 82.000000 | 3.000000 |
# 形式と要素数
df.shape, df.size
((398, 9), 3582)
# ユニークの個数
df.nunique()
MPG 129
Cylinders 5
Displacement 82
Horsepower 94
Weight 351
Acceleration 95
Model Year 13
Origin 3
CarName 305
dtype: int64
1行目の車シボレー・シェベル・マリブ1970
はプロレスラーの高山の愛車らしいが、燃費は18mpg = 18*(1.6/3.785)kmpl
1マイル=1.6km、1ガロン=3.785リットルとして計算すると
18*(1.6/3.785)
7.608982826948481
燃費はリッター7.6キロか。
レクサスのLS460という、今どき4600CCもあるやつが同じくらい(カタログ値)ですと。
早くも工程を次の段階の、Numpyによる重回帰分析に移そうとしている。
だが、この段階で重要な前処理を一ヶ所見過ごしているために、のちほどエラーを出すことになります。そう、Horsepower。
しくじったまま続ける。
Numpy linalg.lstsqで重回帰分析
年式やエンジンのシリンダーの数、馬力などの情報から燃費を推測する回帰分析をすることとします。
- 目的変数 燃費
- 説明変数 mpg,Origin,CarName以外
CarNameは車名で文字列。この2項目は外す。(志村! Horsepower! Horsepower!)
import numpy as np # 目的変数 燃費 二次元の列ベクトルに変換 y = df.values[:, 0].reshape(-1,1) # 表示省略 #y
# 説明変数 mpg,Origin,name以外 x = df.values[:, 1:-2] # 表示 x
array([[8, 307.0, '130.0', 3504.0, 12.0, 70],
[8, 350.0, '165.0', 3693.0, 11.5, 70],
[8, 318.0, '150.0', 3436.0, 11.0, 70],
...,
[4, 135.0, '84.00', 2295.0, 11.6, 82],
[4, 120.0, '79.00', 2625.0, 18.6, 82],
[4, 119.0, '82.00', 2720.0, 19.4, 82]], dtype=object)
# 定数項の生成 値=1 二次元列ベクトル ones = np.ones(df.shape[0]).reshape(-1,1) # 表示省略 #ones
# 説明変数に定数項を横方向に結合 x = np.hstack([x, ones]) # 表示 x
array([[8, 307.0, '130.0', ..., 12.0, 70, 1.0],
[8, 350.0, '165.0', ..., 11.5, 70, 1.0],
[8, 318.0, '150.0', ..., 11.0, 70, 1.0],
...,
[4, 135.0, '84.00', ..., 11.6, 82, 1.0],
[4, 120.0, '79.00', ..., 18.6, 82, 1.0],
[4, 119.0, '82.00', ..., 19.4, 82, 1.0]], dtype=object)
目的変数y、説明変数xの行列が準備できたので、重回帰分析マシーンにかけて偏回帰係数を算出します。
# 偏回帰係数の生成 (出力省略) coef = np.linalg.lstsq(x, y, rcond=None) # 係数表示 #coef
はい、次のErrorが出ました。(出力省略)
TypeError: No loop matching the specified signature and casting was found for ufunc lstsq_n
エラーが出ている原因と対処
「指定した何かが最小2乗法の関数のループ処理かなんかにマッチしていないものが見つかった」云々。わからん。
いろいろ検索してみたら、同じエラーを出してる状況に対して「データの型を確認、変更するように」とアドバイスしてある。
同じデータを使って機械学習の前処理を進める手順などが書かれてある有益なページを見つけたので貼っておく。
Calculate gas mileage of my car — Machine Learning Regression prediction problem
エラーが出ている原因は「Horsepower」列のデータの型にあるようである。Object
型になっている。文字列。気づかなかった。文字列のまま最小2乗法の回帰分析にかけてしまった為にエラーが出た。
- 「Horsepower」の列の型が
object型
なのを見逃した、その主な理由- データの見た目がfloat型風の文字列
- 欠損値ゼロだったので素通りした
- describeの表示にない事も見逃した
- など
確認不足と経験不足。「前処理8割」の序ノ口あたり
float型に成り済ます文字列のfloat型への変換
数値にクォーテーションマークが付いて文字列として処理された「数値成り済まし文字列」は、.astype(変換後の型)
やdtype="変換後の型"
などで変える事ができるというので、次をやった。
# 型変換(表示省略) #df['Horsepower'].astype(float)
ValueError :
could not convert string to float: '?'
またエラー。
「?」って文字列をどうやってfloat型に変換するんだよ!
知らんがな。
成り済ましの中に「?」
というモロな文字列が紛れ込んでいた為に変換できないというエラー。
というか、この「?」が何個か入ってるせいで、他のマトモな数値が全て文字列型に変えられているようなのである。
くせ者の始末
「?」
は何処に隠れ、何匹いるのか…
# くせ者を欠損値として置換 df = df.replace('?', np.nan)
# くせ者の数
df.isnull().sum()
MPG 0
Cylinders 0
Displacement 0
Horsepower 6
Weight 0
Acceleration 0
Model Year 0
Origin 0
CarName 0
dtype: int64
お客様が6名現れましたよ、ザーボンさん、ドトリアさん。
始末しておしまいッ!
# 欠損値を行ごと始末 df = df.dropna() # 生存確認 df.isnull().sum()
MPG 0
Cylinders 0
Displacement 0
Horsepower 0
Weight 0
Acceleration 0
Model Year 0
Origin 0
CarName 0
dtype: int64
フリーザはくせ者を非情にも消してしまった…
欠損値の削除量
欠損値だからと言ってむやみやたらに削除すると重要なデータまで消えかねない。
なので、削除量が元のデータ量の10%未満になるように抑えれば、削除して構わないだろう、ということらしい。
上のリンクページで削除率を算出する関数作ってやっている。
さて、「?」を欠損値に置換し行ごと削除したので、Horsepower列に残ったナンチャッテfloat型の文字列をfloat型の数値に変換する。
# 型を置換 df['Horsepower'] = df['Horsepower'].astype(float)
# 確認
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 392 entries, 0 to 397
Data columns (total 9 columns):
MPG 392 non-null float64
Cylinders 392 non-null int64
Displacement 392 non-null float64
Horsepower 392 non-null float64
Weight 392 non-null float64
Acceleration 392 non-null float64
Model Year 392 non-null int64
Origin 392 non-null int64
CarName 392 non-null object
dtypes: float64(5), int64(3), object(1)
memory usage: 30.6+ KB
Horsepowerがfloat型に変換できました。
清書する
データを整え終えたので、最初から書き直す。
import pandas as pd import numpy as np #---設定--- # ファイル data_file = "auto-mpg.data" # 項目名の設定 column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight', 'Acceleration', 'Model Year', 'Origin', 'CarName'] #---ファイル読込--- df = pd.read_csv( data_file, delim_whitespace=True, #空白区切り names=column_names ) #---データの整形--- # 変な文字列を欠損値として置換 df = df.replace('?',np.nan) # 欠損値を行ごと削除 df = df.dropna() # 型を置換 df['Horsepower'] = df['Horsepower'].astype(float) # 不要な列の削除 df = df.drop(['Origin','CarName'], axis=1) #---偏回帰係数の算出 fit--- # 目的変数 燃費 二次元の列ベクトルに変換 y = df.values[:, 0].reshape(-1,1) # 説明変数 x = df.values[:, 1:] # 定数項の生成 値=1 二次元列ベクトル ones = np.ones(df.shape[0]).reshape(-1,1) # 説明変数に定数項を横方向に結合 x = np.hstack([x, ones]) # 偏回帰係数の生成と表示 coef = np.linalg.lstsq(x, y, rcond=None) print(coef)
(array([[-3.29859089e-01],
[ 7.67843024e-03],
[-3.91355574e-04],
[-6.79461791e-03],
[ 8.52732469e-02],
[ 7.53367180e-01],
[-1.45352505e+01]]), array([4543.34702471]), 7, array([6.15008946e+04, 1.16216808e+03, 3.59615431e+02, 2.61549632e+02,
3.66686294e+01, 1.05318925e+01, 7.20970337e-01]))
以上、データファイルを読ませて重回帰分析し、多項式の偏回帰係数を出す所まで済んだ。
mpg算出値の確認 predict
本来の機械学習ならデータを学習用とテスト用に分割してテスト用データで確かめるべき所を、全部のデータで学習させてしまっております。
インデックス番号を指定してデータと予測mpg値を表示する関数を作って、値を確める。
# 燃費予測の関数 def predict_func(n): import numpy as np if n >= df.shape[0]: print("{}未満で。".format(df.shape[0])) else: exp = x[n] a=coef[0] print(df.iloc[n,:]) return a[0]*exp[0] + a[1]*exp[1] + a[2]*exp[2] + a[3]*exp[3] + a[4]*exp[4] + a[5]*exp[5] + a[6]
# 燃費予測 インデックス番号指定 predict_func(35)
MPG 19.0
Cylinders 6.0
Displacement 250.0
Horsepower 88.0
Weight 3302.0
Acceleration 15.5
Model Year 71.0
Name: 36, dtype: float64
array([17.74574])
おまけ 馬力と燃費の単回帰分析
馬力と燃費の関係図。回転数を上げると馬力が上がるので、その分だけ余計に燃費がかさむ。スポーツタイプのエンジンは燃費悪い。燃費界のラスボス、スーパーカブ号はどのあたりに位置するでしょ?
上で作ったDataFrameを使い回しています。
import matplotlib.pyplot as plt y = df.values[:,0] # mpg x = df.values[:,3] # horsepower # 回帰多項式生成 func = np.poly1d(np.polyfit(x,y , 1)) X = np.linspace(0, 300, 100) Y = func(X) # ---グラフ描画--- plt.xlabel("Horsepower") plt.ylabel("mpg") plt.title("mpg horsepower ratio") plt.scatter(x,y) plt.scatter(X, f(X)) plt.savefig("mpg_1.png") plt.show()
おわりに
機械学習の勉強用に配布されているようなデータは既に整形済みで、ピシャッとしたものばかりでしょう。不審な文字列がたった6個紛れ込んでいるだけで我がジタバタップリ。ずいぶん苦労した。投稿も混乱した感じになっていたかと思う。
前処理8割、9割…
重回帰分析は正規化や標準化、次元削減や主成分分析といったテクニックがたくさん出てきますね。次はその辺りを調べよう。