这是 r 中具有分组和滞后的累积平均值和分组移动平均线的后续问题。
我希望创建一个累积均值字段,其滞后为一个,该字段对多个变量进行分组,但仅根据某些标准计算平均值。因此,对于下面的示例,S-AVG 仅给出 S 的累积平均值,反之亦然。我确信使用 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)
任何帮助,不胜感激。
假设DF2
是我在回答问题中引用的上一篇文章时计算的数据框,即具有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")
另一种方法是简单地使用索引。首先创建一个函数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)])
}