在 foreach 循环中导出命名变量
Export named variable in foreach loop
我有一个很大的 data.table(+1200 万行)需要以这种方式转换:
将具有相同第一列值(让我们称之为 BookId)的每一行折叠成一行,并将其他列合并到一个大的 "data" 字段中。
此 table 包含 270 万个唯一的 BookId
即:
BookId Col1 Col2 ... ColN
B001 作者鲍勃……
B002 作者马克……
B002 编辑鲍勃公司……
B001 MyBooks 编辑器……
预期结果:
BookId 数据
B001 鲍勃,MyBooks,...
B002 马克,鲍勃公司,...
目前,我已经能够使用子集重现此结构,但这真的很慢,构建一行最多需要 300 毫秒,这意味着最多需要 9 天才能完成该过程。
所以我决定使用并行的 foreach 循环来加速这个过程。
我的第一个方法是遍历 bookId 列表,但它只会将全局总时间除以不令人满意的核心数(8 个核心意味着 +1 天)。此外,这意味着每个进程都将自动导出大量数据,因为它们都需要整个 data.table 对象。
我找到了另一种改进流程的方法,方法是根据 bookId 列表将主要 data.table 分成独立的子集,然后让每个集群在这些子集上工作(行数越少意味着子集生成速度越快)。
不幸的是,我无法将我的子集导出到集群,因为它们有一个 "dynamic" 名称。
我尝试了“.export”参数,但我猜它在评估时不知道当前的 "i" 值。
我怎样才能做到这一点?有可能吗?
我是 R 的新手,有人告诉我总是有很多方法可以实现同一件事,我是否选择了最佳方法来实现这一点?
这是我的代码:
# Create cluster based on available cores
cores = detectCores()
cl <- makeCluster(cores)
registerDoParallel(cl)
# Load datas and generate BookId lists
books <- fread("books.tab")
bookId.unique.list <- unique(books$BookId)
bookId.list <- books$BookId
# Split datatable into "equals" subsets
subset.length = ceiling(length(book.unique.list)/cores)
for (i in 1:(cores)) {
start = (i-1)*subset.length
end = (i)*subset.length
list = book.unique.list[start:end]
assign(paste("books",i,sep=""), books[books$BookId %in% list])
assign(paste("book.list",i,sep=""), list )
}
# Prepare resulting DT
res = data.table(BookId = character(0), data = character(0))
# Parallel loop
res <- foreach(i = 1:cores, .combine = rbind, .export = paste0("book", i), .packages = c("data.table")) %dopar% {
#Try to get the named subset corresponding to the current iteration (i)
# IE : Books1, Books2...
BookSubset = get(paste0("book", i))
Book.list.subset = unique(BookSubset$BookId)
temp = data.table(BookId = character(0), data = character(0))
for (i in 1:length(Book.list.subset)) {
bookId = Book.list.subset[i]
subset <- BookSubset[which(Book.list.subset ==bookId)]
output = capture.output(write.table(subset, stdout()quote=FALSE, row.names=FALSE,col.names=FALSE)
temp <- rbind(hist, data.table(zkf_BOOK = c(bookId), data = c(output)))
}
temp
}
这是 dput[head(books))
的结果:
structure(list(BookId = c("BOOKXXXX774051532082", "BOOKXXXX776514515608",
"BOOKXXXX776287821289", "BOOKXXXX776514515608", "BOOKXXXX774051532082",
"BOOKXXXX774051532082"), V2 = c("ZUSRXXXX842901236553",
"ZUSRXXXX371255229634",
"ZUSRXXXX656080986411", "ZUSRXXXX371255229634", "ZUSRXXXX842901236553",
"ZUSRXXXX842901236553"), V3 = c("BOOKEVTX776757835463",
"BOOKEVTX776762775464",
"BOOKEVTX776772854465", "BOOKEVTX776773643466", "", "BOOKEVTX776995487467"
), V4 = c("ZACTIONX215229995154", "ZACTIONX533300043134",
"ZACTIONX533300043134",
"ZACTIONX533300043134", "", "ZACTIONX215229995154"), V5 = c("",
"", "", "", "", ""), V6 = c("", "", "", "", "MAILOUTX776774376684",
""), V7 = c("", "", "", "", "", ""), V8 = c("", "", "", "", "",
""), V9 = c("", "", "", "", "", ""), V10 = c("", "", "", "",
"", ""), V11 = c("", "", "", "", "", "")), .Names = c("zkf_BOOK",
"V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11"), class =
c("data.table",
"data.frame"), row.names = c(NA, -6L))
这是我的 "real" 数据输入的示例:
BOOKXXXX774051532082 ZUSRXXXX842901236553 BOOKEVTX776757835463 ZACTIONX215229995154
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776762775464 ZACTIONX533300043134
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX776772854465 ZACTIONX533300043134
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776773643466 ZACTIONX533300043134
BOOKXXXX774051532082 ZUSRXXXX842901236553 MAILOUTX776774376684
BOOKXXXX774051532082 ZUSRXXXX842901236553 BOOKEVTX776995487467 ZACTIONX215229995154
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX777107387468 ZACTIONX533300043134
和预期输出
BOOKXXXX774051532082 ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154|||||||
BOOKXXXX776514515608 ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134|||||||
BOOKXXXX776287821289 ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||;ZUSRXXXX656080986411|BOOKEVTX777107387468|ZACTIONX533300043134|||||||
OP 请求了两次折叠操作:
- 对于每一行,将所有列(id 列
zkf_BOOK
除外)折叠到一个数据字段中,由 |
分隔。
- 对于每个
zkf_BOOK
组,折叠由 ;
分隔的行
列内折叠是通过调用 Reduce()
完成的,而跨行折叠是使用 paste()
分组完成的。对于 data.table
,by =
参数中的列不包含在对 .SD
的操作中。
library(data.table)
setDT(books)[, paste(Reduce(function(x, y) paste(x, y, sep = "|"), .SD), collapse = ";"),
by = zkf_BOOK]
zkf_BOOK
1: BOOKXXXX774051532082
2: BOOKXXXX776514515608
3: BOOKXXXX776287821289
V1
1: ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154|||||||
2: ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134|||||||
3: ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||
请注意,与预期结果的差异是由于 dput[head(books))
仅返回 6 行,而打印数据输入和预期输出基于 7 行(或更多)。
我有一个很大的 data.table(+1200 万行)需要以这种方式转换:
将具有相同第一列值(让我们称之为 BookId)的每一行折叠成一行,并将其他列合并到一个大的 "data" 字段中。
此 table 包含 270 万个唯一的 BookId
即:
BookId Col1 Col2 ... ColN B001 作者鲍勃…… B002 作者马克…… B002 编辑鲍勃公司…… B001 MyBooks 编辑器……
预期结果:
BookId 数据 B001 鲍勃,MyBooks,... B002 马克,鲍勃公司,...
目前,我已经能够使用子集重现此结构,但这真的很慢,构建一行最多需要 300 毫秒,这意味着最多需要 9 天才能完成该过程。
所以我决定使用并行的 foreach 循环来加速这个过程。
我的第一个方法是遍历 bookId 列表,但它只会将全局总时间除以不令人满意的核心数(8 个核心意味着 +1 天)。此外,这意味着每个进程都将自动导出大量数据,因为它们都需要整个 data.table 对象。
我找到了另一种改进流程的方法,方法是根据 bookId 列表将主要 data.table 分成独立的子集,然后让每个集群在这些子集上工作(行数越少意味着子集生成速度越快)。 不幸的是,我无法将我的子集导出到集群,因为它们有一个 "dynamic" 名称。 我尝试了“.export”参数,但我猜它在评估时不知道当前的 "i" 值。 我怎样才能做到这一点?有可能吗?
我是 R 的新手,有人告诉我总是有很多方法可以实现同一件事,我是否选择了最佳方法来实现这一点?
这是我的代码:
# Create cluster based on available cores
cores = detectCores()
cl <- makeCluster(cores)
registerDoParallel(cl)
# Load datas and generate BookId lists
books <- fread("books.tab")
bookId.unique.list <- unique(books$BookId)
bookId.list <- books$BookId
# Split datatable into "equals" subsets
subset.length = ceiling(length(book.unique.list)/cores)
for (i in 1:(cores)) {
start = (i-1)*subset.length
end = (i)*subset.length
list = book.unique.list[start:end]
assign(paste("books",i,sep=""), books[books$BookId %in% list])
assign(paste("book.list",i,sep=""), list )
}
# Prepare resulting DT
res = data.table(BookId = character(0), data = character(0))
# Parallel loop
res <- foreach(i = 1:cores, .combine = rbind, .export = paste0("book", i), .packages = c("data.table")) %dopar% {
#Try to get the named subset corresponding to the current iteration (i)
# IE : Books1, Books2...
BookSubset = get(paste0("book", i))
Book.list.subset = unique(BookSubset$BookId)
temp = data.table(BookId = character(0), data = character(0))
for (i in 1:length(Book.list.subset)) {
bookId = Book.list.subset[i]
subset <- BookSubset[which(Book.list.subset ==bookId)]
output = capture.output(write.table(subset, stdout()quote=FALSE, row.names=FALSE,col.names=FALSE)
temp <- rbind(hist, data.table(zkf_BOOK = c(bookId), data = c(output)))
}
temp
}
这是 dput[head(books))
的结果:
structure(list(BookId = c("BOOKXXXX774051532082", "BOOKXXXX776514515608",
"BOOKXXXX776287821289", "BOOKXXXX776514515608", "BOOKXXXX774051532082",
"BOOKXXXX774051532082"), V2 = c("ZUSRXXXX842901236553",
"ZUSRXXXX371255229634",
"ZUSRXXXX656080986411", "ZUSRXXXX371255229634", "ZUSRXXXX842901236553",
"ZUSRXXXX842901236553"), V3 = c("BOOKEVTX776757835463",
"BOOKEVTX776762775464",
"BOOKEVTX776772854465", "BOOKEVTX776773643466", "", "BOOKEVTX776995487467"
), V4 = c("ZACTIONX215229995154", "ZACTIONX533300043134",
"ZACTIONX533300043134",
"ZACTIONX533300043134", "", "ZACTIONX215229995154"), V5 = c("",
"", "", "", "", ""), V6 = c("", "", "", "", "MAILOUTX776774376684",
""), V7 = c("", "", "", "", "", ""), V8 = c("", "", "", "", "",
""), V9 = c("", "", "", "", "", ""), V10 = c("", "", "", "",
"", ""), V11 = c("", "", "", "", "", "")), .Names = c("zkf_BOOK",
"V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11"), class =
c("data.table",
"data.frame"), row.names = c(NA, -6L))
这是我的 "real" 数据输入的示例:
BOOKXXXX774051532082 ZUSRXXXX842901236553 BOOKEVTX776757835463 ZACTIONX215229995154
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776762775464 ZACTIONX533300043134
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX776772854465 ZACTIONX533300043134
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776773643466 ZACTIONX533300043134
BOOKXXXX774051532082 ZUSRXXXX842901236553 MAILOUTX776774376684
BOOKXXXX774051532082 ZUSRXXXX842901236553 BOOKEVTX776995487467 ZACTIONX215229995154
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX777107387468 ZACTIONX533300043134
和预期输出
BOOKXXXX774051532082 ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154|||||||
BOOKXXXX776514515608 ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134|||||||
BOOKXXXX776287821289 ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||;ZUSRXXXX656080986411|BOOKEVTX777107387468|ZACTIONX533300043134|||||||
OP 请求了两次折叠操作:
- 对于每一行,将所有列(id 列
zkf_BOOK
除外)折叠到一个数据字段中,由|
分隔。 - 对于每个
zkf_BOOK
组,折叠由;
分隔的行
列内折叠是通过调用 Reduce()
完成的,而跨行折叠是使用 paste()
分组完成的。对于 data.table
,by =
参数中的列不包含在对 .SD
的操作中。
library(data.table)
setDT(books)[, paste(Reduce(function(x, y) paste(x, y, sep = "|"), .SD), collapse = ";"),
by = zkf_BOOK]
zkf_BOOK 1: BOOKXXXX774051532082 2: BOOKXXXX776514515608 3: BOOKXXXX776287821289 V1 1: ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154||||||| 2: ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134||||||| 3: ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||
请注意,与预期结果的差异是由于 dput[head(books))
仅返回 6 行,而打印数据输入和预期输出基于 7 行(或更多)。