2015-01-03 20 views
6

Sto cercando aiuto dopo aver sprecato quasi un giorno. Ho un grande data frame (bdf) e un piccolo data frame (sdf). Voglio aggiungere la variabile z a bdf in base al valore di sdf $ y (che cambia in funzione di una variabile temporale).Creazione di variabili nel frame di dati R in base a un altro frame di dati

Ecco un esempio riproducibile:

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 

voglio creare variabile z in BDF con i seguenti valori di SDF $ y:

  • 0,2 per le righe dove BDF $ tb varia da il primo valore in bdf $ tb a metà strada tra il 1 ° e il 2 ° valore di sdf $ ts. In questo semplice esempio, è il caso delle righe da 1 a 3 di dbf che hanno volte bdf $ tb sotto "2013-05-23 12:00:00 GMT".

  • -0.1 per le righe in cui BDF $ tb va da metà strada tra il 1 ° e 2 ° valore della SDF $ ts a metà strada tra il 2 ° e 3 ° valore di $ SDF ts. In questo semplice esempio, è il caso delle righe 4 e 5 di dbf che hanno volte bdf $ tb tra "2013-05-23 12:00:00 GMT" e "2013-05-27 12:00:00 GMT" .

  • 0.3 per tutte le righe in cui bdf $ tb va da metà strada tra il 2 ° e il 3 ° valore di sdf $ ts fino all'ultimo valore di bdf $ tb. In questo semplice esempio, è il caso delle righe da 1 a 6 a 10 di dbf che hanno tempi maggiori di "2013-05-23 12:00:00 GMT".

Quindi, alla fine, il grande dataframe BDF dovrebbe assomigliare a questa:

    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 

non ho potuto avere successo utilizzando dplyr :: mutare, ma ho nessun posto usando loops ... Qualsiasi aiuto sarebbe molto apprezzato. Spero di aver descritto chiaramente il problema come aderito all'etichetta (è la mia prima domanda).

+1

suona come _join al valore più vicino_ Nel pacchetto 'data.table' forse' roll = "Più vicino", ma non ne ho esperienza e sono curioso di sapere se è possibile in 'dplyr' – ckluss

risposta

3

Questo sembra ora assolutamente necessario, ma nella base di 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] 
} 

Pur essendo poco goffa, ha un vantaggio in chiarezza, che accoglie un facile adattamento a dplyr

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

La seconda opzione è la mia preferita. È la soluzione più semplice in quanto non richiede alcun pacchetto aggiuntivo ed è molto breve. – gattuso

+0

Ha frainteso il sistema di voto e vuole votare questa risposta – gattuso

3

Ecco il mio approccio:

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: la conversione rimosso a numerico (non richiesto)

Breve spiegazione:

  • as.POSIXct(sdf$ts) converte le date per stile POSIXct data volte
  • rollmean(as.POSIXct(sdf$ts), 2) calcola la media mobile di ciascuna delle due righe consecutive. Questo è esattamente il tempo che vuoi usare per separare le osservazioni. rollmean proviene dal pacchetto zoo. Computing a rollmean(..,2) significa che il vettore di uscita è abbreviato di 1 rispetto al vettore di input.
  • Ecco perché avvolgo il risultato di rollmean in c(.., Inf) che significa che il valore infinito viene aggiunto al vettore rollmean come ultimo valore. Ciò garantisce che vengano restituite anche le ultime voci di in sdf (0,3 nell'esempio specifico).
  • uso transform per aggiungere la colonna z di bdf
  • sapply(tb, function(x) which.max(x < m)) passanti attraverso le voci bdf$tb e per ogni voce, calcola l'indice massimo per cui bdf$tb è inferiore (anteriore) di m (che contiene il vettore di voci rollmean).Viene restituito solo l'indice massimo (più recente) per ogni voce bdf$tb.
  • quel vettore di indici viene usato in sdf$y[sapply(tb, function(x) which.max(x < m))] per estrarre gli elementi corrispondenti di sdf$y che saranno poi memorizzate/copiati nella nuova z colonna nella bdf

Speranza che aiuta

+0

Usare' rollmean' è piuttosto lucido. Potrebbe essere usato per compilare il vettore 'findInterval' nel mio metodo e fare i passaggi laterali per i problemi che avevo con' difftime'. –

3

Modifica nota: Inizialmente ho ottenere un risultato leggermente diverso da quello che hai fatto, che ora penso fosse correlato alla mia mancanza di comprensione degli oggetti R difftime. Anche i fusi orari degli oggetti POSIXt rimangono un mistero per me, ma ora vedo che quando ho forzato un oggetto "difftime" a "numerico" ho ottenuto il valore in "giorni".

La funzione findInterval è molto utile come una funzione di creazione dell'indice che mappa un vettore di valori in cui uno ha più intervalli adiacenti non sovrapposti. Hai davvero solo due time-points che si dividono in tre intervalli.

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 

Ho anche controllato per vedere se il mio risultato sarebbe stato influenzato dal fatto che gli intervalli di findIntervals sono stati chiusi alla loro destra piuttosto che a sinistra (default) e ha visto alcuna differenza.

6

Ecco una soluzione che utilizza data.table s' laminazione unisce:

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 converte data.frame a data.table per riferimento.

  • setkey smista la data.table per riferimento al fine dalle colonne forniti crescente, e segna le colonne come colonne chiave (in modo da poter aderire a tali colonne chiave tardi.

  • In data.table, x[i] esegue un join quando i è un data.table. io vi rimando al this answer di recuperare il data.table si unisce, se non sei già familiarità con.

  • x[i] perfo rms an equi-join. Cioè, trova gli indici di riga corrispondenti in x per ogni riga in i e quindi estrae quelle righe da x per restituire il risultato di unione insieme alla riga corrispondente da i. Nel caso in cui una riga in i non trovi gli indici di riga corrispondenti in x, quella riga avrebbe NA per x per impostazione predefinita.

    Tuttavia, x[i, roll = .] esegue un join rolling . Quando non c'è corrispondenza, viene eseguita l'ultima osservazione (roll = TRUE o -Inf) oppure l'osservazione successiva può essere riportata indietro (roll = Inf) oppure arrotolata al valore più vicino (roll = "nearest"). E in questo caso hai bisogno di roll = "nearest" IIUC.

HTH

Problemi correlati