python, R, vimでデータマイニング

python, R, vim で疑問に思ったことなどを

dplyr, mutateを用いたデータハンドリング, data wrangling

モデル構築前の前処理

モデル構築する前には前処理が必要。
むしろ、前処理の方がが時間がかかる。
モデル構築ではトレーニング用とテスト用に分割するので
2つのデータセットに同じ前処理を施す。

この前処理群の管理や新変数名の設定方法など
煩雑になりがちで困っていたが自分なりの実践方法を整理しておきたい。

mutate_eachの気になる点

以前のブログで触れたがmutate_eachの気になる点として、
適用する関数が一つの場合に元変数を上書きしてしまう。

一つの関数で元変数が上書きされる例

library(dplyr)  
## 
## Attaching package: 'dplyr'
## 
##  以下のオブジェクトは 'package:stats' からマスクされています: 
## 
##      filter, lag 
## 
##  以下のオブジェクトは 'package:base' からマスクされています: 
## 
##      intersect, setdiff, setequal, union
iris <- tbl_df(iris)  
mutate_each(iris, funs(log), -Species)  
## Source: local data frame [150 x 5]
## 
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
## 1      1.629241    1.252763    0.3364722  -1.6094379  setosa
## 2      1.589235    1.098612    0.3364722  -1.6094379  setosa
## 3      1.547563    1.163151    0.2623643  -1.6094379  setosa
## 4      1.526056    1.131402    0.4054651  -1.6094379  setosa
## 5      1.609438    1.280934    0.3364722  -1.6094379  setosa
## 6      1.686399    1.360977    0.5306283  -0.9162907  setosa
## 7      1.526056    1.223775    0.3364722  -1.2039728  setosa
## 8      1.609438    1.223775    0.4054651  -1.6094379  setosa
## 9      1.481605    1.064711    0.3364722  -1.6094379  setosa
## 10     1.589235    1.131402    0.4054651  -2.3025851  setosa
## ..          ...         ...          ...         ...     ...

解決策

あまり綺麗な方法ではないがeval関数を使用することで解決する。

mutate_each(iris, funs(eval, log), -Species)  
## Source: local data frame [150 x 13]
## 
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
## 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
## 9           4.4         2.9          1.4         0.2  setosa
## 10          4.9         3.1          1.5         0.1  setosa
## ..          ...         ...          ...         ...     ...
## Variables not shown: Sepal.Length_eval (dbl), Sepal.Width_eval (dbl),
##   Petal.Length_eval (dbl), Petal.Width_eval (dbl), Sepal.Length_log (dbl),
##   Sepal.Width_log (dbl), Petal.Length_log (dbl), Petal.Width_log (dbl)

余分に作成される変数 *_eval はあとで削除する。

mutate_each(iris, funs(eval, log), -Species) %>%  
  select(-ends_with("_eval"))  
## Source: local data frame [150 x 9]
## 
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
## 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
## 9           4.4         2.9          1.4         0.2  setosa
## 10          4.9         3.1          1.5         0.1  setosa
## ..          ...         ...          ...         ...     ...
## Variables not shown: Sepal.Length_log (dbl), Sepal.Width_log (dbl),
##   Petal.Length_log (dbl), Petal.Width_log (dbl)

前処理の関数化

前処理を関数化することで異なるデータセットに処理を適用するようにする。

  1. Species を文字列に型変換
  2. 数値型の4変数を対数変換
  3. 数値型の値が5以上の場合は5に丸める。
.wrangling <- function(.data) {  
  .data %>%  
    mutate(Species_as.character = as.character(Species)) %>%  
    mutate_each(  
      funs(  
        eval,   
        log,  
        round5 = ifelse(. > 5, 5, .)  
      ),  
      -starts_with("Species")  
    ) %>%  
    select(-ends_with("_eval"))  
}  
iris.wrangling <- .wrangling(iris)  
summary(iris.wrangling)  
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##        Species   Species_as.character Sepal.Length_log Sepal.Width_log 
##  setosa    :50   Length:150           Min.   :1.459    Min.   :0.6931  
##  versicolor:50   Class :character     1st Qu.:1.629    1st Qu.:1.0296  
##  virginica :50   Mode  :character     Median :1.758    Median :1.0986  
##                                       Mean   :1.755    Mean   :1.1074  
##                                       3rd Qu.:1.856    3rd Qu.:1.1939  
##                                       Max.   :2.067    Max.   :1.4816  
##  Petal.Length_log Petal.Width_log   Sepal.Length_round5 Sepal.Width_round5
##  Min.   :0.000    Min.   :-2.3026   Min.   :4.300       Min.   :2.000     
##  1st Qu.:0.470    1st Qu.:-1.2040   1st Qu.:5.000       1st Qu.:2.800     
##  Median :1.470    Median : 0.2624   Median :5.000       Median :3.000     
##  Mean   :1.175    Mean   :-0.1723   Mean   :4.955       Mean   :3.057     
##  3rd Qu.:1.629    3rd Qu.: 0.5878   3rd Qu.:5.000       3rd Qu.:3.300     
##  Max.   :1.932    Max.   : 0.9163   Max.   :5.000       Max.   :4.400     
##  Petal.Length_round5 Petal.Width_round5
##  Min.   :1.000       Min.   :0.100     
##  1st Qu.:1.600       1st Qu.:0.300     
##  Median :4.350       Median :1.300     
##  Mean   :3.565       Mean   :1.199     
##  3rd Qu.:5.000       3rd Qu.:1.800     
##  Max.   :5.000       Max.   :2.500