我遇到了一些内存分配问题,尝试使用data.table和rep来按组复制某些数据。
以下是一些示例数据:
ob1 <- as.data.frame(cbind(c(1999),c("THE","BLACK","DOG","JUMPED","OVER","RED","FENCE"),c(4)),stringsAsFactors=FALSE)
ob2 <- as.data.frame(cbind(c(2000),c("I","WALKED","THE","BLACK","DOG"),c(3)),stringsAsFactors=FALSE)
ob3 <- as.data.frame(cbind(c(2001),c("SHE","PAINTED","THE","RED","FENCE"),c(1)),stringsAsFactors=FALSE)
ob4 <- as.data.frame(cbind(c(2002),c("THE","YELLOW","HOUSE","HAS","BLACK","DOG","AND","RED","FENCE"),c(2)),stringsAsFactors=FALSE)
sample_data <- rbind(ob1,ob2,ob3,ob4)
colnames(sample_data) <- c("yr","token","multiple")
我要做的是每年以倍数复制令牌(按当前顺序)。
以下代码有效,并为我提供了我想要的答案:
good_solution1 <- ddply(sample_data, "yr", function(x) data.frame(rep(x[,2],x[1,3])))
good_solution2 <- data.table(sample_data)[, rep(token,unique(multiple)),by = "yr"]
问题在于,当我将其扩展到40mm +行时,我会遇到两种可能解决方案的内存问题。
如果我的理解是正确的,那么这些解决方案基本上都在做一个每次都分配的rbind。
有没有人有更好的解决方案?
我查看了data()的data(),但遇到了问题,因为我想让每个复制的令牌保持相同的顺序。
答案 0 :(得分:3)
一种方法是:
require(data.table)
dt <- data.table(sample_data)
# multiple seems to be a character, convert to numeric
dt[, multiple := as.numeric(multiple)]
setkey(dt, "multiple")
dt[J(rep(unique(multiple), unique(multiple))), allow.cartesian=TRUE]
除了最后一行之外的所有内容都应该是直截了当的。最后一行在J(.)
的帮助下使用键列使用了一个子集。对于J(.)
中的每个值,相应的值与“key column”匹配,并返回匹配的子集。
也就是说,如果您执行dt[J(1)]
,您将获得multiple = 1
的子集。如果你仔细注意,通过dt[J(rep(1,2)]
给你相同的子集,但两次。请注意,传递dt[J(1,1)]
和dt[J(rep(1,2)]
之间存在差异。前者分别匹配(1,1)的值与data.table的 first-two-key-columns ,其中后者通过匹配(1和2)与< em> data.table的第一个键列。
因此,如果我们在J(.)
中将列的相同值传递2次,那么它将被复制两次。我们使用这个技巧传递1次1次,2次2次等等。这就是rep(.)
部分的作用。 rep(.)
给出1,2,2,3,3,3,4,4,4,4。
如果连接导致的行数超过max(nrow(dt), nrow(i))
(i是J(.)
内的rep向量),则必须明确使用allow.cartesian = TRUE
来执行此连接(我猜这是data.table 1.8.8)的新功能。
编辑:以下是我对“相对”大数据所做的一些基准测试。我没有看到两种方法中的内存分配都出现任何峰值。但是我还没有找到一种方法来监视R中函数内的峰值内存使用情况。我相信我已经在SO上看过这样的帖子了,但它此刻让我滑倒了。我会再回信。目前,这是一个测试数据和一些初步结果,以防任何人有兴趣/想要自己运行它。
# dummy data
set.seed(45)
yr <- 1900:2013
sz <- sample(10:50, length(yr), replace = TRUE)
token <- unlist(sapply(sz, function(x) do.call(paste0, data.frame(matrix(sample(letters, x*4, replace=T), ncol=4)))))
multiple <- rep(sample(500:5000, length(yr), replace=TRUE), sz)
DF <- data.frame(yr = rep(yr, sz),
token = token,
multiple = multiple, stringsAsFactors=FALSE)
# Arun's solution
ARUN.DT <- function(dt) {
setkey(dt, "multiple")
idx <- unique(dt$multiple)
dt[J(rep(idx,idx)), allow.cartesian=TRUE]
}
# Ricardo's solution
RICARDO.DT <- function(dt) {
setkey(dt, key="yr")
newDT <- setkey(dt[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr)], 'yr')
newDT[, tokenReps := as.character(NA)]
# Add the rep'd tokens into newDT, using recycling
newDT[, tokenReps := dt[.(y)][, token], by=list(y=yr)]
newDT
}
# create data.table
require(data.table)
DT <- data.table(DF)
# benchmark both versions
require(rbenchmark)
benchmark(res1 <- ARUN.DT(DT), res2 <- RICARDO.DT(DT), replications=10, order="elapsed")
# test replications elapsed relative user.self sys.self
# 1 res1 <- ARUN.DT(DT) 10 9.542 1.000 7.218 1.394
# 2 res2 <- RICARDO.DT(DT) 10 17.484 1.832 14.270 2.888
但正如里卡多所说,如果你的内存耗尽可能无关紧要。因此,在这种情况下,必须在速度和内存之间进行权衡。我要验证的是这两种方法中使用的峰值内存,如果使用Join
更好,最终可以说明。
答案 1 :(得分:1)
您可以先尝试为所有行分配内存,然后迭代填充它们 例如:
# make sure `sample_data$multiple` is an integer
sample_data$multiple <- as.integer(sample_data$multiple)
# create data.table
S <- data.table(sample_data, key='yr')
# optionally, drop original data.frame if not needed
rm(sample_data)
## Allocate the memory first
newDT <- data.table(yr = rep(sample_data$yr, sample_data$multiple), key="yr")
newDT[, tokenReps := as.character(NA)]
# Add the rep'd tokens into newDT, using recycling
newDT[, tokenReps := S[.(y)][, token], by=list(y=yr)]
(1)sample_data$multiple
当前是一个角色,因此在传递给rep
时会被强制(在您的原始示例中)。如果情况确实如此,那么可能值得仔细检查您的实际数据。
(2)我使用以下内容确定每年所需的行数
S[, list(rows=length(token) * unique(multiple)), by=yr]