在R数据表中匹配和更新列

4
我有2个数据表。以下是dput和数据表:
dt1
       email          custtype
1:   abc@yahoo.com     subs
2:   eli@gmail.com     subs
3: tod@hotmail.com     subs

dt1 = setDT(structure(list(email = c("abc@yahoo.com", "eli@gmail.com", "tod@hotmail.com"
), custtype = c("subs", "subs", "subs")), .Names = c("email", 
"custtype"), class = c("data.table", "data.frame"), row.names = c(NA, 
-3L)))

dt2

      emails         range
1:    sam@live.com  orange
2: tod@hotmail.com  orange
3:    ver@live.com  orange
4:   yahoo@yah.com  orange

dt2 = setDT(structure(list(emails = structure(1:4, .Label = c("sam@live.com", 
"tod@hotmail.com", "ver@live.com", "yahoo@yah.com"), class = "factor"), 
    range = structure(c(1L, 1L, 1L, 1L), .Label = "orange", class = "factor")), .Names = c("emails", 
"range"), class = c("data.table", "data.frame"), row.names = c(NA, 
-4L)))

我正在尝试将dt1中的电子邮件与dt2中的电子邮件进行匹配,然后尝试更新dt1中的custtype列。到目前为止我已经尝试了:
dt1[match(email,dt2$emails), custtype:="some value"]

这是什么意思是,它会更改找到匹配的索引处的值,而不是针对找到匹配的电子邮件地址进行更改。
生成的数据表应如下所示。找到匹配项的电子邮件,相应的custtype应使用提供的值进行更新:
  email             custtype
1:   abc@yahoo.com     subs
2:   eli@gmail.com     subs
3: tod@hotmail.com     some value

这是包的一个非常常见的用法,也是我使用它的主要原因之一。虽然vignettes还没有涵盖这个章节,但是阅读它们对于学习该包来说是必不可少的:https://github.com/Rdatatable/data.table/wiki/Getting-started。同时,在此期间,这种用法在该网站上的用户编写的“Docs”中得到了涵盖:http://stackoverflow.com/documentation/data.table/4976/joins-and-merges/17560/update-values-in-a-join#t=201610102031286494036。 - Frank
我仍然是一个data.table的新手,但是dt2[dt1, on = c("emails"="email")][,:=(custtype = dplyr::coalesce(as.character(range), custtype), range=NULL)][]浮现在我的脑海中。 - lukeA
@lukeA 顺便说一下,如果:=只在第一个[]内完成,它将仅修改dt2 - Frank
@Frank 你的意思是这个代码和 dt2[dt1, on = c("emails"="email")][,.(emails, custtype = dplyr::coalesce(as.character(range), custtype))] 是一样的吗? - lukeA
@lukeA 是的,我也这么认为。 - Frank
3个回答

5

你尝试了:

dt1[match(email,dt2$emails), custtype:="some value"]
< p > match() 的作用是找到 dt2 的行号,然后将其作为索引传递给 dt1。这没有任何意义。

想法是直接使用 dt2 索引 dt1

> dt1
             email custtype
1:   abc@yahoo.com     subs
2:   eli@gmail.com     subs
3: tod@hotmail.com     subs

> dt1[dt2, on=c(email="emails"), custtype:="some value"]

> dt1
             email   custtype
1:   abc@yahoo.com       subs
2:   eli@gmail.com       subs
3: tod@hotmail.com some value

看起来这就是您想要的结果。

FAQ 2.14 解释了 A[B] 语法背后的思想。


1
另一种使用 data.table 的方法是通过连接这两个表:
dt2[dt1, on = c(emails = "email"), mult = "first"][!is.na(range), custtype := "some value"][, range := NULL][]

解释

加入

dt2[dt1, on = c(emails = "email"), mult = "first"]

是一个右连接,返回 dt1 的所有行:
            emails  range custtype
1:   abc@yahoo.com     NA     subs
2:   eli@gmail.com     NA     subs
3: tod@hotmail.com orange     subs

mult = "first" 可以防止在 dt2 中存在多个条目具有相同的电子邮件地址但不同的 range 值时产生不必要的重复。

更改 custtype

只有匹配的电子邮件地址才会在 range 列中具有非 NA 条目。

[!is.na(range), custtype := "some value"]

使用此方法仅更改已找到匹配项的行中的custtype

删除range

range列不再需要。可以通过以下方式将其移除:

[, range := NULL]

打印

由于最后一次操作是通过引用进行的更新,因此最终结果

[]

确保结果被打印。

如果同一电子邮件地址有多个范围的值怎么办?这意味着同一电子邮件地址可能有不止一行数据,但范围不同。 - syebill
不错的观点。默认情况下,data.table 的连接函数会暗示参数 mult = "all", 这可能导致不必要的重复条目。我会相应地更新我的回答。 - Uwe

0
发现了一个使用"which"的解决方案,但仍在寻找更优雅的解决方案。
dt1[which(match(email, dt2$emails) > 0), custtype := "some value"]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接