【问题标题】:Group data by 2 columns and do calculation in R按 2 列分组数据并在 R 中进行计算
【发布时间】:2025-11-25 12:45:02
【问题描述】:

假设我有一个 data.frame

> ID=c("S","B","S","S","B","S","S","S","B","B","S")     #"S" means Sell, "B" means Buy
> quant=c(3,2,5,1,2,10,4,4,17,6,6)
> time=c(1,2,3,3,4,4,5,5,5,6,6)
> test=data.frame(ID,quant,time)
    ID quant time
  1  S     3    1
  2  B     2    2
  3  S     5    3
  4  S     1    3
  5  B     2    4
  6  S    10    4
  7  S     4    5
  8  S     4    5
  9  B    17    5
  9  B    6     6
  9  S    6     6

我需要计算每次的净头寸,这样我的最终输出就是

    ID quant time
  1  S     3    1
  2  B     2    2
  3  S     6    3    #Sell 5+1=6
  4  S     8    4    #Sell 10-2=8
  5  B     9    5    #Buy 17-4-4=9
  6  B     0    6    #this row is optional because the position is 0 so I don't need it

我想我需要先按时间然后按 ID 对 data.frame 进行分组,但是我该如何执行净头寸的计算呢?我试过使用aggregate,但它似乎只对按一列分组有效?谢谢!

【问题讨论】:

    标签: r


    【解决方案1】:

    另一个想法,可能看起来有点脆弱,但似乎适用于特定情况:

    tmp = diff(xtabs(quant ~ ID + time, test))
    data.frame(ID = c(ifelse(tmp > 0, "S", "B")), 
               time = colnames(tmp), 
               quant = c(abs(tmp)))
    #  ID time quant
    #1  S    1     3
    #2  B    2     2
    #3  S    3     6
    #4  S    4     8
    #5  B    5     9
    #6  B    6     0
    

    基础 R 中的经典方法:

    do.call(rbind, 
            lapply(split(test, test$time), 
                   function(x) { 
                      s = sum(x[["quant"]][x[["ID"]] == "S"])
                      b = sum(x[["quant"]][x[["ID"]] == "B"])
                      data.frame(time = x$time[1], 
                                 quant = abs(s - b),
                                 ID = if(s > b) "S" else "B")
                   }))
    #  time quant ID
    #1    1     3  S
    #2    2     2  B
    #3    3     6  S
    #4    4     8  S
    #5    5     9  B
    #6    6     0  B
    

    【讨论】:

      【解决方案2】:

      这是dplyr 的一种解决方案。

      library(dplyr)
      result <- test %>% 
            group_by(time) %>%
            summarise(quant = sum(quant[ID == "B"]) - sum(quant[ID == "S"])) %>% 
            mutate(ID = c("S", "B")[(quant >= 0) + 1], quant = abs(quant))
      
      #   time quant ID
      # 1    1     3  S
      # 2    2     2  B
      # 3    3     6  S
      # 4    4     8  S
      # 5    5     9  B
      # 6    6     0  B
      

      c("S", "B")[(quant &gt;= 0) + 1] 是什么意思?

      • 命令(quant &gt;= 0) 创建一个逻辑向量,指示quant 是否等于或大于0。
      • 在下一步中,将 1 添加到该向量。如果数学运算符与逻辑向量一起使用,FALSE 将转换为 0TRUE 将转换为 1。因此,此命令会产生一个 1 和 2 的向量。
      • 该向量用作c("S", "B") 的索引向量,因此产生"S"s 和"B"s 的字符向量。

      【讨论】:

      • @Sven 你能解释一下这一步 c("S", "B")[(quant >= 0) + 1]
      • @koundy 我添加了解释。
      • @Seven 非常感谢.. 即使我尝试使用 dplyr 回答但仍停留在该步骤。
      【解决方案3】:

      使用data.table

      library(data.table)
      setDT(test)[, list(quant = sum(quant[ID == "B"]) - sum(quant[ID == "S"])),
                  by = time][, list(ID = ifelse(quant > 0, "B", "S"), quant = abs(quant), time)]
      
      #    ID quant time
      # 1:  S     3    1
      # 2:  B     2    2
      # 3:  S     6    3
      # 4:  S     8    4
      # 5:  B     9    5
      # 6:  S     0    6
      

      【讨论】: