2015-01-03 16 views
6

Ich suche Hilfe, nachdem ich fast einen Tag verschwendet habe. Ich habe einen großen Datenrahmen (bdf) und einen kleinen Datenrahmen (sdf). Ich möchte die Variable z zu bdf hinzufügen, abhängig vom Wert von sdf $ y (der sich als Funktion einer Zeitvariablen ändert).Variable im R-Datenrahmen abhängig von einem anderen Datenrahmen erstellen

Hier ist ein reproduzierbares Beispiel:

bdf <- data.frame(tb = seq(as.POSIXct("2013-05-19 17:11:22 GMT", tz="GMT"), by=5624*24, length.out=10)) 

bdf 
       tb 
1 2013-05-19 17:11:22 
2 2013-05-21 06:40:58 
3 2013-05-22 20:10:34 
4 2013-05-24 09:40:10 
5 2013-05-25 23:09:46 
6 2013-05-27 12:39:22 
7 2013-05-29 02:08:58 
8 2013-05-30 15:38:34 
9 2013-06-01 05:08:10 
10 2013-06-02 18:37:46 


sdf <- data.frame(ts = as.POSIXct(c("2013-05-22", "2013-05-25", "2013-05-30"), tz="GMT"), y = c(0.2, -0.1, 0.3)) 

> sdf 
     ts y 
1 2013-05-22 0.2 
2 2013-05-25 -0.1 
3 2013-05-30 0.3 

I Variable z in BDF mit den folgenden Werten von sdf $ y erstellen möchten: von

  • 0,2 für Zeilen, in denen BDF $ tb reicht der erste bdf $ tb-Wert in der Mitte zwischen dem 1. und 2. Wert von sdf $ ts. In diesem einfachen Beispiel ist dies der Fall bei den Zeilen 1 bis 3 von dbf, die Zeiten bdf $ tb unter "2013-05-23 12:00:00 GMT" haben.

  • -0,1 für Zeilen, in denen bdf $ tb von der Mitte zwischen dem 1. und 2. Wert von sdf $ ts bis zur Mitte zwischen dem 2. und 3. Wert von sdf $ ts reicht. In diesem einfachen Beispiel ist das der Fall in den Zeilen 4 und 5 von dbf, die Zeiten bdf $ tb zwischen "2013-05-23 12:00:00 GMT" und "2013-05-27 12:00:00 GMT" haben. .

  • 0,3 für alle Zeilen, in denen bdf $ tb von der Mitte zwischen dem 2. und 3. Wert von sdf $ ts bis zum letzten Wert von bdf $ tb reicht. In diesem einfachen Beispiel ist dies der Fall bei den Zeilen 1 bis 6 bis 10 von dbf, die Zeiten haben, die größer sind als "2013-05-23 12:00:00 GMT".

daher am Ende sollte die große Datenrahmen BDF wie folgt aussehen:

    tb z 
1 2013-05-19 17:11:22 0.2 
2 2013-05-21 06:40:58 0.2 
3 2013-05-22 20:10:34 0.2 
4 2013-05-24 09:40:10 -0.1 
5 2013-05-25 23:09:46 -0.1 
6 2013-05-27 12:39:22 0.3 
7 2013-05-29 02:08:58 0.3 
8 2013-05-30 15:38:34 0.3 
9 2013-06-01 05:08:10 0.3 
10 2013-06-02 18:37:46 0.3 

ich nicht gelingen konnte mit dplyr :: mutieren und bekam nirgendwo Loops ... Jede Hilfe wäre sehr geschätzt. Ich hoffe, dass ich das Thema klar als an die Etikette gebunden beschrieben habe (es ist meine erste Frage).

+1

klingt wie _join zum nächsten Wert_ Im Paket 'data.table' vielleicht' roll = "nearest", aber ich habe keine Erfahrung darin und ich bin gespannt, ob es in 'dplyr' möglich ist. – ckluss

Antwort

3

Dies scheint jetzt absolut unnötig, aber in der Basis R

bdf$z <- numeric(nrow(bdf)) 
for(i in seq_along(bdf$z)){ 
    ind <- which.min(abs(bdf$tb[i] - sdf$ts)) 
    bdf$z[i] <- sdf$y[ind] 
} 

Während wenig ungeschickt zu sein, hat es einen Vorteil in der Klarheit, die dplyr

einfache Anpassung empfängt
library(dplyr) 
bdf %>% rowwise() %>% 
    mutate(z= sdf$y[which.min(abs(as.numeric(tb)-as.numeric(sdf$ts)))]) 

#Source: local data frame [10 x 2] 
#Groups: <by row> 

#     tb z 
#1 2013-05-19 17:11:22 0.2 
#2 2013-05-21 06:40:58 0.2 
#3 2013-05-22 20:10:34 0.2 
#4 2013-05-24 09:40:10 -0.1 
#5 2013-05-25 23:09:46 -0.1 
#6 2013-05-27 12:39:22 0.3 
#7 2013-05-29 02:08:58 0.3 
#8 2013-05-30 15:38:34 0.3 
#9 2013-06-01 05:08:10 0.3 
#10 2013-06-02 18:37:46 0.3 
+0

Die zweite Option ist mein Favorit. Es ist die einfachste Lösung, da es kein zusätzliches Paket benötigt und sehr kurz ist. – gattuso

+0

missverstanden das Abstimmungssystem und möchte diese Antwort abstimmen – gattuso

3

Hier ist mein Ansatz:

library(zoo) 
m <- c(rollmean(as.POSIXct(sdf$ts), 2), Inf) 
transform(bdf, z = sdf$y[sapply(tb, function(x) which.max(x < m))]) 
#     tb z 
#1 2013-05-19 17:11:22 0.2 
#2 2013-05-21 06:40:58 0.2 
#3 2013-05-22 20:10:34 0.2 
#4 2013-05-24 09:40:10 -0.1 
#5 2013-05-25 23:09:46 -0.1 
#6 2013-05-27 12:39:22 0.3 
#7 2013-05-29 02:08:58 0.3 
#8 2013-05-30 15:38:34 0.3 
#9 2013-06-01 05:08:10 0.3 
#10 2013-06-02 18:37:46 0.3 

Update: entfernt Umwandlung in numerische (nicht erforderlich)

Kurzerklärung:

  • as.POSIXct(sdf$ts) wandelt die Daten zu POSIXct Stil Datum-mal
  • rollmean(as.POSIXct(sdf$ts), 2) berechnet das rollende Mittel von zwei aufeinanderfolgenden Reihen. Dies ist genau die Zeit, die Sie zum Trennen der Beobachtungen verwenden möchten. rollmean stammt aus Paket zoo. Die Berechnung eines rollmean(..,2) bedeutet, dass der Ausgangsvektor im Vergleich zum Eingangsvektor um 1 verkürzt ist.
  • Deshalb wickle ich das Ergebnis von rollmean in c(.., Inf), was bedeutet, dass der Unendlichkeitswert als letzten Wert zum Rollmean-Vektor hinzugefügt wird. Dies stellt sicher, dass die letzten Einträge von z in sdf ebenfalls zurückgegeben werden (0,3 in dem spezifischen Beispiel).
  • I transform verwenden, um die z Spalte bdf
  • sapply(tb, function(x) which.max(x < m)) Schleifen durch die Einträge in bdf$tb und für jeden Eintrag hinzuzufügen, berechnet den maximalen Index, für die bdf$tb weniger ist (früher) als m (die den Vektor von rollmean Einträge hält).Für jeden bdf$tb Eintrag wird nur der maximale (neueste) Index zurückgegeben.
  • Das Vektor des Indizes in sdf$y[sapply(tb, function(x) which.max(x < m))] verwendet wird, um die entsprechenden Elemente von sdf$y, die dann gespeichert/auf die neuen z Spalt in bdf

Hoffnung kopiert werden zu extrahieren, die

+0

Die Verwendung von' rollmean' ist ziemlich glatt. Kann verwendet werden, um den 'findInterval'-Vektor in meiner Methode zu füllen und die Probleme, die ich mit 'difftime' hatte, zu umgehen. –

3

Notiz bearbeiten hilft: ich zunächst bekomme ein etwas anderes Ergebnis als du, von dem ich denke, dass es mit meinem mangelnden Verständnis von R difftime-Objekten zusammenhängt. Zeitzonen in POSIXt Objekte bleiben mir auch ein Geheimnis, aber ich sehe jetzt, dass, wenn ich ein 'difftime' Objekt zu 'numerisch' zwang, dass ich den Wert in "Tage" bekam.

Die Funktion findInterval ist sehr nützlich als Indexerstellungsfunktion, die einen Wertevektor abbildet, bei dem mehrere zusammenhängende nicht überlappende Intervalle vorhanden sind. Sie haben wirklich nur zwei Zeitpunkte, die sich in drei Intervalle aufteilen.

bdf$z <- c(0.2,-0.1,0.3)[findInterval(bdf$tb, 
       c(-Inf, 
    sdf$ts[2] - 0.5*as.numeric(difftime(sdf$ts[2], sdf$ts[1], units="secs")), 
    sdf$ts[3] - 0.5*as.numeric(difftime(sdf$ts[3], sdf$ts[2],units="sec")), 
       Inf))] 

> bdf 
        tb z 
1 2013-05-19 17:11:22 0.2 
2 2013-05-21 06:40:58 0.2 
3 2013-05-22 20:10:34 0.2 
4 2013-05-24 09:40:10 -0.1 
5 2013-05-25 23:09:46 -0.1 
6 2013-05-27 12:39:22 0.3 
7 2013-05-29 02:08:58 0.3 
8 2013-05-30 15:38:34 0.3 
9 2013-06-01 05:08:10 0.3 
10 2013-06-02 18:37:46 0.3 

Ich habe auch davon ab, ob die Intervalle in findIntervals betroffen sein, wenn mein Ergebnis sehen würde eher auf ihrem Recht geschlossen wurden, als der linken (Standard) und sah keinen Unterschied.

6

Hier ist eine Lösung unter Verwendung von data.table ‚s Walz verbindet:

require(data.table) 
setkey(setDT(sdf), ts) 
sdf[bdf, roll = "nearest"] 
#      ts y 
# 1: 2013-05-19 17:11:22 0.2 
# 2: 2013-05-21 06:40:58 0.2 
# 3: 2013-05-22 20:10:34 0.2 
# 4: 2013-05-24 09:40:10 -0.1 
# 5: 2013-05-25 23:09:46 -0.1 
# 6: 2013-05-27 12:39:22 0.3 
# 7: 2013-05-29 02:08:58 0.3 
# 8: 2013-05-30 15:38:34 0.3 
# 9: 2013-06-01 05:08:10 0.3 
# 10: 2013-06-02 18:37:46 0.3 
  • setDT wandelt data.frame zu data.table durch Bezugnahme.

  • setkey sortiert die data.table durch Bezugnahme erhöht wird, um durch die Spalten vorgesehen sind, und markiert diese Spalten als Schlüsselspalten (so dass wir später auf diesen Schlüsselspalten beitreten können.

  • In data.table führt x[i] eine Verknüpfung, wenn i ein data.table ist. ich Ihnen this answer beziehen werde, um sich über data.table schließt sich, wenn Sie mit nicht vertraut sind.

  • x[i] perfo RMS ein Equi-Join. Das heißt, es findet übereinstimmende Zeilenindizes in x für jede Zeile in i und extrahiert dann diese Zeilen aus x, um das Verknüpfungsergebnis zusammen mit der entsprechenden Zeile aus i zurückzugeben. Wenn eine Zeile in i keine übereinstimmenden Zeilenindizes in x findet, hätte diese Zeile standardmäßig NA für x.

    Allerdings führt x[i, roll = .] eine Rollverbindung. Wenn keine Übereinstimmung vorliegt, wird entweder die letzte Beobachtung weitergeführt (roll = TRUE oder -Inf) oder die nächste Beobachtung kann rückwärts durchgeführt werden (roll = Inf) oder auf den nächsten Wert gerollt werden (roll = "nearest"). Und in diesem Fall benötigen Sie roll = "nearest" IIUC.

HTH

Verwandte Themen