と。

Github: https://github.com/8-u8

RでOne-hot Encodingをする with tidyverse

あけまして

おめでとうございます。今年もよろしくお願いします。

新年の抱負は以下に書きました。

note.com

One-hot Encodingをやるパッケージが使えない

過去にこんな記事を書きました。

socinuit.hatenablog.com

最近気づいたことにここで使っているパッケージdummiesがCRANから削除されているっぽいです。

install.packages("dummies")

Warning in install.packages :
  package ‘dummies’ is not available for this version of R

A version of this package for your version of R might be available elsewhere,
see the ideas at
https://cran.r-project.org/doc/manuals/r-patched/R-admin.html#Installing-packages

しょうがないなあ、と思ったのでこれをtidyverseでどうにかします。

githubは過去のリポジトリをそのまま使いました。

github.com

2023/01/07追記

これを書いた数分後(!?)にAtusyさんがこんな記事を。
tidymodelsでの実装です。はやい。

blog.atusy.net

どちらが優れているか、とかはよくわからないですが、モデルに組み込むことが前提であれば、
tidymodelsでの実装がスマートかもしれないですね。
tidyverseのスタイルに慣れている場合は僕のやり口がいいかもしれない。どっちでも。

考え方

One-hot Encodingはカテゴリ変数をダミー変数に変換する操作なので、

  1. すべて1を返すvalue列を定義
  2. tidyr::pivot_wider()でワイド変換

という操作を行います。
pivot_wider()にはいくつか引数があり、names_fromにダミー変数にしたいカテゴリ変数、
values_fromには上記1で作ったvalueを指定します。
pivot_wider()自体はこれで動きますが、デフォルトの場合はそのカテゴリが該当しない場合の値はNAです。

# A tibble: 32 × 32
   car_name…¹ car_n…² car_n…³ car_n…⁴ car_n…⁵ car_n…⁶ car_n…⁷ car_n…⁸ car_n…⁹ car_n…˟ car_n…˟ car_n…˟
        <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
 1          1      NA      NA      NA      NA      NA      NA      NA      NA      NA      NA      NA
 2         NA       1      NA      NA      NA      NA      NA      NA      NA      NA      NA      NA
 3         NA      NA       1      NA      NA      NA      NA      NA      NA      NA      NA      NA
 4         NA      NA      NA       1      NA      NA      NA      NA      NA      NA      NA      NA
 5         NA      NA      NA      NA       1      NA      NA      NA      NA      NA      NA      NA
 6         NA      NA      NA      NA      NA       1      NA      NA      NA      NA      NA      NA
 7         NA      NA      NA      NA      NA      NA       1      NA      NA      NA      NA      NA
 8         NA      NA      NA      NA      NA      NA      NA       1      NA      NA      NA      NA
 9         NA      NA      NA      NA      NA      NA      NA      NA       1      NA      NA      NA
10         NA      NA      NA      NA      NA      NA      NA      NA      NA       1      NA      NA
# … with 22 more rows, 20 more variables: `car_name_Merc 450SL` <dbl>, `car_name_Merc 450SLC` <dbl>,
#   `car_name_Cadillac Fleetwood` <dbl>, `car_name_Lincoln Continental` <dbl>,
#   `car_name_Chrysler Imperial` <dbl>, `car_name_Fiat 128` <dbl>, `car_name_Honda Civic` <dbl>,
#   `car_name_Toyota Corolla` <dbl>, `car_name_Toyota Corona` <dbl>,
#   `car_name_Dodge Challenger` <dbl>, `car_name_AMC Javelin` <dbl>, `car_name_Camaro Z28` <dbl>,
#   `car_name_Pontiac Firebird` <dbl>, `car_name_Fiat X1-9` <dbl>, `car_name_Porsche 914-2` <dbl>,
#   `car_name_Lotus Europa` <dbl>, `car_name_Ford Pantera L` <dbl>, `car_name_Ferrari Dino` <dbl>, …
# ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names

そのためvalues_fillに0を指定して実行するとよくできるでしょう。
ちなみにデータがネストしている場合はdplyr::group_by() %>% dplyr::summarise()などで集約してから。
今回は特にデータがネストしていない場合を用います。

library(tidyverse)

data("mtcars")

mtcars %>% 
  dplyr::mutate(
    car_names = row.names(.),
    dummy_value = 1) %>% 
  tidyr::pivot_wider(
    names_from = "car_names",
    names_prefix = "car_name_",
    values_from = "dummy_value",
    values_fill = 0
  ) %>% 
  dplyr::select(contains("car_name_"))

# A tibble: 32 × 32
   car_nam…¹ car_n…² car_n…³ car_n…⁴ car_n…⁵ car_n…⁶ car_n…⁷ car_n…⁸ car_n…⁹ car_n…˟ car_n…˟ car_n…˟
       <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
 1         1       0       0       0       0       0       0       0       0       0       0       0
 2         0       1       0       0       0       0       0       0       0       0       0       0
 3         0       0       1       0       0       0       0       0       0       0       0       0
 4         0       0       0       1       0       0       0       0       0       0       0       0
 5         0       0       0       0       1       0       0       0       0       0       0       0
 6         0       0       0       0       0       1       0       0       0       0       0       0
 7         0       0       0       0       0       0       1       0       0       0       0       0
 8         0       0       0       0       0       0       0       1       0       0       0       0
 9         0       0       0       0       0       0       0       0       1       0       0       0
10         0       0       0       0       0       0       0       0       0       1       0       0
# … with 22 more rows, 20 more variables: `car_name_Merc 450SL` <dbl>,
#   `car_name_Merc 450SLC` <dbl>, `car_name_Cadillac Fleetwood` <dbl>,
#   `car_name_Lincoln Continental` <dbl>, `car_name_Chrysler Imperial` <dbl>,
#   `car_name_Fiat 128` <dbl>, `car_name_Honda Civic` <dbl>, `car_name_Toyota Corolla` <dbl>,
#   `car_name_Toyota Corona` <dbl>, `car_name_Dodge Challenger` <dbl>,
#   `car_name_AMC Javelin` <dbl>, `car_name_Camaro Z28` <dbl>, `car_name_Pontiac Firebird` <dbl>,
#   `car_name_Fiat X1-9` <dbl>, `car_name_Porsche 914-2` <dbl>, `car_name_Lotus Europa` <dbl>, …
# ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names

特定のカテゴリのダミー変数が知りたいならdplyr::select()で指定すればよいですね。
なお、pythonでもpandas.get_dummies()で可能です。
どっちもどっちですが、ダミー変数のための変数を作らなくて良い分、回りくどさがないのはpythonかもしれない。

note.nkmk.me