Как перенести отрицательное значение в текущей строке в предыдущую строку во фрейме данных? - PullRequest
0 голосов
/ 26 августа 2018

Я хочу перенести отрицательные значения в текущей строке в предыдущую строку, добавив их в предыдущую строку в каждой группе.Ниже приведены примерные необработанные данные, которые у меня есть:

raw_data <- data.frame(GROUP = rep(c('A','B','C'),each = 6),
                   YEARMO = rep(c(201801:201806),3),
                   VALUE = c(100,-10,20,70,-50,30,20,60,40,-20,-10,50,0,10,-30,50,100,-100))
> raw_data
  GROUP YEARMO VALUE
1      A 201801   100  
2      A 201802   -10
3      A 201803    20
4      A 201804    70
5      A 201805   -50
6      A 201806    30
7      B 201801    20
8      B 201802    60
9      B 201803    40
10     B 201804   -20
11     B 201805   -10
12     B 201806    50
13     C 201801     0
14     C 201802    10
15     C 201803   -30
16     C 201804    50
17     C 201805   100
18     C 201806  -100

Ниже приводится вывод, который мне нужен:

final_data <- data.frame(GROUP = rep(c('A','B','C'),each = 6),
                   YEARMO = rep(c(201801:201806),3),
                   VALUE = c(90,0,20,20,0,30,20,60,10,0,0,50,-20,0,0,50,0,0))
> final_data
   GROUP YEARMO VALUE
1      A 201801    90
2      A 201802     0
3      A 201803    20
4      A 201804    20
5      A 201805     0
6      A 201806    30
7      B 201801    20
8      B 201802    60
9      B 201803    10
10     B 201804     0
11     B 201805     0
12     B 201806    50
13     C 201801   -20
14     C 201802     0
15     C 201803     0
16     C 201804    50
17     C 201805     0
18     C 201806     0

Следующие кадры данных покажут, как можно выполнить преобразование в каждой группе:

Trans_GRP_A <- data.frame(GROUP = rep('A',each = 6),
                   YEARMO = c(201801:201806),
                   VALUE = c(100,-10,20,70,-50,30),
                   ITER_1 = c(100,-10,20,20,0,30),
                   ITER_2 = c(90,0,20,20,0,30))
> Trans_GRP_A
  GROUP YEARMO VALUE ITER_1 ITER_2
1     A 201801   100    100     90
2     A 201802   -10    -10      0
3     A 201803    20     20     20
4     A 201804    70     20     20
5     A 201805   -50      0      0
6     A 201806    30     30     30

> Trans_GRP_B <- data.frame(GROUP = rep('B',each = 6),
+                           YEARMO = c(201801:201806),
+                           VALUE = c(20,60,40,-20,-10,50),
+                           ITER_1 = c(20,60,40,-30,0,50),
+                           ITER_2 = c(20,60,10,0,0,50))
> Trans_GRP_B
  GROUP YEARMO VALUE ITER_1 ITER_2
1     B 201801    20     20     20
2     B 201802    60     60     60
3     B 201803    40     40     10
4     B 201804   -20    -30      0
5     B 201805   -10      0      0
6     B 201806    50     50     50

> Trans_GRP_C <- data.frame(GROUP = rep('C',each = 6),
+                           YEARMO = c(201801:201806),
+                           VALUE = c(0,10,-30,50,100,-100),
+                           ITER_1 = c(0,10,-30,50,0,0),
+                           ITER_2 = c(0,-20,0,50,0,0),
+                           ITER_3 = c(-20,0,0,50,0,0))
> Trans_GRP_C
  GROUP YEARMO VALUE ITER_1 ITER_2 ITER_3
1     C 201801     0      0      0    -20
2     C 201802    10     10    -20      0
3     C 201803   -30    -30      0      0
4     C 201804    50     50     50     50
5     C 201805   100      0      0      0
6     C 201806  -100      0      0      0

Логика переноса следующая:

  1. Заменить отрицательное значение на 0.
  2. Добавить отрицательное значение в текущей строке к значению впредыдущая строка.
  3. Передача отрицательного значения в предыдущую строку до тех пор, пока значение не станет положительным, или 0.
  4. Передача до тех пор, пока в группе не встретится 1-я строка, если передача не приводит к положительному результату.значение, здесь 1-я строка YEARMO = 201801 в каждой группе.

Любое решение приветствуется.Я думаю, что векторизованное решение может работать быстрее.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Вот еще один вариант рекурсивного суммирования положительной части вектора со смещенной отрицательной частью вектора до тех пор, пока не останется больше отрицательных значений или пока он не будет выполнен .N раз (где .N - номер строки длякаждая группа)

setDT(raw_data)[, OUTPUT := {
        posVal <- replace(VALUE, VALUE<0, 0)
        negVal <- replace(VALUE, VALUE>0, 0)
        n <- 1L
        while (any(negVal < 0) && n < .N) {
            posVal <- replace(posVal, posVal<0, 0) + 
                shift(negVal, 1L, type="lead", fill=0) +
                c(negVal[1L], rep(0, .N-1L))
            negVal <- replace(posVal, posVal>0, 0)
            n <- n + 1L
        }
        posVal
    }, by=.(GROUP)]

вывод:

    GROUP YEARMO VALUE OUTPUT
 1:     A 201801   100     90
 2:     A 201802   -10      0
 3:     A 201803    20     20
 4:     A 201804    70     20
 5:     A 201805   -50      0
 6:     A 201806    30     30
 7:     B 201801    20     20
 8:     B 201802    60     60
 9:     B 201803    40     10
10:     B 201804   -20      0
11:     B 201805   -10      0
12:     B 201806    50     50
13:     C 201801     0    -20
14:     C 201802    10      0
15:     C 201803   -30      0
16:     C 201804    50     50
17:     C 201805   100      0
18:     C 201806  -100      0
0 голосов
/ 27 августа 2018

Это сложно. Я пытался найти векторизованное решение, но единственный подход, который до сих пор работал, заключался в цикле назад между строками в каждой группе:

library(data.table)
DT <- as.data.table(raw_data)
DT$final <- final_data$VALUE
DT[, new := {
  x <- VALUE
  sn <- 0
  for (i in .N:1) {
    if (i > 1) {
      if (x[i] < 0) {
        sn <- sn + x[i]
        x[i] <- 0
      } else {
        tmp <- pmax(x[i] + sn, 0)
        sn <- sn + x[i] - tmp
        x[i] <- tmp
      }
    } else {
      x[i] <- x[i] + sn
    }
  }
  x
}, by = GROUP]
DT[]
    GROUP YEARMO VALUE final new
 1:     A 201801   100    90  90
 2:     A 201802   -10     0   0
 3:     A 201803    20    20  20
 4:     A 201804    70    20  20
 5:     A 201805   -50     0   0
 6:     A 201806    30    30  30
 7:     B 201801    20    20  20
 8:     B 201802    60    60  60
 9:     B 201803    40    10  10
10:     B 201804   -20     0   0
11:     B 201805   -10     0   0
12:     B 201806    50    50  50
13:     C 201801     0   -20 -20
14:     C 201802    10     0   0
15:     C 201803   -30     0   0
16:     C 201804    50    50  50
17:     C 201805   100     0   0
18:     C 201806  -100     0   0

sn сохраняет, т.е. накапливает отрицательные значения, которые затем «расходуются» последующими (в обратном порядке) положительными значениями.

...