【问题标题】:Faster solution to looped grouped RLE calculation循环分组 RLE 计算的更快解决方案
【发布时间】:2012-10-18 23:16:31
【问题描述】:

我有一个working solution 来解决我的问题,但我无法使用它,因为它太慢了(我的计算预测整个模拟需要 2-3 年!)。因此,我正在寻找更好(更快)的解决方案。这是(本质上)我正在使用的代码:

N=4
x <-NULL
for (i in 1:N) { #first loop
  v <-sample(0:1, 1000000, 1/2) #generate data
  v <-as.data.frame(v) #convert to dataframe
  v$t <-rep(1:2, each=250) #group
  v$p <-rep(1:2000, each=500) #p.number
  # second loop
  for (j in 1:2000) { #second loop
    #count rle for group 1 for each pnumber
    x <- rbind(x, table(rle(v$v[v$t==1&v$p==j])))
    #count rle for group 2 for each pnumber
    x <- rbind(x, table(rle(v$v[v$t==2&v$p==j])))
  } #end second loop
} #end first loop
#total rle counts for both group 1 & 2
y <-aggregate(x, list(as.numeric(rownames(x))), sum)

简而言之:代码生成抛硬币模拟 (v)。生成一个组因子(1 和 2)。生成一个 p.number 因子 (1:2000)。记录第 1 组和第 2 组的每个 p.number (1:2000) 的运行长度(每个 p.number 在两个组中都有运行)。在N 循环(第一个循环)之后,总运行长度显示为一个表(聚合)(即,每个组的运行长度,对于每个 p.number,超过N 循环的总和)。

我需要第一个循环,因为我正在处理的数据来自单独的文件(所以我正在加载文件,计算各种统计数据等,然后加载下一个文件并执行相同的操作)。我不太喜欢第二个循环,但不知道如何用更快的东西替换它。

可以对第二个循环做些什么来使它(希望,很多)更快?

【问题讨论】:

  • 为什么你将rle分别用于v$tv$p的值,并在创建y时删除所有这些信息?
  • 更多指针:1) 不要为v 使用数据框,它们本来就很慢。我只需要普通向量vt &lt;-rep(1:2, each=250)vp &lt;-rep(1:2000, each=500)。那么你就不需要那么多的子集了。在rle() 电话中。
  • @SvenHohenstein 因为我对它们不感兴趣,只是将它们放在一起。另外,这是我工作的一个元素,我正在以其他方式使用这些数据。
  • 我并不认为这是一个借口——我不是程序员(我是一名地理学家,与社会科学家非常接近),但我已经学会了如何使用我选择的工具, R, 有效。关于rle()table() 位,我怀疑这里是否有任何容易实现的目标,它们将更接近您的舒适区。我要指出的是容易实现的结果,它可能会在不弄脏手优化内部循环的情况下产生错误。
  • 总而言之,因此请不要将其视为对 OP 的攻击:任何科学家、工程师或社会学家根本不可能在不知道如何在现代环境中工作编写和使用软件。我对我公司的 EE 和光学工程师也说了同样的话。必要时参加培训课程,但不要以为不知道如何编写代码就可以生存。

标签: r loops


【解决方案1】:

您犯了在 R 中的 for() 循环中增长对象的主要罪行。不要(我重复不要)这样做。开始时为x分配足够的存储空间,然后随手填写x

x <- matrix(nrow = N * (2000 * 2), ncol = ??)

然后在内部循环中

x[ii, ] <- table(rle(....))

其中ii 是一个循环计数器,您在第一个循环之前初始化为1,并在第二个循环内递增:

x <- matrix(nrow = N * (2000 * 2), ncol = ??)
ii <- 1
for(i in 1:N) {
    .... # stuff here
    for(j in 1:2000) {
        .... # stuff here
        x[ii, ] <- table(rle(....))
        ## increment ii
        ii <- ii + 1
        x[ii, ] <- table(rle(....))
        ## increment ii
        ii <- ii + 1
    } ##  end inner loop
} ## end outer loop

另请注意,您在 bot for()loops which will not work.iis just a normal R object and so bothfor()loops will be overwriting it as the progress. USej` 中重复使用索引 i 用于第二个循环,就像我在上面所做的那样。

先尝试这个简单的优化,看看是否能让真实模拟在可接受的时间内完成。如果没有,请返回一个显示最新代码的新 Q,我们可以考虑其他优化。上面的优化很简单,优化table()rle() 可能需要更多的工作。请注意,您可能会查看在 table() 中执行繁重工作的 tabulate() 函数,这可能是优化该特定步骤的一种途径。

【讨论】:

  • 我必须考虑一下并进行测试。我知道循环不好,但我是社会科学家而不是程序员。感谢您的回答。
  • 不,你误会了。循环不错不好!你完成它们的方式是糟糕for() 将几乎和 lapply() 和朋友一样快,如果你正确编写循环并且不要在循环中增长对象。为整个对象分配存储空间,然后填充该对象。当您将结果rbind() 放在一起时,R 必须通过复制 x 创建一个新对象,使其大 1 行并将新数据分配给该新对象,并将其全部分配回 x。所有这些都需要很多时间。
  • 请注意,我不知道x 的列数应该有多大。从早期的 Q 开始,这还不到 100,所以也许一个起点可能是让 ncol = 100 给你一些回旋余地。循环完成后,您始终可以删除多余的列。
  • 没有。我知道我的做法很糟糕。但它有效。这对我来说是最重要的。其他解决方案只是在我的大脑中引爆了保险丝。我不太明白如何将您的解决方案集成到我的代码中,但肯定会尝试这样做。
  • 我已经向您展示了如何做到这一点。我展示的代码与您的非常相似,只是没有您进行的所有详细计算,以便强调重点。问题是您的代码不适用于您的实际问题。您可能会惊讶于通过正确编写循环可以获得多少效率。您甚至可能不需要执行优化tablerle 的艰巨任务。
【解决方案2】:

如果您只想对v$tv$p 的值的每个组合分别运行rletable,则不需要第二个循环。这种方式要快得多:

values <- v$v + v$t * 10 + v$p * 100
runlength <- rle(values)
runlength$values <- runlength$values %% 2
x <- table(runlength)


y <- aggregate(unclass(x), list(as.numeric(rownames(x))), sum)

整个代码将如下所示。如果N 低至 4,则增长对象x 将不会是一个严重的问题。但总的来说,我同意@GavinSimpson 的观点,认为这不是一种好的编程技术。

N=4
x <-NULL
for (i in 1:N) { #first loop
  v <-sample(0:1, 1000000, 1/2) #generate data
  v <-as.data.frame(v) #convert to dataframe
  v$t <-rep(1:2, each=250) #group
  v$p <-rep(1:2000, each=500) #p.number

  values <- v$v + N * 10 + v$t * 100 + v$p * 1000
  runlength <- rle(values)
  runlength$values <- runlength$values %% 2
  x <- rbind(x, table(runlength))

} #end first loop
y <-aggregate(x, list(as.numeric(rownames(x))), sum) #tota

【讨论】:

  • 哇,真快。虽然不明白。哈。
  • 刚刚测试过。你的方式:elapsed 2.23。我的循环:elapsed 345.28。看来我在这里赢了。唯一的问题。我必须找出在第一个循环中保留这些信息的最佳方法。
  • @RSoul 查看更新。我只是把我的答案放在你的第一个循环中。当然,如果您觉得程序仍然很慢,您可以另外使用@GavinSimpson 的想法来避免增长对象x
  • 我一直在尝试将您的答案整合到第一个循环中,这样会有所帮助(我认为)。不过我的N很大。呸。都很好玩。将继续进行测试。
  • 我相信就是这样!光年比我的循环快。不太确定它是否会扩大规模。但手指交叉。你应该得到更多的支持!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多