这是来自Cumulative Mean with Grouping and Lag和Grouped moving average in r的后续问题。
我希望创建一个滞后为1的累积平均字段,该字段对多个变量进行分组,但仅计算某些条件的平均值。因此,对于下面的示例,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)
感谢任何帮助。
答案 0 :(得分:3)
假设DF2
是在my answer中计算到问题中引用的prior post的数据帧,即具有AVG
列的数据帧。它也在本答案末尾的注释中复制。
如果我们只有一个或一个固定数量的玩家,我们可以通过为每个玩家写一个AVG.*
列(为一个玩家显示)来做到这一点:
transform(DF2, AVG.S = ifelse(Player == "S", AVG, NA))
但接下来是一种更通用的方法。将levs
设置为Player
因素的级别,或者如果您不想要所有玩家,那么levs
应该设置为您想要的玩家的角色向量。然后使用sapply
构造逻辑矩阵并将其转换为1s和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")
答案 1 :(得分:1)
另一种方法是简单地使用索引。首先创建一个函数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)])
}