よちよちpython

独習 python/Qpython/Pydroid3/termux

Numpyだけで回帰分析その7。自動車の燃費を重回帰分析する。

Numpyだけで回帰分析その7。
自動車の燃費の重回帰分析を行う。[emoji:7E5][emoji:B5D]

machine head



実行環境


Androidスマホ
termux
Python3.8
JupyterNotebook

  • Pythonライブラリ
    • Pandas
    • Numpy

目次




データの入手


8 cylinders all mine !!

データはカリフォルニア大学アーバイン校の機械学習リポジトリーのページからauto-mpg.dataなるファイルをダウンロードした。
UCI Machine Learning Repository: Auto MPG Data Set



ここには他にも機械学習用データセットが488個もある。
ホームページ



データセットのリンクをクリックすると「Associated Tasks」と書いてある所がある。ClassificationとかRegressionとか、どういう分析向きのデータかが分かるようになっている。



入手したデータの中身・項目


auto-mpg.dataという名前のファイルをダウンロードしました。データの項目は以下。

  1. mpg: continuous
  2. cylinders: multi-valued discrete
  3. displacement: continuous
  4. horsepower: continuous
  5. weight: continuous
  6. acceleration: continuous
  7. model year: multi-valued discrete
  8. origin: multi-valued discrete
  9. 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

回帰:燃費を予測する  |  TensorFlow Core


一番上の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()

f:id:chayarokurokuro:20200124152039p:plain



おわりに


機械学習の勉強用に配布されているようなデータは既に整形済みで、ピシャッとしたものばかりでしょう。不審な文字列がたった6個紛れ込んでいるだけで我がジタバタップリ。ずいぶん苦労した。投稿も混乱した感じになっていたかと思う。
前処理8割、9割…

重回帰分析は正規化や標準化、次元削減や主成分分析といったテクニックがたくさん出てきますね。次はその辺りを調べよう。