Подстановка или суммирование на основе условия - PullRequest
1 голос
/ 29 сентября 2019

У меня есть набор данных, который выглядит примерно так:

df <- data.frame("id" = c("Alpha", "Alpha", "Alpha","Alpha","Beta","Beta","Beta","Beta"), 
                 "Year" = c(1970,1971,1972,1973,1974,1977,1978,1990), 
                 "Group" = c(1,NA,1,NA,NA,2,2,NA),
                 "Val" = c(2,3,3,5,2,5,3,5))

И я хотел бы создать кумулятивную сумму "Val". Я знаю, как сделать простую кумулятивную сумму

df <- df %>% group_by(id) %>% mutate(cumval=cumsum(Val))

Однако я хотел бы, чтобы мои окончательные данные выглядели так:

final <- data.frame("id" = c("Alpha", "Alpha", "Alpha","Alpha","Beta","Beta","Beta","Beta"), 
                 "Year" = c(1970,1971,1972,1973,1974,1977,1978,1990), 
                 "Group" = c(1,NA,1,NA,NA,2,2,NA),
                 "Val" = c(2,3,3,5,2,5,3,5),
                 "cumval" = c(2,5,6,11,2,7,5,10))

Основная идея заключается в том, чтокогда два "Val" принадлежат одной и той же "группе", то, что происходит позже (год), заменяет предыдущий. Например, в образце набора данных наблюдение 3 имеет «cumval» 6, а не 8, поскольку «Val» во время 1972 года заменил «Val» во время 1970 года. Аналогично для Beta.

Благодарюзаранее за вашу помощь

1 Ответ

1 голос
/ 30 сентября 2019

В моей голове это требует петлю for. Сначала мы разбиваем фрейм данных по столбцу id на список из двух. Затем мы создаем два пустых списка. В списке og мы поместим строку, где встречается первый уникальный идентификатор группы, отличной от NA. Для alpha это первая строка, а для Beta это вторая строка. Мы будем использовать это, чтобы вычесть из накопленной суммы, когда значение будет подставлено.

mylist <- split(df, f = df$id)

og <- list()
vals <- list()

df_num <- 1

Мы будем использовать вложенный цикл, внешний цикл зацикливается на каждом объекте (в данном случае на фрейме данных) в списке ивнутренний цикл зацикливается на каждом значении в столбце Group.

Нам нужно отслеживать номера строк, что мы делаем с переменной r. Первоначально мы установили его на 0 вне цикла for, поэтому добавим 1. Сначала мы проверяем, находимся ли мы в первой строке фрейма данных, и в этом случае кумулятивная сумма просто равна значению в первой строке столбца Val. Затем в рамках теста if мы используем другой тест if, чтобы проверить, является ли идентификатор группы NA. Если это не так, то это первое вхождение числа, которое будет указывать на замену текущего значения, если это число появится снова. Поэтому мы сохраняем число во временную переменную temp. Мы также извлекаем и сохраняем строку, содержащую значение, в список og.

После этого он переходит к следующей итерации. Мы проверяем, является ли текущее значение группы NA. Если это так, то мы просто добавляем значение к совокупной сумме. Если он не равен NA, мы проверяем, является ли значение NA и равно ли оно значению, сохраненному в temp. Если оба верны, то это означает, что мы должны заменить. Мы извлекаем исходное значение из списка og и сохраняем его как старое. Затем мы вычитаем старое значение из накопленной суммы и добавляем текущее значение. Мы также заменяем первоначальное значение в og текущим значением замены. Это потому, что если значение нужно заменить снова, нам нужно будет вычесть текущее значение, а не исходное значение.

Если j - это NA, но оно не равно temp, то это новый экземпляр Group. Поэтому мы сохраняем строку с исходным значением в списке og и сохраняем Group. Сумма продолжается как обычно, поскольку это не случай замены значения. Обратите внимание, что переменная x, используемая для подсчета элементов в списке og, увеличивается только тогда, когда в список добавляется новое вхождение. Таким образом, og[[x-1]] всегда будет значением замены.

for (my_df in mylist) {

  x <- 1
  r <- 0

  for (j in my_df$Group) {

    r <- r + 1

    if (r == 1) {

      vals[[1]] <- my_df$Val[1]

      if (is.na(j)==FALSE) {
        og[[x]] <- df[r, c('Group', 'Val'), drop = FALSE]
        temp <- j 
        x <- x + 1
      }

      next
    }

    if (is.na(j)==TRUE) {

      vals[[r]] <- vals[[r-1]] + my_df$Val[r]

    } else if (is.na(j)==FALSE & j==temp) {

      old <- og[[x-1]]
      old <- old[,2]

      vals[[r]] <- vals[[r-1]] - old + df$Val[r]
      og[[x-1]] <- df[r, c('Group', 'Val'), drop = FALSE]

    } else {

      vals[[r]] <- vals[[r-1]] + my_df$Val[r]
      og[[x]] <- my_df[r, c('Group', 'Val')] 
      temp <- j
      x <- x + 1

    }

    }

  cumval <- unlist(vals) %>% as.data.frame()
  colnames(cumval) <- 'cumval'
  my_df <- cbind(my_df, cumval)
  mylist[[df_num]] <- my_df
  df_num <- df_num + 1
}

Наконец, мы объединяем два кадра данных в списке, связывая их в строках с bind_rows из пакета dplyr. Затем я проверяю, совпадает ли Final фрейм данных с вашим желаемым выводом с помощью identical(), и он оценивается как TRUE

final_df <- bind_rows(mylist)
identical(final_df, final)
[1] TRUE  
...