MatchIt包get_matches()在函数/并行环境中失效的问题求助
解决MatchIt中get_matches()在函数/并行环境下的对象找不到问题
问题背景
使用MatchIt包进行倾向得分匹配时,get_matches()在全局环境中可正常生成匹配后数据,但将其放入自定义函数或foreach()%dopar%{}并行环境(结合groundhog做模拟)时,会出现找不到匹配数据对象的错误,例如函数内调用时报错object 'm.data' not found,并行循环也存在类似问题。需要完成千次模拟,必须依赖并行提升效率。
数据生成代码
dataGen <- function(b0,b1,n = 2000,cor = 0){ # covariate sigma <- matrix(rep(cor,9),3,3) diag(sigma) <- rep(1,3) cov <- MASS::mvrnorm(n, rep(0,3), sigma) # error error <- rnorm(n,0,sqrt(18)) # treatment variable logit <- b0+b1*cov[,1]+0.3*cov[,2]+cov[,3] p <- 1/(1+exp(-logit)) treat <- rbinom(n,1,p) # outcome variable y <- error+treat+cov[,1]+cov[,2] data <- as.data.frame(cbind(cov,treat,y)) return(data) } set.seed(1) data <- dataGen(b0=-0.92, b1=0.8, 900)
全局环境下可正常运行的代码
fm1 <- treat ~ V1+V2+V3 m.out <- MatchIt::matchit(data = data, formula = fm1, link = "logit", m.order = "random", caliper = 0.2) m.data <- MatchIt::get_matches(m.out,data=data) iest.m.WLS <- function(m.data, fm2){ model.1 <- lm(fm2, data = m.data, weights=(weights)) est <- model.1$coefficients["treat"] ## regular robust standard error ignoring pair membership model.1.2 <- lmtest::coeftest(model.1,vcov. = sandwich::vcovHC) CI.r <- confint(model.1.2,"treat",level=0.95) ## cluster robust standard error accounting for pair membership model.2.2 <- lmtest::coeftest(model.1, vcov. = sandwich::vcovCL, cluster = ~subclass) CI.cr <- confint(model.2.2,"treat",level=0.95) return(c(est=est,CI.r,CI.cr)) } fm2 <- y ~ treat+V1+V2+V3 iest.m.WLS(m.data,fm2)
函数内部调用get_matches()报错的代码
执行以下代码会报错:"object 'm.data' not found"
rm(m.data) m.out <- MatchIt::matchit(data = data, formula = fm1, link = "logit", m.order = "random", caliper = 0.2) iest.m.WLS <- function(m.out, fm2){ m.data <- MatchIt::get_matches(m.out,data=data) model.1 <- lm(fm2, data = m.data, weights=(weights)) est <- model.1$coefficients["treat"] ## regular robust standard error ignoring pair membership model.1.2 <- lmtest::coeftest(model.1,vcov. = sandwich::vcovHC) CI.r <- confint(model.1.2,"treat",level=0.95) ## cluster robust standard error accounting for pair membership model.2.2 <- lmtest::coeftest(model.1, vcov. = sandwich::vcovCL, cluster = ~subclass) CI.cr <- confint(model.2.2,"treat",level=0.95) return(c(est=est,CI.r,CI.cr)) } iest.m.WLS(m.out,fm2)
并行循环中调用get_matches()的问题
res=foreach(s = 1:7,.combine="rbind")%dopar%{ m.out <- MatchIt::matchit(data = data, formula = fm.p, distance = data$logit, m.order = "random", caliper = 0.2) m.data <- MatchIt::get_matches(m.out,data=data) ... }
问题原因分析
- 作用域冲突:
matchit对象默认仅保存原始数据的引用而非完整数据,当在函数环境中调用get_matches()时,若未显式传递数据,R会尝试从matchit对象的创建环境(如全局环境)查找数据,可能因作用域隔离导致查找失败。 - 并行环境隔离:
foreach的并行子进程拥有独立的运行环境,全局环境中的变量不会自动传递到子进程,若未显式导出数据或包,get_matches()无法找到所需的原始数据或依赖包。 - 变量查找歧义:
lm中weights=(weights)的写法可能导致R优先在函数环境中查找weights变量,而非从指定的数据框中提取列,间接引发错误。
解决方案
1. 自定义函数内的修复方案
方案一:让matchit对象保存完整数据
在创建matchit对象时添加keep.data = TRUE参数,使匹配对象内置原始数据,get_matches()无需额外传递数据即可提取:
# 修改matchit调用,添加keep.data=TRUE m.out <- MatchIt::matchit(data = data, formula = fm1, link = "logit", m.order = "random", caliper = 0.2, keep.data = TRUE) iest.m.WLS <- function(m.out, fm2){ # 直接从m.out中提取数据,无需传递data参数 m.data <- MatchIt::get_matches(m.out) # 明确从数据框中读取weights列,避免作用域歧义 model.1 <- lm(fm2, data = m.data, weights = weights) est <- model.1$coefficients["treat"] ## regular robust standard error ignoring pair membership model.1.2 <- lmtest::coeftest(model.1,vcov. = sandwich::vcovHC) CI.r <- confint(model.1.2,"treat",level=0.95) ## cluster robust standard error accounting for pair membership model.2.2 <- lmtest::coeftest(model.1, vcov. = sandwich::vcovCL, cluster = ~subclass) CI.cr <- confint(model.2.2,"treat",level=0.95) return(c(est=est,CI.r,CI.cr)) } iest.m.WLS(m.out,fm2)
方案二:显式传递数据到函数
将原始数据作为函数参数传入,确保get_matches()能在函数环境中找到数据:
iest.m.WLS <- function(m.out, fm2, data){ m.data <- MatchIt::get_matches(m.out, data = data) model.1 <- lm(fm2, data = m.data, weights = weights) # 后续代码同方案一 } iest.m.WLS(m.out, fm2, data)
2. 并行循环中的修复方案
并行环境需解决变量传递、包加载和种子设置三个核心问题,以下是适配千次模拟的优化代码:
# 加载必要依赖包 library(foreach) library(doParallel) library(MatchIt) library(lmtest) library(sandwich) library(MASS) library(groundhog) # 若需版本管理可保留 # 设置并行集群(根据CPU核心数调整) cl <- makeCluster(detectCores() - 1) registerDoParallel(cl) # 定义单轮模拟函数 simulate_single <- function(seed) { # 设置独立种子,保证结果可复现 set.seed(seed) # 生成模拟数据 data <- dataGen(b0=-0.92, b1=0.8, 900) fm.p <- treat ~ V1+V2+V3 # 创建匹配对象,内置原始数据 m.out <- MatchIt::matchit(data = data, formula = fm.p, link = "logit", m.order = "random", caliper = 0.2, keep.data = TRUE) m.data <- MatchIt::get_matches(m.out) # 拟合模型并提取结果 fm2 <- y ~ treat+V1+V2+V3 model.1 <- lm(fm2, data = m.data, weights = weights) est <- model.1$coefficients["treat"] model.1.2 <- lmtest::coeftest(model.1, vcov. = sandwich::vcovHC) CI.r <- confint(model.1.2, "treat", level=0.95) model.2.2 <- lmtest::coeftest(model.1, vcov. = sandwich::vcovCL, cluster = ~subclass) CI.cr <- confint(model.2.2, "treat", level=0.95) return(c(est=est, CI.r, CI.cr)) } # 执行千次并行模拟 res <- foreach(seed = 1:1000, .combine="rbind", .packages = c("MatchIt", "lmtest", "sandwich", "MASS")) %dopar% { simulate_single(seed) } # 关闭并行集群 stopCluster(cl)
关键优化点:
- 每个子进程独立生成数据,避免大对象跨进程传递的开销
- 用
seed参数设置独立随机种子,保证模拟结果可复现 - 通过
.packages指定子进程需加载的依赖包 - 用
keep.data=TRUE让matchit对象内置数据,无需额外传递
关键要点总结
- 作用域问题:优先使用
keep.data=TRUE让匹配对象保存原始数据,或显式传递数据参数,避免依赖环境变量 - 并行环境:通过
.export传递全局变量,.packages加载依赖包,子进程独立生成数据或接收数据 - 代码健壮性:在
lm中直接使用列名作为weights参数(因已指定data,R会自动从数据框中查找),避免变量查找歧义
内容的提问来源于stack exchange,提问作者Qidi Man
相关产品推荐
相关产品推荐

