2016-02-01 20 views
5

Ho un dataframe dei dati delle serie temporali con le osservazioni giornaliere delle temperature. Devo creare una variabile fittizia che conta ogni giorno che ha una temperatura superiore alla soglia di 5C. Ciò sarebbe di per sé semplice, ma esiste una condizione aggiuntiva: il conteggio inizia solo dopo dieci giorni consecutivi sopra la soglia. Ecco un esempio di dataframe:Come creare un manichino condizionale in R?

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

Penso di averlo fatto, ma con troppi cicli per i miei gusti. Questo è quello che ho fatto:

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) 

Qualcuno potrebbe suggerire modi più semplici per farlo?

risposta

5

Ecco un'opzione di base R utilizzando rle:

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

Qualche spiegazione: Il compito è un caso d'uso classico per la run-length encoding (rle) funzione, imo. Per prima cosa controllare se il valore di temp è maggiore di 5 (creazione di un vettore logico) e applichiamo rle su quel vettore risultante in:

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

Ora vogliamo trovare quei casi in cui il values è TRUE (cioè temperatura è superiore a 5) e dove allo stesso tempo lo lengths è maggiore di 10 (ovvero almeno dieci valori di temp consecutivi sono maggiori di 5). Facciamo questo in esecuzione:

values & lengths >= 10 

E infine, poiché vogliamo tornare un vettore di le stesse lunghezze come nrow(df), usiamo rep(..., lengths) e as.integer al fine di restituire 1/0 invece di TRUE/FALSE.

+1

Gli altri due suggerimenti lavoro troppo, ma questo era il più semplice e sono in qualche modo incline a usare la soluzione R-base se esiste. Grazie! – Antti

+0

Si consiglia di esaminare attentamente questa soluzione. Ho ottenuto valori fittizi = 1 nei giorni 67-75, nonostante il fatto che questi giorni non facciano parte di una corsa consecutiva di 10 giorni di> 5 gradi temp. – JHowIX

+0

@ JHowIX, puoi fornire un esempio di questo? Si noti che i dati di esempio utilizzano 'rnorm' senza impostare un seed quindi le soluzioni non sono necessarie allo stesso modo (poiché i dati di esempio potrebbero differire) –

5

Penso che sia possibile utilizzare una combinazione di un ifelse semplice e la funzione di applicazione del rotolo nello zoo package per ottenere ciò che si sta cercando. Il passaggio finale riguarda solo il riempimento del risultato per tenere conto dei primi N-1 giorni in cui non ci sono informazioni sufficienti per riempire la finestra.

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

sostituire 'function (x) {sum (x)}' con un semplice 'sum'? –

+1

Suggerisci di scriverlo in questo modo: 'df2 <- transform (transform (df, uncond = temp> 5), head = rollsum (uncond, 10, align =" left ", fill = 0) == 10, tail = rollsum (uncond, 10, align = "right", fill = 0) == 10) + 0' –

2

farei questo:

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