R data.table按组进行有效复制

时间:2013-03-24 00:59:45

标签: r data.table replicate

我遇到了一些内存分配问题,尝试使用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(),但遇到了问题,因为我想让每个复制的令牌保持相同的顺序。

2 个答案:

答案 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]