2016-07-23 14 views
1

Ich habe zwei Datenrahmen x und y, die Spalten für IDs und für Daten enthalten.Join Dataframes nach ID und überlappenden Datumsbereich

id.x <- c(1, 2, 4, 5, 7, 8, 10) 
date.x <- as.Date(c("2015-01-01", "2015-01-02", "2015-01-21", "2015-01-13", "2015-01-29", "2015-01-01", "2015-01-03"),format = "%Y-%m-%d") 
x <- data.frame(id.x, date.x) 
id.y <- c(1, 2, 3, 6, 7, 8, 9) 
date.y <- as.Date(c("2015-01-03", "2015-01-29", "2015-01-22", "2015-01-13", "2015-01-29", "2014-12-31", "2015-01-03"), format = "%Y-%m-%d") 
y <- data.frame(id.y, date.y) 

Ich möchte sie in einen neuen Datenrahmen verbinden, indem z-ID und ob date.y passend tritt innerhalb date.x + 3 Tage, z.B. einzelne "1" hatte Ereignis "y" am Datum auftreten. y = "2015-01-03", die innerhalb von 3 Tagen des Ereignisses x auf date.x = "2015-01-01" ist.

+0

Bitte beenden Sie die Verwendung von 'cbind', um data.frames zu erstellen. Dafür steht eine data.frame-Funktion zur Verfügung. – Arun

+0

@Arun bekannt. Wenn jemand anders neugierig auf den Unterschied zwischen 'cbind.data.frame()' und 'data.frame()' war, ist er hier [https://docs.tibco.com/pub/enterprise-runtime- for-R/1.5.0_may_2013/TERR_1.5.0_LanguageRef/base/cbind.data.frame.html) – user6571411

+0

Verwenden Sie 'check.names = FALSE' im Aufruf von' data.frame() '(um das Verhalten von' cbind.data.frame'), was der einzige Standardunterschied zu sein scheint. – Arun

Antwort

1

Sie können eine ifselse-Anweisung erstellen, die einen Vektor erstellt, der gleich date.x ist, falls date.y < = date.x + 3 und date.y> = date.x und date.y ansonsten identisch sind. Dann verschmelzen die beiden auf dieser Vektor-basierte:

id.x <- c(1, 2, 4, 5, 7, 8, 10) 
date.x <- as.Date(c("2015-01-01", "2015-01-02", "2015-01-21", "2015-01-13", "2015-01-29", "2015-01-01", "2015-01-03"),format = "%Y-%m-%d") 
x <- cbind.data.frame(id.x, date.x) 
id.y <- c(1, 2, 3, 6, 7, 8, 9) 
date.y <- as.Date(c("2015-01-03", "2015-01-29", "2015-01-22", "2015-01-13", "2015-01-29", "2014-12-31", "2015-01-03"), format = "%Y-%m-%d") 
y <- cbind.data.frame(id.y, date.y) 

safe.ifelse <- function(cond, yes, no) structure(ifelse(cond, yes, no), class = class(yes)) 

match <- safe.ifelse(date.y <= date.x+3 & date.y >= date.x, 
      match <- date.x, 
      match <- date.y) 

y$date.x <- match 
names(y)[1] <- "id.x" 

dplyr::left_join(x, y, by=c("id.x","date.x")) 

    id.x  date.x  date.y 
1 1 2015-01-01 2015-01-03 
2 2 2015-01-02  <NA> 
3 4 2015-01-21  <NA> 
4 5 2015-01-13  <NA> 
5 7 2015-01-29 2015-01-29 
6 8 2015-01-01  <NA> 
7 10 2015-01-03  <NA> 

Ich lieh die safe.ifelse Funktion von diesem post, weil die Basis ifelse Anweisung führt zu einem numerischen Vektor eher als ein Datum Vektor.

+0

Dies ist unsicher und würde im Allgemeinen zu falschen Lösungen führen. Ihre Generation von 'match'-Spalten berücksichtigt die 'id'-Spalte überhaupt nicht. – Arun

+0

Ich stieß auf das Problem, dass Arun erwähnt. Meine Arbeit war die folgende 'temp <- fusionieren (x, y, by.x =" id.x ", by.y =" id.y ", alle = TRUE)' 'tempset <- safe.ifelse (date.y <= Datum.x + 3 & Datum.y> = Datum.x, TRUE, FALSE) ' ' joined.df <- temp [was (tempset == TRUE),] ' – user6571411

1

Verwendung der inneren Verknüpfung von y- und x-Datentabellen, indem die Schlüssel für die ID beider Databases festgelegt werden und dann nach Datumsbedingungen gesucht wird und schließlich die wahren Daten extrahiert werden.

library("data.table") 

x <- as.data.table(x) 

y <- as.data.table(y) 

setkey(x, id.x) 

setkey(y, id.y) 

z <- y[x, nomatch = 0][, j = .(is_true = ((date.y <= date.x + 3) & (date.y > date.x)), id.y, date.x, date.y)][i = is_true == TRUE] 

> z 
    is_true id.y  date.x  date.y 
1: TRUE 1 2015-01-01 2015-01-03 
1

die Entwicklungsversion von data.table verwenden, v1.9.7, wo nicht-equi (oder bedingte) wurde Joins vor kurzem implementiert, können wir dies tun in einer einfachen (und effizient) Art und Weise .. Siehe Montageanleitung here.

require(data.table) # v1.9.7+ 
setDT(x) 
setDT(y) ## convert both data.frames to data.tables by reference 

x[, date.x.plus3 := date.x + 3L] 
y[x, .(id.x, date.x, date.y=x.date.y), 
    on=.(id.y == id.x, date.y >= date.x, date.y <= date.x.plus3)] 
# id.x  date.x  date.y 
# 1: 1 2015-01-01 2015-01-03 
# 2: 2 2015-01-02  <NA> 
# 3: 4 2015-01-21  <NA> 
# 4: 5 2015-01-13  <NA> 
# 5: 7 2015-01-29 2015-01-29 
# 6: 8 2015-01-01  <NA> 
# 7: 10 2015-01-03  <NA> 

Lösungen, die auf einer Dummy-Spalte verbinden und dann auf den Bedingungen basierte Filter sind in der Regel nicht skalierbar (wie die Anzahl der Zeilen schnell explodiert) und Lösungen, die Schleife durch Zeilen und die Filterbedingung für jede Zeile ausgeführt werden, langsam, gut, weil sie die Operation zeilenweise ausführen.

Diese Lösung tut dies auch nicht, d. H. Führt den bedingten Join direkt aus und sollte daher sowohl hinsichtlich der Laufzeit als auch des Arbeitsspeichers performant sein.

Verwandte Themen