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