在 df1 和 df2 之间执行left_join,其中 df2 给出了 df1 中连接变量的范围

Perform left_join between df1 and df2 where df2 gives a range for the joining variable in df1

提问人:fsure 提问时间:11/15/2023 最后编辑:Julianfsure 更新时间:11/15/2023 访问量:47

问:

我正在尝试合并两个数据帧。

df1包含包含列和 的公司级别数据。Say 看起来像这样:company_nameindustry_codedf1

company_name industry_code
一个 33
B 43
C 56
D 88

df2包含。Say 看起来像这样:industry_namemin_codemax_codedf2

industry_name min_code max_code
10 19
食物 20 39
关心 40 69
制造业 70 99

我想将行业数据合并到公司数据中。这样,在合并后,数据如下所示:df2df1

company_name industry_code industry_name
一个 33 食物
B 43 关心
C 56 关心
D 88 制造业

请看下面我失败的尝试:

# first attempt
df1 %>%
left_join(df2, by = c("industry_code" = "min_code"))

# second attempt
df1 %>%
mutate(industry_name = ifelse(between(industry_code, df2$min_code, df2$max_code), df2$industry_name, NA)



r dplyr merge left-join

评论

5赞 Jon Spring 11/15/2023
df1 %>% left_join(df2, join_by(between(industry_code, min_code, max_code)))

答:

4赞 Mark 11/15/2023 #1

正如 Jon 已经说过的,使用 dplyr 最简单的答案是使用:join_by()

df1 |>
  left_join(df2, join_by(between(industry_code, min_code, max_code))) |>
  select(-min_code, -max_code)

输出:

# A tibble: 4 × 3
  company_name industry_code industry_name
  <chr>                <dbl> <chr>        
1 A                       33 Food         
2 B                       43 Care         
3 C                       56 Care         
4 D                       88 Manufacturing

第一次尝试的问题是,(我相信你已经知道了),行业代码与最小代码不匹配,所以你会得到 NA。

如果你稍微玩一下,第二次尝试的问题可以更清楚:

between(c(88, 88, 88, 88), df2$min_code, df2$max_code) # [1] FALSE FALSE FALSE  TRUE

between(c(33, 33, 33, 33), df2$min_code, df2$max_code) # [1] FALSE  TRUE FALSE FALSE

between(c(33, 33, 33, 33, 33), df2$min_code, df2$max_code) 
# Error in between(c(33, 33, 33, 33, 33), df2$min_code, df2$max_code) : 
#  Incompatible vector lengths: length(x)==5 length(lower)==4 length(upper)==4. Each should be either length 1 or the length of the longest.

该代码不是根据每个最小值和最大值检查每个行业代码,而是根据第一个最小值和第一个最大值检查第一个行业代码,依此类推。

如果您仍然想使用这种方法,则使用 map_chr() 遍历industry_name是可行的。此外,您的样本数据中没有间隙,因此,如果您的真实数据也是如此,则可以使用基本 R,例如 .findInterval()df2$industry_name[findInterval(df1$industry_code, df2$min_code)]

0赞 Yuriy Saraykin 11/15/2023 #2

数据表

library(data.table)

df2 <- data.frame(
  stringsAsFactors = FALSE,
     industry_name = c("Smoke", "Food", "Care", "Manufacturing"),
          min_code = c(10L, 20L, 40L, 70L),
          max_code = c(19L, 39L, 69L, 99L)
)


df1 <- data.frame(
  stringsAsFactors = FALSE,
      company_name = c("A", "B", "C", "D"),
     industry_code = c(33L, 43L, 56L, 88L)
)

setDT(df1)
setDT(df2)

df2[df1, list(company_name, industry_code, industry_name), on = list(max_code >= industry_code, min_code <= industry_code)]
#>    company_name industry_code industry_name
#> 1:            A            33          Food
#> 2:            B            43          Care
#> 3:            C            56          Care
#> 4:            D            88 Manufacturing

创建于 2023-11-15 with reprex v2.0.2