簡體   English   中英

將data.table拆分成大致相等的部分

[英]Split data.table into roughly equal parts

為了並行化一個任務,我需要將一個大的data.table拆分為大致相等的部分,將一個組保存在一起,即一個列,即id 假設:

N是數據的長度

kid的不同值的數量

M是所需部件的數量

這個想法是M << k << N,所以按id並不好。

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)
t(dt$id)

#     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
# [1,] "a"  "b"  "b"  "b"  "b"  "c"  "c"  "c"  "d"  "d"   "d"   "e"   "e"   "f"   "f"   "f"  

在此示例中, M=3的所需拆分為{{a,b}, {c,d}, {e,f}} ,而M=4{{a,b}, {c}, {d,e}, {f}}

更一般地說,如果id是數字,則截止點應該是
quantile(id, probs=seq(0, 1, length.out = M+1), type=1)或某些類似的分割成大致相等的部分。

有效的方法是什么?

如果id的分布沒有病態偏差,那么最簡單的方法就是這樣:

split(dt, as.numeric(as.factor(dt$id)) %% M)

它使用因子值 mod 數量的桶為桶分配id

對於大多數應用來說,獲得相對均衡的數據分布就足夠了。 你應該小心輸入像時間序列。 在這種情況下,您可以在創建因子時簡單地強制執行級別的隨機順序。 為M選擇素數是一種更穩健的方法,但很可能不太實用。

初步評論

我建議閱讀data.table的主要作者必須說的與它並行化的內容。

我不知道你對data.table有多熟悉,但你可能忽略了它by論點......? 從下面引用@ eddi的評論......

而不是按字面意思拆分數據 - 創建一個新的“parallel.id”列,然后調用

 dt[, parallel_operation(.SD), by = parallel.id] 

答案,假設你不希望使用by

按大小對ID進行排序:

ids   <- names(sort(table(dt$id)))
n     <- length(ids)

重新排列,以便我們按照Arun的交錯技巧在大小ID之間交替

alt_ids <- c(ids, rev(ids))[order(c(1:n, 1:n))][1:n]

按順序拆分ID,每組中的ID數量大致相同(如zero323的答案 ):

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

雖然我在頂部強調了data.table ,但這也適用於data.frame

如果k足夠大,您可以使用此想法將數據拆分為組:

首先,讓我們找出每個ID的大小

group_sizes <- dt[, .N, by = id]

然后創建2個長度為M的空列表,用於檢測組的大小以及它們將包含的ID

grps_vals <- list()
grps_vals[1 : M] <- c(0)

grps_nms <- list()
grps_nms[1 : M] <- c(0)

(這里我特意添加零值以便能夠創建大小為M的列表)

然后在每次迭代時使用循環將值添加到最小的組。 它將使團體大致相等

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"

只是使用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

PS:我根據之前的答案學到了很多有用的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM