¿Cómo reemplazar NA con media por grupo/subconjunto?

Resuelto djhocking asked hace 54 años • 6 respuestas

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?

djhocking avatar Jan 01 '70 08:01 djhocking
Aceptado

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 forbucle:

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)  
    )
Tyler Rinker avatar Feb 17 '2012 04:02 Tyler Rinker

Varias otras opciones:

1) contabla de datosLa nueva nafillfunció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.aggregatefunció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.aggregatees mean; Si desea utilizar otra función, debe especificarla con el FUNpará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)
Jaap avatar Oct 28 '2019 14:10 Jaap

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

parthiban avatar Sep 02 '2015 14:09 parthiban