はじめに

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

連載

使ったデータ

使ったデータは、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はsklearntrain_test_splitが有名で、RはcaretcreateDataPartitionが有名です。
ここでは、学習データ数を全体の20とし、irisのSpeciesの比率が元データと同じになるように設定しております。

Python: train_test_species

train_test_speciesでは、train_sizeを引数に取ることで、学習データ数を指定できます。また、train_sizeは比率と数どちらにも対応しており、下記コードの0.2の箇所に、数字を入れても同じような操作を実行できます。
また、train_sizetest_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

createDataPartitionでは、pを引数に取ることで、学習データの比率を指定できます。
また、listはデフォルトではTRUEとなっているため、FALSEに変更することで、train_idlistではなく、matrix型として得ることができます。今回はmatrixにしようと思うので、下記ではlist = Fとしています。
そして、timestrain_idを得る回数です。times = kの時、train_idk列の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. データをXyに分ける箇所が異なります。PythonではXyに分けてから、抽出を実行します。一方で、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にも適用できます。他にもtableを使うことで、数え上げることもできます。

fct_count(y_train)
fct_count(y_test)

#       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

連載