Багаторазова імпутація пропущених значень


13

Я хотів би використовувати імпутацію для заміни відсутніх значень у моєму наборі даних за певних обмежень.

Наприклад, я хотів би, щоб імпульована змінна x1була більшою або дорівнює сумі двох інших моїх змінних, скажімо, x2і x3. Я також хочу, x3щоб мене вводили будь-який 0або, >= 14і я хочу, x2щоб він був замінений або одним, 0або >= 16.

Я намагався визначити ці обмеження в SPSS для багаторазової імпутації, але в SPSS я можу визначити лише максимальні та мінімальні значення. Чи є якийсь спосіб визначити подальші обмеження в SPSS чи ви знаєте який-небудь пакет R, який дозволив би мені визначити такі обмеження для внесення відсутніх значень?

Мої дані такі:

   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

Я змінив 0 or 16 or >= 16до 0 or >= 16так >=16включає в себе вартість 16. Сподіваюся, що це не зіпсувало ваше значення. Те саме за0 or 14 or >= 14
Олексій

Відповіді:


16

Одне рішення - написати власні власні функції імпутації для miceпакету. Пакет для цього підготовлений, і налаштування дивно безболісне.

Спочатку ми налаштовуємо дані так, як пропонується:

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))

Далі завантажуємо miceпакунок і дивимося, які методи він обрав за замовчуванням:

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

pmmЧи означає самий корінь середнього відповідності - ймовірно, найпопулярніший алгоритм зобов'язання для поставлення безперервних змінних. Він обчислює передбачуване значення за допомогою регресійної моделі та підбирає 5 найближчих елементів до передбачуваного значення (за евклідовою відстані ). Ці обрані елементи називаються пулом донорів, а остаточне значення вибирається випадковим чином із цього пулу донорів.

З матриці прогнозування ми знаходимо, що методи отримують передані змінні, що представляють інтерес для обмежень. Зауважте, що рядок є цільовою змінною, а стовпець - провісниками. Якщо у x1 не було 1 у стовпчику x3, нам доведеться додати це в матрицю:imp_base$predictorMatrix["x1","x3"] <- 1

Тепер до цікавої частини, генеруючи методи імпутації. Тут я вибрав досить грубу методику, де я відкидаю всі значення, якщо вони не відповідають критеріям. Це може призвести до тривалого циклу, і, можливо, буде ефективніше зберегти дійсні імпутації та лише повторити решту, це зажадає трохи більше переробок.

# 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)
  }

Як тільки ми закінчимо визначення методів, ми просто змінимо попередні методи. Якщо ви хочете змінити лише одну змінну, тоді ви можете просто скористатися, imp_base$method["x2"] <- "pmm_x2"але для цього прикладу ми змінимо все (іменування не потрібно):

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)

Тепер давайте подивимось на третій імпульований набір даних:

> 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

Гаразд, це робить роботу. Мені подобається це рішення, оскільки ви можете піггіфікувати поверх основних функцій і просто додавати обмеження, які вам здаються значущими.

Оновлення

Щоб застосувати жорсткі обмеження @ t0x1n, згадані в коментарях, ми можемо захотіти додати наступні здібності до функції обгортки:

  1. Збережіть дійсні значення під час циклів, щоб дані попередніх, частково успішних запусків не були відкинуті
  2. Механізм втечі, щоб уникнути нескінченних петель
  3. Надуйте пул донорів після спроб x разів, не знаходячи відповідного відповідника (це стосується насамперед pmm)

Це призводить до дещо складнішої функції обгортки:

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)
}

Зауважте, що це не так добре, швидше за все, через те, що запропонований набір даних не виконує обмеження для всіх випадків, не пропускаючи їх. Мені потрібно збільшити довжину петлі до 400-500, перш ніж вона навіть почне вести себе. Я припускаю, що це ненавмисно, ваша імпутація повинна імітувати, як створюються фактичні дані.

Оптимізація

Аргумент ryмістить недолічені значення, і ми могли б прискорити цикл, видаливши елементи, які ми знайшли придатні імпутації, але, оскільки я не знайомий із внутрішніми функціями, я утримався від цього.

Я вважаю, що найважливіше, якщо у вас є сильні обмеження, на які потрібен час для повного заповнення, - це паралелізувати свої імпутації ( див. Мою відповідь на CrossValidated ). Більшість сьогодні мають комп’ютери з 4-8 ядрами, а R використовує лише один із них за замовчуванням. Час можна (майже) розрізати навпіл, подвоївши кількість ядер.

Відсутні параметри при імпутації

Щодо проблеми x2відсутності під час імпутації - миші насправді ніколи не вводять пропущені значення в x- data.frame. Метод мишей включає заповнення деякого випадкового значення на початку. Ланцюгова частина імпутації обмежує вплив від цього початкового значення. Якщо ви подивитесь на функцію, miceви можете знайти це перед викликом імпутації ( mice:::sampler-функція):

...
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))
  }
}
...

Функція data.initможе поставлятися у miceфункцію, а вибірка mice.imput.sample - це основна процедура вибірки.

Послідовність відвідування

Якщо послідовність відвідувань важлива, ви можете вказати порядок, в якому miceфункція виконує імпутації. За замовчуванням - від, 1:ncol(data)але ви можете встановити visitSequenceбудь-що, що вам подобається.


+1 Це чудові речі, саме те, що я мав на увазі (дивіться мій коментар до відповіді Френка), і, безумовно, кандидат №1 на премію на сьогодні. pmm_x1Хоча кілька речей мене непокоїть : (1) Прийняття максимальної суми будь-якої можливої ​​комбінації x2та x3з усього набору даних набагато суворіше, ніж початкове обмеження. Правильна річ повинна була б перевірити , що для кожного рядка , x1 < x2 + x3. Звичайно, чим більше рядків у вас є, тим менші шанси дотримуватися такого обмеження (оскільки один поганий рядок все знищує), і чим довше цикл потенційно може отримати.
t0x1n

(2) якщо і те, x1і інше x2відсутнє, ви можете присвоїти значенням, для x1якого обмеження обмежуються (скажімо, 50), але як тільки x2вони будуть замінені, вони будуть порушені (скажімо, що їм приписано 55). Чи є спосіб імпульсувати «горизонтально», а не вертикально? Таким чином , ми могли б приписувати один ряд x1, x2і x3та просто повторно Умовна оцінка його до тих пір , що конкретна рядок не підпадає під обмеження. Це повинно бути досить швидким, і як тільки це буде зроблено, ми можемо перейти до наступного ряду. Звичайно, якщо ІМ є «вертикальним» за своєю суттю, нам не щастить. В такому випадку, може, підхід Олександр згадував?
t0x1n

Класний розчин, +1! Можливо, це буде особливо зручно, оскільки я зараз використовую miceпакет. Дякую, що поділились.
Олександр Блех

1
@ t0x1n Я оновив свою відповідь більш досконалою функцією обгортки відповідно до ваших коментарів. Якщо ви хочете зануритися глибше, я рекомендую вам пограти з вами debug(), щоб побачити, як mice.impute.pmmі його побратими працюють під кришкою.
Макс Гордон

1
@ t0x1n: Я здогадуюсь - перевірте свої введені значення. Якщо вони здаються нереальними, ви можете обрати мій підхід, щоб замінити лише ті, які є менш центральними для моделі. У моєму випадку я вирішив виключити ті, хто не проводив рентгенівські знімки, оскільки вони лежать в основі дослідження, а імпутації не дають клінічно правдоподібних значень (нога стає довшою після перелому). Я не зовсім задоволений цим, але це здається розумним компромісом.
Макс Гордон

8

Найближче, що я міг знайти, це попереднє включення інформації Амелії . Дивіться розділ 4.7 у віньєтці , зокрема 4.7.2:

Пріори рівня спостереження

Дослідники часто мають додаткову попередню інформацію про відсутні дані значення на основі попередніх досліджень, академічного консенсусу чи особистого досвіду. Amelia може включити цю інформацію, щоб зробити значно вдосконалені імпутації. Алгоритм Amelia дозволяє користувачам включати інформативні байесівські пріори про окремі комірки відсутніх даних замість більш загальних параметрів моделі, багато з яких мають невелике пряме значення.

Інкорпорація пріорів проводиться за базовим байєсівським аналізом, де імпутація виявляється середньозваженою середньою моделлю на основі імпутації та попереднього середнього значення, де ваги є функціями відносної сили даних та попереднього: коли модель дуже добре прогнозує , імпутація зменшить вагу попереднього та навпаки (Honaker and King, 2010).

Пріоритети щодо окремих спостережень повинні описувати переконання аналітика щодо розподілу пропущеної комірки даних. Це може мати форму середнього та стандартного відхилень або інтервал конденсації. Наприклад, ми можемо знати, що тарифні ставки 1986 року в Таїланді близько 40%, але ми маємо певну невизначеність щодо точного значення. Наше попереднє переконання щодо розподілу пропущеної комірки даних, тоді, орієнтується на 40 зі стандартним відхиленням, що відображає величину невизначеності, яку ми маємо щодо попередньої віри.

Для введення пріорів ви повинні побудувати матрицю пріорі з чотирма або п’ятьма стовпцями. Кожен рядок матриці являє собою пріоритет будь-якого спостереження або однієї змінної. У будь-якому рядку запис у першому стовпці є рядком спостереження, а запис - другим стовпцем - стовпцем спостереження. У матриці чотирьох пріорів стовпців третій та четвертий стовпці - це середнє та стандартне відхилення попереднього розподілу відсутнього значення.

Таким чином, хоча ви взагалі не зможете сказати щось подібне x1<x2+x3, ви можете перевести цикл на свій набір даних і додати рівень спостереження до кожного відповідного випадку. Також можуть бути застосовані постійні межі (такі як встановлення x1, x2 та x3 як негативні). Наприклад:

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);

5

Обмеження, ймовірно, легше реалізувати в середньому прогнозуванні, що відповідає множинній імпутації. Це передбачає, що існує значна кількість спостережень із відсутніми обмежуючими змінними, які відповідають обмеженням. Я думаю про реалізацію цього в функції Hmiscпакету R. aregImputeВи можете перевірити ще раз через місяць. Важливо буде вказати максимальну відстань від цілі, якою може бути спостереження донора, оскільки обмеження підштовхнуть донорів далі від ідеального необмеженого донора.


Я також хотів би мати це. Скажімо, мені потрібні лише найосновніші мінливі обмеження x<y<z.
t0x1n

Пробачте моє невігластво, якщо я відволікаюсь, але в мене склалося враження, що численні методи імпутації передбачають нанесення значень із відповідного розподілу. Чи не повинно бути простою справою використовувати вибірку відхилення? наприклад, продовжуйте малювати, поки не x1<x2буде виконано певне обмеження (наприклад, )?
t0x1n

Це те, що я міг би зробити з aregImputeфункцією R з середнім прогнозним узгодженням. Але що робити, якщо жодне із спостережень донора (поруч із збігами прогнозів) не задовольняє обмеженням для введення цільового спостереження, хоча вони, очевидно, повинні були відповідати обмеженням для набору змінних донорів?
Френк Харрелл

Можливо, в такому випадку, можливо, приймати прогнозоване значення безпосередньо? Це лише покладатися на регресію (без фази ПММ) для такої вибірки?
t0x1n

Регресна імпутація трохи більше шансів наштовхуватись на імпульсні значення, невідповідні решті записів суб'єкта. Тому я не думаю, що це причина уникати ПММ.
Френк Харрелл

4

Я вважаю, що Ameliaпакет (Amelia II) на даний момент має найбільш повну підтримку для визначення обмежень діапазону значень даних. Однак проблема полягає в тому, що Ameliaпередбачається, що дані є багатовимірними нормальними.

Якщо у вашому випадку припущення про багатоваріантну нормальність не застосовується, ви, можливо, захочете перевірити miceпакет, який реалізує множинні імпутації (MI) за допомогою ланцюгових рівнянь . Цей пакет не передбачає багатовимірної нормальності . Він також має функцію, якої може бути достатньо для уточнення обмежень , але я не впевнений в якій мірі. Функція викликається squeeze(). Про це можна прочитати в документації: http://cran.r-project.org/web/packages/mice/mice.pdf . Додатковою перевагою miceє її гнучкість з точки зору можливості конкретизації визначених користувачем функцій імпутації та більш широкого вибору алгоритмів. Ось підручник із виконання ІМ, використовуючи mice:http://www.ats.ucla.edu/stat/r/faq/R_pmm_mi.htm .

Наскільки я розумію, Hmiscпакет доктора Харрелла , використовуючи ті самі ланцюгові рівняння ( середнє значення прогнозування ), ймовірно, підтримує ненормальні дані (за винятком normpmmметоду). Можливо, він уже реалізував функціонал специфікації обмежень відповідно до своєї відповіді вище. Я не використовував aregImpute(), тому не можу сказати набагато більше про це (я використовував Ameliaі mice, але я безумовно не фахівець в області статистики, просто намагається дізнатися як я можу).

Нарешті, вам може бути цікавий наступний, трохи застарілий, але все ж приємний огляд підходів, методів та програмного забезпечення для багаторазової імпутації даних із відсутніми значеннями: http://www.ncbi.nlm.nih.gov/pmc/articles / PMC1839993 . Я впевнений, що є новіші оглядові документи щодо ІМ, але це все, що мені відомо в даний час. Я сподіваюся, що це дещо корисно.


1
Цей приємний коментар змушує мене думати, що середнє значення прогнозування, яке замінює пропуски фактично спостережуваними значеннями, може вже включати деякі види протипоказань, якщо всі спостережувані дані відповідають цим обмеженням. Я би вдячний, щоб хтось це міркував. Я ще не застосував жодних спеціальних обмежень у aregImpute.
Френк Харрелл

1
Ти правий. Я щойно зрозумів, що спостереження донорів забезпечують значення, які відповідають їхнім іншим змінним, але не іншим змінним цільової змінної.
Френк Харрелл

1
Окрім припущень щодо розповсюдження, зроблених Амелією, чи змогли ви випадково детальніше вказати обмеження, ніж я продемонстрував у своїй відповіді? Проблема squeezeполягає в тому, що її межі постійні, тому нічого подібного не можна вказувати x1<x2. Також, схоже, він посилається на імпутований вектор результату, який, на мою думку, занадто пізно. Мені здається, що межі слід враховувати під час процесу імпутації, тому вони мають більше значення, ніж коригування після факту.
t0x1n

1
@ t0x1n: На жаль, у мене не було можливості вказати обмеження Amelia, оскільки я перейшов з нього mice, як тільки мої тести підтвердили, що мої дані не є багатовимірними нормальними. Однак я нещодавно зіткнувся з цим дуже приємним набором слайдів презентації на тему (методи MI та програмне забезпечення): statistik.lmu.de/~fkreuter/imputation_sose2011/downloads/… . Якщо я правильно зрозумів, він описує потенційне рішення проблеми обмежень (див. Сторінку PDF 50 - не слайд № 50!). Сподіваюсь, це допомагає.
Олександр Блех

1
@ t0x1n: Власне, рішення описано на сторінках 50 та 51.
Олександр Блех

0

Якщо я правильно розумію ваше запитання, мені здається, ви вже знаєте, які значення мають брати змінні, залежно від деяких обмежень. Я не дуже розбираюся в SPSS, але в RI думаю, що ви можете написати функцію для цього (що не повинно бути занадто складним залежно від вашого досвіду, який я повинен сказати). Я не знаю жодного пакету, який працює з такими обмеженнями.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.