对于 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】:你也可以试试
-
添加索引
合并
dt1
和dt2
然后使用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 对象。 你确定吗?我已经对其进行了测试,您可以看到更新的答案。也许你忘记初始化dt1
和dt2
。
添加索引列如果在添加索引列后检查cols
的值,请不要修改cols对象。以上是关于对于 data.table 中的每一行,获取另一个 data.table 中匹配行的随机索引的主要内容,如果未能解决你的问题,请参考以下文章