R中的迭代比例拟合

机器算法验证 r 算法 对数线性
2022-03-24 02:10:00

使命

我正在尝试找到一种在 R 中进行迭代比例拟合的方法。该过程的逻辑是这样的:有一个表格,其中包含例如一些变量的样本分布。让我们说它是这个:

sample1 <- structure(c(6L, 14L, 46L, 16L, 6L, 21L, 62L, 169L, 327L, 174L, 
44L, 72L, 43L, 100L, 186L, 72L, 23L, 42L), .Dim = c(6L, 3L), .Dimnames = list(
    c("Primary", "Lowersec", "Highersec", "Highershort", "Higherlong", 
    "University"), c("B", "F", "W")))

另一个表来自其他来源,比如另一个示例:

sample2 <- structure(c(171796L, 168191L, 240671L, 69168L, 60079L, 168169L, 
954045L, 1040981L, 1872732L, 726410L, 207366L, 425786L, 596239L, 
604826L, 991640L, 323215L, 134066L, 221696L), .Dim = c(6L, 3L
), .Dimnames = list(c("Primary", "Lowerse", "Highersec", "Highershort", 
"Higherlong", "University"), c("B", "F", "W")))

现在,我们想保留在 中找到的变量之间的关系sample1,但我们想将这些关系应用于我们在 中找到的边际分布sample2迭代比例拟合按照此处所述进行此操作(我无法提供更好的解释)。我曾尝试在 LEM 中执行此操作,结果如下:

                    B         F        W
Primary     124204.64  960173.6 637701.7
Lowerse     119749.12 1081459.0 612789.9
Highersec   336934.21 1792001.6 976107.2
Highershort  90512.27  736464.1 291816.6
Higherlong   43486.91  238593.0 119431.0
University  163186.85  418628.6 233835.5

我不是 100% 肯定这个结果,但很有可能(比如 99%)它是。第一个表中的优势比保留在结果表中,而边际分布(行和列总和)与第二个输入表相同。

问题

奇怪的是,这种非常有用的算法在 R 中并不容易获得,至少不是以用户友好的形式。一个可能相关的功能是cat::ipf()但是,我无法弄清楚如何使用该margins=参数。在这个问题上,我当然并不孤单。帮助示例使用了一个 3 维表,这使事情变得更加混乱。

此外,还有一些用户编写的功能,一个在这里,一个在这里找到不幸的是,第一个给出了错误的结果。第二个也是非常不透明的,需要专门预先格式化的 CSV 文件作为输入,而不是 R 矩阵对象。

问题

  1. 谁能解释一下如何实际使用该cat::ipf()功能?
  2. 是否有任何替代函数来实现 IPF 调整任务,使用矩阵作为输入?
  3. (已解决)可以修复此功能以提供正确的结果吗?

谢谢你。

附录:我能够从(3)中的函数获得正确的输出。经过一番考虑,结果发现该函数不接受矩阵作为边际分布的输入,而只是这些边际分布的列表。所以实际上这个问题已经解决了。然而,1 和 2 的正确答案将对更大的社区有用,因为 IPF 在对数线性模型中非常重要。

对于那些将来搜索的人来说,在这里复制一个有效的 IPF 函数似乎是一个好主意:

ipf <- function(Margins_, seedAry, maxiter=100, closure=0.001) {
    #Check to see if the sum of each margin is equal
    MarginSums. <- unlist(lapply(Margins_, sum))
    if(any(MarginSums. != MarginSums.[1])) warning("sum of each margin
                                                   not equal")

    #Replace margin values of zero with 0.001
    Margins_ <- lapply(Margins_, function(x) {
        if(any(x == 0)) warning("zeros in marginsMtx replaced with
                                0.001") 
        x[x == 0] <- 0.001
        x
    })

    #Check to see if number of dimensions in seed array equals the number of
    #margins specified in the marginsMtx
    numMargins <- length(dim(seedAry))
    if(length(Margins_) != numMargins) {
        stop("number of margins in marginsMtx not equal to number of
             margins in seedAry")
    }

    #Set initial values
    resultAry <- seedAry
    iter <- 0
    marginChecks <- rep(1, numMargins)
    margins <- seq(1, numMargins)

    #Iteratively proportion margins until closure or iteration criteria are met
    while((any(marginChecks > closure)) & (iter < maxiter)) {
        for(margin in margins) {
            marginTotal <- apply(resultAry, margin, sum)
            marginCoeff <- Margins_[[margin]]/marginTotal
            marginCoeff[is.infinite(marginCoeff)] <- 0
            resultAry <- sweep(resultAry, margin, marginCoeff, "*")
            marginChecks[margin] <- sum(abs(1 - marginCoeff))
        }    
        iter <- iter + 1
    }

    #If IPF stopped due to number of iterations then output info
    if(iter == maxiter) cat("IPF stopped due to number of iterations\n")

    #Return balanced array
    resultAry
    }

请注意,此函数不接受边际分布的矩阵。以下将有所帮助:

m1 <- rowSums(sample2)
m2 <- colSums(sample2)
m <- list(m1,m2)

然后提供m作为第一个参数。

2个回答

这是旧的,但我们开始:

正如@Henrico 所写,看来您要实现的目标确实是在努力。survey::rake正如@DWin 所建议的那样,您可以使用泊松 GLM “手动”将分布拟合到边缘,而不是使用。要获得正确的频率,您需要使用偏移量。

  • nij成为你的sample1
  • Nij调整表的预期频率
  • N^ij调整表的拟合值

我们需要拟合一个模型(参见JASA 中的 Little & Wu 1991):

log(Nijnij)=λ+λi1+λj2

因此我们有

logN^ijlognij=λ^+λi1^+λj2^

在哪里lognij是提到的偏移量。

您可以使用任何 GLM 软件通过以下方式估算它

  1. 创建一个人工表Nij这将满足独立性并具有所需的边际。
  2. 将主效应对数线性/泊松模型拟合到Nijnijs(观察到的频率,sample1)作为偏移量。
  3. 获取拟合值。

例如,这将为您提供目标频率f

# Your data
sample1 <- structure(c(6L, 14L, 46L, 16L, 6L, 21L, 62L, 169L, 327L, 174L, 
                       44L, 72L, 43L, 100L, 186L, 72L, 23L, 42L), .Dim = c(6L, 3L), .Dimnames = list(
                         c("Primary", "Lowersec", "Highersec", "Highershort", "Higherlong", 
                           "University"), c("B", "F", "W")))

sample2 <- structure(c(171796L, 168191L, 240671L, 69168L, 60079L, 168169L, 
                       954045L, 1040981L, 1872732L, 726410L, 207366L, 425786L, 596239L, 
                       604826L, 991640L, 323215L, 134066L, 221696L), .Dim = c(6L, 3L
                       ), .Dimnames = list(c("Primary", "Lowersec", "Highersec", "Highershort", 
                                             "Higherlong", "University"), c("B", "F", "W")))

library(dplyr)

# Turn to a data frame
d1 <- as_data_frame( as.table(sample1), stringsAsFactors = FALSE)

# Create artificial freqs based on sample2 and join with d1
N <- sum(sample2)
d <- outer(rowSums(sample2)/N, colSums(sample2)/N) %>%
  as.table() %>%
  as_data_frame() %>%
  mutate(
    p = n / sum(n),
    N = round(p * sum(sample2))
    ) %>%
  select(Var1, Var2, p, N) %>%
  left_join(d1)
#> Joining, by = c("Var1", "Var2")

# Fit the model
mod <- glm( N ~ Var1 + Var2 + offset(log(n)), data=d, family=poisson("log") )

# Get the fitted values
d$f <- predict(mod, type="response")
d
#> # A tibble: 18 x 6
#>           Var1  Var2           p       N     n          f
#>          <chr> <chr>       <dbl>   <dbl> <int>      <dbl>
#> 1      Primary     B 0.018763534  168442     6  124197.33
#> 2     Lowersec     B 0.019765059  177432    14  119743.66
#> 3    Highersec     B 0.033832098  303713    46  336937.75
#> 4  Highershort     B 0.012190206  109432    16   90514.26
#> 5   Higherlong     B 0.004374806   39273     6   43486.57
#> 6   University     B 0.008887215   79781    21  163193.43
#> 7      Primary     F 0.111702426 1002761    62  960181.53
#> 8     Lowersec     F 0.117664672 1056285   169 1081463.51
#> 9    Highersec     F 0.201408086 1808056   327 1792009.27
#> 10 Highershort     F 0.072570318  651469   174  736456.24
#> 11  Higherlong     F 0.026043943  233798    44  238592.76
#> 12  University     F 0.052907063  474951    72  418616.69
#> 13     Primary     W 0.061364877  550877    43  637701.15
#> 14    Lowersec     W 0.064640297  580281   100  612790.82
#> 15   Highersec     W 0.110645603  993274   186  976095.99
#> 16 Highershort     W 0.039867250  357891    72  291821.50
#> 17  Higherlong     W 0.014307508  128440    23  119431.67
#> 18  University     W 0.029065039  260919    42  233840.87

使 glm 适合具有泊松误差的边缘(产生对数线性模型),然后predictexpand.grid基于第二个样本的行和列值的 data.frame 上使用。(在使用 IPF 来估计这种对数线性模型时,我没有看到特别的优势。)

require(reshape2)
Loading required package: reshape2
> melt(sample1)
          Var1 Var2 value
1      Primary    B     6
2     Lowersec    B    14
3    Highersec    B    46
4  Highershort    B    16
5   Higherlong    B     6
6   University    B    21
7      Primary    F    62
8     Lowersec    F   169
9    Highersec    F   327
10 Highershort    F   174
11  Higherlong    F    44
12  University    F    72
13     Primary    W    43
14    Lowersec    W   100
15   Highersec    W   186
16 Highershort    W    72
17  Higherlong    W    23
18  University    W    42
> m_sample1<- melt(sample1)
> glm( value ~ Var1+Var2, data=m_sample1)

Call:  glm(formula = value ~ Var1 + Var2, data = msample)

Coefficients:
    (Intercept)    Var1Highersec  Var1Highershort     Var1Lowersec      Var1Primary  
         -36.56           162.00            63.00            70.00            12.67  
 Var1University            Var2F            Var2W  
          20.67           123.17            59.50  

Degrees of Freedom: 17 Total (i.e. Null);  10 Residual
Null Deviance:      121200 
Residual Deviance: 22510    AIC: 197.4 

那是线性模型。这是乘法(对数线性)模型:

> glm( value ~ Var1+Var2, data= m_sample1, family="poisson")

Call:  glm(formula = value ~ Var1 + Var2, family = "poisson", data = m_sample1)

Coefficients:
    (Intercept)    Var1Highersec  Var1Highershort     Var1Lowersec      Var1Primary  
         1.7213           2.0357           1.2779           1.3550           0.4191  
 Var1University            Var2F            Var2W  
         0.6148           2.0515           1.4528  

Degrees of Freedom: 17 Total (i.e. Null);  10 Residual
Null Deviance:      1287 
Residual Deviance: 21.05    AIC: 139.1 

> predict(glm( value ~ Var1+Var2, data=msample,family="poisson"), data.frame(Var1="Lowersec", Var2="B") )
       1 
3.076272 

编辑; 更多细节要求:

将总和乘以 exp(coef(fit)) 中相应条目的组合。中的非截距条目可coef(fit)让您计算“非角”单元格与“角单元格”中比例的估计比率。Var1:University 和 Var2:F 单元格将在原始模型中估计exp( 1.7213 + 0.6148+ 2.0515)(这是predict(fit)predict(fit, expand.grid(data.frame( rows=rowMeans(m_sample1), cols=colMeans(m_sample1))))应该给你的)。然后,您需要乘以新数据的总和与拟合数据的总和的比率。