dplyr left_join por menor que, mayor que la condición de

Esta pregunta es algo relacionado con la Eficiente de la fusión de dos de los marcos de datos en un no-trivial criterios y La comprobación de si la fecha es entre dos fechas en r. Y el que he publicado aquí solicitando si la característica de existir:
GitHub problema

Estoy buscando unir dos dataframes utilizando dplyr::left_join(). La condición que yo uso para unirse a es menor que, mayor que yo.e, <= y >. ¿dplyr::left_join() Compatible con esta característica? o ¿las claves sólo tomar = operador entre ellos. Esto es sencillo de ejecución de SQL (suponiendo que tengo el dataframe en la base de datos)

Aquí es un MWE: tengo dos conjuntos de datos de una empresa-año (fdata), mientras que el segundo es un tipo de datos de la encuesta que sucede una vez cada cinco años. Así que para todos los años en el fdata que están entre los dos años de la encuesta, me uno a la correspondiente año de la encuesta de datos.

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"))

Puedo obtener

Error: cannot join on columns 'TRUE' x 'TRUE': index out of bounds 

Menos si left_join puede manejar la condición, pero mi sintaxis es falta algo?

Usted puede suscribirse a github.com/tidyverse/dplyr/issues/2240

OriginalEl autor rajvijay | 2016-05-18

4 respuestas

  1. 11

    Utilizar un filter. (Pero tenga en cuenta que esta respuesta no producir una correcta LEFT JOIN; pero el MWE da el resultado correcto con un INNER JOIN en su lugar.)

    La dplyr paquete no es feliz si se le pide combinar dos tablas sin algo de combinar, por lo que en el siguiente, yo hago una variable ficticia en ambas tablas para este propósito, a continuación, filtro, a continuación, suelte dummy:

    fdata %>% 
    mutate(dummy=TRUE) %>%
    left_join(sdata %>% mutate(dummy=TRUE)) %>%
    filter(fyear >= byear, fyear < eyear) %>%
    select(-dummy)

    Y tenga en cuenta que si se puede hacer esto en PostgreSQL (por ejemplo), el optimizador de consultas ve a través de la dummy variable, como se evidencia por las siguientes dos consultas explicaciones:

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

    y hacerlo de la manera más limpia con SQL da exactamente el mismo resultado:

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

    OriginalEl autor Ian Gow

  2. 14

    Esto parece que es el tipo de tareas que el paquete fuzzyjoin direcciones. Las diferentes funciones del paquete de apariencia y de trabajo similar a la dplyr unirse funciones.

    En este caso, uno de los fuzzy_*_join funciones de trabajo para usted. La principal diferencia entre dplyr::left_join y fuzzyjoin::fuzzy_left_join es que te dan una lista de funciones a utilizar en el proceso de coincidencia con la match.fun argumento. Nota el by argumento se escribe de la misma como lo haría en left_join.

    A continuación es un ejemplo. Las funciones que utilizan la coincidencia se >= y < para la fyear a byear y la fyear a eyear comparaciones, respectivamente. El

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

    OriginalEl autor aosmith

  3. 12

    data.table agrega que no equi une a partir de v 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

    Usted puede también conseguir que esto funcione con foverlaps en 1.9.6 con un poco más de esfuerzo.

    setDF puede ser utilizado después si alguien quiere devuelve sus conjuntos de datos para datos sin formato.marco

    OriginalEl autor eddi

  4. 2

    Una opción es unirse a modo de fila como una columna de la lista y, a continuación, unnest de la columna:

    # 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
    # ..   ...   ...   ...   ...   ...
    Como mi respuesta, esto no produce un válido LEFT JOIN. Aumentar la izquierda del marco de datos con observaciones fyear==2011 y, a continuación, filtrar el resultado de la consulta en fyear==2011 y no hay nada allí. Esto funciona en SQL: SELECT * FROM fdata LEFT JOIN sdata ON fyear >= year AND fyear < eyear.

    OriginalEl autor alistaire

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *