【问题标题】:Finding nearest value within a data.table在 data.table 中查找最接近的值
【发布时间】:2017-10-23 02:25:38
【问题描述】:

我正在尝试为每个处理过的观察找到最接近的值。数据如下(1.2M obs的部分数据):

> dta
     id treatment      score
 1:   5         0 0.02381024
 2:  10         0 0.05428605
 3:  22         0 0.02118124
 4:  27         0 0.01495214
 5:  45         0 0.01877916
 6:  50         0 0.02120360
 7:  58         0 0.02207263
 8:  60         0 0.02807019
 9:  61         0 0.05432927
10:  65         1 0.59612077
11:  68         0 0.02482168
12:  72         1 0.14582400
13:  73         0 0.02371670
14:  77         0 0.02608826
15:  87         0 0.06852409
16:  88         0 0.07473471
17:  94         0 0.07160314
18:  97         0 0.02040747
19: 104         1 0.09878789
20: 108         0 0.02421807

对于每个处理过的观察结果(即处理 = 1),我希望获得一个未处理的观察值(即处理 = 0),其得分最接近,并将所选观察值标记为不可用于其他处理过的观察值匹配。

例如,第一个处理的观察(第 10 行)将匹配到 id = 88(第 16 行),第 12 行到第 17 行,依此类推。目前我正在运行流动循环:

smpl_treated = dta[treatment == 1]
smpl_untreated = dta[treatment == 0]

n_tmp = nrow(smpl_treated)
matched_id = matrix(0, n_tmp, 1)

smpl_tmp = smpl_untreated

for (i in 1:nrow(smpl_treated)) {

  x = smpl_treated[i]$score

  setkey(smpl_tmp, score)
  tmp = smpl_tmp[J(x), roll = "nearest"]
  matched_id[i] = tmp[[1]]
  smpl_tmp = smpl_tmp[id != tmp[[1]]]

}

matched_smpl = smpl_untreated[id %in% matched_id]

> matched_smpl
   id treatment      score
1: 87         0 0.06852409
2: 94         0 0.07160314
3: 88         0 0.07473471

有什么建议可以在 data.table 中实现或加快循环速度?使用原始的 1.2M obs 循环需要 2 多个小时。提前感谢您的帮助!

【问题讨论】:

  • 假设您有以下 5 个样本:{(id = 1, treatment = 0, score = 0), (id = 2,treatment = 1, score = 0.1), (id = 3,治疗 = 1,得分 = 0.2),(id = 4,治疗 = 1,得分 = 0.3),(id = 5,治疗 = 0,得分 = 0.4)}。换句话说,您有 3 个经过处理的观察值夹在两个未处理的观察值之间。在这种情况下,什么映射到什么?
  • 在我的上下文中,它不会发生。但是,如果发生这种情况,我可能应该反其道而行之——这样做的主要目的是获得经过处理和未经处理的观察结果的平衡样本。

标签: r data.table matching


【解决方案1】:

这详细说明了the already accepted answer of denis 使用data.table 语法的实际 可能性,例如,在加入时使用on 参数而不是setkey()

# determine the minimum number of treated and untreated cases
n <- min(dta[treatment == 0L, .N], dta[treatment == 1L, .N])
# order by descending score 
mdt <- dta[order(-score)][
  # and pick the ids of the top n treated and untreated cases
  # so that the highest untreated score match the highest treated score,
  # the 2nd highest untreated the 2nd highest treated and so forth
  , .(id0 = head(.SD[treatment == 0L, id], n), id1 = head(.SD[treatment == 1L, id], n))]
mdt
   id0 id1
1:  88  65
2:  94  72
3:  87 104
# join the ids two times to show the data of the treated and untreated cases
dta[dta[mdt, on = .(id==id0)], on = .(id = id1)]
    id treatment      score i.id i.treatment    i.score
1:  65         1 0.59612077   88           0 0.07473471
2:  72         1 0.14582400   94           0 0.07160314
3: 104         1 0.09878789   87           0 0.06852409

【讨论】:

  • 这样的解决方案比我的要好得多,并且确实可以完成工作(它不假设治疗和非治疗数据具有不同的分数范围)。谢谢分享
  • @denis 感谢您的好评。但是,您的解决方案指向了正确的方向!
【解决方案2】:

如果您订购数据表、制作子集并使用合并功能,我可能会有一个解决方案。 不确定它是不是最好的解决方案,但它似乎适用于我理解你想要做的事情,而且它肯定会比你的循环更快:

library(data.table) 
dta <- data.table(id = c(5,10,22,27,45,50,58,60,61,65,68,72,73,77,87,88,94,97,104,108), 
                  treatment = c(0, 0  ,0  ,0,  0, 0, 0 ,0 , 0 ,  1, 0 ,1 ,0, 0 ,0 ,0 ,0 ,0 ,1 ,0),
                  score = c(0.02381024, 0.05428605, 0.02118124, 0.01495214, 0.01877916, 0.02120360,
                            0.02207263, 0.02807019, 0.05432927, 0.59612077, 0.02482168, 0.14582400,
                            0.02371670, 0.02608826, 0.06852409, 0.07473471, 0.07160314, 0.02040747,
                            0.09878789, 0.02421807))

setkey(dta, score) # order by score
treated_nbr <- dta[treatment == 1, .N] # just to simplify the next line

selecteddata <- 
  dta[treatment == 0, 
      .SD[(.N - treated_nbr + 1):.N,
          .(correspid = id, 
            correspscore = score, 
            id = dta[treatment == 1, id])]]

这里我们取相同数量的已订购未治疗人员 (.N-treated_nbr+1):.N),以便他们的分数与已订购人员最接近,并将 id 合并到已治疗人员的 id (id = dta[,.SD[treatment == 1,id]])

setkey(selecteddata, id)
setkey(dta, id)
selecteddata[dta] # do the merging

不确定它是否正是您想要的,因为我意识到它只有在您处理的分数高于未处理的分数时才有效(在您的示例中就是这种情况)。 您可以添加一个条件以仅针对得分高于未治疗者的治疗者使用建议的解决方案,否则执行其余操作(否则我看不到直接简单的解决方案)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-01
    • 2017-01-23
    • 2021-12-26
    • 1970-01-01
    • 2021-11-20
    • 2015-07-26
    • 1970-01-01
    相关资源
    最近更新 更多