使用 data.frame 中的上方行和另一列计算值

Posted

技术标签:

【中文标题】使用 data.frame 中的上方行和另一列计算值【英文标题】:Calculate value using row above in data.frame and another column 【发布时间】:2019-08-22 02:28:25 【问题描述】:

您好,我想在 R 中做一些看起来应该很简单的事情,但我似乎大脑衰退了。

对于 data.frame 中的每一行,我想在上面的行中获取 Vol 的值,为该行添加 In 的值并减去取决于此值的值。

这是我的尝试,但延迟只是在开始时回溯一行,而不是在计算下一个值后继续回溯

library(dplyr)
df <- data.frame(In = c(1,4,0,0,1,2,3,0,0), Vol = c(1,rep(NA,8)))

df %>% mutate(Vol = (lag(Vol) + In) -  (lag(Vol) + In)*0.01)

想要的输出 =

  In     Vol
1  1  1.00
2  4  4.95
3  0  4.90
4  0  4.85
5  1  5.79
6  2  7.72
7  3 10.61
8  0 10.50
9  0 10.40

【问题讨论】:

如果您提供test 的样本以及该样本数据的预期输出,将会有所帮助。 哎呀好点@r2evans - 现在已经更新了。 【参考方案1】:

这是使用purrr 包中的accumulate 的解决方案。 accumulate 函数可以将具有两个参数的函数(例如 xy)应用于向量序列。返回值将成为下一轮的输入值。

在下面的示例中,我要求accumulate 函数从In 列的第二个数字开始到末尾。我还为.init 参数提供了1,这将是函数的第一个x

library(dplyr)
library(purrr)

df <- data.frame(In = c(1,4,0,0,1,2,3,0,0), Vol = c(1,rep(NA,8)))

df %>% 
  mutate(Vol = accumulate(In[2:n()], function(x, y) (x + y) * 0.99, .init = 1))
#   In       Vol
# 1  1  1.000000
# 2  4  4.950000
# 3  0  4.900500
# 4  0  4.851495
# 5  1  5.792980
# 6  2  7.715050
# 7  3 10.607900
# 8  0 10.501821
# 9  0 10.396803

此外,Vol 列的第一个值似乎与 In 列的第一个值相同。如果您要做的只是在In 列上进行accumulate 处理,则以下代码会更简洁,您甚至不需要将第一个值复制到Vol 列。

df %>% 
  mutate(Vol = accumulate(In, function(x, y) (x + y) * 0.99))
#   In       Vol
# 1  1  1.000000
# 2  4  4.950000
# 3  0  4.900500
# 4  0  4.851495
# 5  1  5.792980
# 6  2  7.715050
# 7  3 10.607900
# 8  0 10.501821
# 9  0 10.396803

【讨论】:

它似乎非常接近 stats::filter 函数 - 例如 filter(df$In, c(1,-0.01), sides=1, method="rec") @thelatemail 感谢分享。您的代码输出为1.00000 5.00000 4.99000 4.94000 5.89010 7.84070 10.78180 10.70339 10.59557,与 OP 的预期输出不同。 我知道,这就是为什么我说“越来越接近...” - 我认为尝试将其转换为矢量化函数可能会有所帮助。 哦,我想这就是我想要的,我会看看它与我的真实数据的关系,看看是否有任何我遗漏的东西。巧合的是,In 和 Vol 的第一个值相同 @www - 当然,您的答案的基本 R 版本(实际上只是一堆隐藏循环)是 Reduce(function(x, y) (x + y) * 0.99, df$In, accumulate=TRUE)【参考方案2】:

您也可以使用基础R 中的sapply 来替换@Ronak 的for 循环。 invisible 不是必需的函数,只是将 sapply 包裹起来,使其静默工作。

invisible(
  sapply(2:nrow(df), function(i) 
    df$Vol[i] <<- (df$Vol[i-1] + df$In[i]) -  (df$Vol[i-1] + df$In[i])*0.01
  
         )
)

> df
  In       Vol
1  1  1.000000
2  4  4.950000
3  0  4.900500
4  0  4.851495
5  1  5.792980
6  2  7.715050
7  3 10.607900
8  0 10.501821
9  0 10.396803

微基准测试:

Unit: microseconds
            expr      min        lq      mean    median        uq       max neval
           tidy1  578.614  602.3825  736.8518  647.7345  792.1560  3409.963   100
           tidy2  566.256  601.1450 1524.3789  646.5240  801.3490 80219.732   100
        for.loop 4936.829 5288.2650 6007.9584 5635.4895 6540.4290  8982.346   100
          sapply  198.919  218.8710  305.8182  226.3600  243.1750  4489.870   100
 trans.db.reduce  127.456  149.8150  175.4649  172.6280  195.9935   292.835   100
        trans.db  217.416  236.1150  328.3348  255.2275  285.5560  5805.963   100

【讨论】:

您应该重复您的基准测试并包含@d.b 的解决方案,这是最快的。 @jay.sf,看来@d.b 的函数加上Reduce 是最快的。【参考方案3】:

从前一行获取值并更新当前行中的值似乎是一项微不足道的任务。但是,mutate 没有它计算的先前 Vol 值的“知识”,因为它一起计算了整个列的值。

在这种情况下,我们可以使用简单的for 循环

for (i in 2:nrow(df)) 
   df$Vol[i] = (df$Vol[i-1] + df$In[i]) -  (df$Vol[i-1] + df$In[i])*0.01


df
#  In       Vol
#1  1  1.000000
#2  4  4.950000
#3  0  4.900500
#4  0  4.851495
#5  1  5.792980
#6  2  7.715050
#7  3 10.607900
#8  0 10.501821
#9  0 10.396803

数据

test = c(1, 4, 0, 0, 1, 2, 3, 0, 0)
df <- data.frame(In = test, Vol = c(1,rep(NA,8)))

【讨论】:

嗯,感谢您的回答,我希望避免 for 循环,因为在现实生活中我有大量数据,它将按其他列分组,我通常尽量避免他们。也许在这种情况下我别无选择 @user2738526 是的,我自己也经历过这样的例子,如果没有for 循环,我就无法进行任何这样的计算。您可以等待一段时间,看看是否有其他人对此有更好/更智能的解决方案。知道会很有趣。【参考方案4】:

在这种特殊情况下,您可以使用一些代数操作将所有Vol 表达为第一个Vol

transform(df, Vol = c(df$Vol[1], sapply(2:NROW(df), function(n)
    0.99^(n-1) * df$Vol[1] + sum(0.99^((n-1):1) * df$In[2:n])
)))
#  In       Vol
#1  1  1.000000
#2  4  4.950000
#3  0  4.900500
#4  0  4.851495
#5  1  5.792980
#6  2  7.715050
#7  3 10.607900
#8  0 10.501821
#9  0 10.396803

【讨论】:

【参考方案5】:

Reduce 的另一个选项

transform(df, 
          Vol = Reduce(function(x, y)
              x + y - 0.01 * (x + y)    
          ,
          c(df$Vol[1], df$In[-1]),
          accumulate = TRUE))
#  In       Vol
#1  1  1.000000
#2  4  4.950000
#3  0  4.900500
#4  0  4.851495
#5  1  5.792980
#6  2  7.715050
#7  3 10.607900
#8  0 10.501821
#9  0 10.396803

【讨论】:

以上是关于使用 data.frame 中的上方行和另一列计算值的主要内容,如果未能解决你的问题,请参考以下文章

Pandas/Python 检查中间值和另一列的输出值

将函数应用于 data.frame 中的每一行并将结果附加到 R 中的 data.frame

Apache Spark SQL数据集groupBy具有max函数和另一列中的不同值

data.frame 中每一列的利润回撤

用另一列中的值替换缺失值

将 Div 移动到一列的右侧和另一列的下方