【问题标题】:How to subset multiple rows from data.table fast on big data如何在大数据上快速从 data.table 中对多行进行子集化
【发布时间】:2016-12-25 06:29:40
【问题描述】:

我正在使用 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" 的值:

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

【问题讨论】:

  • 您的第一步应该是将日期时间解析为 POSIXct。我不相信你的代码能正常工作/做你认为它做的事。
  • 嗨,我测试了它,对我来说它适用于字符。但我也可以解析它们,但这并不能解决我的问题:)
  • 在这个例子中它恰好是正确的。但是请检查 "01.02.2016 10:00:30" &lt; "21.02.2015 10:02:30" 返回的内容,例如
  • 哦...你找到我了!我犯了一个巨大的错误!对不起@Roland!感谢您指出我的错误!将使用 POSIXct 对象编辑问题。

标签: r data.table


【解决方案1】:

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 年 8 月 26 日):

正如@Arun 所指出的,虽然inrange 使用二元连接,但它需要先对整个向量进行排序,以检查x 中的每个 值是否在any 之间 的区间提供在下、上。 在您的情况下,这有点开销,因为您只比较两个值,因此最近用 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 

【讨论】:

  • 更多操作的自动索引目前计划在 2.0.0 - data.table#1068
  • @Arun fixed the bug 所以现在你可以看到bmerge 开始更新帖子。您需要 Gh 上的最新版本
  • 请注意,如果您只有一个范围,between() 会更快(尤其是现在它已经用 C 语言实现并并行化了)。 inrange 在处理超过 1 个范围的子集时非常有用。在DT[] 中使用时,我会进一步优化它,很快(使用现有索引)..
  • @Arun between 是我第一个想到的,但检查后发现它只是在进行矢量扫描,还是我错了?还有,它会比 Ops Date &gt;= &amp; Date &lt;= 更快尝试?
  • 大卫,很好!谢谢。我将尝试找出一种重用索引的方法,以便在索引已经存在的情况下处理 2.17s。仍然为 1 个间隔(在未排序的数据上),之间可能会稍快..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 2020-07-24
  • 2021-07-23
  • 1970-01-01
相关资源
最近更新 更多