【问题标题】:Rowwise while loop over dataframe?逐行while循环数据帧?
【发布时间】:2020-12-17 05:08:32
【问题描述】:

我有一个基于输入变量计算某些东西的函数,但我想根据函数的结果更改输入变量。我觉得这是一个非常琐碎的问题,但我还没有找到一个可行的解决方案。

我有一个类似这样的数据集:

v1<-sample(5:12, 10, replace=T)
v2<-rep(100, 10)
v3<-rep(1,10)
v4<-sample(1:4, 10, replace=T)
t1<-sample(10:30, 10, replace=T)

还有一个使用变量 v1 到 v4 进行计算的函数:

fun<-function(v1, v2, v3, v4){
  #does complicated things
  result<-((v2/(v1*v4))*v3
  return(result)
}

out<-fun(v1, v2, v3, v4)
df<-data.frame(v1, v2, v3, v4, t1, out)

我需要 t1

while (out < t1){
  v3= v3 + 1
  out<- fun(v1, v2, v3, v4)
}

理想情况下,v3 的最后一个值以某种方式存储。

v1  v1  v2 v3 v4 t1 out out_new v3_new
1   6 100  1  2 15  8.333333 16 2
2  12 100  1  2 17  4.166667 20 5
3   5 100  1  1 12 20.000000 20 1
4  10 100  1  4 26  2.500000 . . 
5   8 100  1  2 15  6.250000 . .
6   6 100  1  3 18  5.555556 
7  10 100  1  4 20  2.500000 
8  11 100  1  2 12  4.545455 
9  12 100  1  3 28  2.777778
10  6 100  1  2 25  8.333333

我正在寻找的输出是一个如下所示的数据框: 我正在寻找的是一种适用于大型(100.000 行)数据框/小标题的矢量化解决方案。我尝试了使用 dplyr 的不同方法(使用 case_when、ifelse 进行变异),但未能提出可行的解决方案。

【问题讨论】:

  • 我不明白你的问题。你能展示你预期的最终输出吗?
  • 你到底要计算什么?为什么不直接计算所有行的整个结果,然后使用filter 删除t1 &gt;= out 所在的行。 df %&gt;% mutate(out = fun(v1, v2, v3, v4)) %&gt;% filter(t1 &lt; out).
  • 如果您不想过滤掉行,您可以将filter 更改为第二个mutate(out = ifelse(t1 &gt;= out, NULL, out))
  • 我认为他想增加v3 直到out 大于t1。
  • 这是有道理的。问题是 v3 是否应该在结果中更新,以及它是否是固定间隔增加的。在 mutate 中使用 v3 + seq(n()) 可以实现 while 循环的效果。

标签: r dataframe while-loop dplyr


【解决方案1】:

使用mapply() 和其他一些您可以在基础 R 中进行的定义:

set.seed(42)
df <- data.frame(v1=sample(5:12, 10, replace=T), v2=rep(100, 10), v3=rep(1,10), 
                 v4=sample(1:4, 10, replace=T), t1=sample(10:30, 10, replace=T))

fun<-function(v1, v2, v3, v4) {
  #does complicated things
  result <- ((v2/(v1*v4))*v3)
  return(result)
}

fun2 <- function(v1, v2, v3, v4, t) {
  out <- fun(v1, v2, v3, v4)
  while (out < t){
    v3 <- v3 + 1
    out <- fun(v1, v2, v3, v4)
  }
  return(list(v3new=v3, out=out))
}

cbind(df, t(mapply(fun2, df$v1, df$v2, df$v3, df$v4, df$t1)))
> cbind(df, t(mapply(fun2, df$v1, df$v2, df$v3, df$v4, df$t1)))
#    v1  v2 v3 v4 t1 v3new      out
# 1   5 100  1  3 24     4 26.66667
# 2   9 100  1  4 12     5 13.88889
# 3   5 100  1  3 18     3       20
# 4   5 100  1  4 13     3       15
# 5   6 100  1  1 14     1 16.66667
# 6   8 100  1  1 22     2       25
# 7   6 100  1  2 14     2 16.66667
# 8   6 100  1  4 29     7 29.16667
# 9   5 100  1  2 11     2       20
# 10 12 100  1  2 17     5 20.83333

【讨论】:

  • 我想知道为什么你的方法是四舍五入out,不过看起来和我的很相似。
  • 我现在看到我的错误是当我试图使第二个函数工作时忘记在 while 循环之前定义“out”。感谢您的解决方案!
  • @DanChaltiel 我无法复制您的数据,因为您没有在代码中使用set.seed(...)
  • @Jogo 是的,但实际上是因为你的代码有一个错误:你使用了两次df$v2 ;-)
【解决方案2】:

有一种方法可以使用dplyr::rowwise()

library(tidyverse)
v1<-sample(5:12, 10, replace=T)
v2<-rep(100, 10)
v3<-rep(1,10)
v4<-sample(1:4, 10, replace=T)
t1<-sample(10:30, 10, replace=T)

fun<-function(v1, v2, v3, v4){
  #does complicated things
  result<-(v1/v2)*v4*v3
  return(result)
}

reallyWeirdFunction = function(v1, v2, v3, v4, t){
  out<- fun(v1, v2, v3, v4)
  while (out < t){
    v3= v3 + 1
    out<- fun(v1, v2, v3, v4)
  }
  out
}

df<-data.frame(v1, v2, v3, v4, t1)
df %>% 
  rowwise() %>% 
  mutate(
    out=fun(v1, v2, v3, v4), 
    out2=reallyWeirdFunction(v1, v2, v3, v4, t1)
  )
#> # A tibble: 10 x 7
#> # Rowwise: 
#>       v1    v2    v3    v4    t1   out  out2
#>    <int> <dbl> <dbl> <int> <int> <dbl> <dbl>
#>  1     7   100     1     3    17  0.21  17.0
#>  2    11   100     1     2    24  0.22  24.2
#>  3    12   100     1     1    11  0.12  11.0
#>  4    10   100     1     4    15  0.4   15.2
#>  5    10   100     1     2    22  0.2   22  
#>  6     9   100     1     1    16  0.09  16.0
#>  7     5   100     1     2    24  0.1   24  
#>  8    12   100     1     2    23  0.24  23.0
#>  9     8   100     1     3    30  0.24  30  
#> 10     7   100     1     2    14  0.14  14.

Created on 2020-08-28 by the reprex package (v0.3.0)

但是,在 R 中使用 while 循环通常是一个非常糟糕的主意,并且rowwise() 在大型数据集中可能会非常慢。您绝对应该尝试找到比像您正在做的那样增加 v3 更好的算法。但是我找不到你的例子。

【讨论】:

  • 这就是我要找的!我认为对于大型数据集,jogo 的方法可能会更快。谢谢!
  • @user45017 注意,Jogo 的方法并不渴望得到完全相同的结果。我不确定为什么以及哪一个是正确的,这可能来自比较的严格性。
  • 我在 100k 行样本上运行了一个小基准测试,并且两个接近的速度都相同 :-)
猜你喜欢
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多