对于一个看似简单的任务,我遇到了一些问题:使用dplyr删除所有变量都是NA
的所有行。我知道可以使用基R (Remove rows in R matrix where all data is NA和Removing empty rows of a data file in R)来完成它,但我很想知道是否有一种使用dplyr的简单方法。
示例:
library(tidyverse)
dat <- tibble(a = c(1, 2, NA), b = c(1, NA, NA), c = c(2, NA, NA))
filter(dat, !is.na(a) | !is.na(b) | !is.na(c))
上面的filter
调用做了我想做的事情,但在我所面临的情况下(因为有大量的变量),它是不可行的。我想我们可以通过使用filter_
并首先使用(long)逻辑语句创建一个字符串来做到这一点,但是看起来应该有一种更简单的方法。
另一种方法是使用rowwise()
和do()
na <- dat %>%
rowwise() %>%
do(tibble(na = !all(is.na(.)))) %>%
.$na
filter(dat, na)
但这看起来不太好,虽然它完成了工作。其他想法?
发布于 2018-05-02 09:36:51
由于dplyr 0.7.0新的,范围过滤动词存在。使用filter_any,您可以轻松地筛选至少有一个不缺少的列的行:
# dplyr 0.7.0
dat %>% filter_all(any_vars(!is.na(.)))
使用@hejseb基准测试算法,该解决方案似乎与f4一样有效。
更新:
由于dplyr 1.0.0,上述作用域动词被取代。相反,引入了跨函数族,它允许在多个(或所有)列上执行一个函数。过滤至少有一个列不是NA的行现在看起来如下所示:
# dplyr 1.0.0
dat %>% filter(if_any(everything(), ~ !is.na(.)))
发布于 2020-10-25 10:41:58
我建议你用这个很棒的看门人包。看门人对用户非常友好:
janitor::remove_empty(dat, which = "rows")
发布于 2017-01-12 11:16:49
基准测试
@DavidArenburg提出了一些备选方案。这里是他们的一个简单的基准。
library(tidyverse)
library(microbenchmark)
n <- 100
dat <- tibble(a = rep(c(1, 2, NA), n), b = rep(c(1, 1, NA), n))
f1 <- function(dat) {
na <- dat %>%
rowwise() %>%
do(tibble(na = !all(is.na(.)))) %>%
.$na
filter(dat, na)
}
f2 <- function(dat) {
dat %>% filter(rowSums(is.na(.)) != ncol(.))
}
f3 <- function(dat) {
dat %>% filter(rowMeans(is.na(.)) < 1)
}
f4 <- function(dat) {
dat %>% filter(Reduce(`+`, lapply(., is.na)) != ncol(.))
}
f5 <- function(dat) {
dat %>% mutate(indx = row_number()) %>% gather(var, val, -indx) %>% group_by(indx) %>% filter(sum(is.na(val)) != n()) %>% spread(var, val)
}
# f1 is too slow to be included!
microbenchmark(f2 = f2(dat), f3 = f3(dat), f4 = f4(dat), f5 = f5(dat))
使用Reduce
和lapply
似乎是最快的:
> microbenchmark(f2 = f2(dat), f3 = f3(dat), f4 = f4(dat), f5 = f5(dat))
Unit: microseconds
expr min lq mean median uq max neval
f2 909.495 986.4680 2948.913 1154.4510 1434.725 131159.384 100
f3 946.321 1036.2745 1908.857 1221.1615 1805.405 7604.069 100
f4 706.647 809.2785 1318.694 960.0555 1089.099 13819.295 100
f5 640392.269 664101.2895 692349.519 679580.6435 709054.821 901386.187 100
使用更大的数据集107,880 x 40
dat <- diamonds
# Let every third row be NA
dat[seq(1, nrow(diamonds), 3), ] <- NA
# Add some extra NA to first column so na.omit() wouldn't work
dat[seq(2, nrow(diamonds), 3), 1] <- NA
# Increase size
dat <- dat %>%
bind_rows(., .) %>%
bind_cols(., .) %>%
bind_cols(., .)
# Make names unique
names(dat) <- 1:ncol(dat)
microbenchmark(f2 = f2(dat), f3 = f3(dat), f4 = f4(dat))
f5
太慢了,所以它也被排除在外。f4
似乎比以前做得更好。
> microbenchmark(f2 = f2(dat), f3 = f3(dat), f4 = f4(dat))
Unit: milliseconds
expr min lq mean median uq max neval
f2 34.60212 42.09918 114.65140 143.56056 148.8913 181.4218 100
f3 35.50890 44.94387 119.73744 144.75561 148.8678 254.5315 100
f4 27.68628 31.80557 73.63191 35.36144 137.2445 152.4686 100
https://stackoverflow.com/questions/41609912
复制相似问题