【问题标题】:Matching and Updating a column in R data table匹配和更新 R 数据表中的列
【发布时间】:2016-10-10 20:20:51
【问题描述】:

我有 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

【问题讨论】:

  • 这是包的一个很常见的用法,也是我使用它的主要原因之一。这些小插曲还没有涵盖此内容的章节,但对于学习该软件包来说是必不可少的阅读材料:github.com/Rdatatable/data.table/wiki/Getting-started 同时,此用法包含在本网站上用户编写的“文档”中:stackoverflow.com/documentation/data.table/4976/…
  • 我还是个 data.table 新手,但我想到了dt2[dt1, on = c("emails"="email")][,:=(custtype = dplyr::coalesce(as.character(range), custtype), range=NULL)][]
  • @lukeA 仅供参考,:= 只会修改 dt2 如果它在第一个 [] 内完成。
  • @Frank 你的意思是和dt2[dt1, on = c("emails"="email")][,.(emails, custtype = dplyr::coalesce(as.character(range), custtype))]一样?
  • @lukeA 是的,我想是的。

标签: r data.table match


【解决方案1】:

你试过了:

dt1[match(email,dt2$emails), custtype:="some value"]

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] 语法背后的思想。

【讨论】:

    【解决方案2】:

    另一种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]
    

    打印

    由于最后一个操作是更新引用最终

    []
    

    确保打印结果。

    【讨论】:

    • 如果同一封电子邮件的范围有多个值怎么办。是否意味着同一封电子邮件不止一行但范围不同?
    • 好点。默认情况下,data.table 连接隐含参数mult = "all",这可能会导致不需要的重复条目。我会相应地更新我的答案。
    【解决方案3】:

    找到了一个解决方案。但仍在寻找更优雅的解决方案。

    dt1[which(match(email, dt2$emails) > 0), custtype := "some value"]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 1970-01-01
      • 1970-01-01
      • 2015-07-12
      • 1970-01-01
      相关资源
      最近更新 更多