dplyr left_join より小さい、より大きい条件 質問する

dplyr left_join より小さい、より大きい条件 質問する

この質問は、問題に多少関連しています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)

おすすめ記事