匹配一个变量后,仅将一个变量从一个 R data.table 复制到另一个

Posted

技术标签:

【中文标题】匹配一个变量后,仅将一个变量从一个 R data.table 复制到另一个【英文标题】:Copy only one variable from one R data.table to another after matching on a variable 【发布时间】:2021-11-20 18:21:32 【问题描述】:

我可以通过类似的方式将数据框中的列的匹配副本复制到另一个中

DF2$y <- DF1[match(DF2$id2, DF1$id1), "z"]  # DF1 and DF2 are data frames

DF2$id2DF1$id1 匹配。我想知道对于这种操作我可以用 数据表 做什么。我的数据表有数百万行和数百列。我已经完成了setkey(DT1, id1)setkey(DT2, id2)

这行得通:

DT2[, y := DT1[match(DT2$id2, DT1$id1), "z"]]  # DT1 and DT2 are data tables

但我担心match 部分可能会花费很长时间。 (或者这是不可避免的?)

我了解我还可以使用列选择、merge 和重命名:

tmp <- DT1[, c("id1", "z")]  # column selection
DT3 <- merge(DT2, tmp, by.x = "id2", by.y = "id1", all.x = TRUE, suffixes = c("", ".y")) # merge
setnames(DT3, "z.y", "y")  # rename

(前两行可以写在一行上)但这似乎有点太复杂了。会有更简单快捷的解决方案吗?

谢谢。

例子:

library(data.table)
DF1 <- data.frame(id1=2:4, x=LETTERS[1:3], z=11:13)
DF2 <- data.frame(id2=1:4, x=LETTERS[5:8], z=21:24)
DF1
#   id1 x  z
# 1   2 A 11
# 2   3 B 12
# 3   4 C 13
DF2
#   id2 x  z
# 1   1 E 21
# 2   2 F 22
# 3   3 G 23
# 4   4 H 24

DT1 <- data.table(DF1)
DT2 <- data.table(DF2)
setkey(DT1, id1)
setkey(DT2, id2)

DF2$y <- DF1[match(DF2$id2, DF1$id1), "z"]
DF2  # correct
#   id2 x  z  y
# 1   1 E 21 NA
# 2   2 F 22 11
# 3   3 G 23 12
# 4   4 H 24 13

DT2[, y := DT1[match(DT2$id2, DT1$id1), "z"]]
DT2
#    id2 x  z  y
# 1:   1 E 21 NA
# 2:   2 F 22 11
# 3:   3 G 23 12
# 4:   4 H 24 13
DT2[, y := NULL]

tmp <- DT1[, c("id1", "z")]
DT3 <- merge(DT2, tmp, by.x = "id2", by.y = "id1", all.x = TRUE, suffixes = c("", ".y"))
setnames(DT3, "z.y", "y")
DT3
#    id2 x  z  y
# 1:   1 E 21 NA
# 2:   2 F 22 11
# 3:   3 G 23 12
# 4:   4 H 24 13

## Simpler alternatives?

【问题讨论】:

@GregorThomas 谢谢。但我只想“合并”一个变量,而不是完整的数据集。为此,我需要按照我在问题中的解释进行列选择、合并,然后重命名。有更简单的方法吗? merge 很简单。对于这个特定的示例,您的 ID 列的名称不匹配,并且您要添加的列的名称需要更改,但重命名是一种非常有效的单行代码,这很烦人。 我同意你的担心——我希望data.table::mergebase::match 快得多,尤其是在键控表上。但我不同意您的看法,即重命名几列过于复杂。 或者更简单一点,merge(DT2, setnames(DT1[, c("id1", "z")], c("id2", "y")), all.x = TRUE) @GregorThomas 当然。谢谢。我想知道是否还有其他解决方案,我很高兴得知我不需要寻找其他解决方案。我已经在这个问题上徘徊了这么久。 【参考方案1】:

如果我理解正确,OP 希望将列 zDT1 附加到 DT2 作为 id 列匹配的列 y

使用data.table,可以使用更新连接来解决这个问题:

library(data.table)
DT2[DT1, on = .(id2 = id1), y := i.z]
DT2
   id2 x  z  y
1:   1 E 21 NA
2:   2 F 22 11
3:   3 G 23 12
4:   4 H 24 13

请注意,DT2 是通过引用更新的,也就是说,不会复制整个数据对象。这对于 OP 数百万行的大型生产数据集可能很方便。

警告

这是可行的,因为id1id2独特的,这是示例用例的情况。因此,请确保在对重复值执行 更新连接 时得到想要的结果。

让我们看看如果id1列中有重复值会发生什么,例如

如果DT1id1 == 4 重复

(DT1 <- data.table(id1 = c(2:4, 4), x = LETTERS[1:4], z = 11:14))
   id1 x  z
1:   2 A 11
2:   3 B 12
3:   4 C 13
4:   4 D 14

然后

DT2[DT1, on = .(id2 = id1), y := i.z][]

返回

   id2 x  z  y
1:   1 E 21 NA
2:   2 F 22 11
3:   3 G 23 12
4:   4 H 24 14

所以,更新加入

尚未在 DT2 中创建额外的行(这可能是您可能希望避免复制大型数据集的原因), 已选择最后一次出现的 z 以防多个匹配项。

【讨论】:

【参考方案2】:

我有两个可能的想法:

    使用merge 并选择列并在那里进行重命名
DT3 <- merge(DT2, DT1[, .(id1, y = z)], by.x = "id2", by.y = "id1", all.x = TRUE)
    使用data.tableon 语法。我将此解决方案直接基于此处的答案,该答案显示了如何进行完整的外部联接: https://***.com/a/46904676/9244371
unique_keys <- unique(c(DT1[, id1], DT2[, id2]))
DT3 <- DT2[DT1[.(unique_keys), .(id1, y = z)]]

【讨论】:

以上是关于匹配一个变量后,仅将一个变量从一个 R data.table 复制到另一个的主要内容,如果未能解决你的问题,请参考以下文章

如何从 R 中的 GLM 调用中检索原始变量名称的列表?

使用列名列表从数据表中选择匹配的列[重复]。

R按因子子集后的空数据框

R中字符串的类字典匹配

R 中的重塑问题:我重塑的数据框将 3 个变量变为 1 个

Shiny pickerInput - conditionalPanel 仅将值设置为第一个选择