Вычислить значение, используя строку выше в data.frame и другом столбце - PullRequest
4 голосов
/ 01 апреля 2019

Привет, я хочу сделать что-то в 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

Ответы [ 5 ]

5 голосов
/ 01 апреля 2019

Вот решение с использованием accumulate из пакета purrr.Функция accumulate может применять функцию с двумя аргументами, такими как x и y, к последовательности вектора.Возвращаемое значение станет входным значением следующего раунда.

В следующем примере я попросил функцию accumulate начинать со второго числа столбца In до конца.Я также предоставил 1 аргументу .init, который будет первым 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.Если вы пытаетесь сделать accumulate процесс просто в столбце In, следующий код будет более кратким, и вам даже не нужно копировать первое значение в столбец 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
2 голосов
/ 01 апреля 2019

Вы также можете сделать это, используя sapply из базы R для замены цикла for @ Ronak. 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

microbenchmark:

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
2 голосов
/ 01 апреля 2019

Другой вариант с 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
2 голосов
/ 01 апреля 2019

В этом конкретном случае вы можете использовать некоторые алгебраические манипуляции, чтобы выразить все 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
2 голосов
/ 01 апреля 2019

Кажется тривиальной задачей получить значения из предыдущей строки и обновить значение в текущей строке. Тем не менее, 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)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...