機械学習・Pythonお勉強(k最近傍法を例にして 実践編)
はじめに
今回はkNN法の実践編を書く。なお、その理論編は以下の通り。 abcxyzonetwothree.hatenablog.com 今回も前回の実践編と同様に有名なtitanicデータを用いる事にする。なお、前回の実践編の記事は以下の通り。 abcxyzonetwothree.hatenablog.com
使用するデータと変数
今回使用する変数は「性別」「Pclass」「家族人数」とする。これらを用いてSurvived or notをkNN法で予測してみる。細かい変数の作成方法については以下のコードを参照されたい。なお、前回同様データの80%を学習データ、残りの20%をテストデータとする。
##### パッケージの読み込み #### import pandas as pd import seaborn as sns import numpy as np import matplotlib.pyplot as plt import random ##### データの読み込み #### data = pd.read_csv('train.csv') # train.csvという名前でデータ保存 ## Survived ## #度数 data['Survived'].value_counts() ## sex ## # 度数 data['Sex'].value_counts() # 男性 = 1のダミー変数 a = [] for i in data['Sex']: if i == 'male': a.append(1) elif i == 'female': a.append(0) data['Sex'] = a #Pclassの度数 data['Pclass'].value_counts() ## 家族人数(n_fam = SibSp + Parch)## # n_fam作成 data['SibSp'].value_counts() data['Parch'].value_counts() data['n'] = data['SibSp'] + data['Parch'] data['n'].value_counts() n_fam = [] for x in data['n']: if x >= 3: n_fam.append(3) else : n_fam.append(x) data['n_fam'] = n_fam data['n_fam'].value_counts() #データ分割 random.seed(1234) N = len(data['Sex']) #サンプル数 train_data_N = round(N * 0.8) #学習データのN test_data_N = N - train_data_N #テストデータのN train_data = data.sample(n = train_data_N, replace = False) #ランダムに80%サンプル test_data = data.drop(train_data.index) #残りをtest_data train_data = train_data[['Sex', 'Pclass', 'n_fam', 'Survived']] test_data = test_data[['Sex', 'Pclass', 'n_fam', 'Survived']] train_data = train_data.dropna(subset = ['Sex', 'Pclass', 'n_fam', 'Survived'], axis = 0) test_data = test_data.dropna(subset = ['Sex', 'Pclass', 'n_fam', 'Survived'], axis = 0) train_data = train_data.reset_index() test_data = test_data.reset_index()
kNN法のコード
kNN法は以下のコードのように実行した。あっているかなぁ…。データは全てz変換することとした。
#### kNN法 ##### # distance btw sample1 and sample2 def dis(sample1, sample2): res = [] for i, j in zip(sample1, sample2): a = (i - j) ** 2 res.append(a) return sum(res) ** 0.5 # dataをz変換 def z_score(data): for i in data: z = [] colname = str(i) mean = (data[colname]).mean() sd = (data[colname]).std() for x in data[colname]: z.append((x - mean) / sd) data[colname] = z z_score(train_data) # kNN def kNN(train_data, test_data, k): ncol = len(test_data.columns) # N of column col = ncol - 1 # 最後のcolumnは予測すべきクラスなので除外 res = [] for i in range(len(test_data)): sample1 = test_data.iloc[i, 0:col] # サンプルiのtest_data distance = [] for j in range(len(train_data)): sample2 = train_data.iloc[j, 0:col] # サンプルjのtrain_data distance.append(dis(sample1, sample2)) # 距離計算 comp_data = pd.DataFrame(distance) # DataFrameで保管 comp_data = comp_data.sort_values(by = 0) # sortで小さい順に整列 k_data = comp_data[:k] # 上からk番目を抽出 index = k_data.index # k_dataのindexを取得 res_class = train_data.iloc[index, -1] # indexの結果を取得 # 結果は多数決 同点の場合はランダム if sum(res_class) < k/2: prediction = 0 elif sum(res_class) > k/2: prediction = 1 elif sum(res_class) == k: prediction = random.choice([1, 0]) res.append(prediction) # 結果を収納 # 結果出力 N_sucess = 0 N_fail = 0 for i, j in zip(test_data['Survived'], res): if i == j: N_sucess += 1 elif i != j: N_fail += 1 rate = N_sucess / (N_sucess + N_fail) return rate kNN(train_data, test_data, k = 10)
k = 10で実行してみた。その結果、正解率は63.48%となった。低い…。