Esta es la contestación más exacta que encomtrarás dar, pero primero obsérvala pausadamente y valora si es compatible a tu proyecto.
Solución:
Desde dplyr 0.7.0, existen nuevos verbos de filtrado de ámbito. Usando filter_any puede filtrar fácilmente filas con al menos una columna que no falta:
dat %>% filter_all(any_vars(!is.na(.)))
Usando el algoritmo de evaluación comparativa @hejseb, parece que esta solución es tan eficiente como f4.
evaluación comparativa
@DavidArenburg sugirió varias alternativas. Aquí hay una evaluación comparativa simple de ellos.
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))
Utilizando Reduce
y lapply
parece ser el mas rapido:
> 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
Uso de un conjunto de datos más grande 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
es demasiado lento por lo que también se excluye. f4
parece hacerlo relativamente mejor que antes.
> 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
Comenzando con dyplr 1.0, la viñeta colwise da un caso similar como ejemplo:
filter(across(everything(), ~ !is.na(.x))) #Remove rows with *any* NA
Podemos ver que usa la misma lógica "&" implícita filter
usos con múltiples expresiones. Entonces, el siguiente ajuste menor selecciona todas las filas NA:
filter(across(everything(), ~ is.na(.x))) #Remove rows with *any* non-NA
Pero la pregunta pide el conjunto inverso: eliminar filas con todos N / A.
- Podemos hacer un simple
setdiff
usando el anterior, o - podemos usar el hecho de que
across
devuelve un tibble lógico yfilter
efectivamente hace una fila sabiaall()
(es decir &).
P.ej:
rowAny = function(x) apply(x, 1, any)
anyVar = function(fcn) rowAny(across(everything(), fcn)) #make it readable
df %<>% filter(anyVar(~ !is.na(.x))) #Remove rows with *all* NA
O:
filterout = function(df, ...) setdiff(df, filter(df, ...))
df %<>% filterout(across(everything(), is.na)) #Remove rows with *all* NA
O incluso combinar los 2 anteriores para expresar el primer ejemplo más directamente:
df %<>% filterout(anyVar(~ is.na(.x))) #Remove rows with *any* NA
En mi opinión, el tidyverse filter
La función se beneficiaría de un parámetro que describa la 'lógica de agregación'. Por defecto podría ser "todos" y preservar el comportamiento, o permitir "cualquiera" para que no tengamos que escribir anyVar
-como funciones auxiliares.
Valoraciones y comentarios
Si te gusta este mundo, eres capaz de dejar una reseña acerca de qué le añadirías a esta reseña.