【问题标题】:Substituting or summing based on condition根据条件代入或求和
【发布时间】:2019-09-29 17:08:22
【问题描述】:

我有一个看起来像这样的数据集

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 &lt;- df %&gt;% group_by(id) %&gt;% 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,因为 1972 年的“Val”取代了 1970 年的“Val”。Beta 也是如此。

提前感谢您的帮助

【问题讨论】:

  • 类似df %&gt;% group_by(id) %&gt;% mutate(cumval = cumsum(replace(Val, duplicated(Val), first(Val))))
  • 因为 1972 年 (3) 时的 val 与 1970 年 (2) 时的 val 一样是“组”1。基本上,对于同一组内的 val,后一个值代替前一个值。因此这里 val 1972 代替了 val 1970(并与 val 1971 相加),这是一个不同的组(NA)
  • 确定:obs 1: cumval = val =2, obs 2: cumval = val(obs1) + val(obs2) =2+3=5, obs 3: cumval = val (obs2) + val(obs3) = 3+3 = 6... etc..这个想法是因为 obs 1 和 3 属于同一组,id obs 3 代替 obs 1
  • 好的,我尝试了几件事,但“Beta”部分仍然不匹配
  • 那是不幸的!感谢您无论如何都尝试了一些东西..

标签: r conditional-statements data-manipulation cumulative-sum


【解决方案1】:

在我看来,这需要一个for 循环。首先,我们将数据框按id 列拆分为两个列表。然后我们创建两个空列表。在og 列表中,我们将放置第一个唯一的非 NA 组标识符出现的行。对于alpha,这是第一行,对于Beta,这是第二行。当值被替换时,我们将使用它从累积和中减去。

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

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

df_num <- 1

我们将使用嵌套循环,外层循环遍历列表中的每个对象(在本例中为数据帧),内层循环遍历 Group 列中的每个值。

我们需要跟踪行号,这需要使用r 变量。我们最初在 for 循环外将其设置为 0,因此我们添加了 1。首先,我们检查我们是否在数据框的第一行,在这种情况下,累积和只是等于Val 列第一行的值。然后在if 测试中,我们使用另一个if 测试来检查Group id 是否为NA。如果不是,那么这是第一次出现的数字,如果该数字再次出现,则表示当前值的替换。所以我们将数字保存到临时变量temp。我们还将包含该值的行提取并保存到og 列表中。

在此之后,进入下一个迭代。我们检查当前 Group 值是否为 NA。如果是,那么我们只需将该值添加到累积总和中。如果不等于 NA,我们检查该值是否为 NA 并且等于存储在temp 中的值。如果两者都是真的,那么这意味着我们需要替换。我们提取存储在og 列表中的原始值并将其保存为旧值。然后我们从累积和中减去旧值并添加当前值。我们还将og 中的原始值替换为当前替换值。这是因为如果需要再次替换该值,我们将需要减去当前值而不是原始值。

如果j 是NA 但不等于temp,那么这是一个新的Group 实例。所以我们将原始值所在的行保存到og列表中,并保存Group。总和继续正常,因为这不是替换值的实例。请注意,用于计算og 列表中的元素的变量x 仅在列表中添加新出现时才会增加。因此,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
}

最后,我们通过使用dplyr 包中的bind_rows 将它们绑定在行上来组合列表中的两个数据框。然后我检查Final 数据帧是否与您想要的identical() 输出相同,它的计算结果为TRUE

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-25
    • 2016-07-09
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    相关资源
    最近更新 更多