有条件地选择 dplyr 中某些值比例为 NA 的列
Posted
技术标签:
【中文标题】有条件地选择 dplyr 中某些值比例为 NA 的列【英文标题】:Conditionally selecting columns in dplyr where certain proportion of values is NA 【发布时间】:2016-04-23 11:16:19 【问题描述】:数据
我正在使用类似于下面生成的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
【问题讨论】:
您可以使用Filter
,即Filter(function(x) sum(is.na(x)) < length(x)/2, dta)
@akrun 一如既往,感谢您的帮助。我只是想知道,filter
不应该放弃观察吗?我有兴趣删除列而不是行。
它正在删除列,即Filter
,大写为F
@akrun 现在,我找到你了?Filter != ?filter
:)
我用summarise_each
添加了一个解决方案。也许对你有帮助。
【参考方案1】:
也许像这样?
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]
【讨论】:
【参考方案2】:也许是 2020 年的更新,现在 dplyr
已达到 1.0.0,其中包含 where()
:
dta %>% select(where(function(x) sum(is.na(x)) / length(x) < 0.5))
【讨论】:
【参考方案3】:我认为这样做可以:
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
`
【讨论】:
【参考方案4】:在得到summarise_each/unlist
的逻辑向量后,我们可以使用magrittr
中的extract
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)
【讨论】:
我有点无法理解整个问题。似乎dta[colMeans(is.na(dta)) < .5]
是最简洁/矢量化/简单/可读的解决方案,所有这些Filter
或dplyr/magrittr
的东西有什么意义?我可能老了。
@DavidArenburg 你知道colMeans
、colSums
等将数据转换为matrix
。它可能没有那么有效(可能是我错了)。但是,我喜欢Filter
,因为它为它增添了一种异国情调(就像question 中的filter
。除此之外,magrittr
的东西仅供dplyr/magritr
的粉丝使用(我的拙见)。跨度>
其实is.na
已经转换成矩阵了。 col*
函数已经在矩阵上运行。因此,如果您愿意,我们可以做dta[colMeans(sapply(dta, is.na)) < .5]
,而且它仍然非常易读。在那个链接的问题中,filter
实际上是最简洁/矢量化/简单的解决方案,而不是一个异国情调的解决方案。但我猜你不是我农庄的地址。
@DavidArenburg 问题是关于在dplyr
管道中交付转换。我认为base
解决方案在这种情况下可能会更有效,但我的目标是在dplyr
管道内 进行这种转换。这主要是出于个人偏好的动机,即在dplyr
范围内与其他转换一起进行这种转换,这主要导致代码演示和 R 脚本更适合我。我同意base
可以证明更有效,但是。我也喜欢@akrun 将exotic touch 添加到解决方案中的想法。
@DavidArenburg 我还可以补充一点,这种转换是更长的工作流程的一部分,包括最后的图表。实际上,这些转换仅用于生成特定更改的目的,因此撇开性能问题不谈,代码在 take master data -> do some things on it 行上读得更少是有意义的-> 生成图表。它只是很好,我会更简洁地阅读。正如通过教学,我们学习正确指出的那样,主要是关于工作流程而不是其他任何事情。在我的辩护中,我会说这在一定程度上是品味问题。以上是关于有条件地选择 dplyr 中某些值比例为 NA 的列的主要内容,如果未能解决你的问题,请参考以下文章
R语言dplyr包na_if函数根据条件将数据对象替换为NA值实战
R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:基于条件判断创建布尔型指示变量将异常离散编码转化为NA值
R语言dplyr包将dataframe中的NA值替换(replace)为0实战:所有NA值替换(replace)为0具体列的NA值替换(replace)为0若干列的NA值替换(replace)为0