niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

(R) janitorパッケージを使う(1回目)

久々に真面目に書きますよ…

前回はこの記事でちょっとだけ紹介したjantorパッケージですが、結構便利な関数が揃っていて実務でも使い始めているので、少しずつ紹介していこうと思います。

niszet.hatenablog.com

あうぇ…awesome1本で有名な前処理大全で知ったのですが、これはデータの前処理というよりはデータクレンジングに相当するのですね。

gihyo.jp

Twitter上での発言

すでに何度か呟いているのですが、これらも含めて少しずつやっていきましょうか。

get_dupes()

get_dupes()はデータフレームの重複のある行を見つけて返してくれます。返却されたときにはtibbleになってるけど…。

列名を指定しない場合、すべての列をみて、重複している行を返します。
みんな大好きirisデータ、さすがに全列で重複している行はないかなと思ってやってみると…

> janitor::get_dupes(iris)
No variable names specified - using all columns.

# A tibble: 2 x 6
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species   dupe_count
         <dbl>       <dbl>        <dbl>       <dbl> <fct>          <int>
1         5.80        2.70         5.10        1.90 virginica          2
2         5.80        2.70         5.10        1.90 virginica          2

あるんだ…。重複がある場合、dupe_count列にその条件で重複した行数が入ります。この場合は重複した2行それぞれに2が入っていますね。

条件を指定する場合は、

> janitor::get_dupes(iris, Sepal.Length, Sepal.Width)
# A tibble: 60 x 6
   Sepal.Length Sepal.Width dupe_count Petal.Length Petal.Width Species
          <dbl>       <dbl>      <int>        <dbl>       <dbl> <fct>  
 1         4.70        3.20          2         1.30       0.200 setosa 
 2         4.70        3.20          2         1.60       0.200 setosa 
 3         4.80        3.00          2         1.40       0.100 setosa 
 4         4.80        3.00          2         1.40       0.300 setosa 
 5         4.80        3.40          2         1.60       0.200 setosa 
 6         4.80        3.40          2         1.90       0.200 setosa 
 7         4.90        3.10          2         1.50       0.100 setosa 
 8         4.90        3.10          2         1.50       0.200 setosa 
 9         5.00        3.40          2         1.50       0.200 setosa 
10         5.00        3.40          2         1.60       0.400 setosa 
# ... with 50 more rows

のようになります。選択した列が左側にその順に来て、そのあとにget_dupes列が来る仕様のようです。必要ならselectなどで適宜整形しましょう。

なお、列名は""で囲ってはダメで、

> janitor::get_dupes(iris, "Sepal.Length", "Sepal.Width")
Error in check_vars_in_df(dat, df_name, var_names) : 
  These variables do not match column names in iris: "Sepal.Length", "Sepal.Width"

と、エラーになってしまいます。

Twitterでも呟いた通り、行の重複があるとtidyr::spread()が使えないので、この関数で前もって重複行がないか確認すると良いです。重複がない場合は空のtibbleが返ってきます。

clean_names()

列名をいい感じのsnake_caseにしてくれる関数で、

  • ' と " を削除、
  • %はpercent_に置き換え
  • #はnumber_に置き換え
  • 文字列先頭にある、空白か句読点ではない記号、(@*[]など)は削除

などをしたうえで、大文字を小文字にして、最終的に.などは_に置き換えてくれます。こんな感じ。

> head(janitor::clean_names(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

ただ、この関数は日本語名のついた列については

janitor::clean_names(data.frame(ほげ=1,ふが=2))あ
  ほ_げ ふ_が
1     1     2

のような挙動となってしまうのでイマイチ…。内部的にはsnakecase::to_any_caseを呼んでいるので、ここの挙動を理解できれば[^1]…。 なお、全角2文字で英数字を入れていた場合には半角にしてくれます。

janitor::clean_names(data.frame(index=1,123=2))
  index 123
1     1   2

これは何気に便利ではないかな。なお、caseの引数を変えれば蛇さん以外にもできますので、詳しくはマニュアルなどを参照してください。

remove_empty()

空行、あるいは空列を削除できます。ここで空行あるいは空列は全てNAであるような行、列を指します。

実際にやってみると、

# あまりよろしくはないが…空列を追加
iris$hoge <- NA

# 確認
> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species hoge
1          5.1         3.5          1.4         0.2  setosa   NA
2          4.9         3.0          1.4         0.2  setosa   NA
3          4.7         3.2          1.3         0.2  setosa   NA
4          4.6         3.1          1.5         0.2  setosa   NA
5          5.0         3.6          1.4         0.2  setosa   NA
6          5.4         3.9          1.7         0.4  setosa   NA

で、出来たdata.frameに対して、空列を削除してみます。

> head(janitor::remove_empty(iris, "cols"))
  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

出来ました。行の場合は第二引数を"rows"としてあげてください。

なお、列と行それぞれに対応したremove_empty_cols()remove_empty_rows()もありますが、deprecatedだと警告が出ますので、使わないようにしましょう。

とりあえず1回目としてはこのくらいでいいかな…。

Enjoy!!


  1. まだ全部は解読できていないのです

  2. って今でもいうよね…?