【问题标题】:Aggregate rows based on the value of the next row根据下一行的值聚合行
【发布时间】:2018-02-27 14:52:59
【问题描述】:

假设我有以下数据框:

Category = c("blue", "red",  "red", "blue", "blue", "blue", "red", "red", "red","blue", "red", "red","blue","blue","red","blue","red")
Purchase  = c(0,1,1,0,0,0,1,1,1,0,1,1,0,0,1,0,1)
Number  = c(1,1,1,1,2,2,2,2,2,1,1,2,2,2,2,2,2)
Id = c("a","a","a","a","a","a","a","a","a","b","b","b","b","b","b","b","b")
Country = c("NL","BE","BE","UK","UK","NL","UK","UK","UK","BE","NL","NL","BE","UK","UK","BE","NL")

df = data.frame(Id, Number,Category, Purchase, Country)
    > df
   Id Number Category Purchase Country
1   a      1     blue        0      NL
2   a      1      red        1      BE
3   a      1      red        1      BE
4   a      1     blue        0      UK
5   a      2     blue        0      UK
6   a      2     blue        0      NL
7   a      2      red        1      UK
8   a      2      red        1      UK
9   a      2      red        1      UK
10  b      1     blue        0      BE
11  b      1      red        1      NL
12  b      2      red        1      NL
13  b      2     blue        0      BE
14  b      2     blue        0      UK
15  b      2      red        1      UK
16  b      2     blue        0      BE
17  b      2      red        1      NL

我想汇总红色后跟红色的行,按 ID 和数字分组,以便汇总这些行的购买。因此,我想要的输出是:

    > desired
   Id Number Category Purchase Country
1   a      1     blue        0      NL
2   a      1      red        2      BE
3   a      1     blue        0      UK
4   a      2     blue        0      UK
5   a      2     blue        0      NL
6   a      2      red        3      UK
7   b      1     blue        0      BE
8   b      1      red        1      NL
9   b      2      red        1      NL
10  b      2     blue        0      BE
11  b      2     blue        0      UK
12  b      2      red        1      UK
13  b      2     blue        0      BE
14  b      2      red        1      NL

因此,应保持类别出现的顺序,并且仅应聚合类别为“红色”的类别。此外,在我的真实数据框中,我有几个列,例如 Country 列,我也希望它们出现在输出中,但我不想手动定义所有这些列。我尝试过使用aggregate函数或ddply,但还是没有整理出来。

有人可以帮我解决这个考虑行顺序的聚合问题吗?

【问题讨论】:

  • @jogo 已删除答案的变体:library(data.table); setDT(df)[, .(Purchase = if (Category=="red") sum(Purchase) else Purchase), by=.(Id, Number, Category, rleid(Id, Number, Category))]

标签: r dataframe aggregate


【解决方案1】:

这是data.table 的一个选项。将 'data.frame' 转换为 'data.table' (setDT(df)),按逻辑列 (Category == "red") 的 run-length-id 以及 'Id'、'Number' 和 'Category' 分组, if元素个数大于1且all'Category'元素为'red',则获取'Purchase'的sumelse返回'Purchase'

library(data.table)
setDT(df)[, .(Purchase = if(.N > 1 & all("red" %in% Category)) sum(Purchase) 
            else Purchase), by = .(grp = rleid(Category == "red"), Id, Number, Category)
          ][, grp := NULL][]
#    Id Number Category Purchase
# 1:  a      1     blue        0
# 2:  a      1      red        2
# 3:  a      1     blue        0
# 4:  a      2     blue        0
# 5:  a      2     blue        0
# 6:  a      2      red        3
# 7:  b      1     blue        0
# 8:  b      1      red        1
# 9:  b      2      red        1
#10:  b      2     blue        0
#11:  b      2     blue        0
#12:  b      2      red        1
#13:  b      2     blue        0
#14:  b      2      red        1

【讨论】:

  • x %in% y 周围的 all 是不必要的,因为 x 的长度为 1。
  • @Frank 你是对的。我把它作为一般情况,以防 OP 想要扩展到多个“类别”
  • @akrun 谢谢你,这真的很有帮助!在我的真实数据框中,我的列比示例中的列多,我应该如何调整您的答案以确保输出中存在这些列,而不必手动输入所有这些列?
  • @MC09 当你说有更多的列时不清楚。您的意思是有成对的“购买”/“类别”列吗?
  • @akrun 我会调整我的问题,以便清楚我想说什么。
【解决方案2】:
df$temp = with(data = rle(as.character(df$Category)),
     cumsum(unlist(sapply(seq_along(values), function(i){
         if(values[i] == "red"){
             c(1, rep(0, lengths[i]-1))
         }else{
             rep(1, lengths[i])
         }}))))
aggregate(Purchase~., df, sum)
#   Id Number Category temp Purchase
#1   a      1     blue    1        0
#2   a      1      red    2        2
#3   a      1     blue    3        0
#4   a      2     blue    4        0
#5   a      2     blue    5        0
#6   a      2      red    6        3
#7   b      1     blue    7        0
#8   b      1      red    8        1
#9   b      2      red    8        1
#10  b      2     blue    9        0
#11  b      2     blue   10        0
#12  b      2      red   11        1
#13  b      2     blue   12        0
#14  b      2      red   13        1

【讨论】:

    【解决方案3】:

    这是一种使用dpyr的方法。

    首先,我构建了一个子组,当组中的颜色发生变化时递增,以及IdNumber 它定义了子data.frames

    然后我在包含red 的子data.frames 上使用do 来汇总购买。

    然后我清理组和额外的列。

    df %>%
      group_by(Id,Number,subgroup = cumsum(c(TRUE,head(Category,-1) != tail(Category,-1)))) %>%
      do({if(.$Category[1] == "red") aggregate(Purchase ~ .,.,sum) else .}) %>%
      ungroup %>%
      select(-subgroup) 
    
    # # A tibble: 14 x 4
    #        Id Number Category Purchase
    #    <fctr>  <dbl>   <fctr>    <dbl>
    #  1      a      1     blue        0
    #  2      a      1      red        2
    #  3      a      1     blue        0
    #  4      a      2     blue        0
    #  5      a      2     blue        0
    #  6      a      2      red        3
    #  7      b      1     blue        0
    #  8      b      1      red        1
    #  9      b      2      red        1
    # 10      b      2     blue        0
    # 11      b      2     blue        0
    # 12      b      2      red        1
    # 13      b      2     blue        0
    # 14      b      2      red        1
    

    【讨论】:

      猜你喜欢
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 2021-01-27
      • 2023-03-08
      • 1970-01-01
      • 2019-07-06
      • 2016-05-01
      • 2017-08-25
      相关资源
      最近更新 更多