Follow @data_no_memo

メモ

個人的なメモです。他者にわかりやすく書くよりも未来の自分にわかりやすく書いています。なお、記事内容の正確さは保証できません。勉強中の身ですので、間違い等ご指摘頂けたら幸いです。

機械学習・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%となった。低い…。