【问题标题】:Improve run-time of loop提高循环的运行时间
【发布时间】:2016-01-18 15:23:50
【问题描述】:

我正在尝试提高以下进程的计算效率。我已经创建了使用数据进行审查的玩具示例。第一种方法的运行时间是第二种方法的一半。

如何改进第一种方法的运行时间?

library(sqldf)
id = c(1,1,1,1,2,2,2,5,5,5,5,5,5)
qn = c(0,0,1,1,0,1,0,0,0,1,0,1,0)
d = data.frame(cbind(id,qn))
names(d) = c("id", "qn")

un = unique(d$id)
holder = matrix(0,length(un), 1)
counter = 0

x = proc.time()

for (i in un)
{
  z = head(which(d[d$id == i,]$qn==1),1)
  counter = counter + 1
  holder[counter,] = z
}

proc.time() - x
f = sqldf("select id, count(qn) from d group by id", drv = 'SQLite')
f = cbind(f,holder)
#################################
un = unique(d$id)
holder = matrix(0,length(un), 1)
counter = 0

x = proc.time()

for (i in 1:length(un))
{
  y = paste("select * from d where id = ", un[i])
  y = sqldf(y, drv = 'SQLite')
  y = min(which(y$qn==1))
  counter = counter + 1
  holder[counter,] = y
}

proc.time() - x
f = sqldf("select id, count(qn) from d group by id", drv = 'SQLite')
f = cbind(f,holder)

我正在尝试为每个 id 计算 1 的第一个实例。

预期输出:

# id first
# 1:  1     3
# 2:  2     2
# 3:  5     3

【问题讨论】:

  • 最好尝试描述您正在尝试做的事情,而不是仅仅展示您是如何做到的。最终目标是什么?
  • 感谢您的快速回复!我尝试为每个 id 计算 1 的第一个实例。

标签: r loops sqldf


【解决方案1】:

我们也可以使用data.table

library(data.table)
setDT(d)[, list(first= which.max(qn)) , id]

【讨论】:

  • 谢谢!这样可行。速度现在下降到 0.01-0.02 秒!这是赢家!我将使用所有示例更新代码。
  • 完成!由于声誉很小,我无法投票,但如果可以的话,我会这样做!
【解决方案2】:

您可以在没有sqldf 的情况下使用dplyr 来做到这一点

library(dplyr)
d %>% 
    group_by(id) %>% 
    summarize(first=first(which(qn==1)))

【讨论】:

【解决方案3】:

1) 在 lapply 中使用 sqldf:

do.call(rbind,
        lapply(split(d, id), function(i)
          sqldf("SELECT id, min(rowid) AS first
                 FROM (SELECT rowid, *
                       FROM i) AS x
                 WHERE qn = 1"))
        )

##   id first
## 1  1     3
## 2  2     2
## 5  5     3

2) 或者对于纯 SQL 解决方案,从每组的第一个 qn=1 的 rowid 中减去每组第一行的 rowid 并加 1:

sqldf("select id, min_row1 - min_row + 1 first 
       from (select id, min(rowid) min_row 
             from d 
             group by id)
       join (select id, min(rowid) min_row1 
             from d where qn = 1 
             group by id) using (id)")


##   id first
## 1  1     3
## 2  2     2
## 3  5     3

3) 或者对于另一种纯 SQL 解决方案,在内部选择中的 id 内创建一个序列seq,然后在 id 组中挑选出第一个具有 qn = 1 的序列:

sqldf("select id, min(seq) first 
       from (select x.id, x.qn, count() seq 
             from d x 
             join d y on x.rowid >= y.rowid and x.id = y.id 
             group by x.rowid)
       where qn = 1
       group by id")

##   id first
## 1  1     3
## 2  2     2
## 3  5     3

【讨论】:

  • @G.Grothendieck 有没有办法在不使用RPostgreSQL 的情况下获得row_number over(partition by id) 功能?然后我们可以放弃 lapply。
  • 哥们太棒了!我什至不知道它可以在常规 SQL 中完成。谢谢!我测试了它,但它比我的方法 1 运行得慢,但比我的方法 2 快。
猜你喜欢
  • 2017-04-21
  • 2016-04-15
  • 2016-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-28
相关资源
最近更新 更多