Colapsar filas donde algunas son todas NA, otras son disjuntas con algunas NA
aggregate (4)
Tengo un marco de datos simple como tal:
ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 25 30 NA NA
2 NA NA 35 40
Y me gustaría reformatearlo como tal:
ID Col1 Col2 Col3 Col4
1 5 10 15 20
2 25 30 35 40
(tenga en cuenta que el conjunto de datos real tiene miles de filas y los valores provienen de datos biológicos: los
NA
no siguen un patrón simple, excepto que los
NA
son disjuntos, y sí, hay exactamente 3 filas para cada
ID
).
PASO UNO
: deshacerse de las filas que solo tienen valores de
NA
.
En la superficie esto parecía simple, pero me he encontrado con algunos problemas.
complete.cases(DF)
devuelve todo
FALSE
, por lo que realmente no puedo usar esto para eliminar las filas con todos los
NA
, como en
DF[complete.cases(DF),]
.
Esto se debe a que todas las filas contienen al menos un
NA
.
Como los
NA
quieren propagarse, otros esquemas que usan
is.na
fallan por la misma razón.
PASO DOS : contraiga las dos filas restantes en una.
Pensando en usar algo como
aggregate
para lograr esto, pero tiene que haber una manera más fácil que
this
, que no funciona en absoluto.
Gracias por cualquier consejo
Aquí hay un enfoque de tabla de datos que usa
na.omit()
en las columnas, agrupadas por ID.
library(data.table)
setDT(df)[, lapply(.SD, na.omit), by = ID]
# ID Col1 Col2 Col3 Col4
# 1: 1 5 10 15 20
# 2: 2 25 30 35 40
Aquí hay un par de intentos agregados:
aggregate(. ~ ID, data=dat, FUN=na.omit, na.action="na.pass")
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
Dado que
aggregate
interfaz de fórmula de la
aggregate
usa
na.omit
en todos los datos antes de hacer cualquier agrupación, eliminará cada fila de datos, ya que todos contienen al menos un valor de
NA
.
Pruébelo:
nrow(na.omit(dat))
devuelve
0
.
Entonces, en este caso, use
na.pass
en
aggregate
y luego
na.omit
para omitir los
NA
que se pasaron.
Alternativamente, no use la interfaz de fórmula y especifique las columnas para agregar manualmente:
aggregate(dat[-1], dat[1], FUN=na.omit )
aggregate(dat[c("Col1","Col2","Col3","Col4")], dat["ID"], FUN=na.omit)
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
Tratar
library(dplyr)
DF %>% group_by(ID) %>% summarise_each(funs(sum(., na.rm = TRUE)))
Editar:
para tener en cuenta el caso en el que una columna tiene todos los
NAs
para un determinado
ID
, necesitamos la función
sum_NA()
que devuelve
NA
si todos son
NA
txt <- "ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 NA 30 NA NA
2 NA NA 35 40"
DF <- read.table(text = txt, header = TRUE)
# original code
DF %>%
group_by(ID) %>%
summarise_each(funs(sum(., na.rm = TRUE)))
# `summarise_each()` is deprecated.
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
# To map `funs` over all variables, use `summarise_all()`
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 0 30 35 40
sum_NA <- function(x) {if (all(is.na(x))) x[NA_integer_] else sum(x, na.rm = TRUE)}
DF %>%
group_by(ID) %>%
summarise_all(funs(sum_NA))
DF %>%
group_by(ID) %>%
summarise_if(is.numeric, funs(sum_NA))
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 NA 30 35 40
la manera simple es:
as.data.frame(lapply(myData[,c(''Col1'',''Col2'',''Col3'',''Col4'')],function(x)[!is.na(x)]))
pero si no todas las columnas tienen el mismo número de valores que no son
NA
, deberá recortarlos así:
temp <- lapply(myData[,c(''Col1'',''Col2'',''Col3'',''Col4'')],function(x)x[!is.na(x)])
len <- min(sapply(temp,length))
as.data.frame(lapply(temp,`[`,seq(len)))