【问题标题】:What is the least memory demanding methods to do these steps?执行这些步骤的内存要求最低的方法是什么?
【发布时间】:2011-09-28 10:51:19
【问题描述】:

我发了a question yesterday,得到了专家们的精彩回应。但是,我现在面临另一个问题,因为我的起始文件(df1)太大,我发现无法在我的真实数据中完成这些工作。我想知道是否有更快的方法可以在不使用 adply 或 for 循环的情况下完成相同的工作?

我原来的问题如下:

第 1 步:我有一个这样的简化数据框:

df1 = data.frame (B=c(1,0,1), C=c(1,1,0)
  , D=c(1,0,1), E=c(1,1,0), F=c(0,0,1)
  , G=c(0,1,0), H=c(0,0,1), I=c(0,1,0))

  B C D E F G H I
1 1 1 1 1 0 0 0 0
2 0 1 0 1 0 1 0 1
3 1 0 1 0 1 0 1 0

第 2 步:我想做逐行减法,即 (row1 - row2)、(row1 - row3) 和 (row2 - row3)

row1-row2    1  0    1  0    0  -1   0  -1
row1-row3    0  1    0  1   -1   0  -1   0
row2-row3   -1  1   -1  1   -1   1  -1   1

第 3 步:将所有 -1 替换为 0

row1-row2   1   0   1   0   0   0   0   0
row1-row3   0   1   0   1   0   0   0   0
row2-row3   0   1   0   1   0   1   0   1

您能不能教我如何以较少占用内存的方式做到这一点?

【问题讨论】:

  • 另外,您的数据是否总是二进制的,即 0 和 1?
  • 这个(以及相关的 Q)与您几周前提出的目标是乘法而不是减法的 Q 有何不同? stackoverflow.com/questions/7297505/…
  • 如果您使用内存映射文件,通过像 bigmemory 这样的包,您可以使用非常少的 RAM 并拥有非常大的数据集 - 您可以在 HD 空间中容纳的所有内容。
  • 我在@Andrie 之后重复:你的data.frame 中有多少行和多少列?简单的dim(df1) 输出。

标签: r memory


【解决方案1】:

我知道执行第 2 步的最快方法是使用 df1 中的索引来进行您想要进行的各种成对比较。 combn() 函数可用于生成所需的逐行比较集。 (使用这将是 big 数据集的速率限制步骤。)

对于我们想要形成的逐行操作的组合:

> cmb <- combn(as.numeric(rownames(df1)), 2)
> cmb
     [,1] [,2] [,3]
[1,]    1    1    2
[2,]    2    3    3

cmb 的行代表df1 所需的两组索引,这些索引需要形成您请求的输出的三行。 (第 3 列代表预期结果中的 3 行。)

下一步是使用cmb 的两行索引df1 并通过- 在R 中使用标准向量化操作,例如:

> (out <- df1[cmb[1,], ] - df1[cmb[2,], ])
     B C  D E  F  G  H  I
1    1 0  1 0  0 -1  0 -1
1.1  0 1  0 1 -1  0 -1  0
2   -1 1 -1 1 -1  1 -1  1

现在可以完成第 3 步,尽管我假设结果输出中只能有 10-1 值:

> out[out < 0] <- 0
> out
    B C D E F G H I
1   1 0 1 0 0 0 0 0
1.1 0 1 0 1 0 0 0 0
2   0 1 0 1 0 1 0 1

这与您要求的输出一致。

对于大型运算,使用矩阵执行此操作总体上可能更快。所以我们可以这样做:

> mat <- data.matrix(df1)
> cmb <- combn(seq_len(NROW(mat)), 2)
> cmb
     [,1] [,2] [,3]
[1,]    1    1    2
[2,]    2    3    3
> out2 <- mat[cmb[1,], ] - mat[cmb[2,], ]
> out2[out2 < 0] <- 0
> out2
     B C D E F G H I
[1,] 1 0 1 0 0 0 0 0
[2,] 0 1 0 1 0 0 0 0
[3,] 0 1 0 1 0 1 0 1

如果您需要显示的行名,那么您可以在最后轻松生成这些:

> apply(cmb, 2, function(x) paste("row", x[1], "-row", x[2], sep = ""))
[1] "row1-row2" "row1-row3" "row2-row3"

可以用作:

> rownames(out) <- apply(cmb, 2, function(x) paste("row", x[1], "-row", x[2], sep = ""))
> out
          B C D E F G H I
row1-row2 1 0 1 0 0 0 0 0
row1-row3 0 1 0 1 0 0 0 0
row2-row3 0 1 0 1 0 1 0 1

【讨论】:

    【解决方案2】:

    直接使用 sqldf 包或 RSQLite 可以让所有计算在 R 之外完成,这样就不需要中间存储。我们使用 sqldf 进行说明。请参阅sqldf home page 了解更多信息。

    替代方案 1 在此方法中,请注意我们使用dbname = tempfile(),以便它在外部数据库(它动态创建并自动删除)中执行所有计算,而不是在内存中执行。

    library(sqldf)
    gc()
    DF <- sqldf("select x.rowid x, y.rowid y,
        max(x.B - y.B, 0) B, max(x.C - y.C, 0) C, 
        max(x.D - y.D, 0) D, max(x.E - y.E, 0) E,
        max(x.F - y.F, 0) F, max(x.G - y.G, 0) G, 
        max(x.H - y.H, 0) H, max(x.I - y.I, 0) I
        from df1 x, df1 y
        where x.rowid > y.rowid", dbname = tempfile())
    

    这只要求我们能够在工作区中存储df1DF

    备选方案 2。如果即使溢出,我们也可以写出df1,删除它,执行下面的计算,然后我们只需要足够的存储空间来存储结果,DF

    read.csv.sql 默认使用dbname = tempfile(),所以在这种情况下我们不需要指定它。

    write.table(df1, "data.txt", sep = ",", quote = FALSE)
    rm(df1)
    gc()
    DF <- read.csv.sql("data.txt", sql = "select
        x.rowid x, y.rowid y, 
        max(x.B - y.B, 0) B, max(x.C - y.C, 0) C, 
        max(x.D - y.D, 0) D, max(x.E - y.E, 0) E,
        max(x.F - y.F, 0) F, max(x.G - y.G, 0) G, 
        max(x.H - y.H, 0) H, max(x.I - y.I, 0) I
        from file x, file y
        where x.rowid > y.rowid")
    

    (当然,如果它真的这么大,那么您可能也无法对其进行任何后续计算。)

    输出。无论如何,两种备选方案都给出了如下所示的相同结果。 x 和 y 显示减去了哪些输入行。

    > DF
      x y B C D E F G H I
    1 2 1 0 0 0 0 0 1 0 1
    2 3 1 0 0 0 0 1 0 1 0
    3 3 2 1 0 1 0 1 0 1 0
    

    注意。虽然这个问题要求优化内存而不是速度,但如果速度是一个问题,那么可以添加索引。

    【讨论】:

      【解决方案3】:

      由于数据是同质的,所以使用矩阵表示。组织它,使“行”是列,如

      m <- t(as.matrix(df1))
      mode(m) <- "integer"  # maybe already true?
      

      为答案预先分配空间

      n <- ncol(m) - 1
      ans <- matrix(0L, nrow(m), (n+1) * n / 2)
      

      我们想将1 列与1:n + 1L 列进行比较(1L 将数字一视为整数值,而不是实数)。这是m[,1] - m[, 1:n + 1L],使用 R 的回收。遍历列,idxoff 有助于跟踪我们要比较的列的索引,以及答案中的放置列

      off <- 0
      for (i in 1:n) {
          idx <- i:n + 1L
          ans[, off + seq_along(idx)] <- m[, i] - m[, idx]
          off <- off + length(idx)
      }
      

      最后一步是

      ans[ans<0L] <- 0L
      

      也许通过实现原始运算下的真值表为 0 会带来额外的效率,除非 m[,1] == 1 &amp; m[, 1:n + 1L] == 0。同样,如果空间是一个严重的问题,那么数据可能会表示为mode(m) &lt;- "raw",然后将算术运算替换为刚才建议的比较,如下所示:

      m <- t(as.matrix(df1))
      mode(m) <- "raw"
      
      off <- 0
      x0 <- as.raw(0); x1 <- as.raw(1)
      ans <- matrix(raw(), nrow(m), (n+1) * n / 2)
      for (i in 1:n) {
          idx <- i:n + 1L
          updt <- which((m[, i] == x1) & (m[, idx] == x0))
          ans[off + updt] <- x1
          off <- off + length(idx) * nrow(ans)
      }
      

      【讨论】:

      • 很好的答案 - 我可以建议 mode(m) &lt;- 'integer' 在顶部和 ans[ans&lt;0] &lt;- 0L 在最后保留结果 integer 并节省 50% 的内存吗? ...但是您使用raw 的想法可能会更好,尽管raw 使用起来很尴尬。
      • 感谢 Tommy,我按照您的建议更新了代码,并完成了“原始”解决方案的一个版本。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多