R 将data.table拆分为大致相等的部分
要并行化任务,我需要将一个大数据表拆分为大致相等的部分, 将由列R 将data.table拆分为大致相等的部分,r,parallel-processing,data.table,R,Parallel Processing,Data.table,要并行化任务,我需要将一个大数据表拆分为大致相等的部分, 将由列id定义的组放在一起。假设: N是数据的长度 k是id M是所需零件的数量 其想法是M如果ID的分布没有病理性偏斜,最简单的方法就是这样: split(dt, as.numeric(as.factor(dt$id)) %% M) 它使用因子值mod number of bucket将id分配给bucket 对于大多数应用程序来说,它只足以获得相对平衡的数据分布。不过,您应该注意时间序列之类的输入。在这种情况下,您可以在创建因子时简
id
定义的组放在一起。假设:
N
是数据的长度
k
是id
M
是所需零件的数量
其想法是M如果ID的分布没有病理性偏斜,最简单的方法就是这样:
split(dt, as.numeric(as.factor(dt$id)) %% M)
它使用因子值mod number of bucket将id
分配给bucket
对于大多数应用程序来说,它只足以获得相对平衡的数据分布。不过,您应该注意时间序列之类的输入。在这种情况下,您可以在创建因子时简单地强制执行随机级别顺序。为M选择一个素数是一种更稳健的方法,但很可能不太实用 初步评论 我建议阅读关于并行化的内容 我不知道您对data.table有多熟悉,但您可能通过参数忽略了它的
。。。?从下面引用@eddi的评论
创建一个新的“parallel.id”列,然后调用
dt[, parallel_operation(.SD), by = parallel.id]
回答,假设您不想使用by
按大小对ID进行排序:
ids <- names(sort(table(dt$id)))
n <- length(ids)
尽管我在顶部强调了data.table
,但这也适用于data.frame
。如果k足够大,您可以使用此想法将数据分成组:
首先,让我们查找每个ID的大小
group_sizes <- dt[, .N, by = id]
组大小只是使用dplyr的一种替代方法。逐步运行链接脚本,以可视化数据集在每个步骤中的变化。这是一个简单的过程
library(data.table)
library(dplyr)
set.seed(1)
N <- 16 # in application N is very large
k <- 6 # in application k << N
dt <- data.table(id = sample(letters[1:k], N, replace=T), value=runif(N)) %>%
arrange(id)
dt %>%
select(id) %>%
distinct() %>% # select distinct id values
mutate(group = ntile(id,3)) %>% # create grouping
inner_join(dt, by="id") # join back initial information
库(data.table)
图书馆(dplyr)
种子(1)
N%#选择不同的id值
mutate(group=ntile(id,3))%>%#创建分组
内部连接(dt,by=“id”)#连接回初始信息
PS:根据前面的答案,我学到了很多有用的东西。太好了,这就是我要找的for@selig我认为答案要好得多,更不用说数据表了。问题不在于数据拆分的速度,而在于需要拆分为M个核。@selig我同意。我引用的部分(对我)表明,在这个问题的上下文中,您认为效率是一个问题(据我所知,这只是关于分割数据)。与其逐字分割数据,不如创建一个新的“parallel.id”列,然后调用dt[,parallel_operation(.SD),by=parallel.id]
@eddi好的,我已将您的评论复制到答案中。我想您可能希望对组大小进行排序,以便首先添加最大的组。如果最后添加最大的,则大小可能会非常不平衡。进行排序也是一个好主意!另一方面,我在每次迭代中找到最小的组。给出一个事实,你把M的数量加到最小的组中,但你加到它的不一定是小的。假设M=2,组大小为(2,2,4)。因为您直接在组大小上循环,所以随着循环的进行,分区大小将依次为{2,0}、{2,2}、{6,2}。但是,如果您首先将组大小排序为(4,2,2),则将有{4,0}、{4,2}、{4,4},这会更好。我不确定我是否在这里说清楚了…是的,我知道你的想法。那会更好!谢谢,我认为这与zero的答案基本相同;它实际上忽略了N
(当您使用distinct
时)。我假设(仅通过查看a、b、c……示例)我们需要分组的ID具有某种顺序。如果没有,那么当我得到不同的ID时,我可以将它们洗牌,以防前2-3个ID有大量的观察结果,并使用洗牌版本进行分组。如果需要,我可以更新我的答案。此外,我认为目标不是通过将ID分发给组来分割数据集,而是在其中一个组中保留相同的ID。正如他提到的:“M=3的理想分割是{a,b},{c,d},{e,f},而M=4的理想分割是{a,b},{c},{d,e},{f}”。我只在一个组中看到每个ID。我遗漏了什么吗?是的,重点是将ID划分为多个组;所有的答案都是这样的,我的评论没有反驳,所以我真的不知道你在说什么。我不是要你更新它;你可以自己决定。只是指出这和zero的回答中的操作大致相同。不,我没有说你让我做进一步的操作。我只是一时糊涂。
gs <- split(alt_ids, ceiling(seq(n) / (n/M)))
res <- vector("list", M)
setkey(dt, id)
for (m in 1:M) res[[m]] <- dt[J(gs[[m]])]
# if using a data.frame, replace the last two lines with
# for (m in 1:M) res[[m]] <- dt[id %in% gs[[m]],]
# using the OP's example data...
sapply(res, nrow)
# [1] 7 9 for M = 2
# [1] 5 5 6 for M = 3
# [1] 1 6 3 6 for M = 4
# [1] 1 4 2 3 6 for M = 5
group_sizes <- dt[, .N, by = id]
grps_vals <- list()
grps_vals[1 : M] <- c(0)
grps_nms <- list()
grps_nms[1 : M] <- c(0)
for ( i in 1:nrow(group_sizes)){
sums <- sapply(groups, sum)
idx <- which(sums == min(sums))[1]
groups[[idx]] <- c(groups[[idx]], group_sizes$N[i])
}
grps_nms <- lapply(grps_nms, function(x){x[-1]})
> grps_nms
[[1]]
[1] "a" "d" "f"
[[2]]
[1] "b"
[[3]]
[1] "c" "e"
library(data.table)
library(dplyr)
set.seed(1)
N <- 16 # in application N is very large
k <- 6 # in application k << N
dt <- data.table(id = sample(letters[1:k], N, replace=T), value=runif(N)) %>%
arrange(id)
dt %>%
select(id) %>%
distinct() %>% # select distinct id values
mutate(group = ntile(id,3)) %>% # create grouping
inner_join(dt, by="id") # join back initial information