この質問は、問題に多少関連しています2つのデータフレームを非自明な基準で効率的にマージするそしてR で日付が 2 つの日付の間にあるかどうかを確認するそして、私がここに投稿して、その機能が存在するかどうかを尋ねたものがあります:GitHub の問題
2 つのデータフレームを を使用して結合したいと考えていますdplyr::left_join()
。結合に使用する条件は、より小さい、より大きい、つまり、<=
および です>
。 はdplyr::left_join()
この機能をサポートしていますか? または、キーは=
それらの間で演算子のみを使用しますか。 これは、SQL から簡単に実行できます (データベースにデータフレームがあると仮定)
MWE は次のとおりです。データセットが 2 つあります。1 つは企業年度 ( fdata
)、もう 1 つは 5 年に 1 回行われる調査データです。したがって、2 つの調査年度の間にある のすべての年度について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
left_join
もし条件が処理できないなら、私の構文に何かが欠けているのでしょうか?
ベストアンサー1
以下の元の回答は、別の回答で指摘されているように、古くなっています。 の新しいバージョンではdplyr
、次のようにするだけです。(この構文は、 を使用するデータベース バックエンドでも機能することに注意してくださいdbplyr
。)
fdata %>%
left_join(sdata,
join_by(fyear >= byear, fyear < eyear))
元の回答が作成されたとき、 を使用して不等結合を実行する簡単な方法はありませんでしたdplyr
。
元の回答
を使用してくださいfilter
。(ただし、この回答ではない正しい を生成しますLEFT JOIN
が、MWE は代わりに で正しい結果を返しますINNER JOIN
。
マージする対象がない状態で 2 つのテーブルをマージするように要求された場合、パッケージdplyr
は満足しません。そのため、以下では、この目的のために両方のテーブルにダミー変数を作成し、フィルタリングしてからドロップしますdummy
。
fdata %>%
mutate(dummy=TRUE) %>%
left_join(sdata %>% mutate(dummy=TRUE)) %>%
filter(fyear >= byear, fyear < eyear) %>%
select(-dummy)
また、これを PostgreSQL などで実行すると、dummy
次の 2 つのクエリの説明で示されるように、クエリ オプティマイザーが変数を認識することに注意してください。
> 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)