这可能看起来像一个无关紧要的简单问题,但执行需要很长时间。任何加速它或矢量化等的想法都将非常感激。
我有一个包含500万行和50列的R数据框:OriginalDataFrame
该框架中的指数列表:IndexList
(55000 [numIndex
]个唯一索引)
它是一个时间序列,因此有5500个独特索引的约500万行。
OriginalDataFrame
已由dataIndex
订购。 IndexList
中的所有索引都不在OriginalDataFrame
中。任务是找到存在的索引,并构建一个新的数据框:FinalDataFrame
目前我正在使用library(foreach)
运行此代码:
FinalDataFrame <- foreach (i=1:numIndex, .combine="rbind") %dopar% {
OriginalDataFrame[(OriginalDataFrame$dataIndex == IndexList[i]),]
}
我在一台24核和128GB RAM的机器上运行它,但这需要大约6个小时才能完成。
我做了一些非常愚蠢的事情,或者R有更好的方法来做这件事吗?
答案 0 :(得分:3)
这是一个比较data.table和data.frame的基准。如果您知道此案例的特殊数据表调用,则速度提高约7倍,忽略了设置索引的成本(相对较小,通常会在多个调用中分摊)。如果您不知道特殊语法,它只会快一点。 (注意问题大小比原始小一点,以便更容易探索)
library(data.table)
library(microbenchmark)
options(digits = 3)
# Regular data frame
df <- data.frame(id = 1:1e5, x = runif(1e5), y = runif(1e5))
# Data table, with index
dt <- data.table(df)
setkey(dt, "id")
ids <- sample(1e5, 1e4)
microbenchmark(
df[df$id %in% ids , ], # won't preserve order
df[match(ids, df$id), ],
dt[id %in% ids, ],
dt[match(ids, id), ],
dt[.(ids)]
)
# Unit: milliseconds
# expr min lq median uq max neval
# df[df$id %in% ids, ] 13.61 13.99 14.69 17.26 53.81 100
# df[match(ids, df$id), ] 16.62 17.03 17.36 18.10 21.22 100
# dt[id %in% ids, ] 7.72 7.99 8.35 9.23 12.18 100
# dt[match(ids, id), ] 16.44 17.03 17.36 17.77 61.57 100
# dt[.(ids)] 1.93 2.16 2.27 2.43 5.77 100
我原本以为你也可以这样做 rownames,我认为它构建了一个哈希表并进行了索引 有效率的。但事实显然并非如此:
df2 <- df
rownames(df2) <- as.character(df$id)
df2[as.character(ids), ],
microbenchmark(
df[df$id %in% ids , ], # won't preserve order
df2[as.character(ids), ],
times = 1
)
# Unit: milliseconds
# expr min lq median uq max neval
# df[df$id %in% ids, ] 15.3 15.3 15.3 15.3 15.3 1
# df2[as.character(ids), ] 3609.8 3609.8 3609.8 3609.8 3609.8 1
答案 1 :(得分:0)
检查data.table
包裹。它的工作方式与data.frame
相似,但更快。
像这样(其中df是你的数据框):
table <- data.table(df)
并使用表格
答案 2 :(得分:0)
如果您有5M行,并且使用==
来标识要子集的行,那么对于循环的每次传递,您将执行5M比较。如果您改为键入数据(因为它本身就是这样),那么您可以显着提高效率:
library(data.table)
OriginalDT <- as.data.table(OriginalDataFrame)
setkey(OriginalDT, dataIndex)
# Now inside your foreach:
OriginalDT[ .( IndexList[[i]] ) ]
请注意,setkey
函数使用基数排序的非常快速的实现。但是,如果您的数据已经被保证排序,@ eddi或@arun已经发布了一个很好的黑客,只需将属性设置为DT。 (我现在找不到它,但也许有人可以编辑这个答案并链接到它)。
您可以尝试将所有结果收集到data.tables列表中,然后使用rbindlist
并将速度与使用.combine=rbind
进行比较(如果您这样做,请随时发布基准测试结果)。我从来没有测试过.combine=rbindlist
,但这可能会起作用并且尝试很有意思。
如果唯一的任务是索引data.frame,那么只需使用:
dataIndex[ .( IndexList ) ]
不需要foreach
,您仍然可以利用密钥的DT