inner_join(),其中键之一(年)的取值范围

时间:2019-06-15 03:55:01

标签: r dplyr inner-join tidyverse fuzzyjoin

我有两个这样格式化的数据集:

df1
#>           Artist          Album Year
#> 1        Beatles  Sgt. Pepper's 1967
#> 2 Rolling Stones Sticky Fingers 1971

df2
#>            Album Year      Producer
#> 1  Sgt. Pepper's 1966 George Martin
#> 2 Sticky Fingers 1971  Jimmy Miller

我想按专辑和年份来制作inner_join,但有时“年份”字段会关闭一年:例如Sgt。 Peppers在df2中被列为1967年的df1中的1967年。

所以,如果我跑步:

df3 <- inner_join(df1, df2, by = c("Album", "Year"))

我得到:

df3
#>           Artist          Album Year     Producer
#> 1 Rolling Stones Sticky Fingers 1971 Jimmy Miller

我希望两张专辑都可以加入,只要是(df1$Year == df2$Year + 1)|(df1$Year == df2$Year - 1)之类的东西。

我不能仅仅通过“相册”简单地加入,因为在我的真实数据集中有一些标题相同的“相册”,以“年份”来区分。

以下数据集的代码:

df1 <- data.frame(stringsAsFactors=FALSE,
      Artist = c("Beatles", "Rolling Stones"),
       Album = c("Sgt. Pepper's", "Sticky Fingers"),
        Year = c(1967, 1971)
)
df1

df2 <- data.frame(stringsAsFactors=FALSE,
       Album = c("Sgt. Pepper's", "Sticky Fingers"),
        Year = c(1966, 1971),
    Producer = c("George Martin", "Jimmy Miller")
)
df2

5 个答案:

答案 0 :(得分:3)

我们可以在这里尝试使用sqldf包,因为可以使用SQL连接轻松表达您的要求:

library(sqldf)

sql <- "SELECT t1.Artist, t1.Album, t1.Year, t2.Album, t2.Year, t2.Producer
        FROM df1 t1
        INNER JOIN df2 t2
            ON ABS(t1.Year - t2.Year) <= 1"
df3 <- sqldf(sql)

如果要从两个表中选择所有字段,请使用:

SELECT t1.*, t2.* FROM ...

但是请注意,通常SELECT *并不适用,最好总是列出要选择的列。

答案 1 :(得分:2)

也许rolling join会解决这个问题。它适用于您的数据样本,但是实际数据中可能会存在一些棘手的情况。

在下面的代码中,roll="nearest"将与每个专辑的最接近的年份值匹配(“滚动”部分仅适用于最后一个联接列,在这种情况下为Year)。

library(data.table)

setDT(df1)
setDT(df2)

setkey(df1, Album, Year)
setkey(df2, Album, Year)

joined = df1[df2, roll="nearest"]

joined
           Artist          Album Year      Producer
1:        Beatles  Sgt. Pepper's 1966 George Martin
2: Rolling Stones Sticky Fingers 1971  Jimmy Miller

答案 2 :(得分:1)

Year + 1添加到df2,然后加入吗?如果您想同时覆盖两个方向的范围,也可以添加Year - 1

library(dplyr)

inner_join(df1, df2 %>%  bind_rows(df2 %>%  mutate(Year = Year + 1)),
                by = c("Album", "Year"))

#          Artist          Album Year      Producer
#1        Beatles  Sgt. Pepper's 1967 George Martin
#2 Rolling Stones Sticky Fingers 1971  Jimmy Miller

答案 3 :(得分:1)

出于完整性考虑,也可以使用data.table非等额联接来解决:

library(data.table)
setDT(df1)[, c(.SD, .(ym1 = Year - 1, yp1 = Year + 1))][
  setDT(df2), on = .(Album, ym1 <= Year, yp1 >= Year), nomatch = 0L]
           Artist          Album Year  ym1  yp1      Producer
1:        Beatles  Sgt. Pepper's 1967 1966 1966 George Martin
2: Rolling Stones Sticky Fingers 1971 1971 1971  Jimmy Miller

setDT(df1)[, c("ym1", "yp1") := .(Year - 1, Year + 1)][setDT(df2), 
           on = .(Album, ym1 <= Year, yp1 >= Year), nomatch = 0L]
           Artist          Album Year  ym1  yp1      Producer
1:        Beatles  Sgt. Pepper's 1967 1966 1966 George Martin
2: Rolling Stones Sticky Fingers 1971 1971 1971  Jimmy Miller

修改df1


顺便说一句:有一个功能请求https://github.com/Rdatatable/data.table/issues/1639,允许on 中的动态列。如果实现了,上面的表达式将变成

setDT(df1)[setDT(df2), on = .(Album, Year - 1 <= Year, Year + 1 >= Year), nomatch = 0L]

答案 4 :(得分:0)

如果将来有人在阅读这个问题,那么以上答案是不错的。另一个答案是:

  1. 加入所有匹配的相册
  2. 仅过滤掉年份接近的记录:

https://stackoverflow.com/a/55863846/8742237

inner_join(df1, df2, by = c("Album")) %>% 
  filter(abs(Year.x - Year.y)<2)

>           Artist          Album Year.x Year.y      Producer
> 1        Beatles  Sgt. Pepper's   1967   1966 George Martin
> 2 Rolling Stones Sticky Fingers   1971   1971  Jimmy Miller