缺失值的多重插补


13

我想在某些约束下使用插补替换数据集中的缺失值。

例如,我希望估算的变量x1大于或等于我的另外两个变量,例如x2x3。我也想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 >= 160 or >= 16因为>=16包含值16。希望那不会弄乱你的意思。相同0 or 14 or >= 14
Alexis 2014年

Answers:


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在x3列中没有1,则必须在矩阵中将其添加: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-function,则可以在插补调用(mice:::sampler-function)之前找到它:

...
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-函数运行插补的顺序。默认值为from,1:ncol(data)但是您可以将设置visitSequence为任意值。


+1这是很棒的东西,正好是我的初衷(请参阅我对弗兰克答案的评论),并且可以肯定的是,到目前为止,奖金排名第一的候选人。但是,有两件事使我感到烦恼pmm_x1:(1)从整个数据集中获取x2和/或x3从所有数据集中进行任何可能的组合的最大和比原始约束要严格得多。正确的做法是,以测试为每一行x1 < x2 + x3。当然,您拥有的行越多,遵守这种约束的机会就越小(因为单个坏行会破坏所有内容),并且循环可能获得的时间越长。
t0x1n 2014年

(2)如果x1x2都缺失,则可以估算一个为其x1保留约束的值(比如说50),但是一旦x2被估算,它们就会被破坏(比如估算为55)。有没有办法“水平”地而不是垂直地估算?我们可以归咎于单排通过这种方式x1x2以及x3和简单地重新转嫁给它,直到特定行下降的制约下。那应该足够快,一旦完成,我们就可以移到下一行。当然,如果MI本质上是“垂直的”,那我们就不走运了。在那种情况下,也许亚历山大·安德鲁斯提到的方法?
t0x1n 2014年

很酷的解决方案,+ 1!可能特别方便,因为我当前使用的是micepackage。感谢分享。
Aleksandr Blekh

1
@ t0x1n我已经根据您的评论使用更高级的包装器功能更新了答案。如果您想更深入地学习,我建议您与一起玩,debug()以了解mice.impute.pmm引擎盖及其兄弟姐妹如何工作。
Max Gordon

1
@ t0x1n:我猜-检查您的估算值。如果它们看起来不切实际,那么您可以选择我的方法来只估算那些对模型不太重要的方法。在我的情况下,我选择排除那些没有进行X射线检查的患者,因为它们是研究的核心,并且估算值无法提供临床上合理的值(骨折后腿变长)。我对此并不完全满意,但这似乎是一个合理的妥协。
Max Gordon

8

我能找到的最接近的东西是Amelia的先前信息。请参见小插图中的 4.7章,尤其是4.7.2:

观察级先验

研究人员通常会基于先前的研究,学术共识或个人经验而获得有关丢失数据值的其他先前信息。阿米莉亚(Amelia)可以合并这些信息以产生大大改善的估算结果。Amelia算法允许用户包括有关单个缺失数据单元的信息性贝叶斯先验信息,而不是更一般的模型参数,其中许多模型参数没有直接意义。

先验的合并遵循基本的贝叶斯分析,在此基础上,推算结果是基于模型的推算和先验均值的加权平均值,其中权重是数据和先验的相对强度的函数:当模型预测得很好时,估算将降低先验权,反之亦然(Honaker和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

在预测均值匹配多重插补中,约束可能更容易实现。假设存在大量的观测结果,其中非缺失约束变量满足约束条件。我正在考虑在R HmiscaregImpute功能中实现此功能。您可能要在一个月左右的时间内回来查看。指定供体观察距离目标的最大距离非常重要,因为约束条件将使供体离理想的不受约束的供体更远。


我也想拥有这个。我只需要最基本的变量间约束x<y<z
t0x1n 2014年

如果我走了,请原谅我的无知,但我的印象是,多种插补技术涉及从适当的分布中提取值。那么使用拒绝采样不是一个简单的事情吗?例如继续绘制,直到满足某些指定的约束条件(例如x1<x2)?
t0x1n 2014年

那就是我可能对aregImpute具有预测均值匹配的R 函数所做的事情。但是,即使捐助者的意见显然必须满足对捐助者变量的约束,如果所有捐助者的意见(预测的近似匹配项)都不满足要推算的目标意见的约束,该怎么办?
Frank Harrell 2014年

在这种情况下,也许直接取预测值?对于这样的样本,这仅依赖于回归(没有PMM阶段)吗?
t0x1n 2014年

回归估算的可能性更高,其估算值与受试者的其他记录不一致。因此,我认为这不是避免使用PMM的原因。
Frank Harrell 2014年

4

我相信Amelia(Amelia II)软件包目前对指定数据值范围约束提供了最全面的支持。但是,问题在于Amelia假设数据是多元正态的。

如果您的情况不适用多元正态性假设,则可能需要检查mice程序包,该程序包通过链式方程实现多重插补(MI)。该软件包不具有多元正态性的假设。它还具有一个足以指定约束的功能,但是我不确定达到什么程度。该函数称为。您可以在以下文档中阅读有关该文档:http : //cran.r-project.org/web/packages/mice/mice.pdf。它的另一个好处是它的灵活性,允许指定用户定义的插补函数和更广泛的算法选择。这是有关使用MI进行执行的教程:squeeze()micemicehttp://www.ats.ucla.edu/stat/r/faq/R_pmm_mi.htm

据我了解,Harrell博士的Hmisc软件包使用相同的链式方程预测均值匹配)方法,可能支持非正态数据(normpmm方法除外)。也许他已经按照上面的回答实现了约束规范功能。我还没有使用过aregImpute(),所以不能多说(我曾经使用过Ameliamice,但是我绝对不是统计学专家,只是想尽可能多地学习)。

最后,您可能会发现以下有趣但略显过时但仍然不错的方法,用于对缺少值的数据进行多次插补的方法,方法和软件的概述http : //www.ncbi.nlm.nih.gov/pmc/articles / PMC1839993。我敢肯定,有关于MI的较新的概述论文,但这是我目前所知道的全部。我希望这会有所帮助。


1
这个不错的评论使我认为,如果所有观察到的数据都满足那些约束条件,则用均值匹配代替预测值,将其替换为实际观察到的值,可能已经包含了某些矛盾。我将不胜感激有人对此有所考虑。我尚未在中实施任何特殊约束aregImpute
Frank Harrell 2014年

1
你是对的。我刚刚意识到,捐助者观察提供的值与其其他变量一致,但与目标变量中的其他变量不一致。
Frank Harrell 2014年

1
除了Amelia所做的分布假设之外,您是否有机会比我在答案中所展示的更详细地指定约束?问题squeeze在于它的边界是恒定的,因此您不能指定x1<x2。而且,它似乎是在估算结果向量上调用的,我认为为时已晚。在我看来,应该在插补过程中考虑界限,因此,界限比事后调整更有意义。
t0x1n 2014年

1
@ t0x1n:不幸的是,我没有机会在中指定约束Amelia,因为mice一旦我的测试确认我的数据不是多变量正态的,我就将约束从切换到。但是,我最近浏览了关于主题(MI方法和软件)的一组非常不错的演示幻灯片:statistik.lmu.de/~fkreuter/imputation_sose2011/downloads/…。如果我理解正确,它描述了约束问题的潜在解决方案(请参阅PDF第50页-而不是幻灯片编号50!)。希望这可以帮助。
Aleksandr Blekh

1
@ t0x1n:其实,解决方法是在50和51页描述
亚历山大Blekh

0

如果我正确理解了您的问题,在我看来,您已经知道缺少的变量应遵循一些约束的值。我在SPSS方面不是很精通,但是在RI中,您可以编写一个函数来做到这一点(根据您的经验,这应该不太困难)。我不知道有什么软件包可以使用这种约束。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.