对于 data.table 中的每一行,获取另一个 data.table 中匹配行的随机索引

Posted

技术标签:

【中文标题】对于 data.table 中的每一行,获取另一个 data.table 中匹配行的随机索引【英文标题】:For each row in a data.table, get random index for matching rows in another data.table 【发布时间】:2022-01-11 16:50:29 【问题描述】:

我有一个包含几列因素的 data.table (dt1),例如

ID    Factor1    Factor2    Factor3
01          A          X          J
02          B          X          L
03          C          Y          J

每一行都有一个唯一的 ID,但可能有多行具有相同的因子变量组合。我有另一个表(dt2),它具有完全相同的因子变量,但要大得多,并且没有 ID 列。 dt1 中的每个因子组合都会在 dt2 中出现多次。例如

Factor1    Factor2    Factor3
      A          Z          K
      A          X          J
      A          X          J
      B          J          L
      B          X          L
      C          X          J
      C          Y          J

我想为 dt1 中的每一行返回 dt2 中具有相同因子变量组合的随机行的索引。

我想我可以使用.I[sample(.N,1)] 返回一个随机索引,但不知道如何按每行的相关因素进行分组。我是否需要创建一个函数来依次获取 dt1 中的每一行,或者是否有一种矢量化的方式来实现这一点?

所需的输出可以是长度为nrow(dt1) 的向量,也可以是 dt1 中的附加列。它将包含来自 dt2 的行索引。例如像这样(ID 01 的索引可能是 2 或 3):

ID    Factor1    Factor2    Factor3    Index
01          A          X          J        3
02          B          X          L        5
03          C          Y          J        7

非常感谢任何帮助。

【问题讨论】:

请提供dt2的样本数据和想要的输出。 @Wimpel 添加了这些 【参考方案1】:
# create index in dt2
dt2[, index := .I]
# set unique key dt1
setkey(dt1, ID)
# get factor columns
cols <- grep("Factor", names(dt1), value = TRUE)
#build code to eval/parse in a string
run.text <- paste0("dt1[dt1, index := sample(dt2[", 
                   paste0(cols, " == i.", cols, collapse = " & "), 
                   ", ]$index, 1), by = .EACHI]")
#eval/parse the string
eval(parse(text = run.text))
#    ID Factor1 Factor2 Factor3 index
# 1:  1       A       X       J     2
# 2:  2       B       X       L     5
# 3:  3       C       Y       J     1
# 4:  4       A       X       J     3

使用的样本数据(在 dt1 中有重复)

library(data.table)
dt1 <- fread("ID    Factor1    Factor2    Factor3
01          A          X          J
02          B          X          L
03          C          Y          J
04          A          X          J")

dt2 <- fread("Factor1    Factor2    Factor3
      A          Z          K
      A          X          J
      A          X          J
      B          J          L
      B          X          L
      C          X          J
      C          Y          J")

【讨论】:

谢谢 - 这看起来不错。在我的实际用例中,dt1 和 dt2 中有很多 Factor 列 - 有没有办法像这样匹配它们而无需手动将它们全部写出来? 已编辑答案.. 我能想到的第一件事是一个 eval/parse 解决方案,您在运行之前将整行粘贴在一起.. 似乎可以解决问题。【参考方案2】:

UPD 我想你想做这样的事情:

dt2[, Index := as.character(.I)]
cols = c("Factor1", "Factor2", "Factor3")
dt = dt2[, lapply(.SD, list), keyby = cols]
dt = merge(dt1, dt, by = cols, all.x = T)
dt[, .(Index = sample(Index[[1]], 1)), keyby = c("ID", cols)]

【讨论】:

谢谢。如问题中所述, dt1 中的多行可以具有相同的因子变量组合。使用这种方法,它们都将匹配相同的索引 - 我希望每行的选择都是随机的。即如果 dt1 中有两行与 ID 01 具有相同的特征,那么我希望每行都能够匹配索引 2 或 3。 是的,你是对的。我更新代码。【参考方案3】:
library(data.table)

# set up the data
set.seed(94)
dt1 <- setnames(as.data.table(matrix(sample(3, 9, TRUE), 3)), paste0("Factor", 1:3))
dt2 <- dt1[sample(3, 10, TRUE)]
dt1
#>    Factor1 Factor2 Factor3
#> 1:       2       1       2
#> 2:       2       3       3
#> 3:       2       3       2
dt2
#>     Factor1 Factor2 Factor3
#>  1:       2       3       2
#>  2:       2       3       3
#>  3:       2       3       2
#>  4:       2       3       2
#>  5:       2       3       2
#>  6:       2       1       2
#>  7:       2       3       2
#>  8:       2       3       2
#>  9:       2       3       2
#> 10:       2       3       3

# create helper columns and do a rolling join
dt2[, R := seq_len(.N)/.N, by = names(dt2)][, Index := .I]
print(dt2[dt1[, R := runif(.N)], on = names(dt1), roll = -Inf][, R := NULL])
#>    Factor1 Factor2 Factor3 Index
#> 1:       2       1       2     6
#> 2:       2       3       3     2
#> 3:       2       3       2     3

【讨论】:

【参考方案4】:

你也可以试试

    添加索引 合并dt1dt2 然后使用sample(.N,1) 采样
cols = names(dt2)
dt2[,index := .I]
dt2[dt1, on = (cols)][,.SD[sample(.N,1)],.(ID)]

或一排答案

dt2[,index := .I][dt1, on = (cols)][,.SD[sample(.N,1)],.(ID)]

更新

library(data.table)
dt1 <- fread("ID    Factor1    Factor2    Factor3
01          A          X          J
02          B          X          L
03          C          Y          J")
dt2 <- fread("Factor1    Factor2    Factor3
      A          Z          K
      A          X          J
      A          X          J
      B          J          L
      B          X          L
      C          X          J
      C          Y          J")
cols <- names(dt2)[1:3]
dt2[,index := .I][dt1, on = (cols)][,.SD[sample(.N,1)],.(ID)]
#>       ID Factor1 Factor2 Factor3 index
#>    <int>  <char>  <char>  <char> <int>
#> 1:     1       A       X       J     3
#> 2:     2       B       X       L     5
#> 3:     3       C       Y       J     7

由reprex package 创建于 2021-12-07 (v2.0.1)

【讨论】:

我无法让它工作,我认为是因为添加索引列也会修改 cols 对象。 你确定吗?我已经对其进行了测试,您可以看到更新的答案。也许你忘记初始化dt1dt2 添加索引列如果在添加索引列后检查cols的值,请不要修改cols对象。

以上是关于对于 data.table 中的每一行,获取另一个 data.table 中匹配行的随机索引的主要内容,如果未能解决你的问题,请参考以下文章

对于表中的每一行,选择另一个表中由值连接的最近日期

对于文件中的每一行,同步执行命令并保存到另一个文件的换行符

R:根据来自另一个data.table的条件“标记”一行

如何将每一行熊猫数据帧附加到另一个数据帧的每一行

PySpark:对于每一行,根据条件计算另一个表

为数据框中的每一行应用一个函数,用于另一个数据框中的每一行