2016-02-01 7 views
5

Ich habe einen Datenrahmen von Zeitreihendaten mit täglichen Beobachtungen von Temperaturen. Ich muss eine Dummy-Variable erstellen, die jeden Tag zählt, dessen Temperatur über einer Schwelle von 5C liegt. Dies wäre an sich einfach, aber es gibt eine zusätzliche Bedingung: Die Zählung beginnt erst, wenn zehn aufeinanderfolgende Tage über dem Schwellenwert liegen. Hier ist ein Beispiel Datenrahmen:Wie erstelle ich einen bedingten Dummy in R?

df <- data.frame(date = seq(365), 
     temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365)) 

Ich denke, ich habe es geschafft, aber mit zu vielen Schleifen für meinen Geschmack. Dies ist, was ich getan habe:

df$dummyUnconditional <- 0 
df$dummyHead <- 0 
df$dummyTail <- 0 

for(i in 1:nrow(df)){ 
    if(df$temp[i] > 5){ 
     df$dummyUnconditional[i] <- 1 
    } 
} 

for(i in 1:(nrow(df)-9)){ 
    if(sum(df$dummyUnconditional[i:(i+9)]) == 10){ 
     df$dummyHead[i] <- 1 
    } 
} 

for(i in 9:nrow(df)){ 
    if(sum(df$dummyUnconditional[(i-9):i]) == 10){ 
     df$dummyTail[i] <- 1 
    } 
} 

df$dummyConditional <- ifelse(df$dummyHead == 1 | df$dummyTail == 1, 1, 0) 

Könnte jemand einfachere Möglichkeiten vorschlagen, dies zu tun?

Antwort

5

Hier ist eine Basis R-Option rle:

df$dummy <- with(rle(df$temp > 5), rep(as.integer(values & lengths >= 10), lengths)) 

Eine Erklärung: Die Aufgabe ist ein klassischer Anwendungsfall für die Lauflängencodierung (rle) Funktion, imo. Wir prüfen zunächst, ob der Wert von temp größer als 5 (eine logische Vektor erstellen) und anwenden rle auf diesem Vektor führt:

> rle(df$temp > 5) 
#Run Length Encoding 
# lengths: int [1:7] 66 1 1 225 2 1 69 
# values : logi [1:7] FALSE TRUE FALSE TRUE FALSE TRUE ... 

Jetzt wollen wir die Fälle finden, wo die values ist TRUE (dh temp größer als 5) und wobei gleichzeitig lengths größer als 10 ist (dh mindestens zehn aufeinanderfolgende temp Werte sind größer als 5). Wir tun dies, indem Laufen:

values & lengths >= 10 

Und schließlich, da wir einen Vektor der gleichen Länge wie nrow(df) zurückkehren möchten, verwenden wir rep(..., lengths) und as.integer um 1/0 statt TRUE/FALSE zurückzukehren.

+1

Die beiden anderen Vorschläge zu arbeiten, aber das war die einfachste und ich bin irgendwie geneigt R-Basislösung zu verwenden, wenn eine solche vorhanden ist. Vielen Dank! – Antti

+0

Sie können diese Lösung näher untersuchen. Ich habe Dummy-Werte = 1 an den Tagen 67-75, trotz der Tatsache, dass diese Tage nicht Teil eines konsekutiven 10-Tage-Laufs von> 5 Grad Temps sind. – JHowIX

+0

@JHowIX, können Sie ein Beispiel dafür geben? Beachten Sie, dass die Beispieldaten 'rnorm' verwenden, ohne einen Seed zu setzen, so dass die Lösungen nicht gleich sind (da die Beispieldaten abweichen können) –

5

Ich denke, Sie könnten eine Kombination aus einer einfachen ifelse und der Rolle anwenden Funktion in der zoo package verwenden, um zu erreichen, was Sie suchen. Der letzte Schritt beinhaltet nur das Auffüllen des Ergebnisses für die ersten N-1 Tage, wo es nicht genug Informationen gibt, um das Fenster zu füllen.

library(zoo) 

df <- data.frame(date = seq(365), 
       temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365)) 

df$above5 <- ifelse(df$temp > 5, 1, 0) 
temp <- rollapply(df$above5, 10, sum) 
df$conseq <- c(rep(0, 9),temp) 
+1

ersetzen Sie 'function (x) {sum (x)}' mit einer einfachen 'sum'? –

+1

Schlagen Sie es wie folgt vor: 'df2 <- transform (transformieren (df, uncond = temp> 5), kopf = rollsum (uncond, 10, align =" links ", fill = 0) == 10, tail = Rollsum (uncond, 10, align = "rechts", fill = 0) == 10) + 0' –

2

Ich würde dies tun:

set.seed(42) 
df <- data.frame(date = seq(365), 
       temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365)) 
thr <- 5 
df$dum <- 0 

#find first 10 consecutive values above threshold 
test1 <- filter(df$temp > thr, rep(1,10), sides = 1) == 10L 
test1[1:9] <- FALSE 
n <- which(cumsum(test1) == 1L) 

#count days above threshold after that 
df$dum[(n+1):nrow(df)] <- cumsum(df$temp[(n+1):nrow(df)] > thr) 
Verwandte Themen