匹配一个变量后,仅将一个变量从一个 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$id2
与 DF1$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::merge
比base::match
快得多,尤其是在键控表上。但我不同意您的看法,即重命名几列过于复杂。
或者更简单一点,merge(DT2, setnames(DT1[, c("id1", "z")], c("id2", "y")), all.x = TRUE)
@GregorThomas 当然。谢谢。我想知道是否还有其他解决方案,我很高兴得知我不需要寻找其他解决方案。我已经在这个问题上徘徊了这么久。
【参考方案1】:
如果我理解正确,OP 希望将列 z
从 DT1
附加到 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 数百万行的大型生产数据集可能很方便。
警告
这是可行的,因为id1
和id2
是独特的,这是示例用例的情况。因此,请确保在对重复值执行 更新连接 时得到想要的结果。
让我们看看如果id1
列中有重复值会发生什么,例如
如果DT1
有id1 == 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.table
的on
语法。我将此解决方案直接基于此处的答案,该答案显示了如何进行完整的外部联接:
https://***.com/a/46904676/9244371
unique_keys <- unique(c(DT1[, id1], DT2[, id2]))
DT3 <- DT2[DT1[.(unique_keys), .(id1, y = z)]]
【讨论】:
以上是关于匹配一个变量后,仅将一个变量从一个 R data.table 复制到另一个的主要内容,如果未能解决你的问题,请参考以下文章