2015-07-13 7 views
12

Oggi mi sono imbattuto nel seguente problema e mi chiedo se esiste un modo migliore per realizzare ciò che sto cercando di fare.Utilizzo di data.table con funzioni di sostituzione in

Supponiamo Ho il seguente data.table (solo un timestamp ogni ora):

library(data.table) 
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour')) 

> tdt 
        Timestamp 
    1: 1980-01-01 00:00:00 
    2: 1980-01-01 01:00:00 
    3: 1980-01-01 02:00:00 
    4: 1980-01-01 03:00:00 
    5: 1980-01-01 04:00:00 
    ---      
306813: 2014-12-31 20:00:00 
306814: 2014-12-31 21:00:00 
306815: 2014-12-31 22:00:00 
306816: 2014-12-31 23:00:00 
306817: 2015-01-01 00:00:00 

Il mio obiettivo è quello di cambiare il verbale della timestamp, per esempio, 10 minuti.

So che posso usare:

library(lubridate) 
minute(tdt$Timestamp) <- 10 

ma questo non utilizza la velocità veloce eccellente di tabella di dati (che ho bisogno). Sul mio portatile ha preso questo:

> system.time(minute(tdt$Timestamp) <- 10) 
    user system elapsed 
    11.29 0.16 11.45 

Quindi, la mia domanda è: Possiamo usare in qualche modo una funzione di sostituzione nella sintassi tabella di dati in modo che possa fare quello che voglio con data.table 's di velocità? Se la risposta è no, qualsiasi altra soluzione di data.table per fare questo veloce, sarebbe accettabile.

Se vi chiedete una delle cose che ho provato è:

tdt[, Timestamp2 := minute(Timestamp) <- 10] 

che non funziona.

Output previsto (ma con tabella dati sintassi):

> tdt 
        Timestamp 
    1: 1980-01-01 00:10:00 
    2: 1980-01-01 01:10:00 
    3: 1980-01-01 02:10:00 
    4: 1980-01-01 03:10:00 
    5: 1980-01-01 04:10:00 
    ---      
306813: 2014-12-31 20:10:00 
306814: 2014-12-31 21:10:00 
306815: 2014-12-31 22:10:00 
306816: 2014-12-31 23:10:00 
306817: 2015-01-01 00:10:00 
+1

* think * questo può puntare nella giusta direzione: http://stackoverflow.com/questions/10449366/levels-what-sorcery -è questo . Ma almeno lo scorso anno 'data.table' non supportava bene i formati data/ora. Potrebbe essere migliorato ormai. –

+6

Che dire di solo 'tdt [, Timestamp: = Timestamp + 600L]'? –

+0

@ AriB.Friedman Grazie per questo collegamento. Ho visto questa domanda e sono a conoscenza di come funziona una funzione di sostituzione, ma non ero ancora in grado di usarla in quel modo ... – LyzandeR

risposta

7

funzioni ricambio vengono eseguite in due fasi:

  1. Una funzione che crea l'output desiderato,
  2. Questo risultato viene quindi assegnato al risultato.

È possibile eseguire step 1 without running step 2. Questo risultato può quindi essere utilizzato per impostare la colonna data.table (set utilizzata qui, ma è possibile utilizzare anche).

library(lubridate) 
library(data.table) 
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour')) 
minute(tdt$Timestamp) <- 20 
print(`minute<-`(tdt$Timestamp,11)) 
set(tdt, j=1L,value=`minute<-`(tdt$Timestamp,11) ) 

Edit: Piccole data.table vs Big Data.tabella comparativa

library(lubridate) 
library(data.table) 
library(microbenchmark) 

# Config 
tms <- 5L 

# Sample data, 1 column 
tdt <- data.table(Timestamp = seq(as.POSIXct("1980-01-01 00:00:00"), as.POSIXct("2015-01-01 00:00:00"), '1 hour')) 
minute(tdt$Timestamp) <- 20 

tdf <- as.data.frame(tdt) 


# Sample data, lots of columns 
bdf <- cbind(tdf, as.data.frame(replicate(100, runif(nrow(tdt))))) 
bdt <- as.data.table(bdf) 

# Benchmark 
microbenchmark(
    `minute<-`(tdt$Timestamp,10), # How long does the operation to generate the new vector itself take? 
    set(tdt, j=1L,value=`minute<-`(tdt$Timestamp,11) ), # One column: How long does it take to generate the new vector and replace the contents in the data.table? 
    minute(tdf$Timestamp) <- 12, # One column: How long does it take to do it with a data.frame? 
    set(tdt, j=1L,value=`minute<-`(bdt$Timestamp,13) ), # Many columns: How long does it take to generate the new vector and replace the contents in the data.table? 
    minute(bdf$Timestamp) <- 14, # Many columns: How long does it take to do it with a data.frame? 
    times = tms 
) 

Unit: seconds 
                expr  min  lq  mean median  uq  max neval 
          `minute<-`(tdt$Timestamp, 10) 1.304388 1.385883 1.417616 1.389316 1.459166 1.549327  5 
set(tdt, j = 1L, value = `minute<-`(tdt$Timestamp, 11)) 1.314495 1.344277 1.376241 1.352124 1.389083 1.481225  5 
          minute(tdf$Timestamp) <- 12 1.342104 1.349231 1.488639 1.378840 1.380659 1.992358  5 
set(tdt, j = 1L, value = `minute<-`(bdt$Timestamp, 13)) 1.337944 1.383429 1.402802 1.418211 1.418922 1.455503  5 
          minute(bdf$Timestamp) <- 14 1.332482 1.333713 1.355331 1.335728 1.342607 1.432127  5 

Sembra che non è più veloce, che smentisce la mia comprensione di ciò che sta accadendo. Strano.

+0

Che bello, mi chiedo come ridimensionerà. Dal mio punto di riferimento è stato esattamente lo stesso timing –

+0

Questo è fantastico! La risposta di David (ho svaligiato Dave: P) è molto più veloce per essere onesto per l'esempio che ho dato, ma in tutta onestà questa è la risposta generica che stavo cercando. Grazie mille Ari. – LyzandeR

+0

Dato che l'esempio consiste in una singola colonna, mi aspetto che la sostituzione della colonna e la sostituzione dell'intero data.frame siano simili nella velocità. Prova a confrontarlo con un data.table con poche centinaia di colonne? –

10

Un oggetto POSIXct è solo una doppia con alcuni attributi

storage.mode(as.POSIXct("1980-01-01 00:00:00")) 
## [1] "double" 

Quindi, al fine di manipolarlo efficiente si può solo trattalo come uno, per esempio

tdt[, Timestamp := Timestamp + 600L] 

aggiungerà 600 secondi (10 minuti) per ogni riga con riferimento


Alcuni parametri

tdt <- data.table(Timestamp = seq(as.POSIXct("1600-01-01 00:00:00"), 
            as.POSIXct("2015-01-01 00:00:00"), 
            '1 hour')) 
system.time(minute(tdt$Timestamp) <- 10) 
# user system elapsed 
# 124.86 1.95 127.68 
system.time(set(tdt, j = 1L, value = `minute<-`(tdt$Timestamp, 10))) 
# user system elapsed 
# 124.99 1.83 128.25 
system.time(tdt[, Timestamp := Timestamp + dminutes(10)]) 
# user system elapsed 
# 0.39 0.04 0.42 
system.time(tdt[, Timestamp := Timestamp + 600L]) 
# user system elapsed 
# 0.01 0.00 0.01 
+1

Grazie mille per questa risposta Dave. Ho appena modificato il nucleo della mia funzione (secondo questa risposta) e l'ho applicato su questo timestamp del campione e funziona in meno di un secondo. È estremamente veloce e preciso. Grazie mille mi hai risparmiato un sacco di tempo per aspettare che le cose vadano. Vorrei poter accettare entrambe le risposte. Uno mi ha mostrato un modo di usare una funzione di sostituzione (che sebbene non sembri funzionare alla grande, risponde direttamente alla mia domanda), l'altra mi ha aiutato a risolvere il mio problema reale. Sono confuso: S. L'overflow dello stack deve essere fatto per tali occasioni! – LyzandeR

+1

Va bene, Ari ha fornito una bella risposta e merita il segno di spunta. –

+1

Grazie mille Dave, mi hai fatto sentire meglio :). Grazie per tutto il tuo aiuto e la soluzione reale. – LyzandeR

3

Credo che questo dovrebbe fare il trucco per voi:

library(data.table) 
library(lubridate) 

tdt <- data.table(
    Timestamp = seq(as.POSIXct("1980-01-01 00:00:00") 
    , as.POSIXct("2015-01-01 00:00:00") 
    , '1 hour')) 
tdt[, Timestamp := Timestamp + dminutes(10)] 
+0

Grazie per la risposta. Questo è praticamente equivalente alla risposta di Dave e sarà leggermente più lento dato che sta usando una funzione esterna. – LyzandeR

+0

Sì, anzi è equivalente. Ma non l'ho visto prima di aver postato :) Tuttavia, il mio mi piace un po 'meglio grazie alla comprensione più chiara di ciò che sta accadendo. –

Problemi correlati