¿Cómo reemplazar NA con media por grupo/subconjunto?
Tengo un marco de datos con las longitudes y anchos de varios artrópodos de las entrañas de las salamandras. Debido a que algunas tripas tenían miles de presas determinadas, solo medí un subconjunto de cada tipo de presa. Ahora quiero reemplazar cada individuo no medido con la longitud y el ancho medios de esa presa. Quiero conservar el marco de datos y simplemente agregar columnas imputadas (largo2, ancho2). La razón principal es que cada fila también tiene columnas con datos sobre la fecha y el lugar en que se recopiló la salamandra. Podría completar el NA con una selección aleatoria de los individuos medidos, pero a efectos de argumentación, supongamos que solo quiero reemplazar cada NA con la media.
Por ejemplo, imagina que tengo un marco de datos que se parece a:
id taxa length width
101 collembola 2.1 0.9
102 mite 0.9 0.7
103 mite 1.1 0.8
104 collembola NA NA
105 collembola 1.5 0.5
106 mite NA NA
En realidad, tengo más columnas y alrededor de 25 taxones diferentes y un total de ~30.000 presas en total. Parece que el paquete plyr podría ser ideal para esto, pero no sé cómo hacerlo. No soy muy experto en R ni en programación, pero estoy tratando de aprender.
No es que sepa lo que estoy haciendo, pero intentaré crear un pequeño conjunto de datos para jugar si me ayuda.
exampleDF <- data.frame(id = seq(1:100), taxa = c(rep("collembola", 50), rep("mite", 25),
rep("ant", 25)), length = c(rnorm(40, 1, 0.5), rep("NA", 10), rnorm(20, 0.8, 0.1), rep("NA",
5), rnorm(20, 2.5, 0.5), rep("NA", 5)), width = c(rnorm(40, 0.5, 0.25), rep("NA", 10),
rnorm(20, 0.3, 0.01), rep("NA", 5), rnorm(20, 1, 0.1), rep("NA", 5)))
Aquí hay algunas cosas que he probado (que no han funcionado):
# mean imputation to recode NA in length and width with means
(could do random imputation but unnecessary here)
mean.imp <- function(x) {
missing <- is.na(x)
n.missing <-sum(missing)
x.obs <-a[!missing]
imputed <- x
imputed[missing] <- mean(x.obs)
return (imputed)
}
mean.imp(exampleDF[exampleDF$taxa == "collembola", "length"])
n.taxa <- length(unique(exampleDF$taxa))
for(i in 1:n.taxa) {
mean.imp(exampleDF[exampleDF$taxa == unique(exampleDF$taxa[i]), "length"])
} # no way to get back into dataframe in proper places, try plyr?
otro intento:
imp.mean <- function(x) {
a <- mean(x, na.rm = TRUE)
return (ifelse (is.na(x) == TRUE , a, x))
} # tried but not sure how to use this in ddply
Diet2 <- ddply(exampleDF, .(taxa), transform, length2 = function(x) {
a <- mean(exampleDF$length, na.rm = TRUE)
return (ifelse (is.na(exampleDF$length) == TRUE , a, exampleDF$length))
})
¿Alguna sugerencia?
No es mi propia técnica. La vi en los foros hace un tiempo:
dat <- read.table(text = "id taxa length width
101 collembola 2.1 0.9
102 mite 0.9 0.7
103 mite 1.1 0.8
104 collembola NA NA
105 collembola 1.5 0.5
106 mite NA NA", header=TRUE)
library(plyr)
impute.mean <- function(x) replace(x, is.na(x), mean(x, na.rm = TRUE))
dat2 <- ddply(dat, ~ taxa, transform, length = impute.mean(length),
width = impute.mean(width))
dat2[order(dat2$id), ] #plyr orders by group so we have to reorder
Editar Un enfoque sin plyr con un for
bucle:
for (i in which(sapply(dat, is.numeric))) {
for (j in which(is.na(dat[, i]))) {
dat[j, i] <- mean(dat[dat[, "taxa"] == dat[j, "taxa"], i], na.rm = TRUE)
}
}
Edite muchas lunas más tarde, aquí hay un enfoque de data.table y dplyr :
tabla de datos
library(data.table)
setDT(dat)
dat[, length := impute.mean(length), by = taxa][,
width := impute.mean(width), by = taxa]
dplyr
library(dplyr)
dat %>%
group_by(taxa) %>%
mutate(
length = impute.mean(length),
width = impute.mean(width)
)
Varias otras opciones:
1) contabla de datosLa nueva nafill
función
library(data.table)
setDT(dat)
cols <- c("length", "width")
dat[, (cols) := lapply(.SD, function(x) nafill(x, type = "const", fill = mean(x, na.rm = TRUE)))
, by = taxa
, .SDcols = cols][]
2) conzoona.aggregate
función de
library(zoo)
library(data.table)
setDT(dat)
cols <- c("length", "width")
dat[, (cols) := lapply(.SD, na.aggregate)
, by = taxa
, .SDcols = cols][]
La función predeterminada de na.aggregate
es mean
; Si desea utilizar otra función, debe especificarla con el FUN
parámetro - (ejemplo FUN = median
:). Consulte también el archivo de ayuda con ?na.aggregate
.
Por supuesto, también puedes usar esto en tidyverse:
library(dplyr)
library(zoo)
dat %>%
group_by(taxa) %>%
mutate_at(cols, na.aggregate)
Antes de responder a esto, quiero decir que soy un principiante en R. Por lo tanto, avíseme si cree que mi respuesta es incorrecta.
Código:
DF[is.na(DF$length), "length"] <- mean(na.omit(telecom_original_1$length))
y aplicar lo mismo para el ancho.
DF significa nombre del marco de datos.
Gracias, parthi