いろいろ倉庫

KNIME、EXCEL、R、Pythonなどの備忘録

【R】data.frameのNAの行や列を何とかしたい+α

・お題:データには欠損値がつきものだけれど、欠損値があるとエラーが出ることもある。欠損値のある行や列を消したい。

 

・適当にデータフレームを作る。

> library(tidyverse)#dplyr必須なので、tidyverseを起動
> df<- data.frame(ID = c("A-01","A-02","B-01",NA,"C-01","C-02"),
+                 Score1 = c(1,3,5,2,4,6),
+                 Score2 = c(3,NA,NA,1,5,9)
+                 )
> df
    ID Score1 Score2
1 A-01      1      3
2 A-02      3     NA
3 B-01      5     NA
4 <NA>      2      1
5 C-01      4      5
6 C-02      6      9
> df %>% str()
'data.frame':    6 obs. of  3 variables:
 $ ID    : chr  "A-01" "A-02" "B-01" NA ...
 $ Score1: num  1 3 5 2 4 6
 $ Score2: num  3 NA NA 1 5 9

 

・このまま計算すると、Score2はエラーになる。

> mean(df$Score1)
[1] 3.5
> mean(df$Score2)
[1] NA

・ちなみに、引数na.rmにTRUEを与えると計算してくれる。

> mean(df$Score2, na.rm = TRUE)
[1] 4.5

・そう良い感じの引数が設定されているとも限らないので、NAの処理を考える。

・とりあえず、行を除くことを考える。

> df %>% drop_na(.)

・いずれかの変数にNAが一つでも入っていると除かれるらしい。Score2の解析をしたい場合、IDが空でも問題ないかもしれない。Score2にだけ着目して、NAの行を削除する。

> df %>% drop_na(., Score2)

・着目する列が決まっているのなら、filterを使うのもありかも。

> !is.na(df$Score2)
[1]  TRUE FALSE FALSE  TRUE  TRUE  TRUE

> df %>% filter(.,
+               !is.na(Score2))

結果は割愛。

・場合によっては代表値で埋めても良いかもしれない。

> df %>% replace_na(.,
+                   replace = list(Score2 = mean(df$Score2, na.rm = TRUE)))

・次に、NAのある列を削除することを考える。drop_naのようなシンプルな記載は難しそう。ということで、列を抽出するselect関数を使おうと思ったが、select関数はfilter関数と仕様が異なり、TREU/FALSEのベクトルをもとに列を選び出す感じではないらしい。

> df %>% select(.,
+               c(1,3))

> df %>% select(.,
+               c(TRUE, FALSE, TRUE))

・そこで、頑張って「『NAを含まない列』の名前のリスト」を作成してfilterする方針で考えてみた。

> df %>% select(.,
+               df %>%
+                 names(.) %>%
+                 .[df %>% lapply(., anyNA) %>% unlist(.) %>% !.]
+               )

・一応目的の結果を得ることはできたが、センスの無さがにじみ出ている。

・調べてみたところ、filterに似た仕様の列バージョンの関数として、select_ifが用意されているらしいので、これを使えばわざわざ列名のベクトルを作成しなくても抽出できることになる([ ]の中身を条件にすればいけそう)。

・最後に、型で判別して列を抽出することを考える。具体的には、データ型が数値(num)の列だけ抽出する。判別には、is.numeric関数を使ってみる。

> is.numeric(df$Score1)
[1] TRUE

> df %>% select_if(.,
+                  df %>% lapply(., is.numeric) %>% unlist(.))

 

・数字の列を抽出⇒NAの行を削除、あたりは結構使いそう。

 

おわり