LoginSignup
17
19

More than 3 years have passed since last update.

山を登る時、ルートもわからん!頂上がどこにあるかもわからんでは遭難は確実なんじゃ!

Last updated at Posted at 2021-04-08

プログラミングの勉強を始める時、最初はとても退屈で苦痛です。

まず実行環境を整えなければなりません。初心者はそこでつまづきます。たいていはここで、熟練者の助けが必要になります。

さて、何とか実行環境が整ったら、次はプログラミング言語の文法を勉強します。ところがその作業は非常に退屈で苦痛です。

「そもそもプログラミング勉強したら、何ができるようになるの?」

それを理解するためには、プログラミングを勉強しなければいけないというデッドロック。

「山を登る時、ルートもわからん!頂上がどこにあるかもわからんでは遭難は確実なんじゃ!」

そう、これはPythonの勉強に限らず、コーラを飲んだらゲップが出るっていうくらい確実な宇宙の普遍的法則というべきでしょう。

そうなんですねー。

Python で遭難しないために

Python を使うと、いろんなことができます。色々すぎて説明しきれません。そこで、たくさんある「頂上」のひとつをここでお見せしたいと思います。まずは、意味がわからなくても構いませんので、「コピペ」で頂上まで登ってみてください。以下のコードは全て Google Colaboratory という実行環境での使用を想定しています。

まずは、図を描画するときに日本語を使えるようにするための呪文を唱えておきます。

!pip install japanize-matplotlib
Collecting japanize-matplotlib
[?25l  Downloading https://files.pythonhosted.org/packages/aa/85/08a4b7fe8987582d99d9bb7ad0ff1ec75439359a7f9690a0dbf2dbf98b15/japanize-matplotlib-1.1.3.tar.gz (4.1MB)
[K     |████████████████████████████████| 4.1MB 3.4MB/s 
[?25hRequirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from japanize-matplotlib) (3.2.2)
Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.7/dist-packages (from matplotlib->japanize-matplotlib) (1.19.5)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->japanize-matplotlib) (2.4.7)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->japanize-matplotlib) (2.8.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->japanize-matplotlib) (0.10.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->japanize-matplotlib) (1.3.1)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib->japanize-matplotlib) (1.15.0)
Building wheels for collected packages: japanize-matplotlib
  Building wheel for japanize-matplotlib (setup.py) ... [?25l[?25hdone
  Created wheel for japanize-matplotlib: filename=japanize_matplotlib-1.1.3-cp37-none-any.whl size=4120276 sha256=f1117e17562a3146802c6bf230ed7c2f5db395b38a791ca1b7881a1ad4d72573
  Stored in directory: /root/.cache/pip/wheels/b7/d9/a2/f907d50b32a2d2008ce5d691d30fb6569c2c93eefcfde55202
Successfully built japanize-matplotlib
Installing collected packages: japanize-matplotlib
Successfully installed japanize-matplotlib-1.1.3

ケーススタディ1:家賃

家賃のデータを解析します。

データ取得

まずは、データの取得を行います。

import urllib.request 

url = "https://raw.githubusercontent.com/maskot1977/toydata/main/home.txt"
urllib.request.urlretrieve(url, 'data.txt')
('data.txt', <http.client.HTTPMessage at 0x7f010d549090>)

家賃のデータの中身はこんな感じです。

import pandas as pd

df = pd.read_csv('data.txt', delimiter="\t")
df
家賃(円) 徒歩(分) 専有面積(㎡) 築年数(年) 階数(階) 新築 2階以上 南向き 駐車場 オートロック エアコン バストイレ別 追い焚き フローリング ペット相談可
0 88500 10 20.70 3 8 0 1 0 1 1 1 1 0 1 1
1 86700 10 20.70 3 7 0 1 1 1 1 1 1 0 1 1
2 87300 10 20.70 3 2 0 1 1 1 1 1 1 0 1 1
3 88200 10 20.70 3 9 0 1 1 1 1 1 1 0 1 1
4 170000 10 41.40 3 11 0 1 1 1 1 1 1 1 1 1
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
95 88000 8 21.29 10 4 0 1 0 0 1 1 1 0 1 1
96 88000 8 20.58 10 5 0 1 0 0 1 1 1 0 1 1
97 88000 8 22.04 10 9 0 1 0 0 1 1 1 0 1 0
98 88000 8 20.58 10 8 0 1 0 0 1 1 1 0 0 0
99 93000 7 22.50 7 2 0 1 0 0 1 1 1 0 1 0

100 rows × 15 columns

散布図

占有面積を横軸に、家賃を縦軸にして散布図を描いてみましょう。明らかな相関があることが分かります。

import matplotlib.pyplot as plt
import japanize_matplotlib 

plt.scatter( df['専有面積(㎡)'], df['家賃(円)'], alpha=0.5)
plt.xlabel('専有面積(㎡)')
plt.ylabel('家賃(円)')
plt.show()

output_7_0.png

主要駅からの距離が、家賃に影響するかどうか調べてみましょう。駅に近ければ近いほど高い、というわけではなさそうです。

import matplotlib.pyplot as plt

plt.scatter(df['徒歩(分)'], df['家賃(円)'], alpha=0.5)
plt.xlabel('徒歩(分)')
plt.ylabel('家賃(円)')
plt.show()

output_9_0.png

築年数との関係も調べてみましょう。ある程度以上古くなると安くなるものの、そんなに簡単な関係ではなさそうですね。

import matplotlib.pyplot as plt

plt.scatter(df['築年数(年)'], df['家賃(円)'], alpha=0.5)
plt.xlabel('築年数(年)')
plt.ylabel('家賃(円)')
plt.show()

output_11_0.png

何階建ての建物かと、家賃との関係も、緩やかな相関はあるかもしれませんが単純な話ではなさそうです。

import matplotlib.pyplot as plt

plt.scatter(df['階数(階)'], df['家賃(円)'], alpha=0.5)
plt.xlabel('築年数(年)')
plt.ylabel('階数(階)')
plt.show()

output_13_0.png

これまでに調べた関係をまとめて図示すると次のようになります。

import matplotlib.pyplot as plt
import japanize_matplotlib 

pd.plotting.scatter_matrix(df.iloc[:, :5], figsize=(8, 8)) 
plt.show()

output_15_0.png

ヒストグラム

次に、1か0か(2択)しかない属性との関係を調べてみましょう。まず、新築かどうかです。新築なら家賃が高いかと言われると、どうもそういう話ではなさそうです。

plt.hist(df[df['新築'] == 1]['家賃(円)'], alpha=0.5, label="新築", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['新築'] == 0]['家賃(円)'], alpha=0.5, label="中古", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_17_0.png

1階かどうかは、家賃とかなり関係がありそうですね。

plt.hist(df[df['2階以上'] == 1]['家賃(円)'], alpha=0.5, label="2階以上", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['2階以上'] == 0]['家賃(円)'], alpha=0.5, label="1階", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_19_0.png

南向きかどうかは、あまり関係なさそうに見えますね。

plt.hist(df[df['南向き'] == 1]['家賃(円)'], alpha=0.5, label="南向き", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['南向き'] == 0]['家賃(円)'], alpha=0.5, label="南向き以外", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_21_0.png

駐車場付きの物件は高めになる傾向がありそうです。

plt.hist(df[df['駐車場'] == 1]['家賃(円)'], alpha=0.5, label="駐車場あり", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['駐車場'] == 0]['家賃(円)'], alpha=0.5, label="駐車場なし", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_23_0.png

オートロックがついてるかどうかですが...うーん、このデータは本当に正しいのかな?と個人的には疑っています。

plt.hist(df[df['オートロック'] == 1]['家賃(円)'], alpha=0.5, label="オートロックあり", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['オートロック'] == 0]['家賃(円)'], alpha=0.5, label="オートロックなし", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_25_0.png

今の時代、エアコンがあるのが普通で、エアコンのない物件は安いようですね。

plt.hist(df[df['エアコン'] == 1]['家賃(円)'], alpha=0.5, label="エアコンあり", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['エアコン'] == 0]['家賃(円)'], alpha=0.5, label="エアコンなし", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_27_0.png

バストイレ別についても同様。

plt.hist(df[df['バストイレ別'] == 1]['家賃(円)'], alpha=0.5, label="バストイレ別", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['バストイレ別'] == 0]['家賃(円)'], alpha=0.5, label="ユニットバス", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_29_0.png

追い焚きがついてる物件は高めになるようです。

plt.hist(df[df['追い焚き'] == 1]['家賃(円)'], alpha=0.5, label="追い焚きあり", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['追い焚き'] == 0]['家賃(円)'], alpha=0.5, label="追い焚きなし", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_31_0.png

フローリングは、あるのが普通で、うーん、この分布はどう解釈すればいいんだ。

plt.hist(df[df['フローリング'] == 1]['家賃(円)'], alpha=0.5, label="フローリングあり", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['フローリング'] == 0]['家賃(円)'], alpha=0.5, label="フローリングなし", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_33_0.png

ペット相談可かどうかは、意外と、家賃との関係はなさそうに見えます。

plt.hist(df[df['ペット相談可'] == 1]['家賃(円)'], alpha=0.5, label="ペット相談可", ec='navy', bins=15, range=(50000, 200000))
plt.hist(df[df['ペット相談可'] == 0]['家賃(円)'], alpha=0.5, label="ペット相談不可", ec='navy', bins=15, range=(50000, 200000))
plt.legend()
plt.xlabel('家賃(円)')
plt.ylabel('件数')
plt.show()

output_35_0.png

以上の話は、賃貸物件に詳しい方が見れば、ツッコミどころが多いかもしれません。私は賃貸の専門家ではありませんので、すみません。とりあえず、データ解析の例をお見せしてみました。

回帰直線

解析をさらに進めてみましょう。次は「回帰直線」です。家賃が占有面積と強い正の相関があることがわかったので、占有面積から家賃を予測する回帰直線を描いてみましょう。

import matplotlib.pyplot as plt
import japanize_matplotlib 
from sklearn import linear_model

model = linear_model.LinearRegression()
model.fit(df['専有面積(㎡)'].values.reshape(len(df),1), df['家賃(円)'].T) # 予測モデルを作成
plt.scatter( df['専有面積(㎡)'], df['家賃(円)'], alpha=0.5)
plt.plot(df['専有面積(㎡)'], model.predict(df['専有面積(㎡)'].values.reshape(100,1)))
plt.xlabel('専有面積(㎡)')
plt.ylabel('家賃(円)')
plt.show()

output_37_0.png

この回帰直線が、南向き物件か否かで、どう違ってくるか調べてみましょう。

import matplotlib.pyplot as plt
import japanize_matplotlib 
from sklearn import linear_model

df1 = df[df['南向き'] == 1]
df2 = df[df['南向き'] == 0]

model1 = linear_model.LinearRegression()
model1.fit(df1['専有面積(㎡)'].values.reshape(len(df1),1), df1['家賃(円)'].T) # 予測モデルを作成
plt.scatter(df1['専有面積(㎡)'], df1['家賃(円)'], alpha=0.8)
plt.plot(df1['専有面積(㎡)'], model1.predict(df1['専有面積(㎡)'].values.reshape(len(df1),1)), label="南向き")

model2 = linear_model.LinearRegression()
model2.fit(df2['専有面積(㎡)'].values.reshape(len(df2),1), df2['家賃(円)'].T) # 予測モデルを作成
plt.scatter(df2['専有面積(㎡)'], df2['家賃(円)'], alpha=0.8)
plt.plot(df2['専有面積(㎡)'], model2.predict(df2['専有面積(㎡)'].values.reshape(len(df2),1)), label="南向き以外")

plt.xlabel('専有面積(㎡)')
plt.ylabel('家賃(円)')
plt.legend()
plt.show()

output_39_0.png

ペット相談可能物件かどうかで比べてみましょう。

import matplotlib.pyplot as plt
import japanize_matplotlib 
from sklearn import linear_model

df1 = df[df['ペット相談可'] == 1]
df2 = df[df['ペット相談可'] == 0]

model1 = linear_model.LinearRegression()
model1.fit(df1['専有面積(㎡)'].values.reshape(len(df1),1), df1['家賃(円)'].T) # 予測モデルを作成
plt.scatter(df1['専有面積(㎡)'], df1['家賃(円)'], alpha=0.8)
plt.plot(df1['専有面積(㎡)'], model1.predict(df1['専有面積(㎡)'].values.reshape(len(df1),1)), label="ペット相談可")

model2 = linear_model.LinearRegression()
model2.fit(df2['専有面積(㎡)'].values.reshape(len(df2),1), df2['家賃(円)'].T) # 予測モデルを作成
plt.scatter(df2['専有面積(㎡)'], df2['家賃(円)'], alpha=0.8)
plt.plot(df2['専有面積(㎡)'], model2.predict(df2['専有面積(㎡)'].values.reshape(len(df2),1)), label="ペット相談不可")

plt.xlabel('専有面積(㎡)')
plt.ylabel('家賃(円)')
plt.legend()
plt.show()

output_41_0.png

さて、問題は、これらの結果をどう解釈するかですけれども。占有面積や家賃の分布は一様ではなくて、25平米、10万円付近に固まった分布をしているので、解釈は慎重にしたほうがいいような気がします。

ケーススタディ2:偽札

次のデータは、ある紙幣の縦の長さ、横の長さなどを計測したものです。

データ取得

import urllib.request 

url = "https://raw.githubusercontent.com/maskot1977/ipython_notebook/master/toydata/sbnote_dataJ.txt"
urllib.request.urlretrieve(url, 'data.txt')
('data.txt', <http.client.HTTPMessage at 0x7f00f9d5ca50>)
import pandas as pd
df = pd.read_csv('data.txt', delimiter="\t")
data = df.iloc[:, :-1]
target = df['class']
data
length left right bottom top diagonal
0 214.8 131.0 131.1 9.0 9.7 141.0
1 214.6 129.7 129.7 8.1 9.5 141.7
2 214.8 129.7 129.7 8.7 9.6 142.2
3 214.8 129.7 129.6 7.5 10.4 142.0
4 215.0 129.6 129.7 10.4 7.7 141.8
... ... ... ... ... ... ...
195 215.0 130.4 130.3 9.9 12.1 139.6
196 215.1 130.3 129.9 10.3 11.5 139.7
197 214.8 130.3 130.4 10.6 11.1 140.0
198 214.7 130.7 130.8 11.2 11.2 139.4
199 214.3 129.9 129.9 10.2 11.5 139.6

200 rows × 6 columns

散布図

分布を見てみましょう。すると、どうやらこの紙幣は2つのグループから成るように見えます。

pd.plotting.scatter_matrix(data, figsize=(8, 8)) 
plt.show()

output_46_0.png

正解ラベル

実際その通りで、本物の紙幣と、偽物の紙幣(偽札)が混じったデータになります。本物か偽物かで色分けすると、次のようになります(どっちがどの色か忘れました)。

pd.plotting.scatter_matrix(data, figsize=(8, 8), c=list(target)) 
plt.show()

output_48_0.png

教師なし機械学習

では、本物か偽物かを区別する方法は作れないものでしょうか?いくつか方法はあります。一つは「教師なし機械学習」と呼ばれる手法で、クラスターというグループに自動で分割する手法です。その中で「k-means」という手法を試してみましょう。

from sklearn.cluster import KMeans 

kmeans_model = KMeans(n_clusters = 2).fit(data)
pd.plotting.scatter_matrix(data, figsize=(8, 8), c=kmeans_model.labels_) 
plt.show()

output_50_0.png

同じような結果が得られましたね。k-meansによる分類と、実際の正解データがどのくらい違うのか、混合行列を計算してみましょう。

from sklearn.metrics import confusion_matrix # 混合行列

# 予測結果と、正解(本当の答え)がどのくらい合っていたかを表す混合行列
pd.DataFrame(confusion_matrix(kmeans_model.labels_, list(target)), index=['predicted 0', 'predicted 1'], columns=['real 0', 'real 1'])
real 0 real 1
predicted 0 100 0
predicted 1 0 100

なんと、完全一致でした。

教師あり機械学習

次に、「教師あり機械学習」の一種である「ロジスティック回帰」を使ってみたいと思います。先程の「K-mean」では、「正解ラベル(本物か偽物か)」の情報を用いずに分類したのに対して、以下の「ロジスティック回帰」では、「正解ラベル(本物か偽物か)」の情報を用いて分類します。

from sklearn.linear_model import LogisticRegression # ロジスティック回帰
model = LogisticRegression(max_iter=530000) #モデルの生成
model.fit(data, list(target))
pd.plotting.scatter_matrix(data, figsize=(8, 8), c=model.predict(data)) 
plt.show()

output_54_0.png

from sklearn.metrics import confusion_matrix # 混合行列

# 予測結果と、正解(本当の答え)がどのくらい合っていたかを表す混合行列
pd.DataFrame(confusion_matrix(model.predict(data), list(target)), index=['predicted 0', 'predicted 1'], columns=['real 0', 'real 1'])
real 0 real 1
predicted 0 99 0
predicted 1 1 100

惜しい!1個だけ、外してしまいました。

以上の解析結果は、ロジスティック回帰よりもk-meansのほうが優れている、ということではありません。データの分布の形によって、どの手法が適しているかは変わります。

終わりに

以上、プログラミング初学者が陥りがちな「プログラミングで何ができるの?」というデッドロックを回避するために、ケーススタディを2つお見せしました。最初は「コピペ」だけで大丈夫です。「頂上」(何ができるか)がイメージできたら、次は、その詳細を理解したり、個別の目的のために改変したりできるように「ルート」を進んでいきましょう。

17
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
19