【问题标题】:Endless function/loop in R: Data ManagementR中的无尽功能/循环:数据管理
【发布时间】:2011-12-21 22:33:45
【问题描述】:

我正在尝试重构一个巨大的数据框(大约 12.000 个案例):在旧的数据框中,一个人是一行,大约有 250 列(例如,Person 1、test A1、testA2、testB...)我想要测试 A 的所有结果(1 - 10 A 的总体结果和该人在一列中的 24 个项目(AY),所以一个人最终有 24 列和 10 行。在项目 AY 之前还有一个固定的数据框部分开始(个人信息,如年龄、性别等),我想保持原样(fixdata)。 该函数/循环适用于 30 个案例(我提前尝试过),但对于 12.000,它仍在计算,现在将近 24 小时。任何想法为什么?

restructure <- function(data, firstcol, numcol, numsets){
    out <- data.frame(t(rep(0, (firstcol-1)+ numcol)) )
    names(out) <- names(daten[0:(firstcol+numcol-1)])
      for(i in 1:nrow(daten)){
         fixdata <- (daten[i, 1:(firstcol-1)])

          for (j in (seq(firstcol, ((firstcol-1)+ numcol* numsets), by = numcol))){
              flexdata <- daten[i, j:(j+numcol-1)]
              tmp <- cbind(fixdata, flexdata)
              names(tmp) <- names(daten[0:(firstcol+numcol-1)])
              out <- rbind(out,tmp)
          }  
      }
    out <- out[2:nrow(out),]
    return(out)
}

提前致谢!

【问题讨论】:

  • 这听起来像是一个重塑问题。查看包reshape2 中的函数melt。你的data.frame 真的没有那么大。如果melt 不能在不到一秒的时间内处理这个问题,我会感到非常惊讶。 (类似的问题经常出现在 SO 上。搜索 [r] reshape 以获得一些灵感。
  • 安德烈是正确的。一般来说,尽量不要在大数据集上使用 for 循环。同时,您可以进行如下实验:system.time(restructure([30datasets])),然后是system.time(restructure([300datasets])),等等。这至少可以让您了解现有代码需要多长时间才能处理 N 个数据集。
  • 添加一个测试用例会有所帮助...
  • @CarlWitthoft:循环不是主要问题,内存分配才是。内部循环内的cbindrbind 调用将使代码比在第一个循环外预先分配out 并通过索引分配结果时慢得多。
  • 好的,我会尝试“融化”。我以前从未使用过 reshape 包。你能告诉我在那种特定情况下我将如何使用“melt”吗?

标签: r function for-loop data-management large-data


【解决方案1】:

知道原因:您在每次迭代中 rbindout。随着 out 的增长,每次迭代需要更长的时间 - 因此,随着数据集的增加,您必须期望运行时间的线性增长超过线性增长。

所以,正如 Andrie 所说,您可以查看 melt

或者您可以使用核心 R:stack。 然后需要自己将固定部分cbind到结果中,(需要用each = n.var.cols重复固定列

第三种选择是来自arrayhelpers 包的array2df

【讨论】:

  • melt 似乎很有帮助,但是我想知道如何将数据框分解成块。假设我有 ID、TestA1、TestA2、TestB1 和 TestB2。我想要类似的东西:row 1: ID | TestA1 | TestA2row 2: ID | TestB1 | TestB2 等等。融化有可能吗?
  • melt 将数据集一路融化,cast 将其恢复为您想要的形状。
  • 我没有让它融化成块,它只会融化所有的变量……有人知道怎么做吗? (见上例)
  • 这个答案帮助我理解循环可能不适用于我的数据,所以我尝试了不同的方式:stackoverflow.com/questions/8011786/…
【解决方案2】:

我同意其他人的观点,看看reshape2plyr 包,只是想在另一个方向添加一点。特别是meltcastdcast 可能会对您有所帮助。另外,使用智能列名可能会有所帮助,例如:

As<-grep("^testA",names(yourdf))
# returns a vector with the column position of all testA1 through 10s.

此外,如果您在测试# 和测试类型上“花费”data.frame 的两个维度,那么该人显然没有任何剩余。当然,您可以通过 ID 识别它们,您可以在绘图时为其添加美感,但根据您想要做什么,您可能希望将它们存储在 list 中。因此,您最终会得到一个包含每个人的 data.frame 的人员列表。我不确定您要做什么,但仍然希望这会有所帮助。

【讨论】:

  • 也许您还想为我们创建一个最小的示例;)
【解决方案3】:

也许您没有获得用于重塑数据组件的 plyr 或其他功能。更直接和低层次的东西怎么样。如果你目前只有一行 A1、A2、A3...A10、B1-B10 等,然后从你的数据框中提取那块东西,我猜是第 11-250 列,然后就做分割你想要的形状并将它们重新组合在一起。

yDat <- data[, 11:250]
yDF <- lapply( 1:nrow(data), function(i) matrix(yDat[i,], ncol = 24) )
yDF <- do.call(rbind, y) #combine the list of matrices returned above into one
yDF <- data.frame(yDF) #get it back into a data.frame
names(yDF) <- LETTERS[1:24] #might as well name the columns

这是以所需形状获取大量数据的最快方法。 lapply 函数所做的只是为每一行添加维度属性,使它们具有您想要的形状,然后将它们作为列表返回,并与后续行一起处理。但现在它没有来自主 data.frame 的任何 ID 信息。您只需将前 10 列的每一行复制 10 次。或者您可以使用便利功能merge 来帮助解决这个问题。将前 10 行中已有的公共列作为新 data.frame 的列之一,然后将它们合并。

yInfo <- data[, 1:10]
ID <- yInfo$ID
yDF$ID <- rep( yInfo$ID, each = 10 )
newDat <- merge(yInfo, yDF)

现在你已经完成了......大多数情况下,你可能想要创建一个额外的列来命名新行

newDat$condNum <- rep(1:10, nrow(newDat)/10)

这将是非常快速运行的代码。您的 data.frame 真的没有那么大,上面的大部分内容将在几秒钟内执行。

这就是你应该如何看待 R 中的数据。并不是说没有方便的函数来处理大部分数据,而是你应该这样做以尽可能避免循环。从技术上讲,上面发生的事情只有一个循环,一开始就使用了lapply。它在那个循环中也很少(当你使用它们时它们应该是紧凑的)。您正在编写标量代码,并且在 R 中它非常非常慢......即使您在执行此操作时并没有真正滥用内存和增长数据。此外,请记住,虽然您不能总是避免某种循环,但您几乎总是可以避免嵌套循环,这是您最大的问题之一。

(阅读this 以更好地了解您在此代码中的问题...您已经在其中犯了大部分大错误)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 2019-02-09
    • 2021-12-24
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    相关资源
    最近更新 更多