以下で生成されたdata.frame
に似たデータセットを使用しています。
set.seed(1)
dta <- data.frame(observation = 1:20,
valueA = runif(n = 20),
valueB = runif(n = 20),
valueC = runif(n = 20),
valueD = runif(n = 20))
dta[2:5,3] <- NA
dta[2:10,4] <- NA
dta[7:20,5] <- NA
列にはNA
値があり、最後の列には観測値の60%以上がNAs
あります。
> sapply(dta, function(x) {table(is.na(x))})
$observation
FALSE
20
$valueA
FALSE
20
$valueB
FALSE TRUE
16 4
$valueC
FALSE TRUE
11 9
$valueD
FALSE TRUE
6 14
dplyr
パイプラインのこの列をなんとかしてselect
引数に渡して削除できるようにしたいと思います。
これはbase
で簡単に実行できます。たとえば、50%NAs
未満の列を選択するには次のようにします。
dta[, colSums(is.na(dta)) < nrow(dta) / 2]
これは以下を生成します:
> head(dta[, colSums(is.na(dta)) < nrow(dta) / 2], 2)
observation valueA valueB valueC
1 1 0.2655087 0.9347052 0.8209463
2 2 0.3721239 NA NA
dplyr
パイプラインで同じ柔軟性を実現することに興味があります。
Vectorize(require)(package = c("dplyr", # Data manipulation
"magrittr"), # Reverse pipe
char = TRUE)
dta %<>%
# Some transformations I'm doing on the data
mutate_each(funs(as.numeric)) %>%
# I want my select to take place here
たぶんこんな感じ?
dta %>% select(which(colMeans(is.na(.)) < 0.5)) %>% head
# observation valueA valueB valueC
#1 1 0.2655087 0.9347052 0.8209463
#2 2 0.3721239 NA NA
#3 3 0.5728534 NA NA
#4 4 0.9082078 NA NA
#5 5 0.2016819 NA NA
#6 6 0.8983897 0.3861141 NA
更新colMeans
の代わりにcolSums
を使用します。これは、行数で除算する必要がなくなったことを意味します。
そして、記録のために、ベースRではcolMeans
を使用することもできます。
dta[,colMeans(is.na(dta)) < 0.5]
私はこれが仕事をしていると思います:
dta %>% select_if(~mean(is.na(.)) < 0.5) %>% head()
observation valueA valueB valueC
1 0.2655087 0.9347052 0.8209463
2 0.3721239 NA NA
3 0.5728534 NA NA
4 0.9082078 NA NA
5 0.2016819 NA NA
6 0.8983897 0.3861141 NA
`
summarise_each/unlist
で論理ベクトルを取得した後、extract
からmagrittr
を使用できます。
library(magrittr)
library(dplyr)
dta %>%
summarise_each(funs(sum(is.na(.)) < n()/2)) %>%
unlist() %>%
extract(dta,.)
または、base R
からFilter
を使用します
Filter(function(x) sum(is.na(x)) < length(x)/2, dta)
または少しコンパクトなオプションは
Filter(function(x) mean(is.na(x)) < 0.5, dta)