如何在大数据上快速从data.table中对多行进行子集化

时间:2016-08-18 10:49:03

标签: r data.table

我正在使用R-Studio并希望从data.table中获取多行。

我们说我有一个data.table,包含以下数据:

Date                           Column 1
"01.02.2016 10:00:00  CEST"    10        
"01.02.2016 10:01:00  CEST"    12
"01.02.2016 10:02:00  CEST"    13
"01.02.2016 10:03:00  CEST"    11
"01.02.2016 10:04:00  CEST"    17

我想从" 01.02.2016 10:00:30" " 01.02.2016 10:02:30&#获取值34; 像这样:

    Date                       Column 1       
"01.02.2016 10:01:00  CEST"    12
"01.02.2016 10:02:00  CEST"    13

目前我通过这样做实现了这一目标:

x <- data.table[Date >= "01.02.2016 10:00:30  CEST" & Date <= "01.02.2016 10:02:30  CEST"]

但这对我来说太慢了,因为在一个600k行的data.table上需要大约0.4秒。

相反,这要快得多:

setkey(data.table, Date)
x <- prozessdaten.data.table[J(c("01.02.2016 10:01:00  CEST", "01.02.2016 10:02:00  CEST"))]

我的问题是否有可能使用具有指定时间范围而非精确值的二进制搜索函数J()?

1 个答案:

答案 0 :(得分:11)

data.table v1.9.7+已实施non-equi joins并添加了一项新功能inrange,该功能使用此新功能并可实现您想要的功能

## Loading data
library(data.table) #v 1.9.7+
DT <- data.table(date = c('01.02.2016 10:00:00','01.02.2016 10:01:00',
                          '01.02.2016 10:02:00','01.02.2016 10:03:00',
                          '01.02.2016 10:04:00'),
                 column1 = c(10, 12, 13, 11, 17))

## Converting to POSIXct class
DT[, date := as.POSIXct(date, format = "%d.%m.%Y %H:%M:%S")]

## Validating that forder/bmerge kicks in 
options(datatable.verbose = TRUE)
DT[date %inrange% as.POSIXct(c("2016-02-01 10:00:30", "2016-02-01 10:02:30"))]
# forderv(query) took ... 0 secs
# Starting bmerge ...done in 0 secs <~~~~~~~~ (Thanks to @Arun for fixing the bug)
# Generating final logical vector ... done in 0 secs
#                   date column1
# 1: 2016-02-01 10:01:00      12
# 2: 2016-02-01 10:02:00      13

但是,您应该知道,因为data.table 1.9.4 secondary keys已经实现,这意味着对于矢量扫描的某些变体,在第一次运行之后,正在添加一个键,从现在开始,甚至操作例如==%in%正在使用bmerge。这似乎不适用于POSIXct类,但您可以在数字列column1上观察到此行为

## Running for first time
options(datatable.verbose = TRUE)
DT[column1 == 10]
# Creating new index 'column1'
# forder took 0 sec <~~~ forder kicks in, hence first time is a bit slow
# Starting bmerge ...done in 0 secs
#                   date column1
# 1: 2016-02-01 10:00:00      10

## Running for second time and on
DT[column1 == 10]
# Using existing index 'column1'
# Starting bmerge ...done in 0 secs <~~ bmerge kicks in from now on
#                   date column1
# 1: 2016-02-01 10:00:00      10

正如@Jan所提到的那样,计划在非等连接中实现这一点starting from v2.0.0

编辑(2016年6月26日):

正如@Arun所指出的,尽管inrange正在使用二进制连接,但它需要先对整个向量进行排序,以检查x中的每个值是否在下部,上部提供的任何间隔。 在你的情况下,这只是一个开销,因为你只是比较两个值,因此最近在C between函数中重写将更适合你

set.seed(123)
DT <- data.table(x = sample(5e8))

system.time(res1 <- DT[x > 1e3L & x < 1e5L])
#  user  system elapsed 
# 10.23    1.22   11.45 

system.time(res2 <- DT[x %inrange% c(1e3L, 1e5L)])
# forderv(query) took ... 29.09 secs
# Starting bmerge ...done in 0 secs
# Generating final logical vector ... done in 0.43 secs
#  user  system elapsed 
# 29.28    0.70   30.06 

system.time(res3 <- DT[x %between% c(1e3L, 1e5L)])
# user  system elapsed 
# 2.01    2.60    0.84

正如您所看到的,尽管bmerge几乎是即时的,但排序需要花费大量时间。虽然between是最快的,但它不需要将x转换为逻辑向量两次。哎呀,between非常快,elapseduser + system

更小

但是,如果您的数据已经排序,那么inrange可以很好地抓住

setorder(DT, x)
system.time(res1 <- DT[x > 1e3L & x < 1e5L])
#  user  system elapsed 
# 10.41    1.02   11.45 

system.time(res2 <- DT[x %inrange% c(1e3L, 1e5L)])
# forderv(query) took ... 2.17 secs
# Starting bmerge ...done in 0 secs
# Generating final logical vector ... done in 0.44 secs
#  user  system elapsed 
# 2.47    0.71    3.20 

system.time(res3 <- DT[x %between% c(1e3L, 1e5L)])
#  user  system elapsed 
# 2.30    2.62    0.88