R-将大型数据帧并行拆分为列表

R-将大型数据帧并行拆分为列表,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

我有一个大的事务数据集(大约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(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")
}