2013-12-09 6 views
7

Я не уверен, что могу задать этот вопрос здесь, дайте мне знать, если я буду делать это где-то в другом месте.Улучшить производительность data.table date + время вставки?

У меня есть data.table с 1E6 строки, имеющие эту структуру:

 V1  V2  V3 
1: 03/09/2011 08:05:40 1145.0 
2: 03/09/2011 08:06:01 1207.3 
3: 03/09/2011 08:06:17 1198.8 
4: 03/09/2011 08:06:20 1158.4 
5: 03/09/2011 08:06:40 1112.2 
6: 03/09/2011 08:06:59 1199.3 

Я преобразование переменных V1 и V2 к уникальной переменной даты и времени, используя этот код:

system.time(DT[,`:=`(index= as.POSIXct(paste(V1,V2), 
         format='%d/%m/%Y %H:%M:%S'), 
        V1=NULL,V2=NULL)]) 

    user system elapsed 
    47.47 0.16 50.27 

Is есть ли какой-либо метод для повышения производительности этого преобразования?

Здесь dput(head(DT)):

DT <- structure(list(V1 = c("03/09/2011", "03/09/2011", "03/09/2011", 
"03/09/2011", "03/09/2011", "03/09/2011"), V2 = c("08:05:40", 
"08:06:01", "08:06:17", "08:06:20", "08:06:40", "08:06:59"), 
    V3 = c(1145, 1207.3, 1198.8, 1158.4, 1112.2, 1199.3)), .Names = c("V1", 
"V2", "V3"), class = c("data.table", "data.frame"), row.names = c(NA, 
-6L), .internal.selfref = <pointer: 0x00000000002a0788>) 
+2

Если ваши даты GMT и больше, чем на 1 января, 1970 Вы можете попробовать ' быстрое время' упаковка. – aseidlitz

+0

@aseidlitz Спасибо. Я забываю про «быстрое время». Я думаю, что это хороший застой. Но я не использую внутренне пакет data.table? – agstudy

+0

Были некоторые дискуссии о добавлении поддержки fasttime в таблицу data.table, но насколько я вижу, это все еще открытый запрос в [R Forge] (https://r-forge.r-project.org/tracker/?func= detail & atid = 978 & aid = 2582 & group_id = 240) – aseidlitz

ответ

7

Этот подход, который, как представляется, работает на скорости ~ 40X быстрее, чем OP, использует таблицы поиска и использует преимущества чрезвычайно быстрых соединений таблиц данных. Кроме того, он использует тот факт, что, хотя может быть 1е6 комбинаций даты и времени, может быть не более 86400 уникальных времен и, возможно, еще меньше дат. Наконец, он полностью избегает использования paste(...).

library(data.table) 
library(stringr) 

# create a dataset with 1MM rows 
set.seed(1) 
x <- 1000*sample(1:1e5,1e6,replace=T) 
dt <- data.table(id=1:1e6, 
       V1=format(as.POSIXct(x,origin="2011-01-01"),"%d/%m/%Y"), 
       V2=format(as.POSIXct(x,origin="2011-01-01"),"%H:%M:%S"), 
       V3=x) 
DT <- dt 

index.date <- function(dt) { 
    # Edit: this change processes only times from the dataset; slightly more efficient 
    V2 <- unique(dt$V2) 
    dt.time <- data.table(char.time=V2, 
         int.time=as.integer(substr(V2,7,8))+ 
          60*(as.integer(substr(V2,4,5))+ 
           60*as.integer(substr(V2,1,2)))) 
    setkey(dt.time,char.time) 
    # all dates from dataset 
    dt.date <- data.table(char.date=unique(dt$V1), int.date=as.integer(as.POSIXct(unique(dt$V1),format="%d/%m/%Y"))) 
    setkey(dt.date,char.date) 
    # join the dates 
    setkey(dt,V1) 
    dt <- dt[dt.date] 
    # join the times 
    setkey(dt,V2) 
    dt <- dt[dt.time, nomatch=0] 
    # numerical index 
    dt[,int.index:=int.date+int.time] 
    # POSIX date index 
    dt[,index:=as.POSIXct(int.index,origin='1970-01-01')] 
    # get back original order 
    setkey(dt,id) 
    return(dt) 
} 
# new approach 
system.time(dt<-index.date(dt)) 
# user system elapsed 
# 2.26 0.00 2.26 


# original approach 
DT <- dt 
system.time(DT[,`:=`(index= as.POSIXct(paste(V1,V2), 
             format='%d/%m/%Y %H:%M:%S'), 
        V1=NULL,V2=NULL)]) 
# user system elapsed 
# 84.33 0.06 84.52 

Обратите внимание, что производительность зависит от количества уникальных дат. В тестовом случае было ~ 1200 уникальных дат.

РЕДАКТИРОВАНИЕ предложение написать функцию в более data.table сахара синтаксиса и избежать «$» для Подменю:

index.date <- function(dt,fmt="%d/%m/%Y") { 
    dt.time <- data.table(char.time = dt[,unique(V2)],key='char.time') 
    dt.time[,int.time :=as.integer(substr(char.time,7,8))+ 
              60*(as.integer(substr(char.time,4,5))+ 
                 60*as.integer(substr(char.time,1,2)))] 
    # all dates from dataset 
    dt.date <- data.table(char.date = dt[,unique(V1)],key='char.date') 
    dt.date[,int.date:=as.integer(as.POSIXct(char.date,format=fmt))] 
    # join the dates 
    setkey(dt,V1) 
    dt <- dt[dt.date] 
    # join the times 
    setkey(dt,V2) 
    dt <- dt[dt.time, nomatch=0] 
    # numerical index 
    dt[,int.index:=int.date+int.time] 
    # POSIX date index 
    dt[,index:=as.POSIXct.numeric(int.index,origin='1970-01-01')] 
    # remove extra/temporary variables 
    dt[,`:=`(int.index=NULL,int.date=NULL,int.time=NULL)] 
} 
+0

Незначительное изменение для обработки только уникальных времен из набора данных; немного более эффективный (~ 2.2s против 3.9s раньше). – jlhoward

+0

Спасибо. очень хорошая идея использовать data.table join. Я немного модифицирую ваше решение, чтобы поместить его в более синтаксис сахара data.table. – agstudy

+0

@agstudy: Очень чище, спасибо. Но почему последний оператор восстанавливает первоначальный порядок? – jlhoward

2

Если есть много временных меток, которые будут повторяться в ваших данных, вы можете попробовать добавить ,by=list(V1, V2), но там должны быть достаточно повторением, чтобы оплатить стоимость расщепления ,

Заглушка бутылки здесь - это вставка & конверсия, так что это заставляет меня думать, что ответ отрицательный. (Если вы не используете альтернативный метод преобразования в POSIX)

+0

Спасибо. Я забываю упомянуть, что у меня не должно быть дублированных дат (это в основном данные временных рядов). – agstudy

+5

И, кроме того, узкое место действительно конверсия, которая занимает примерно 40 раз дольше, чем шаг пасты. Если ваши даты были в истинном формате POSIX (т. Е. «1970-01-01» вместо «01/01/1970»), вы могли бы ускорить работу с использованием библиотеки быстрого времени Саймона Урбанека [как указано здесь] (http: // stackoverflow .com/вопросы/12898318/преобразование символов современной-быстро-в-г/12898544 # 12898544). –

+1

@ JoshO'Brien, вы правы в быстром времени. Например, используя что-то вроде 'DT [, fastPOSIXct (paste (as.Date (V1, format ="% d /% m /% Y "), V2))] * улучшает производительность не менее 4 раз. – agstudy

Смежные вопросы