R 中用于大型复杂调查数据集的方法?

2024-01-19

我不是调查方法学家或人口统计学家,但我是 Thomas Lumley 的 R 调查包的狂热粉丝。我一直在处理一个相对较大的复杂调查数据集,即医疗保健成本和利用项目 (HCUP) 国家急诊室样本 (NEDS https://www.hcup-us.ahrq.gov/nedsoverview.jsp)。正如医疗保健研究和质量局所描述的,这是“来自 30 个州 947 家医院的急诊就诊的出院数据,近似于美国医院急诊的 20% 分层样本”

2006 年至 2012 年的完整数据集包含 198,102,435 个观测值。我已将数据分组为 40,073,358 例与外伤相关的出院,其中包含 66 个变量。即使对这些数据运行简单的调查程序也需要非常长的时间。我尝试过使用 RAM(2013 年末的 Mac Pro,3.7GHz 四核,128GB(!)内存),使用多核 http://r-survey.r-forge.r-project.org/survey/html/surveyoptions.html有空的时候,子集化 http://r-survey.r-forge.r-project.org/survey/html/subset.survey.design.html,与一个内存不足的数据库管理系统 https://faculty.washington.edu/tlumley/tutorials/user-biglm.pdf like MonetDB https://github.com/ajdamico/asdfree/blob/da164016bfdd533b12f40b55045d7a6007a24d12/IPUMS%20International/download%20import%20design%20into%20monetdb.R。基于设计的调查程序仍然需要几个小时。有时要好几个小时。一些不太复杂的分析需要 15 个小时以上。我猜测大部分计算工作都与巨大的协方差矩阵有关?

正如人们所预料的那样,处理原始数据的速度要快几个数量级。更有趣的是,根据程序的不同,对于如此大的数据集,未经调整的估计值可能非常接近调查结果。 (参见下面的示例)基于设计的结果显然更精确且更受欢迎,但几个小时的计算时间与几秒钟的计算时间对于增加的精度来说是一个不可忽视的成本。它开始看起来像是绕着街区走了很长一段路。

有没有人有这方面的经验?有没有办法优化大型数据集的 R 调查程序?也许更好地利用并行处理?贝叶斯方法是否使用INLA https://www.stat.washington.edu/research/reports/2011/tr583.pdf or 哈密​​顿量 http://www.stat.columbia.edu/~gelman/research/published/Si_et_al-BA14.pdf像斯坦这样的方法可能是解决方案吗?或者,当调查规模足够大且具有足够代表性时,一些未经调整的估计(尤其是相对指标)是否可以接受?

以下是一些未经调整的估计值近似调查结果的示例。

在第一个示例中,内存中的 svymean 花费了不到一个小时,内存不足则需要 3 个多小时。直接计算只需要不到一秒钟的时间。更重要的是,点估计(svymean 为 34.75,未调整为 34.77)以及标准误差(0.0039 和 0.0037)非常接近。

    # 1a. svymean in memory 

    svydes<- svydesign(
        id = ~KEY_ED ,
        strata = ~interaction(NEDS_STRATUM , YEAR),   note YEAR interaction
        weights = ~DISCWT ,
        nest = TRUE,
        data = inj
    )

    system.time(meanAGE<-svymean(~age, svydes, na.rm=T))
         user   system  elapsed
     3082.131  143.628 3208.822 
     > meanAGE 
           mean     SE
     age 34.746 0.0039 

    # 1b. svymean out of memory
    db_design <-
        svydesign(
            weight = ~discwt ,                                   weight variable column
            nest = TRUE ,                                        whether or not psus are nested within strata
            strata = ~interaction(neds_stratum , yr) ,           stratification variable column
            id = ~key_ed ,                                          
            data = "nedsinj0612" ,                               table name within the monet database
            dbtype = "MonetDBLite" ,
            dbname = "~/HCUP/HCUP NEDS/monet"  folder location
        )

    system.time(meanAGE<-svymean(~age, db_design, na.rm=T))
          user    system   elapsed
     11749.302   549.609 12224.233
     Warning message:
     'isIdCurrent' is deprecated.
     Use 'dbIsValid' instead.
     See help("Deprecated")
           mean     SE
     age 34.746 0.0039 


    # 1.c unadjusted mean and s.e.
    system.time(print(mean(inj$AGE, na.rm=T)))
     [1] 34.77108
        user  system elapsed
       0.407   0.249   0.653
      sterr <- function(x) sd(x, na.rm=T)/sqrt(length(x))  # write little function for s.e.
     system.time(print(sterr(inj$AGE)))
     [1] 0.003706483
        user  system elapsed
       0.257   0.139   0.394 

svymean 与使用 svyby(近 2 小时)与 tapply(4 秒左右)应用于数据子集的平均值的结果之间存在类似的对应关系:

# 2.a svyby .. svymean
system.time(AGEbyYear<-svyby(~age, ~yr, db_design, svymean, na.rm=T, vartype = c( 'ci' , 'se' )))
     user   system  elapsed
 4600.050  376.661 6594.196 
        yr      age          se     ci_l     ci_u
 2006 2006 33.83112 0.009939669 33.81163 33.85060
 2007 2007 34.07261 0.010055909 34.05290 34.09232
 2008 2008 34.57061 0.009968646 34.55107 34.59014
 2009 2009 34.87537 0.010577461 34.85464 34.89610
 2010 2010 35.31072 0.010465413 35.29021 35.33124
 2011 2011 35.33135 0.010312395 35.31114 35.35157
 2012 2012 35.30092 0.010313871 35.28071 35.32114


# 2.b tapply ... mean
system.time(print(tapply(inj$AGE, inj$YEAR, mean, na.rm=T)))
     2006     2007     2008     2009     2010     2011     2012
 33.86900 34.08656 34.60711 34.81538 35.27819 35.36932 35.38931
    user  system elapsed
   3.388   1.166   4.529

system.time(print(tapply(inj$AGE, inj$YEAR, sterr)))
        2006        2007        2008        2009        2010        2011        2012
 0.009577755 0.009620235 0.009565588 0.009936695 0.009906659 0.010148218 0.009880995
    user  system elapsed
   3.237   0.990   4.186

调查和未调整结果之间的对应关系开始因绝对计数而崩溃,这需要编写一个吸引调查对象的小函数,并使用 Lumley 博士的一些代码来对计数进行加权:

# 3.a svytotal

system.time(print(svytotal(~adj_cost, svydes, na.rm=T)))
             total       SE
adj_cost 9.975e+10 26685092
     user    system   elapsed 
10005.837   610.701 10577.755 

# 3.b "direct" calculation

SurvTot<-function(x){
    N <- sum(1/svydes$prob)
    m <- mean(x, na.rm = T)
    total <- m * N
    return(total)
}

> system.time(print(SurvTot(inj$adj_cost)))
[1] 1.18511e+11
   user  system elapsed 
  0.735   0.311   0.989 

结果更难以让人接受。尽管仍在调查程序确定的误差范围内。但同样,为了获得更精确的结果,3 小时与 1 秒相比,成本相当可观。

更新:2016 年 2 月 10 日

感谢塞维林和安东尼允许我借用你们的突触。抱歉延迟跟进,花了很少的时间来尝试您的建议。

Severin,您的观察是正确的,革命分析/MOR 构建对于某些操作来说更快。看起来它与 CRAN R 附带的 BLAS(“基本线性代数子程序”)库有关。它更精确,但速度较慢。因此,我使用允许多线程的专有(但 Mac 上免费)Apple Accelerate vecLib 优化了我的机器上的 BLAS(请参阅http://blog.quadrivio.com/2015/06/improved-r-performance-with-openblas.html http://blog.quadrivio.com/2015/06/improved-r-performance-with-openblas.html)。这似乎减少了一些操作时间,例如从 svyby/svymean 的 3 小时到 2 小时多一点。

安东尼在复制重量设计方法方面运气不佳。 type="bootstrap" withreplicates=20 运行了大约 39 小时,然后我退出了; type =“BRR”返回错误“无法在层中分割奇数个 PSU”,当我将选项设置为small =“merge”,large =“merge”时,它运行了几个小时,然后操作系统抛出了一个错误巨大的叹息,耗尽了应用程序内存; type="JKn" 返回错误“无法分配大小为 11964693.8 Gb 的向量”

再次非常感谢您的建议。现在,我将让自己在很长一段时间内零碎地进行这些分析。如果我最终想出更好的方法,我会发布在SO上


对于巨大的数据集,线性化设计(svydesign)比复制设计慢得多(svrepdesign)。检查其中的加权函数survey::as.svrepdesign并使用其中之一直接进行复制设计。您不能使用线性化来完成此任务。你可能甚至不使用会更好as.svrepdesign而是使用其中的函数。

举个例子,使用cluster=, strata=, and fpc=直接进入重复加权设计,参见

https://github.com/ajdamico/asdfree/blob/master/Censo%20Demografico/download%20and%20import.R#L405-L429 https://github.com/ajdamico/asdfree/blob/master/Censo%20Demografico/download%20and%20import.R#L405-L429

请注意,您还可以在此处查看每分钟的速度测试(带有每个事件的时间戳)http://monetdb.cwi.nl/testweb/web/eanthony/ http://monetdb.cwi.nl/testweb/web/eanthony/

还要注意的是replicates=参数几乎 100% 决定了设计的运行速度。因此,也许可以进行两种设计,一种用于系数(只需几次重复),另一种用于SE(具有您可以容忍的尽可能多的值)。以交互方式运行您的系数并优化白天所需的数字,然后让需要 SE 计算的更大进程在夜间运行

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

R 中用于大型复杂调查数据集的方法? 的相关文章

随机推荐