inner_join () с диапазоном значений для одного из ключей (год) - PullRequest
2 голосов
/ 15 июня 2019

У меня есть два набора данных, которые отформатированы так:

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 по альбомам и годам, но иногда поле 'Год' отключено на один год: например, сержант. Перец числится как 1967 в df1, в 1966 году в df2.

Так что, если я бегу:

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 ]

3 голосов
/ 15 июня 2019

Мы можем попытаться использовать пакет 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 * не одобряется, и желательно всегда перечислять столбцы для выбора.

2 голосов
/ 15 июня 2019

Возможно, подвижное соединение позаботится об этом. Это работает для вашей выборки данных, но, возможно, в ваших реальных данных могут быть сложные крайние случаи.

В приведенном ниже коде 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
1 голос
/ 18 июня 2019

Для полноты картины это также можно решить с помощью 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]
1 голос
/ 15 июня 2019

Добавить 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
0 голосов
/ 15 июня 2019

Если кто-то читает этот вопрос в будущем, приведенные выше ответы будут отличными.Другой ответ:

  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
...