Dplyr left_join меньше, чем условие
Этот вопрос несколько связан с проблемами Эффективное слияние двух фреймов данных по нетривиальным критериям и Проверка даты между двумя датами в г. И тот, который я разместил здесь, запрашивая, существует ли функция:
Проблема GitHub
Я хочу присоединиться к двум фреймам данных, используя dplyr::left_join()
. Условие, которое я использую для соединения, меньше, чем, больше, чем i.e, <=
и >
. Поддерживает ли dplyr::left_join()
эту функцию? или клавиши имеют только оператор =
между ними. Это просто для запуска из SQL (при условии, что у меня есть dataframe в базе данных)
Вот MWE: у меня есть два набора данных один год фирмы (fdata
), а второй - это данные опроса, которые происходят раз в пять лет. Так что за все годы в fdata
, которые находятся между двумя годами исследования, я присоединяюсь к данным соответствующего года исследования.
id <- c(1,1,1,1,
2,2,2,2,2,2,
3,3,3,3,3,3,
5,5,5,5,
8,8,8,8,
13,13,13)
fyear <- c(1998,1999,2000,2001,1998,1999,2000,2001,2002,2003,
1998,1999,2000,2001,2002,2003,1998,1999,2000,2001,
1998,1999,2000,2001,1998,1999,2000)
byear <- c(1990,1995,2000,2005)
eyear <- c(1995,2000,2005,2010)
val <- c(3,1,5,6)
sdata <- tbl_df(data.frame(byear, eyear, val))
fdata <- tbl_df(data.frame(id, fyear))
test1 <- left_join(fdata, sdata, by = c("fyear" >= "byear","fyear" < "eyear"))
Я получаю
Error: cannot join on columns 'TRUE' x 'TRUE': index out of bounds
Если if left_join
может обрабатывать условие, но мой синтаксис отсутствует что-то?
Ответы
Ответ 1
Используйте filter
. (Но учтите, что этот ответ не дает правильного LEFT JOIN
, но MWE дает правильный результат с помощью INNER JOIN
.)
Пакет dplyr
недоволен, если его попросят объединить две таблицы без слияния, поэтому в следующем случае я создаю для этих целей фиктивную переменную в обеих таблицах, затем фильтрую, а затем отбрасываю dummy
:
fdata %>%
mutate(dummy=TRUE) %>%
left_join(sdata %>% mutate(dummy=TRUE)) %>%
filter(fyear >= byear, fyear < eyear) %>%
select(-dummy)
И обратите внимание, что если вы это сделаете в PostgreSQL (например), оптимизатор запросов видит через переменную dummy
, о чем свидетельствуют следующие два объяснения запроса:
> fdata %>%
+ mutate(dummy=TRUE) %>%
+ left_join(sdata %>% mutate(dummy=TRUE)) %>%
+ filter(fyear >= byear, fyear < eyear) %>%
+ select(-dummy) %>%
+ explain()
Joining by: "dummy"
<SQL>
SELECT "id" AS "id", "fyear" AS "fyear", "byear" AS "byear", "eyear" AS "eyear", "val" AS "val"
FROM (SELECT * FROM (SELECT "id", "fyear", TRUE AS "dummy"
FROM "fdata") AS "zzz136"
LEFT JOIN
(SELECT "byear", "eyear", "val", TRUE AS "dummy"
FROM "sdata") AS "zzz137"
USING ("dummy")) AS "zzz138"
WHERE "fyear" >= "byear" AND "fyear" < "eyear"
<PLAN>
Nested Loop (cost=0.00..50886.88 rows=322722 width=40)
Join Filter: ((fdata.fyear >= sdata.byear) AND (fdata.fyear < sdata.eyear))
-> Seq Scan on fdata (cost=0.00..28.50 rows=1850 width=16)
-> Materialize (cost=0.00..33.55 rows=1570 width=24)
-> Seq Scan on sdata (cost=0.00..25.70 rows=1570 width=24)
и сделать это более чисто с SQL дает точно такой же результат:
> tbl(pg, sql("
+ SELECT *
+ FROM fdata
+ LEFT JOIN sdata
+ ON fyear >= byear AND fyear < eyear")) %>%
+ explain()
<SQL>
SELECT "id", "fyear", "byear", "eyear", "val"
FROM (
SELECT *
FROM fdata
LEFT JOIN sdata
ON fyear >= byear AND fyear < eyear) AS "zzz140"
<PLAN>
Nested Loop Left Join (cost=0.00..50886.88 rows=322722 width=40)
Join Filter: ((fdata.fyear >= sdata.byear) AND (fdata.fyear < sdata.eyear))
-> Seq Scan on fdata (cost=0.00..28.50 rows=1850 width=16)
-> Materialize (cost=0.00..33.55 rows=1570 width=24)
-> Seq Scan on sdata (cost=0.00..25.70 rows=1570 width=24)
Ответ 2
Похоже, что это та задача, которая содержит пакеты fuzzyjoin. Различные функции пакета выглядят и работают аналогично функциям объединения dplyr.
В этом случае одна из функций fuzzy_*_join
будет работать для вас. Основное различие между dplyr::left_join
и fuzzyjoin::fuzzy_left_join
заключается в том, что вы предоставляете список функций, которые следует использовать в процессе сопоставления с аргументом match.fun
. Обратите внимание, что аргумент by
все еще написан так же, как в left_join
.
Ниже приведен пример. Функции, которые я использовал для сравнения, - это >=
и <
для сравнений fyear
to byear
и fyear
to eyear
.
library(fuzzyjoin)
fuzzy_left_join(fdata, sdata,
by = c("fyear" = "byear", "fyear" = "eyear"),
match_fun = list(`>=`, `<`))
Source: local data frame [27 x 5]
id fyear byear eyear val
(dbl) (dbl) (dbl) (dbl) (dbl)
1 1 1998 1995 2000 1
2 1 1999 1995 2000 1
3 1 2000 2000 2005 5
4 1 2001 2000 2005 5
5 2 1998 1995 2000 1
6 2 1999 1995 2000 1
7 2 2000 2000 2005 5
8 2 2001 2000 2005 5
9 2 2002 2000 2005 5
10 2 2003 2000 2005 5
.. ... ... ... ... ...
Ответ 3
data.table
добавляет не equic-соединения, начиная с версии 1.9.8
library(data.table) #v>=1.9.8
setDT(sdata); setDT(fdata) # converting to data.table in place
fdata[sdata, on = .(fyear >= byear, fyear < eyear), nomatch = 0,
.(id, x.fyear, byear, eyear, val)]
# id x.fyear byear eyear val
# 1: 1 1998 1995 2000 1
# 2: 2 1998 1995 2000 1
# 3: 3 1998 1995 2000 1
# 4: 5 1998 1995 2000 1
# 5: 8 1998 1995 2000 1
# 6: 13 1998 1995 2000 1
# 7: 1 1999 1995 2000 1
# 8: 2 1999 1995 2000 1
# 9: 3 1999 1995 2000 1
#10: 5 1999 1995 2000 1
#11: 8 1999 1995 2000 1
#12: 13 1999 1995 2000 1
#13: 1 2000 2000 2005 5
#14: 2 2000 2000 2005 5
#15: 3 2000 2000 2005 5
#16: 5 2000 2000 2005 5
#17: 8 2000 2000 2005 5
#18: 13 2000 2000 2005 5
#19: 1 2001 2000 2005 5
#20: 2 2001 2000 2005 5
#21: 3 2001 2000 2005 5
#22: 5 2001 2000 2005 5
#23: 8 2001 2000 2005 5
#24: 2 2002 2000 2005 5
#25: 3 2002 2000 2005 5
#26: 2 2003 2000 2005 5
#27: 3 2003 2000 2005 5
# id x.fyear byear eyear val
Вы также можете заставить это работать с foverlaps
в 1.9.6 с меньшими усилиями.
Ответ 4
Один из вариантов заключается в объединении строки в виде столбца списка, а затем в столбец:
# evaluate each row individually
fdata %>% rowwise() %>%
# insert list column of single row of sdata based on conditions
mutate(s = list(sdata %>% filter(fyear >= byear, fyear < eyear))) %>%
# unnest list column
tidyr::unnest()
# Source: local data frame [27 x 5]
#
# id fyear byear eyear val
# (dbl) (dbl) (dbl) (dbl) (dbl)
# 1 1 1998 1995 2000 1
# 2 1 1999 1995 2000 1
# 3 1 2000 2000 2005 5
# 4 1 2001 2000 2005 5
# 5 2 1998 1995 2000 1
# 6 2 1999 1995 2000 1
# 7 2 2000 2000 2005 5
# 8 2 2001 2000 2005 5
# 9 2 2002 2000 2005 5
# 10 2 2003 2000 2005 5
# .. ... ... ... ... ...