Mark Chang's Blog

Machine Learning, Deep Learning and Python

R Data Splitting

在做機器學習演算法時,常常需要把資料分成 training setvalidation set 這兩個資料組。

但是,要如何切割,才可以讓具有不同 label 的資料,在這兩個資料組中,平均分佈?

用統計語言 R 處理 iris 資料組為例。iris 的資料如下:

index Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
……
51 7.0 3.2 4.7 1.4 versicolor
52 6.4 3.2 4.5 1.5 versicolor
……
101 6.3 3.3 6.0 2.5 virginica
102 5.8 2.7 5.1 1.9 virginica
……

iris 的中文意思是 鳶尾花 。這筆資料中,給出了 Sepal (萼片)和 Petal (花瓣)的長寬,以及 Species (物種)。有了這筆資料,就可以把花瓣和萼片的長度,當成是 feature ,而把物種當成 label ,就可以用機器學習的演算法,以花瓣和萼片的長度,來推測物種是什麼。

首先,到 R console 中,直接輸入 iris ,就會顯示出以下資料,如下:

1
2
3
4
5
6
7
8
9
10
> iris
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
2            4.9         3.0          1.4         0.2     setosa
3            4.7         3.2          1.3         0.2     setosa
4            4.6         3.1          1.5         0.2     setosa
5            5.0         3.6          1.4         0.2     setosa
6            5.4         3.9          1.7         0.4     setosa
7            4.6         3.4          1.4         0.3     setosa
8            5.0         3.4          1.5         0.2     setosa

table 指令,可以統計一下每個物種的個數,各有幾種。

1
2
3
4
> table(iris$Species)

    setosa versicolor  virginica
        50         50         50

在這資料組中,共有三個物種,每個物種都有50筆資料。由於每種物種的個數比為1:1:1,希望在切割完後的 training setvalidation set 中,各物種的個數比也都為1:1:1。

由於這個資料已經先根據 species 排序過了,不可以直接取一個 index 把資料切成兩半。

如下,假設要取前120筆資料作為training set 而後30筆資料作為 validation set ,語法如下。

1
2
> trainSet <- iris[1:120,]
> valSet <- iris[121:150,]

table 來統計這兩組資料中 species 的分佈,會發現以下情形:

1
2
3
4
5
6
7
8
9
> table(trainSet$Species)

    setosa versicolor  virginica
        50         50         20

> table(valSet$Species)

    setosa versicolor  virginica
         0          0         30

trainSet 中,大部分的 speciessetosaversicolor ,而在 valSet 中,只有 virginica ,各物種的分佈不平均。

Split by sample

sample 是隨機在一個數字範圍中,取出任意數字(不重複或可重複)。例如在 1~10 中隨機取五個不重複的數字,可用以下方法:

1
2
> sample(1:10, size=5)
[1] 8 7 9 3 4

由於 sample 產生的結果是隨機的,所以要先設定一下 seed ,以便讓每次產生的結果都一樣。

1
> set.seed(123456)

可以隨機在 irisindex 範圍中,隨機取出 120 個數字作為 training setindex,而剩下沒取到的 30 個數字的作為 validation setindex ,作法如下:

1
2
3
> trainIndex <- sample(nrow(iris), size=120)
> trainSet <- iris[trainIndex,]
> valSet <- iris[-trainIndex,]

其中, trainIndex 是要取出來作為 training setindex 。用 table 後統計結果如下:

1
2
3
4
5
6
7
8
9
> table(trainSet$Species)

    setosa versicolor  virginica
        41         35         44

> table(valSet$Species)

    setosa versicolor  virginica
         9         15          6

看起來分佈比較平均了,但是仍有點誤差,還不是1:1:1。

Split by createDataPartition

如果要讓各個物種能夠在 training setvalidation set 中,達到1:1:1的平均分佈,可以用 createDataPartition

首先,要載入模組 caret

1
> library(caret)

createDataPartition 根據 irisspecies 種類,隨機取出120筆資料,作為 training setindex ,作法如下:

1
2
3
> trainIndex <- createDataPartition(iris$Species, p = .8, list=FALSE)
> trainSet <- iris[trainIndex,]
> valSet <- iris[-trainIndex,]

其中, iris$Species 表示要根據 irisspecies 來取 index, 而 p = .8 表示要取出 0.8150 筆資料,而 list=FALSE 表示產生出的結果不是 *list ,而是 vector

table 來統計一下分割的結果,物種以1:1:1的平均分佈在 trainSetvalSet

1
2
3
4
5
6
7
8
9
> table(trainSet$Species)

    setosa versicolor  virginica
        40         40         40

> table(valSet$Species)

    setosa versicolor  virginica
        10         10         10

Reference

關於 createDataPartition 可參考以下網址:

http://www.inside-r.org/packages/cran/caret/docs/createDataPartition

關於 Data splitting 的其他方法,可參考以下網址:

http://topepo.github.io/caret/splitting.html

Comments