R-将大型数据帧并行拆分为列表
我有一个大的事务数据集(大约500万行),我需要按ID(大约100万个唯一ID)拆分所有事务。预期结果将是列表中项目的唯一ID 我尝试了最简单和直接的方法来分割事务数据集(通过引用),我知道将dataframe转换为datatable可能更有效 样本源dfR-将大型数据帧并行拆分为列表,r,list,split,parallel-processing,R,List,Split,Parallel Processing,我有一个大的事务数据集(大约500万行),我需要按ID(大约100万个唯一ID)拆分所有事务。预期结果将是列表中项目的唯一ID 我尝试了最简单和直接的方法来分割事务数据集(通过引用),我知道将dataframe转换为datatable可能更有效 样本源df set.seed(123) n = 500000 #number of sample data (500k as trial) x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round
set.seed(123)
n = 500000 #number of sample data (500k as trial)
x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round(n/3), n, replace = TRUE),sep = ""),
Item= sample(c('apple','orange','lemon','tea','rice'), n, replace=TRUE)
)
问题:运行2小时后,在我的机器(4核、16Gb RAM、Win10、R 3.4.3)上,它仍在运行,从未完成。我确实在运行时检查了我的CPU使用情况,它只消耗了35-40%的CPU使用率
我的想法:
我在想有没有办法充分利用我的机器的计算能力(并行运行“拆分”),只使用detectCores()-1=3个核
1st:按ID将大型事务数据集拆分为3个较小的分区(较小的数据集)
2nd:使用foreach循环将3个分区(较小的数据集)并行运行到列表中,然后为每次迭代追加(行绑定)每个列表,直到结束
问题:我的想法可行吗?我确实读过关于
mclappy
的内容,它是mc.cores
,但似乎mc.cores=1
是windows的唯一选项,因此对我的情况没有帮助。有没有更好更有效的方法来分割大数据集?欢迎评论,谢谢令人惊讶和有趣的是,考虑<>代码> <代码>(面向对象的包装器>代码> TestEng/Cuth>),它在数据框上类似于<代码>拆分<代码>,具有添加的功能,以将分裂运行成函数调用。与split
等效的方法是返回参数或调用identity
by(x$Item, x$ID, function(x) x)
by(x$Item, x$ID, identity)
请注意,by
的返回是一个by
类对象,它本质上是一个具有附加属性的列表
使用您的随机数据帧示例,base::split
在1小时后没有完成,但是base::by
在我的带有64 GB RAM的机器上的运行时间远低于5分钟!通常,我认为by
作为apply家族的兄弟会有更多的开销,但我的观点可能很快就会改变
50K行示例
set.seed(123)
n = 50000 #number of sample data (50k as trial)
x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round(n/3), n, replace = TRUE),sep = ""),
Item= sample(c('apple','orange','lemon','tea','rice'), n, replace=TRUE)
)
system.time( xx <- split(x$Item, x$ID) )
# user system elapsed
# 20.09 0.00 20.09
system.time( xx2 <- by(x$Item, x$ID, identity) )
# user system elapsed
# 1.55 0.00 1.55
all.equal(unlist(xx), unlist(xx2))
# [1] TRUE
identical(unlist(xx), unlist(xx2))
# [1] TRUE
set.seed(123)
n = 500000 #number of sample data (500k as trial)
x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round(n/3), n, replace = TRUE),sep = ""),
Item= sample(c('apple','orange','lemon','tea','rice'), n, replace=TRUE)
)
system.time( xx <- split(x$Item, x$ID) )
# DID NOT FINISH AFTER 1 HOUR
system.time( xx2 <- by(x$Item, x$ID, identity) )
# user system elapsed
# 23.00 0.06 23.09
相反地,by.data.frame
的源代码显示了对tapply
的调用,该调用本身是对lappy
的包装:
getAnywhere(by.data.frame)
function (data, INDICES, FUN, ..., simplify = TRUE)
{
if (!is.list(INDICES)) {
IND <- vector("list", 1L)
IND[[1L]] <- INDICES
names(IND) <- deparse(substitute(INDICES))[1L]
}
else IND <- INDICES
FUNx <- function(x) FUN(data[x, , drop = FALSE], ...)
nd <- nrow(data)
structure(eval(substitute(tapply(seq_len(nd), IND, FUNx,
simplify = simplify)), data), call = match.call(), class = "by")
}
getAnywhere(by.data.frame)
函数(数据、索引、乐趣等,simplify=TRUE)
{
如果(!is.列表(索引)){
IND这些因素似乎是这里的关键。我没有64GB的RAM。使用并行机制拆分内存中的对象的一个问题是大多数(不是全部)这些机制中的一个期望它已经在一个列表中。您可以将它们均匀地分割,并将每个进程按ID
进行分割,然后您需要将各个ID的列表连接起来。分割的目的是什么?您能不能不处理整个数据帧?Hi@Onyambu我的最终目标是生成列表,然后将列表转换为事务对象basket在这种情况下,Windows中并行的问题也是,它需要将数据复制到每个并行进程,这可能比不带因子的拆分需要更长的时间。嗨@Chase Clark,感谢您提供了另一个关于细节比较的深刻见解:)不带因子的拆分被证明具有更好的性能性能!您好@Parfait感谢您通过比较split
和by
提供了非常好的洞察力。我确实尝试使用xx2@yc.koong来拆分500万行。只需使用uclass(results)
。这应该会给您一个列表,以防您有by
object@Onyambu我找不到任何函数名uclass()
你的意思是取消类(结果)
?@yc.koong如果它不工作,就强行将它强制到类列表。即as(results,'list')
将它强制到类列表
@Onyambu通过使用as(results,'list')
将对象转换为列表
对象,谢谢!
set.seed(123)
n = 50000 #number of sample data (50k as trial)
x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round(n/3), n, replace = TRUE),sep = ""),
Item= sample(c('apple','orange','lemon','tea','rice'), n, replace=TRUE)
)
system.time( xx <- split(x$Item, x$ID) )
# user system elapsed
# 20.09 0.00 20.09
system.time( xx2 <- by(x$Item, x$ID, identity) )
# user system elapsed
# 1.55 0.00 1.55
all.equal(unlist(xx), unlist(xx2))
# [1] TRUE
identical(unlist(xx), unlist(xx2))
# [1] TRUE
set.seed(123)
n = 500000 #number of sample data (500k as trial)
x <- data.frame(ID = paste(LETTERS[1:8],sample(1:round(n/3), n, replace = TRUE),sep = ""),
Item= sample(c('apple','orange','lemon','tea','rice'), n, replace=TRUE)
)
system.time( xx <- split(x$Item, x$ID) )
# DID NOT FINISH AFTER 1 HOUR
system.time( xx2 <- by(x$Item, x$ID, identity) )
# user system elapsed
# 23.00 0.06 23.09
getAnywhere(split.data.frame)
function (x, f, drop = FALSE, sep = ".", lex.order = FALSE, ...)
{
if (!missing(...))
.NotYetUsed(deparse(...), error = FALSE)
if (is.list(f))
f <- interaction(f, drop = drop, sep = sep, lex.order = lex.order)
else if (!is.factor(f))
f <- as.factor(f)
else if (drop)
f <- factor(f)
storage.mode(f) <- "integer"
if (is.null(attr(x, "class")))
return(.Internal(split(x, f)))
lf <- levels(f)
y <- vector("list", length(lf))
names(y) <- lf
ind <- .Internal(split(seq_along(x), f))
for (k in lf) y[[k]] <- x[ind[[k]]]
y
}
getAnywhere(by.data.frame)
function (data, INDICES, FUN, ..., simplify = TRUE)
{
if (!is.list(INDICES)) {
IND <- vector("list", 1L)
IND[[1L]] <- INDICES
names(IND) <- deparse(substitute(INDICES))[1L]
}
else IND <- INDICES
FUNx <- function(x) FUN(data[x, , drop = FALSE], ...)
nd <- nrow(data)
structure(eval(substitute(tapply(seq_len(nd), IND, FUNx,
simplify = simplify)), data), call = match.call(), class = "by")
}