【问题标题】:Cumulative Mean with Conditions有条件的累积平均值
【发布时间】:2016-05-28 08:26:49
【问题描述】:

这是来自Cumulative Mean with Grouping and LagGrouped moving average in r 的后续问题。

我希望创建一个累积平均值字段,该字段具有滞后于多个变量的分组,但仅计算某些标准的平均值。因此对于下面的示例,S-AVG 仅给出 S 的累积平均值,反之亦然 O-AVG 和 J-AVG。我确信使用 ave 和 cumsum 可以做到这一点,但我不确定该怎么做。

这是所需的输出:

Player  Goals   **S-AVG**    **O-AVG**    **J-AVG**
S       5                         
S       2       5
S       7       3.5         
O       3                    
O       9                     3
O       6                     6      
O       3                     3 
S       7       4.66         
O       1                     5.25
S       7       5.25         
S       3       5.6         
Q       8                     4.4            
S       3       5.16                  
O       4                     5           
P       1                     4.857
S       9       4.857         
S       4       5.375         
Z       6                     4.375        
S       3       5.22         
O       8                     4.55            
S       3       5                  
O       4                     4.9      
O       1                     4.81      
S       9       4.81                  
S       4       5.16                  
O       6                     4.5       
J       6                     

这是 r 的数据输入

Player <- c('S','S','S','O','O','O','O','S','O','S','S','O','S','O','O','S','S','O','S','O','S','O','O','S','S','O','J')
Goals <- c(5,2,7,3,9,6,3,7,1,7,3,8,3,4,1,9,4,6,3,8,3,4,1,9,4,6,6)
data.frame(Player, Goals)

感谢任何帮助。

【问题讨论】:

  • 我认为如果您只是将子集分成 3 个数据帧,计算累积平均值然后重新组合,这可能会更直接。
  • 你不能将数据从以前的anwser重塑为这种格式吗?
  • 此外,熔化和铸造也可能有所帮助。可能很好看。
  • 我希望能够做到这一点,而不必进行子集化和重新合并,因为我将大规模地这样做。我一直在用 cumroll

标签: r


【解决方案1】:

假设DF2 是在my answer 中计算到问题中引用的prior post 的数据框,即具有AVG 列的数据框。它也在此答案末尾的注释中复制。

如果我们只有一个或少数固定数量的玩家,我们可以通过为每个玩家写一个AVG.* 列来做到这一点(显示为一个玩家):

transform(DF2, AVG.S = ifelse(Player == "S", AVG, NA))

但以下是更通用的方法。将levs 设置为Player 因子的级别,或者如果您不想要所有玩家,则应将levs 设置为您想要的玩家的字符向量。然后使用sapply 构造一个逻辑矩阵并将其转换为1 和NA 的矩阵,然后将其标量乘以AVG

该解决方案具有许多理想的功能——它不会覆盖其输入(这很容易出错)并且避免了不必要的重复限定(都感谢transform),它使用整个对象方法而不是循环和下标,它利用现有代码避免重复(通过使用该问题是后续解决方案的结果)并且很简短——两行代码。它不使用任何包。

(另请注意,作为替代,sapply(...) 可以替换为 model.matrix(~ Player + 0),在这种情况下,列名会略有不同。)

levs <- levels(DF2$Player)
transform(DF2, Avg = ifelse(sapply(levs, `==`, Player), 1, NA) * AVG)

给予:

   Player Goals      AVG Avg.J    Avg.O    Avg.S
1       S     5       NA    NA       NA       NA
2       S     2 5.000000    NA       NA 5.000000
3       S     7 3.500000    NA       NA 3.500000
4       O     3       NA    NA       NA       NA
5       O     9 3.000000    NA 3.000000       NA
6       O     6 6.000000    NA 6.000000       NA
7       O     3 6.000000    NA 6.000000       NA
8       S     7 4.666667    NA       NA 4.666667
9       O     1 5.250000    NA 5.250000       NA
10      S     7 5.250000    NA       NA 5.250000
11      S     3 5.600000    NA       NA 5.600000
12      O     8 4.400000    NA 4.400000       NA
13      S     3 5.166667    NA       NA 5.166667
14      O     4 5.000000    NA 5.000000       NA
15      O     1 4.857143    NA 4.857143       NA
16      S     9 4.857143    NA       NA 4.857143
17      S     4 5.375000    NA       NA 5.375000
18      O     6 4.375000    NA 4.375000       NA
19      S     3 5.222222    NA       NA 5.222222
20      O     8 4.555556    NA 4.555556       NA
21      S     3 5.000000    NA       NA 5.000000
22      O     4 4.900000    NA 4.900000       NA
23      O     1 4.818182    NA 4.818182       NA
24      S     9 4.818182    NA       NA 4.818182
25      S     4 5.166667    NA       NA 5.166667
26      O     6 4.500000    NA 4.500000       NA
27      J     6       NA    NA       NA       NA

注意:这被用作上面的输入:

DF2 <- structure(list(Player = structure(c(3L, 3L, 3L, 2L, 2L, 2L, 2L, 
3L, 2L, 3L, 3L, 2L, 3L, 2L, 2L, 3L, 3L, 2L, 3L, 2L, 3L, 2L, 2L, 
3L, 3L, 2L, 1L), .Label = c("J", "O", "S"), class = "factor"), 
    Goals = c(5, 2, 7, 3, 9, 6, 3, 7, 1, 7, 3, 8, 3, 4, 1, 9, 
    4, 6, 3, 8, 3, 4, 1, 9, 4, 6, 6), AVG = c(NA, 5, 3.5, NA, 
    3, 6, 6, 4.66666666666667, 5.25, 5.25, 5.6, 4.4, 5.16666666666667, 
    5, 4.85714285714286, 4.85714285714286, 5.375, 4.375, 5.22222222222222, 
    4.55555555555556, 5, 4.9, 4.81818181818182, 4.81818181818182, 
    5.16666666666667, 4.5, NA)), .Names = c("Player", "Goals", 
"AVG"), row.names = c(NA, -27L), class = "data.frame")

【讨论】:

  • 这是一个很好的解决方案,但不是回顾前一个数据框不是很长的路要走吗?我还想指出,transform() 对参数的评估是非标准的,因此只能以交互方式使用。
  • 关于变换,这不是“个人风格”。这几乎就是帮助页面上的警告。子集也一样。当以交互方式使用时,它们可以使代码更易于阅读。如果你在它周围包裹一个函数,我参考?transform 以获取 R 核心团队的建议。
  • 有没有想过为什么mutate 存在以及为什么Hadley 不简单地使用transform,它的作用完全一样?为什么他提供了一个论点.dots -quote- 来解决非标准评估 -unquote- ?你可以称之为意见,但我不是唯一拥有它的人。实际上,这种观点是 dplyr 包存在的原因之一。
【解决方案2】:

另一种方法是简单地使用索引。首先制作一个函数cummean(这很简单……):

cummean <- function(x){
  cumsum(x) / seq_along(x)
}

然后计算累积均值并存储在列表中(simplify = FALSE):

avgs <- with(mydf,
             tapply(Goals,Player,cummean,
                    simplify = FALSE))

最后,根据玩家名称创建变量,方便地添加为tapply返回的列表的名称。我专门使用for 循环来避免每次都重建完整的数据框。使用索引,我可以以一种更有效的方式填充数据框,并且仍然具有您想要的滞后。 :

for(i in names(avgs)){
  theavg <- avgs[[i]]
  mydf[[i]][mydf$Player == i] <- c(NA, theavg[-length(theavg)])
}

【讨论】:

    猜你喜欢
    • 2015-11-19
    • 2019-12-21
    • 1970-01-01
    • 2016-07-11
    • 2015-12-10
    • 2012-06-19
    • 1970-01-01
    相关资源
    最近更新 更多