在R中进行子集操作的最快方法

4

我有一个名为main的数据框,它有400,000行,我想对其进行子集处理以检索1个或多个行。

这里是一个数据框的例子,展示了我使用subset 函数进行子集处理的方式:

main <- data.frame(date = as.POSIXct(c("2015-01-01 07:44:00 GMT","2015-02-02 09:46:00 GMT")),
                   name= c("bob","george"),
                   value=c(1,522), 
                   id= c(5,2))

subset(main, date == "2015-01-01 07:44:00" & name == "bob" & value == 1)

这个操作是可行的,但速度较慢,我认为这是因为我正在处理一个有400k行的数据框。有什么方法可以使子集操作更快吗?

2
你可以使用 [ 代替 subset 来获得更快的速度,或者可能是 data.table - akrun
1
或者 library(dplyr); filter(main, date == "2015-01-01 07:44:00" & name == "bob" & value == 1),尽管我还没有对其进行基准测试。 - talat
这是一个相当小的任务。我猜你的内存有限,可能正在分页到虚拟内存中。 - IRTFM
3
这个例子并没有展示出慢速,但如果您的变量有重复值,那么使用键控数据表会非常快,例如 setkey(data);data[J("2015-01-01 07:44:00","bob",1)] - Frank
@Frank,你能给个例子吗?我需要转换成data.table吗?我需要在所有列上设置键吗?J()函数是什么?如何在条件中使用>或<? - user3022875
显示剩余2条评论
1个回答

5

我建议使用带有键值的data.table。以下是如何设置(以修改后的示例为例):

require(data.table)
mainDT <- data.table(main)
setkey(mainDT,V1,V2,V3)

现在我们可以使用如下语法基于相等条件进行子集划分:
mainDT[J("a","A")]

或者
mainDT[J(c("a","b"),"A",1)]

这是关于it技术的翻译内容: V1 %in% c("a","b") 表示在哪些子集中使用(等同于 V1=="a"|V1=="b")。


下面是速度比较:

require(rbenchmark)
benchmark(
  "["       = main[main$V1=="a" & main$V2=="A",],
  "subset"  = subset(main,V1=="a" & V2=="A"),
  "DT[J()]" = mainDT[J("a","A")],
  replications=5
)[,1:6]

这在我的电脑上得到了以下结果:

     test replications elapsed relative user.self sys.self
1       [            5    5.96       NA      5.38     0.57
3 DT[J()]            5    0.00       NA      0.00     0.00
2  subset            5    6.93       NA      6.20     0.72

所以,使用J进行子集划分是瞬间完成的,而其他两种方法需要几秒钟。然而,使用J进行子集划分有以下限制:
  • 仅适用于等式条件。
  • 对于上述简单语法,您需要按键的顺序传递参数。但是,您可以使用mainDT[J("a",unique(V2),2)]选择V1=="a" & V3 == 2,并且速度仍然很快。

您可以使用data.table执行数据框中的所有操作。例如,subset(mainDT,V1=="a" & V2=="A")仍然有效。因此,通常情况下将数据框转换为data.table不会有任何损失。您可以使用setDT(main)在原地将其转换为data.table。


这是示例代码:
n  = 1e7
n3 = 1e3

set.seed(1)
main <- data.frame(
  V1=sample(letters,n,replace=TRUE),
  V2=sample(c(letters,LETTERS),n,replace=TRUE),
  V3=sample(1:n3,n,replace=TRUE),
  V4=rnorm(n))

上面基准测试中的改进将根据您的数据而异。当您有许多观测值(n)或键的唯一值较少时(例如,n3),使用带键的data.table进行子集化的好处应该更大。

感谢您的热情回应。我在这里增加了更多关于我的问题的见解:https://dev59.com/Dorda4cB1Zd3GeqPLFqp ,您能否帮忙看一下?我想使用data.table,但不确定是否可以使用>=、<=和或运算符。 - user3022875

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接