はじめに

deepblueインターン生の中山です。
【列分割・NA操作・サンプリング】PythonとRのデータフレーム操作比較 vol.4の続きの記事です。
今回は短い内容ですが、学習データとテストデータの分割について触れたいと思います。

連載

【読み込み、要約統計量、行・列選択、代入】PythonとRのデータフレーム操作比較 vol.1
【列追加、重複削除、列名変更、結合、集約】PythonとRのデータフレーム操作比較 vol.2
【集計、ソート、縦持ち・横持ち変換】PythonとRのデータフレーム操作比較 vol.3
【列分割・NA操作・サンプリング】PythonとRのデータフレーム操作比較 vol.4
【train/test分割】PythonとRのデータフレーム操作比較 vol.5

使ったデータ

使ったデータは、irisデータです。
3クラスの分類を行いたかったので、irisにしました。

データ読み込み

Python

import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['Target'] = iris.target_names[iris.target]

R

library(tidyverse)
library(caret)
df <- iris %>%
  rename(Target = Species)

データの分割

データの分割方法に関しては、いくつかありますが、ここでは以下の2つを用いて説明します。
Pythonはsklearnのtrain_test_splitが有名で、RはcaretのcreateDataPartitionが有名です。
ここでは、学習データ数を全体の20とし、irisのSpeciesの比率が元データと同じになるように設定しております。

PythonR
train_test_split()createDataPartition

Python: train_test_species

train_test_speciesでは、train_sizeを引数に取ることで、学習データ数を指定できます。また、train_sizeは比率と数どちらにも対応しており、下記コードの0.2の箇所に、30と入れても同じような操作を実行できます。
また、train_sizeをtest_sizeと変更しても、テストデータに対して同じ操作を実行できます。
そして、stratify(層別化の意)にyを入れることで、yの比率を一定にできます。

X = df.drop('Target',axis=1)
y = df[['Target']]
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 0.2, stratify = y)
# 下記でも実行可能
# X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 30, stratify = y)  

R: createDataPartition

train_test_speciesでは、pを引数に取ることで、学習データの比率を指定できます。
また、listはデフォルトではTRUEとなっているため、FALSEに帰ることで、train_idをlistではなく、matrixにして得ることができます。個人的にはmatrixが好きなので、下記ではFにしています。
そして、timesはtrain_idを得る回数です。timesがkの時、train_idはk列のmatrixとなって得ることができます。複数回のシミュレーションを実行したいときには便利です。

train_id <- df$Target %>%
  createDataPartition(p = 0.2, list = F, times = 1)
X_train <- df[train_id, !(colnames(df) %in% 'Target')]
X_test <- df[-train_id, !(colnames(df) %in% 'Target')]
y_train <- df[train_id, 'Target']
y_test <- df[-train_id, 'Target']

PythonとRでの差異

  1. データをXとyに分ける箇所が異なります。PythonではXとyに分けてから、抽出を実行します。一方で、Rではデータフレームをそのままに分割を実行できます。
  2. 学習データ数を指定できるかが変わります。Pythonでは、比率に加えて、int型の値を入れることで学習データ数を指定できます。ただ、Rでは数字を入れることはできず、比率のみの設定となります。
  3. 繰り返しの回数が異なります。Pythonでは、毎回train_test_splitを実行しますが、createDataPartitionであればtimesに回数を指定する事で、データを得ることができます。

この2つの違いは小さなものなので、正直そこまで気にするほどではないと思います。

注意点

詳細は割愛しますが、Rのirisは、Speciesがfactor型となっています。なので、以下のように2つのSpeciesだけを取得して実行してもエラーが出てしまいます。

df <- iris %>%
  rename(Target = Species) %>%
  filter(Target == 'setosa'|Target == 'versicolor')
train_id <- df$Target %>%
  createDataPartition(p = 0.2, list = F, times = 1)
X_train <- df[train_id, !(colnames(df) %in% 'Target')]
X_test <- df[-train_id, !(colnames(df) %in% 'Target')]
y_train <- df[train_id, 'Target']
y_test <- df[-train_id, 'Target']
# createDataPartition(., p = 0.2, list = FALSE, times = 1) で:
#   Some classes have no records ( virginica ) and these will be ignored 

このような時は、Targetの型をchr型に変更すると回避できます。
具体的には、下記コードをtrain_idの前に挿入します。
他にもいくつか回避法はありますが、ここでは一例だけ示しておきます。

df$Target <- as.character(df$Target)

結果の確認方法

Python: value_counts()

yの中に含まれるクラスの数をカウントします。
value_counts()でdataframeの中を数えることができます。

print(y_train.value_counts(),'\n')
print(y_test.value_counts())
# Target
# virginica   10
# versicolor  10
# setosa      10
# dtype: int64
# Target
# virginica   40
# versicolor  40
# setosa      40
# dtype: int64 

R: fct_count

yの中に含まれるクラスの数をカウントします。
fct_count()でカウントできます。fctとありますが、factorだけでなくcharacterにも適用できます。

fct_count(y_train)
fct_count(y_test)
# A tibble: 3 x 2
#       f          n
#  1 setosa       10
#  2 versicolor   10
#  3 virginica    10
# A tibble: 3 x 2
#       f          n
#  1 setosa       40
#  2 versicolor   40
#  3 virginica    40

連載

【読み込み、要約統計量、行・列選択、代入】PythonとRのデータフレーム操作比較 vol.1
【列追加、重複削除、列名変更、結合、集約】PythonとRのデータフレーム操作比較 vol.2
【集計、ソート、縦持ち・横持ち変換】PythonとRのデータフレーム操作比較 vol.3
【列分割・NA操作・サンプリング】PythonとRのデータフレーム操作比較 vol.4
【train/test分割】PythonとRのデータフレーム操作比較 vol.5