【问题标题】:R loop/lapply, cumulative totals with group byR循环/lapply,按分组累积总计
【发布时间】:2019-04-24 03:36:41
【问题描述】:

我正在尝试在我的数据集中创建新变量,这些变量是基于其他变量(使用 group by)重新启动的累积总数......我希望这些成为数据集中的新列,这是我正在努力解决的部分...

使用下面的数据,我想为每个 Product 和 Product_Cat 分组创建累积销售和利润列。

下面的代码部分给了我我需要的东西,但变量不是新变量,而是覆盖现有的销售/利润......我哪里错了?我想这很简单,没有发现任何东西。

注意:我使用的是 lapply,因为我的真实数据集有 40 多个变量,我需要为其创建计算。

DT <- setDT(Data)[,lapply(.SD, cumsum), by = .(Product,Product_Cat) ]

数据举例:

Product <- c('A','A','A','B','B','B','C','C','C')
Product_Cat <- c('S1','S1','S2','C1','C1','C1','D1','E1','F1')
Sale <- c(10,15,5,20,15,10,5,5,5)
Profit <- c(2,4,2,6,8,2,4,6,8)
Sale_Cum <- c(10,25,5,20,35,45,5,5,5)
Profit_Cum <- c(2,6,2,6,14,16,4,6,8)

Data <- data.frame(Product,Product_Cat,Sale,Profit)
Desired_Data <- data.frame(Product,Product_Cat,Sale,Profit,Sale_Cum,Profit_Cum)

【问题讨论】:

  • 我只想说,这个标题的节奏让我很开心

标签: r data.table lapply cumsum cumulative-sum


【解决方案1】:

这是一些非常糟糕的代码,一步一步地完成所有事情

#sample data
d<-sample(1:10)
f<-sample(1:10)
p<-c("f","f","f","f","q","q","q","w","w","w")
pc<-c("c","c","d","d","d","v","v","v","b","b")
cc<-data.table(p,pc,d,f)

#storing the values that are overwritten first.
three<-cc[,3]
four<- cc[,4]
#applying your function 
dt<-setDT(c)[,lapply(.SD,cumsum), by=.(p,pc)]

#binding the stored values to your function and renaming everything.
x<-cbind(dt,three,four)
colnames(x)[5]<-"sale"
colnames(x)[6]<-"profit"
colnames(x)[4]<-"CumSale"
colnames(x)[3]<-"CumProfit"

#reordering the columns
xx<-x[,c("p","pc","profit","sale","CumSale","CumProfit")]
xx

【讨论】:

    【解决方案2】:
    library(data.table)
    setDT(Data)
    
    cols <- names(Data)[3:4]
    
    Data[, paste0(cols, '_cumsum') := lapply(.SD, cumsum)
         , by = .(Product, Product_Cat) 
         , .SDcols = cols]
    

    【讨论】:

    • 谢谢 - 这很简单,正是我想要的 - 也很容易在 40 多列中重复。谢谢!
    • 对不起,也许这是一个单独的帖子...但是如果我希望 cumsum 是累计总数减去当前值怎么办?在这种情况下,每个值的第一个 cumsum 将为 0...第 6 行 (B, C1) 的 Sale_cumsum 将是 35 而不是 45。这有意义吗?可以做到吗?
    • 在这种情况下将 lapply(.SD, cumsum) 更改为 lapply(.SD, function(x) cumsum(x) - x)
    • 完美——这行得通。我不能感谢你!为我节省了大量时间!
    【解决方案3】:

    数据:

    structure(list(Product = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 
    3L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), Product_Cat = structure(c(5L, 
    5L, 6L, 1L, 1L, 1L, 2L, 3L, 4L), .Label = c("C1", "D1", "E1", 
    "F1", "S1", "S2"), class = "factor"), Sale = c(10L, 15L, 5L, 
    20L, 15L, 10L, 5L, 5L, 5L), Profit = c(2L, 4L, 2L, 6L, 8L, 2L, 
    4L, 6L, 8L), Sale_Cum = c(10, 25, 5, 20, 35, 45, 5, 5, 5), Profit_Cum = c(2, 
    6, 2, 6, 14, 16, 4, 6, 8)), .Names = c("Product", "Product_Cat", 
    "Sale", "Profit", "Sale_Cum", "Profit_Cum"), row.names = c(NA, 
    -9L), class = "data.frame")`
    

    我们可以根据Product和Product_Cat对数据帧进行迭代切片,并且对于每次迭代,将cumsum()产生的输出分配给Sale_Cum和Product_Cum:

    cols <- c('Sale', 'Profit')
    
    for (column in cols){
      x[, paste0(column, '_Cum')] <- 0
      for(p in unique(x$Product)){
        for (pc in unique(x$Product_Cat)){
          x[x$Product == p & x$Product_Cat == pc, paste0(column, '_Cum')] <- cumsum(x[x$Product == p & x$Product_Cat == pc, column])
        }
      }
    }
    print(x)
    # Product Product_Cat Sale Profit Sale_Cum Profit_Cum
    # 1       A          S1   10      2       10          2
    # 2       A          S1   15      4       25          6
    # 3       A          S2    5      2        5          2
    # 4       B          C1   20      6       20          6
    # 5       B          C1   15      8       35         14
    # 6       B          C1   10      2       45         16
    # 7       C          D1    5      4        5          4
    # 8       C          E1    5      6        5          6
    # 9       C          F1    5      8        5          8
    

    【讨论】:

    • 这可能有效,但我试图避免为每个变量写一个计算...我的真实数据集有 40 多个变量,我需要计算累积总数,即为什么我采用 lapply 方法。
    • 已编辑。没有什么是三重嵌套 for 循环解决不了的!
    【解决方案4】:

    这并不使用 group by 本身,但我认为它可以实现您正在寻找的内容,因为它很容易扩展到许多列:

    D2 <- data.frame(lapply(Data[,c(3,4)], cumsum))
    names(D2) <- gsub("$", "_cum", names(Data[,c(3,4)]))
    Data <- cbind(Data, D2)
    

    如果您有 40 多列,只需更改 c(3,4) 以包含您之后的所有列。

    编辑:

    我忘记了 OP 希望它为每个类别重置。在这种情况下,您可以修改原始代码:

    DT <- setDT(Data)[,lapply(.SD, cumsum), by = .(Product,Product_Cat) ]
    names(D2)[c(-1,-2)] <- gsub("$", "_cum", names(Data)[c(-1,-2)])
    cbind(Data, D2[,c(-1,-2)])
    

    【讨论】:

    • 它似乎并没有随着每个新的Product_Cat 重新启动(Sales_Cum 的前三个值是 10、25、30,它们应该是 10、25、5)
    • 你是对的!请参阅使用原始问题中的一些代码的修改后的解决方案。
    猜你喜欢
    • 2021-10-11
    • 2015-09-11
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 2020-12-23
    • 2015-12-08
    • 1970-01-01
    相关资源
    最近更新 更多