为什么拆分在具有许多组的大型数据帧上效率低下?
Why is split inefficient on large data frames with many groups?
df %>% split(.$x)
对于大量的 x 唯一值变得缓慢。如果我们改为手动将数据帧拆分为更小的子集,然后对每个子集执行拆分,我们至少可以将时间减少一个数量级。
library(dplyr)
library(microbenchmark)
library(caret)
library(purrr)
N <- 10^6
groups <- 10^5
df <- data.frame(x = sample(1:groups, N, replace = TRUE),
y = sample(letters, N, replace = TRUE))
ids <- df$x %>% unique
folds10 <- createFolds(ids, 10)
folds100 <- createFolds(ids, 100)
运行 microbenchmark
给我们
## Unit: seconds
## expr mean
l1 <- df %>% split(.$x) # 242.11805
l2 <- lapply(folds10, function(id) df %>%
filter(x %in% id) %>% split(.$x)) %>% flatten # 50.45156
l3 <- lapply(folds100, function(id) df %>%
filter(x %in% id) %>% split(.$x)) %>% flatten # 12.83866
split
不是为大型团体设计的吗?除了手动初始子集化之外还有其他选择吗?
我的笔记本电脑是 2013 年末的 macbook pro,2.4GHz 8GB
与其说是答案,不如说是解释。子设置一个大 data.frame 比子设置一个小数据框
成本更高
> df100 = df[1:100,]
> idx = c(1, 10, 20)
> microbenchmark(df[idx,], df100[idx,], times=10)
Unit: microseconds
expr min lq mean median uq max neval
df[idx, ] 428.921 441.217 445.3281 442.893 448.022 475.364 10
df100[idx, ] 32.082 32.307 35.2815 34.935 37.107 42.199 10
split()
为每个组支付此费用。
原因可以看运行 Rprof()
> Rprof(); for (i in 1:1000) df[idx,]; Rprof(NULL); summaryRprof()
$by.self
self.time self.pct total.time total.pct
"attr" 1.26 100 1.26 100
$by.total
total.time total.pct self.time self.pct
"attr" 1.26 100 1.26 100
"[.data.frame" 1.26 100 0.00 0
"[" 1.26 100 0.00 0
$sample.interval
[1] 0.02
$sampling.time
[1] 1.26
所有时间都花在了对 attr()
的调用上。使用 debug("[.data.frame")
单步执行代码表明痛苦涉及像
这样的调用
attr(df, "row.names")
这个小例子展示了 R 用来避免表示不存在的行名称的技巧:使用 c(NA, -5L)
,而不是 1:5
。
> dput(data.frame(x=1:5))
structure(list(x = 1:5), .Names = "x", row.names = c(NA, -5L), class = "data.frame")
请注意,attr()
returns 向量 -- row.names 是动态创建的,对于大量 data.frame 大量 row.names 已创建
> attr(data.frame(x=1:5), "row.names")
[1] 1 2 3 4 5
所以人们可能会认为即使是无意义的 row.names 也会加快计算速度
> dfns = df; rownames(dfns) = rev(seq_len(nrow(dfns)))
> system.time(split(dfns, dfns$x))
user system elapsed
4.048 0.000 4.048
> system.time(split(df, df$x))
user system elapsed
87.772 16.312 104.100
拆分向量或矩阵也会很快。
这不是严格意义上的 split.data.frame
问题,对于许多组来说 data.frame 的可扩展性存在更普遍的问题。
如果您使用 split.data.table
,您可以获得相当不错的加速。我在常规 data.table 方法之上开发了这个方法,它似乎在这里扩展得很好。
system.time(
l1 <- df %>% split(.$x)
)
# user system elapsed
#200.936 0.000 217.496
library(data.table)
dt = as.data.table(df)
system.time(
l2 <- split(dt, by="x")
)
# user system elapsed
# 7.372 0.000 6.875
system.time(
l3 <- split(dt, by="x", sorted=TRUE)
)
# user system elapsed
# 9.068 0.000 8.200
sorted=TRUE
将 return 与 data.frame 方法相同顺序的列表,默认情况下 data.table 方法将保留输入数据中存在的顺序。如果你想坚持 data.frame,你可以在最后使用 lapply(l2, setDF)
。
PS。 split.data.table
1.9.7加入,开发版安装非常简单
install.packages("data.table", type="source", repos="http://Rdatatable.github.io/data.table")
中有更多相关信息
利用 dplyr 0.8.3 或更高版本的 group_split
的一个非常好的作弊:
random_df <- tibble(colA= paste("A",1:1200000,sep = "_"),
colB= as.character(paste("A",1:1200000,sep = "_")),
colC= 1:1200000)
random_df_list <- split(random_df, random_df$colC)
random_df_list <- random_df %>% group_split(colC)
将几分钟的操作减少到几秒钟!
df %>% split(.$x)
对于大量的 x 唯一值变得缓慢。如果我们改为手动将数据帧拆分为更小的子集,然后对每个子集执行拆分,我们至少可以将时间减少一个数量级。
library(dplyr)
library(microbenchmark)
library(caret)
library(purrr)
N <- 10^6
groups <- 10^5
df <- data.frame(x = sample(1:groups, N, replace = TRUE),
y = sample(letters, N, replace = TRUE))
ids <- df$x %>% unique
folds10 <- createFolds(ids, 10)
folds100 <- createFolds(ids, 100)
运行 microbenchmark
给我们
## Unit: seconds
## expr mean
l1 <- df %>% split(.$x) # 242.11805
l2 <- lapply(folds10, function(id) df %>%
filter(x %in% id) %>% split(.$x)) %>% flatten # 50.45156
l3 <- lapply(folds100, function(id) df %>%
filter(x %in% id) %>% split(.$x)) %>% flatten # 12.83866
split
不是为大型团体设计的吗?除了手动初始子集化之外还有其他选择吗?
我的笔记本电脑是 2013 年末的 macbook pro,2.4GHz 8GB
与其说是答案,不如说是解释。子设置一个大 data.frame 比子设置一个小数据框
成本更高> df100 = df[1:100,]
> idx = c(1, 10, 20)
> microbenchmark(df[idx,], df100[idx,], times=10)
Unit: microseconds
expr min lq mean median uq max neval
df[idx, ] 428.921 441.217 445.3281 442.893 448.022 475.364 10
df100[idx, ] 32.082 32.307 35.2815 34.935 37.107 42.199 10
split()
为每个组支付此费用。
原因可以看运行 Rprof()
> Rprof(); for (i in 1:1000) df[idx,]; Rprof(NULL); summaryRprof()
$by.self
self.time self.pct total.time total.pct
"attr" 1.26 100 1.26 100
$by.total
total.time total.pct self.time self.pct
"attr" 1.26 100 1.26 100
"[.data.frame" 1.26 100 0.00 0
"[" 1.26 100 0.00 0
$sample.interval
[1] 0.02
$sampling.time
[1] 1.26
所有时间都花在了对 attr()
的调用上。使用 debug("[.data.frame")
单步执行代码表明痛苦涉及像
attr(df, "row.names")
这个小例子展示了 R 用来避免表示不存在的行名称的技巧:使用 c(NA, -5L)
,而不是 1:5
。
> dput(data.frame(x=1:5))
structure(list(x = 1:5), .Names = "x", row.names = c(NA, -5L), class = "data.frame")
请注意,attr()
returns 向量 -- row.names 是动态创建的,对于大量 data.frame 大量 row.names 已创建
> attr(data.frame(x=1:5), "row.names")
[1] 1 2 3 4 5
所以人们可能会认为即使是无意义的 row.names 也会加快计算速度
> dfns = df; rownames(dfns) = rev(seq_len(nrow(dfns)))
> system.time(split(dfns, dfns$x))
user system elapsed
4.048 0.000 4.048
> system.time(split(df, df$x))
user system elapsed
87.772 16.312 104.100
拆分向量或矩阵也会很快。
这不是严格意义上的 split.data.frame
问题,对于许多组来说 data.frame 的可扩展性存在更普遍的问题。
如果您使用 split.data.table
,您可以获得相当不错的加速。我在常规 data.table 方法之上开发了这个方法,它似乎在这里扩展得很好。
system.time(
l1 <- df %>% split(.$x)
)
# user system elapsed
#200.936 0.000 217.496
library(data.table)
dt = as.data.table(df)
system.time(
l2 <- split(dt, by="x")
)
# user system elapsed
# 7.372 0.000 6.875
system.time(
l3 <- split(dt, by="x", sorted=TRUE)
)
# user system elapsed
# 9.068 0.000 8.200
sorted=TRUE
将 return 与 data.frame 方法相同顺序的列表,默认情况下 data.table 方法将保留输入数据中存在的顺序。如果你想坚持 data.frame,你可以在最后使用 lapply(l2, setDF)
。
PS。 split.data.table
1.9.7加入,开发版安装非常简单
install.packages("data.table", type="source", repos="http://Rdatatable.github.io/data.table")
中有更多相关信息
利用 dplyr 0.8.3 或更高版本的 group_split
的一个非常好的作弊:
random_df <- tibble(colA= paste("A",1:1200000,sep = "_"),
colB= as.character(paste("A",1:1200000,sep = "_")),
colC= 1:1200000)
random_df_list <- split(random_df, random_df$colC)
random_df_list <- random_df %>% group_split(colC)
将几分钟的操作减少到几秒钟!