13 votos

Imputación múltiple de valores perdidos

Me gustaría utilizar la imputación para reemplazar los valores perdidos en mi conjunto de datos bajo ciertas restricciones.

Por ejemplo, me gustaría que la variable imputada x1 sea mayor o igual que la suma de mis otras dos variables, digamos x2 y x3 . También quiero x3 para ser imputado por cualquiera de los dos 0 o >= 14 y quiero x2 para ser imputado por cualquiera de los dos 0 o >= 16 .

Intenté definir estas restricciones en SPSS para la imputación múltiple, pero en SPSS sólo puedo definir valores máximos y mínimos. ¿Hay alguna forma de definir más restricciones en SPSS o conoce algún paquete de R que me permita definir dichas restricciones para la imputación de valores perdidos?

Mis datos son los siguientes:

   x1 =c(21, 50, 31, 15, 36, 82, 14, 14, 19, 18, 16, 36, 583, NA,NA,NA, 50, 52, 26, 24)
   x2 = c(0, NA, 18,0, 19, 0, NA, 0, 0, 0, 0, 0, 0,NA,NA, NA, 22, NA, 0, 0)
   x3 = c(0, 0, 0, 0, 0, 54, 0 ,0, 0, 0, 0, 0, 0, NA, NA, NA, NA, 0, 0, 0)
   dat=data.frame(x1=x1, x2=x2, x3=x3)
   > dat
       x1 x2 x3
   1   21  0  0
   2   50 NA  0
   3   31 18  0
   4   15  0  0
   5   36 19  0
   6   82  0 54
   7   14 NA  0
   8   14  0  0
   9   19  0  0
   10  18  0  0
   11  16  0  0
   12  36  0  0
   13 583  0  0
   14  NA NA NA
   15  NA NA NA
   16  NA NA NA
   17  50 22 NA
   18  52 NA  0
   19  26  0  0
   20  24  0  0

16voto

Max Gordon Puntos 2361

Una solución es escribir sus propias funciones de imputación personalizadas para el mice paquete. El paquete está preparado para ello y la configuración es sorprendentemente sencilla.

Primero configuramos los datos como se sugiere:

dat=data.frame(x1=c(21, 50, 31, 15, 36, 82, 14, 14, 19, 18, 16, 36, 583, NA,NA,NA, 50, 52, 26, 24), 
               x2=c(0, NA, 18,0, 19, 0, NA, 0, 0, 0, 0, 0, 0,NA,NA, NA, 22, NA, 0, 0), 
               x3=c(0, 0, 0, 0, 0, 54, 0 ,0, 0, 0, 0, 0, 0, NA, NA, NA, NA, 0, 0, 0))

A continuación, cargamos el mice y ver qué métodos elige por defecto:

library(mice)
# Do a non-imputation
imp_base <- mice(dat, m=0, maxit = 0)

# Find the methods that mice chooses
imp_base$method
# Returns: "pmm" "pmm" "pmm"

# Look at the imputation matrix
imp_base$predictorMatrix
# Returns:
#   x1 x2 x3
#x1  0  1  1
#x2  1  0  1
#x3  1  1  0

El pmm significa coincidencia de medias predictivas - probablemente el algoritmo de imputación más popular para imputar variables continuas. Calcula el valor predicho utilizando un modelo de regresión y elige los 5 elementos más cercanos al valor predicho (por Distancia euclidiana ). Estos elementos elegidos se denominan pool de donantes y el valor final se elige al azar de este pool de donantes.

A partir de la matriz de predicción encontramos que los métodos obtienen las variables pasadas que son de interés para las restricciones. Nótese que la fila es la variable objetivo y la columna los predictores. Si x1 no tuviera 1 en la columna x3 tendríamos que añadirlo en la matriz: imp_base$predictorMatrix["x1","x3"] <- 1

Ahora pasamos a la parte divertida, la generación de los métodos de imputación. Aquí he elegido un método bastante burdo en el que descarto todos los valores si no cumplen los criterios. Esto puede dar lugar a un largo tiempo de bucle y puede ser potencialmente más eficiente para mantener las imputaciones válidas y sólo rehacer los restantes, que requeriría un poco más de ajuste sin embargo.

# Generate our custom methods
mice.impute.pmm_x1 <- 
  function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "", 
            ...) 
  {
    max_sum <- sum(max(x[,"x2"], na.rm=TRUE),
                   max(x[,"x3"], na.rm=TRUE))
    repeat{
      vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
                              version = "", ...)
      if (all(vals < max_sum)){
        break
      }
    }
    return(vals)
  }

mice.impute.pmm_x2 <- 
  function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "", 
            ...) 
  {
    repeat{
      vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
                              version = "", ...)
      if (all(vals == 0 | vals >= 14)){
        break
      }
    }
    return(vals)
  }

mice.impute.pmm_x3 <- 
  function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "", 
            ...) 
  {
    repeat{
      vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
                              version = "", ...)
      if (all(vals == 0 | vals >= 16)){
        break
      }
    }
    return(vals)
  }

Una vez que hemos terminado de definir los métodos, simplemente cambiamos los métodos anteriores. Si sólo quieres cambiar una sola variable entonces puedes usar simplemente imp_base$method["x2"] <- "pmm_x2" pero para este ejemplo lo cambiaremos todo (la denominación no es necesaria):

imp_base$method <- c(x1 = "pmm_x1", x2 = "pmm_x2", x3 = "pmm_x3")

# The predictor matrix is not really necessary for this example
# but I use it just to illustrate in case you would like to 
# modify it
imp_ds <- 
  mice(dat, 
       method = imp_base$method, 
       predictorMatrix = imp_base$predictorMatrix)

Veamos ahora el tercer conjunto de datos imputados:

> complete(imp_ds, action = 3)
    x1 x2 x3
1   21  0  0
2   50 19  0
3   31 18  0
4   15  0  0
5   36 19  0
6   82  0 54
7   14  0  0
8   14  0  0
9   19  0  0
10  18  0  0
11  16  0  0
12  36  0  0
13 583  0  0
14  50 22  0
15  52 19  0
16  14  0  0
17  50 22  0
18  52  0  0
19  26  0  0
20  24  0  0

Bien, eso hace el trabajo. Me gusta esta solución, ya que se puede añadir a las funciones principales y añadir las restricciones que se consideren significativas.

Actualización

Para hacer cumplir las rigurosas restricciones que @t0x1n mencionó en los comentarios, podríamos añadir las siguientes capacidades a la función envolvente:

  1. Guardar los valores válidos durante los bucles para que no se descarten los datos de las ejecuciones anteriores, parcialmente exitosas
  2. Un mecanismo de escape para evitar los bucles infinitos
  3. Inflar la reserva de donantes después de intentar x veces sin encontrar una coincidencia adecuada (esto se aplica principalmente a pmm)

Esto da lugar a una función envolvente ligeramente más complicada:

mice.impute.pmm_x1_adv <-   function (y, ry, 
                                      x, donors = 5, 
                                      type = 1, ridge = 1e-05, 
                                      version = "", ...) {
  # The mice:::remove.lindep may remove the parts required for
  # the test - in those cases we should escape the test
  if (!all(c("x2", "x3") %in% colnames(x))){
    warning("Could not enforce pmm_x1 due to missing column(s):",
            c("x2", "x3")[!c("x2", "x3") %in% colnames(x)])
    return(mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
                           version = "", ...))
  }

  # Select those missing
  max_vals <- rowSums(x[!ry, c("x2", "x3")])

  # We will keep saving the valid values in the valid_vals
  valid_vals <- rep(NA, length.out = sum(!ry))
  # We need a counter in order to avoid an eternal loop
  # and for inflating the donor pool if no match is found
  cntr <- 0
  repeat{
    # We should be prepared to increase the donor pool, otherwise
    # the criteria may become imposs
    donor_inflation <- floor(cntr/10)
    vals <- mice.impute.pmm(y, ry, x, 
                            donors = min(5 + donor_inflation, sum(ry)), 
                            type = 1, ridge = 1e-05,
                            version = "", ...)

    # Our criteria check
    correct <- vals < max_vals
    if (all(!is.na(valid_vals) |
              correct)){
      valid_vals[correct] <-
        vals[correct]
      break
    }else if (any(is.na(valid_vals) &
                    correct)){
      # Save the new valid values
      valid_vals[correct] <-
        vals[correct]
    }

    # An emergency exit to avoid endless loop
    cntr <- cntr + 1
    if (cntr > 200){
      warning("Could not completely enforce constraints for ",
              sum(is.na(valid_vals)),
              " out of ",
              length(valid_vals),
              " missing elements")
      if (all(is.na(valid_vals))){
        valid_vals <- vals
      }else{
        valid_vals[is.na(valid_vals)] <- 
          vals[is.na(valid_vals)]
      }
      break
    }
  }
  return(valid_vals)
}

Obsérvese que esto no funciona tan bien, probablemente debido a que el conjunto de datos sugerido no cumple con las restricciones para todo casos sin faltar. Tengo que aumentar la longitud del bucle a 400-500 antes de que empiece a comportarse. Supongo que esto es involuntario, su imputación debe imitar cómo se generan los datos reales.

Optimización

El argumento ry contiene los valores no ausentes y posiblemente podríamos acelerar el bucle eliminando los elementos que hemos encontrado imputables, pero como no estoy familiarizado con las funciones internas me he abstenido de hacerlo.

Creo que lo más importante cuando se tienen fuertes restricciones que tardan en completarse es paralelizar las imputaciones ( ver mi respuesta en CrossValidated ). La mayoría tiene hoy en día ordenadores con 4-8 núcleos y R sólo utiliza uno de ellos por defecto. El tiempo puede ser (casi) cortado a la mitad duplicando el número de núcleos.

Parámetros ausentes en la imputación

En cuanto al problema de x2 que faltan en el momento de la imputación - los ratones en realidad nunca introducen los valores que faltan en el x - data.frame . El ratones incluye la introducción de un valor aleatorio al inicio. La parte de la imputación en cadena limita el impacto de este valor inicial. Si se observa el mice -se puede encontrar esto antes de la llamada de imputación (el mice:::sampler -función):

...
if (method[j] != "") {
  for (i in 1:m) {
    if (nmis[j] < nrow(data)) {
      if (is.null(data.init)) {
        imp[[j]][, i] <- mice.impute.sample(y, 
                                            ry, ...)
      }
      else {
        imp[[j]][, i] <- data.init[!ry, j]
      }
    }
    else imp[[j]][, i] <- rnorm(nrow(data))
  }
}
...

El data.init puede ser suministrado al mice y la función mice.imput.sample es un procedimiento básico de muestreo.

Secuencia de visitas

Si la secuencia de visitas es importante, puede especificar el orden en el que los mice -función ejecuta las imputaciones. Por defecto es de 1:ncol(data) pero se puede establecer el visitSequence para ser lo que quieras.

8voto

t0x1n Puntos 130

Lo más parecido que pude encontrar es Amelia's inclusión de información previa. Véase el capítulo 4.7 de la viñeta En concreto, el punto 4.7.2:

Previsiones a nivel de observación

Los investigadores suelen tener información previa adicional información adicional sobre los valores de los datos que faltan, basada en investigaciones anteriores el consenso académico o la experiencia personal. Amelia puede incorporar esta información para producir imputaciones muy mejoradas. El algoritmo Amelia permite a los usuarios incluir información bayesiana a priori sobre de datos individuales que faltan en lugar de los parámetros más generales del modelo, muchos de los cuales tienen poco significado directo. más generales del modelo, muchos de los cuales tienen poco significado directo.

La La incorporación de priores sigue el análisis bayesiano básico en el que la imputación resulta ser una media ponderada de la imputación basada en el modelo imputación basada en el modelo y la media a priori, donde las ponderaciones son funciones de la de la fuerza relativa de los datos y de la prioridad: cuando el modelo predice muy cuando el modelo predice muy bien, la imputación ponderará a la baja la media a priori, y viceversa (Honaker y King, 2010).

Las priores sobre las observaciones individuales deben describir la creencia del analista sobre la distribución de la celda de datos que falta. Esto puede adoptar la forma de una media y una desviación estándar o un intervalo de condencia. Por ejemplo, podríamos saber que las tasas de tari de 1986 en Tailandia rondan el 40%, pero tenemos algunas incertidumbre sobre el valor exacto. Nuestra creencia previa sobre la Nuestra creencia previa sobre la distribución de la celda de datos que falta, se centra en 40 con una desviación estándar que refleja la cantidad de datos que faltan. desviación estándar que refleja la cantidad de incertidumbre que tenemos sobre nuestra creencia previa.

Para introducir las priores hay que construir una matriz de priores con cuatro o cinco columnas. Cada fila de la matriz representa una a priori sobre una observación o una variable. En cualquier fila, la entrada en la primera columna es la fila de la observación y la entrada es la segunda columna es la columna de la observación. En la matriz de cuatro columnas de cuatro columnas, la tercera y la cuarta columna son la media y la desviación de la distribución a priori del valor perdido.

Por lo tanto, aunque no podrá decir en general algo como x1<x2+x3 En este caso, se podría hacer un bucle sobre el conjunto de datos y añadir una prioridad a nivel de observación para cada caso relevante. También se pueden aplicar límites constantes (como establecer que x1, x2 y x3 sean no negativos). Por ejemplo:

priors = matrix(NA, nrow=0, ncol=5);
for (i in seq(1, length(data))) 
{
    x1 = data$x1[i];
    x2 = data$x2[i];
    x3 = data$x3[i];

    if (is.na(x1) && !is.na(x2) && !is.na(x3))
    {
        priors = rbind(priors, c(i, 1, 0, x2+x3, 0.999999))
    }
}

amelia(data, m=1, bound = rbind(c(1, 0, Inf), c(2, 0, Inf), c(3, 0, Inf)), pr = priors);

6voto

dan90266 Puntos 609

Las restricciones son probablemente más fáciles de implementar en la imputación múltiple de coincidencia de medias predictivas. Esto supone que hay un número significativo de observaciones con variables de restricción no ausentes que cumplen las restricciones. Estoy pensando en implementar esto en el programa R Hmisc paquete aregImpute función. Es posible que quieras volver a comprobarlo dentro de un mes más o menos. Será importante especificar la distancia máxima desde el objetivo que puede tener una observación de donante, porque las restricciones empujarán a los donantes más lejos del donante ideal sin restricciones.

4voto

AusTravel Puntos 6

Creo que el Amelia (Amelia II) tiene actualmente el soporte más completo para especificar restricciones de rango de valores de datos. Sin embargo, el problema es que Amelia asume que los datos son normales multivariados.

Si en su caso no se aplica el supuesto de normalidad multivariante, podría comprobar mice que implementa imputación múltiple (MI) a través de ecuaciones encadenadas . Este paquete no tiene el supuesto de normalidad multivariante . También tiene una función que podría ser suficiente para especificar limitaciones pero no estoy seguro de hasta qué punto. La función se llama squeeze() . Puede leerlo en la documentación: http://cran.r-project.org/web/packages/mice/mice.pdf . Una ventaja adicional de mice es su flexibilidad al permitir la especificación de funciones de imputación definidas por el usuario y una mayor selección de algoritmos. Aquí hay un tutorial sobre la realización de IM, utilizando mice : http://www.ats.ucla.edu/stat/r/faq/R_pmm_mi.htm .

Por lo que tengo entendido, el Dr. Harrell Hmisc utilizando el mismo paquete ecuaciones encadenadas ( coincidencia de medias predictivas ), probablemente admita datos no normales (con la excepción de normpmm método). Tal vez ya haya implementado la funcionalidad de especificación de restricciones según su respuesta anterior. No he utilizado aregImpute() Así que no puedo decir mucho más al respecto (he utilizado Amelia y mice (pero definitivamente no soy un experto en estadística, sólo intento aprender todo lo que puedo).

Por último, puede que le resulte interesante lo siguiente, un poco anticuado, pero todavía agradable, resumen de enfoques, métodos y programas informáticos para la imputación múltiple de datos con valores perdidos: http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1839993 . Estoy seguro de que hay documentos más recientes sobre la IM, pero esto es todo lo que conozco en este momento. Espero que esto sea algo útil.

0voto

Bruno von Paris Puntos 123

Si he entendido bien tu pregunta, me parece que ya sabes qué valores deben tomar las variables que faltan con algunas restricciones. No estoy muy familiarizado con el SPSS, pero en R creo que se puede escribir una función para hacer eso (que no debería ser demasiado difícil dependiendo de tu experiencia, diría). No conozco ningún paquete que trabaje con tales restricciones.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X