具有不同分母的除法的累积和 R

Posted

技术标签:

【中文标题】具有不同分母的除法的累积和 R【英文标题】:Cumulative Sum of a division with varying denominators R 【发布时间】:2017-02-08 10:36:36 【问题描述】:

好的,这是我希望使用高效、优雅的解决方案(例如 data.table 或 dplyr)来解决的问题。

定义:

DT = data.table(group=c(rep("A",3),rep("B",5)),value=c(2,9,2,3,4,1,0,3))

   time group value  
1:    1     A     2  
2:    2     A     9 
3:    3     A     2 
4:    1     B     3     
5:    2     B     4 
6:    3     B     1 
7:    4     B     0 
8:    5     B     3 

我想要得到的是一组值的累积总和除以它们被观察到的那一刻的逆序。

   time group value    RESULT
1:    1     A     2  2.000000
2:    2     A     9 10.000000
3:    3     A     2  7.166667
4:    1     B     3  3.000000
5:    2     B     4  5.500000
6:    3     B     1  4.000000
7:    4     B     0  2.583333
8:    5     B     3  4.933333

在第 5 行,结果是: 4/1 + 3/2 = 5.5 因为在时间 2,B 组有 2 个观察值,最后一个除以 1,前一个除以 1。 接下来在第 6 行,结果是: 1/1 + 4/2+ 3/3 = 4 由于在时间 3,B 组有 3 个观察值,最后一个除以 1,前一个除以 2,仍然前一个除以 3。在第 7 行,0/1 + 1/2 + 4/3 + 3/4 = 2.583333,依此类推...

数据很大,所以避免循环很重要!

【问题讨论】:

【参考方案1】:

我会使用矩阵代数:

n_max = DT[, .N, by=group][, max(N)]
m     = matrix(0, n_max, n_max)
m[]   = ifelse( col(m) >= row(m), 1 / (col(m) - row(m) + 1 ), m)

DT[, res := value %*% m[seq_len(.N), seq_len(.N)], by=group ]

   group value       res
1:     A     2  2.000000
2:     A     9 10.000000
3:     A     2  7.166667
4:     B     3  3.000000
5:     B     4  5.500000
6:     B     1  4.000000
7:     B     0  2.583333
8:     B     3  4.933333

【讨论】:

【参考方案2】:

您可以*apply 跨越组长度的序列,使序列索引value,并反过来将其除以。与dplyr

library(tidyverse)

DT %>% group_by(group) %>% 
    mutate(result = sapply(seq(n()), function(x)sum(value[seq(x)] / rev(seq(x)))))

## Source: local data frame [8 x 3]
## Groups: group [2]
## 
##    group value    result
##   <fctr> <dbl>     <dbl>
## 1      A     2  2.000000
## 2      A     9 10.000000
## 3      A     2  7.166667
## 4      B     3  3.000000
## 5      B     4  5.500000
## 6      B     1  4.000000
## 7      B     0  2.583333
## 8      B     3  4.933333

或使用purrr::map_dbl 代替sapply

DT %>% group_by(group) %>% 
    mutate(result = map_dbl(seq(n()), ~sum(value[seq(.x)] / rev(seq(.x)))))

返回相同的东西。您也可以将相同的逻辑转换为基本 R:

DT$result <- ave(DT$value, 
                 DT$group, 
                 FUN = function(v)sapply(seq_along(v), 
                                          function(x)sum(v[seq(x)] / rev(seq(x)))))

DT

##   group value    result
## 1     A     2  2.000000
## 2     A     9 10.000000
## 3     A     2  7.166667
## 4     B     3  3.000000
## 5     B     4  5.500000
## 6     B     1  4.000000
## 7     B     0  2.583333
## 8     B     3  4.933333

虽然我没有进行基准测试,但这些方法对于大多数工作来说应该足够快。不过,如果速度至关重要,我怀疑@Frank 的答案可能会更快。

【讨论】:

【参考方案3】:

如果你有空闲的内存,你可以使用笛卡尔连接来预分配行,这样在 by 中完成的操作就更简单了,并且可以利用 data.table 的 GForce 优化。这可能/可能不会比其他解决方案更快,因为它基本上是用内存换取在 by 中使用更优化的代码。

> DT[, .SD
     ][DT, on='group', allow.cartesian=T
     ][, setnames(.SD, 'i.time', 'groupRow')
     ][time <= groupRow
     ][, timeRev := .N:1, .(group, groupRow)
     ][, res := value / timeRev
     ][, .(res=sum(res)), .(group, groupRow, i.value)
     ][, groupRow := NULL
     ][, setnames(.SD, 'i.value', 'value')
     ]
   group value    res
1:     A     2  2.000
2:     A     9 10.000
3:     A     2  7.167
4:     B     3  3.000
5:     B     4  5.500
6:     B     1  4.000
7:     B     0  2.583
8:     B     3  4.933
> 

【讨论】:

DT[, .SD ]DT 相同,所以您这样做只是为了使括号对齐? @Frank 是的,我更关心格式和可读性而不是内存副本。但这只是我

以上是关于具有不同分母的除法的累积和 R的主要内容,如果未能解决你的问题,请参考以下文章

python常用运算符-各种除法运算符

如何在 R 中生成具有累积频率和相对频率的频率表

R具有条件和重置的累积和

假设分母<>0,整数除法是不是会溢出/下溢? [复制]

从同一个累积事实表中分离和独立的计数

为什么方差的分母有时是n,有时是n-1 源于总体方差和样本方差的不同