Использование data.table с функциями замены в r

Сегодня я столкнулся со следующей проблемой, и мне интересно, есть ли лучший способ выполнить то, что я пытаюсь сделать.

Предположим, что у меня есть следующий data.table (только почасовая метка времени):

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

Моя цель - изменить минуты метки времени, скажем, на 10 минут.

Я знаю, что могу использовать:

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

но это не использует сверхбыструю скорость таблицы данных (которая мне нужна). На моем ноутбуке это заняло:

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

Итак, мой вопрос: можем ли мы каким-то образом использовать функцию замены в синтаксисе таблицы данных, чтобы она выполняла то, что я хочу, используя скорость data.table? Если ответ отрицательный, любое другое решение data.table, чтобы сделать это быстро, было бы приемлемым.

Если вам интересно, одна из вещей, которую я пробовал:

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

который не работает.

Ожидаемый результат (но с синтаксисом таблицы данных):

> 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

Функции замены выполняются в два этапа:

  • Функция, которая создает желаемый результат,
  • Затем этот результат присваивается результату.

Вы можете выполнить шаг 1 без выполнения шага 2. Затем этот результат можно использовать для установки столбца data.table(set, используемого здесь, но вы также можете использовать :=).

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

Редактирование: Небольшие данные. таблица против большого сравнения данных. таблица

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

Похоже, что это не быстрее, что противоречит моему пониманию того, что происходит. Странно.

Ответ 2

Объект A POSIXct является просто двойным с некоторыми атрибутами

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

Итак, чтобы эффективно управлять им, вы можете просто рассматривать его как один, например

tdt[, Timestamp := Timestamp + 600L]

Будет добавлено 600 секунд (10 минут) к каждой строке по ссылке


Некоторые тесты

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 

Ответ 3

Я думаю, это должно сделать трюк для вас:

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)]