【问题标题】:Use look-up table in R to create new column based on another df使用 R 中的查找表基于另一个 df 创建新列
【发布时间】:2018-08-04 18:40:57
【问题描述】:

我很难理解如何在 R 中执行相当于查找表的操作。我看到有人建议您应该使用“合并”来代替查找表,但我不是确定正确的方法是什么:

假设我有以下内容:

set.seed(42)
person_ids <- data.frame(person_1_id = stringi::stri_rand_strings(100, 10, '[A-Z]'), 
                 person_2_id = stringi::stri_rand_strings(100, 10, '[A-Z]'))

team_id_lookup <- data.frame(person_id = stringi::stri_rand_strings(100, 10, '[A-Z]'), 
                  team_ids = floor(runif(100, min=0, max=500)))

我想在 person_ids 中创建两个新列 -- team_id_1 和 team_id_2,它们使用查找数据框来查找给定 person_id 的相应 team_ids 并获取该值。

这里的正确方法是什么?

【问题讨论】:

  • 一种想法是您可以使用hash 包,您也可以考虑使用命名向量而不是数据框来查找值。甚至是一个命名列表。然后,您可以根据匹配 person_id 的名称设置两个 team_id 值。
  • 由于您的随机字符串生成过程,似乎没有匹配项。我将尝试发布一个类似但更简单的示例。希望对您有所帮助....
  • 您不需要查找表中的 200 个名称并让这些名称与人员表中的名称匹配吗?
  • 我确实想指出,对于由 10 个随机选择的字母组成的名称,您不太可能得到重复的名称,但在现实世界中却是。因此,在进行任何连接之前,您需要引入某种重复检查。从这个意义上说,给每个人一个这样的随机 id 可能是有用的。

标签: r


【解决方案1】:
set.seed(42)
person_ids <- data.frame(person_1_id = stringi::stri_rand_strings(10, 1, '[A-Z]'), 
                         person_2_id = stringi::stri_rand_strings(10, 1, '[A-Z]'))

team_id_lookup <- data.frame(person_id = stringi::stri_rand_strings(5, 1, '[A-Z]'), 
                             team_ids = floor(runif(5, min=0, max=500)))

library(dplyr)

person_ids %>%
  left_join(team_id_lookup, by=c("person_1_id"="person_id")) %>%
  left_join(team_id_lookup, by=c("person_2_id"="person_id")) %>%
  rename(team_id_1 = team_ids.x,
         team_id_2 = team_ids.y)

#    person_1_id person_2_id team_id_1 team_id_2
# 1            X           L       257        NA
# 2            Y           S       223        NA
# 3            H           Y        NA       223
# 4            V           G        NA        NA
# 5            Q           M        NA        NA
# 6            N           Y        NA       223
# 7            T           Z        NA       452
# 8            D           D       195       195
# 9            R           M        NA        NA
# 10           S           O        NA        NA

只有在您的查找表中找到的人才会有匹配项。所有其他人将拥有NA。

【讨论】:

  • 这正是我想要的。谢谢!顺便说一句,如果我的 team_id_lookup 列包含的列多于我在最终数据框中不想要的列,是否有一种快速方法可以在管道链中对 team_id_lookup 进行子集化?我尝试做一个子集命令,dplyr 不喜欢它
  • 如果您想忽略某些列,请尝试使用 dplyr 包中的 select 命令。使用select(x,y) 保留x,y 或使用select(-x,-y) 删除x,y。
  • 另请注意,您会收到一些警告,因为您的数据集包含 factor 变量。您可以提前将它们更新为character 变量。
  • 对 100 行使用一个字母名称会使名称不唯一,但是,只是说,理论很好。但是我认为如果有 100 行,您很可能至少会得到几乎所有的字母。
  • 正确,我们真的不知道是否预期会有这么多 NA,以及是否可以在两列中使用相同的名称或在同一列中使用重复的名称。即使 n=10 也存在生日问题。
【解决方案2】:

我没有完全遵循只有 100 个团队价值观的逻辑,所以我做了 200 个。但这是另一种方法。

set.seed(42)
person_ids <- data.frame(person_1_id = stringi::stri_rand_strings(100, 10, '[A-Z]'), 
                         person_2_id = stringi::stri_rand_strings(100, 10, '[A-Z]'), 
                                        stringsAsFactors = FALSE)

all_pid <- c(person_ids$person_1_id, person_ids$person_2_id)

team_ids <- floor(runif(200, min=0, max=500))
names(team_ids) <- all_pid

person_ids$team_id_1 <- team_ids[person_ids$person_1_id]
person_ids$team_id_2 <- team_ids[person_ids$person_2_id]
head(person_ids)

  person_1_id person_2_id team_id_1 team_id_2
1  XYHVQNTDRS  WBVOMAOSGV       128       207
2  LSYGMYZDMO  KODFEVCGUH       362       422
3  XDZYCNKXLV  WBIIDJMBZX        78       428
4  TVKRAVAFXP  ZEBDJOYQMC       157       225
5  JLAZLYXQZQ  YJJQSFPVZA       148       366
6  IJKUBTREGN  VFCRHVQNAH       339       337 

【讨论】:

    【解决方案3】:

    只是在这里指出一个基本的 R 解决方案:

    查找表的想法是有一个表,您可以在其中轻松地根据索引查找信息。 R中的索引将是例如表的行名,您可以分配例如:

    rownames(team_id_lookup) <- team_id_lookup$person_id
    

    然后您可以使用它来查找您的团队成员身份:

    person_ids$team1 <- team_id_lookup[person_ids$person_1_id,"team_ids"]
    person_ids$team2 <- team_id_lookup[person_ids$person_2_id,"team_ids"]
    person_ids
       person_1_id person_2_id team1 team2
    1            X           L   257    NA
    2            Y           S   223    NA
    3            H           Y    NA   223
    4            V           G    NA    NA
    5            Q           M    NA    NA
    6            N           Y    NA   223
    7            T           Z    NA   452
    8            D           D   195   195
    9            R           M    NA    NA
    10           S           O    NA    NA
    

    为了重现性:我使用与 @AntoniosK 相同的数据,但根据 Antonios 的评论将 stringsAsFactors 设置为 FALSE。

    set.seed(42)
    person_ids <- data.frame(
            person_1_id = stringi::stri_rand_strings(10, 1, '[A-Z]'), 
            person_2_id = stringi::stri_rand_strings(10, 1, '[A-Z]'), 
            stringsAsFactors = F)
    team_id_lookup <- data.frame(
            person_id = stringi::stri_rand_strings(5, 1, '[A-Z]'), 
            team_ids = floor(runif(5, min=0, max=500)), 
            stringsAsFactors = F)
    

    【讨论】:

    • 啊,这太干净了!非常感谢
    • 是的,base R 有时会非常优雅。 :)
    • 不确定是否重要,但我收到了警告:Setting row names on a tibble is deprecated.。知道不弃用的策略是什么吗?
    • 那么不要使用小标题。使用as.data.frame() 将其转换为data.frame。
    • @coffeinjunky 我假设您使用的数据与我相同,但您的结果不同。似乎这种方法对factor 变量存在问题,因为它使用与因子水平相对应的数字。快速仔细检查一下,你会发现它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    相关资源
    最近更新 更多