使用 dplyr 和 apply family 通过多个条件获取多列的滚动平均值

Getting rolling average of multiple column by multiple condition, with dplyr and apply family

我正在对篮球数据进行分析。这是我的数据集的样子(它的一个真正的例子):

df<-data.frame(gmID = 1:20, 
               H.Team = c("CLE", "MIA", "LAL", "PHI", "CLE", "DET", "CHI", "DAL", "UTA", "PHO", "POR", "WAS", "ORL", "CHA", "BOS", "CHI", "ATL", "DAL", "CLE", "WAS"),
               A.Team = c("WAS", "BOS", "DAL", "DEN", "IND", "HOU", "SAC", "WAS", "DAL", "CLE", "LAL", "OKC", "DEN", "IND", "MIL", "CLE", "HOU", "MIA", "UTA", "DEN"),
               H.PTS = c(94, 120, 91, 84, 88, 96, 93, 95, 113, 85, 116, 86, 102, 90, 88, 86, 102, 104, 88, 111),
               A.PTS = c(84, 107, 99, 75, 90, 105, 87, 99, 94, 87, 106, 84, 89, 89, 99, 115, 109, 84, 86, 88),
               H.AST = c(22, 25, 24, 18, 18, 21, 21, 26, 24, 16, 19, 27, 24, 18, 22, 23, 23, 27, 23, 25),
               A.AST = c(26, 24, 22, 19, 22, 28, 14, 22, 20, 19, 21, 18, 22, 19, 26, 34, 22, 18, 19, 16))
df
   gmID H.Team A.Team H.PTS A.PTS H.AST A.AST
1     1    CLE    WAS    94    84    22    26
2     2    MIA    BOS   120   107    25    24
3     3    LAL    DAL    91    99    24    22
4     4    PHI    DEN    84    75    18    19
5     5    CLE    IND    88    90    18    22
6     6    DET    HOU    96   105    21    28
7     7    CHI    SAC    93    87    21    14
8     8    DAL    WAS    95    99    26    22
9     9    UTA    DAL   113    94    24    20
10   10    PHO    CLE    85    87    16    19
11   11    POR    LAL   116   106    19    21
12   12    WAS    OKC    86    84    27    18
13   13    ORL    DEN   102    89    24    22
14   14    CHA    IND    90    89    18    19
15   15    BOS    MIL    88    99    22    26
16   16    CHI    CLE    86   115    23    34
17   17    ATL    HOU   102   109    23    22
18   18    DAL    MIA   104    84    27    18
19   19    CLE    UTA    88    86    23    19
20   20    WAS    DEN   111    88    25    16

为了简化问题,我从原始数据集中选择了 20 行和 2 对游戏统计数据,主队 (H.) 和客队 (A.) 的得分 (PTS) 和助攻 (AST) (我的数据中还有 50 对游戏统计数据)。 请注意,行按日期排序,因此不需要进行排列。

目标是对比赛结果进行预测(如果 H.Team 获胜则为 1,如果 A.Team 获胜则为 0),但是现在的数据形式没有用,因为统计信息比赛结束后报告。

因此,想法是前n个匹配的滚动平均值替换数据框的每个值。我将在我的工作中设置 n=7n=10,但现在这并不重要,所以为了简化我将设置 n=3.

输出应如下所示:

   gmID H.Team A.Team   H.PTSav   A.PTSav  H.ASTav  A.ASTav
1     1    CLE    WAS        NA        NA       NA       NA
2     2    MIA    BOS        NA        NA       NA       NA
3     3    LAL    DAL        NA        NA       NA       NA
4     4    PHI    DEN        NA        NA       NA       NA
5     5    CLE    IND  94.00000        NA 22.00000       NA
6     6    DET    HOU        NA        NA       NA       NA
7     7    CHI    SAC        NA        NA       NA       NA
8     8    DAL    WAS  99.00000  84.00000 22.00000 26.00000
9     9    UTA    DAL        NA  97.00000       NA 24.00000
10   10    PHO    CLE        NA  91.00000       NA 20.00000
11   11    POR    LAL        NA  91.00000       NA 24.00000
12   12    WAS    OKC  91.50000        NA 24.00000       NA
13   13    ORL    DEN        NA  75.00000       NA 19.00000
14   14    CHA    IND        NA  90.00000       NA 22.00000
15   15    BOS    MIL 107.00000        NA 24.00000       NA
16   16    CHI    CLE  93.00000  89.66667 21.00000 19.66667
17   17    ATL    HOU        NA 105.00000       NA 28.00000
18   18    DAL    MIA  96.00000 120.00000 22.66667 25.00000
19   19    CLE    UTA  96.66667 113.00000 23.66667 24.00000
20   20    WAS    DEN  89.66667  82.00000 25.00000 20.50000

例如,对于打了 5 场比赛的球队 CLE,平均 PTS 值如下:

   gmID    avpts
1    1       NA    ---> NA
2    5 94.00000    ---> 94/1 
3   10 91.00000    ---> (94+88)/2 
4   16 89.66667    ---> (94+88+87)/3 
5   19 96.66667    ---> (88+87+115)/3 

我使用了dplyr,特别是zoo::rollaply函数来获取上面显示的值,代码如下:

library(dplyr)
library(zoo)

sub<- df %>%
  filter(H.Team == "CLE" | A.Team == "CLE") %>%
  mutate(avpts = lag(rollapply(ifelse(H.Team == "CLE", H.PTS,A.PTS), width=3, FUN=mean, align="right", fill=NA, partial=1))) %>%
  select(gmID,avpts)
sub

我这样做只是为了一个团队和一个变量,但我可以很容易地为更多在 mutate() 中指定它的变量做这件事,就像这样:

 mutate(avpts = lag(rollapply(ifelse(H.Team == "CLE", H.PTS,A.PTS), width=3, FUN=mean, align="right", fill=NA, partial=1)),
     avast = lag(rollapply(ifelse(H.Team == "CLE", H.AST,A.AST), width=3, FUN=mean, align="right", fill=NA, partial=1)))

问题是我应该为其他 50 个变量执行此操作,最重要的是我需要计算所有团队的值,而不仅仅是一个团队。 此外,我获得了一个具有正确值的列,但我不知道如何将它们替换到“正确”的位置。

我(部分)解决问题的想法是将上面的代码包装在一个函数中,然后使用 apply 系列中的另一个函数来获取所有团队的值,而不使用 for 循环。

我写了下面的函数:

avstats<- function(team) {
  sub <- df %>%
    filter(.data$H.Team == !!team | .data$A.Team == !!team) %>%
    mutate(avpts = lag(rollapply(ifelse(H.Team == !!team, .data$H.PTS, .data$A.PTS),3,mean,align="right",fill=NA,partial=1))) %>%
    select(.data$gmID, .data$avpts)
}

最后,我使用 lapply() 浏览了这个小数据集中的团队列表。

teams <- c("CLE", "MIA", "LAL", "PHI", "DET", "CHI", "DAL", "UTA", "PHO", "POR", "WAS", "ORL", "CHA", "BOS", "ATL", "DEN", "IND", "HOU", "SAC", "OKC","MIL")

lapply(teams,avstats)

这两个函数似乎一切正常。

但是还有两个主要问题我想得到回答:

也许我应该修改我的函数 avstats 添加一些参数并因此使用另一个 apply() 函数,例如 mapply(),但我真的不知道该怎么做。

你要这个吗? (使用了 library(runner) 中的 mean_run)。

  • 您可以为任意数量的变量自动执行此过程。只需在 mutate(across...
  • .cols 参数中使用他们的名字
  • 要更改滚动 window 大小,只需根据选择更改 mean_run 中的 k
df %>% pivot_longer(!gmID, names_to = c("H_T", ".value"),
                    names_pattern = "(.+)\.(.+)") %>%
  group_by(Team) %>%
  mutate(across(.cols = c(PTS, AST), 
                ~ runner::mean_run(x = ., k = 3, lag = 1), 
                .names = '{.col}_av')) %>%
  pivot_wider(id_cols = gmID, 
              names_from = H_T, 
              names_glue = "{H_T}_{.value}", 
              values_from = -c(gmID, H_T))

# A tibble: 20 x 11
    gmID H_Team A_Team H_PTS A_PTS H_AST A_AST H_PTS_av A_PTS_av H_AST_av A_AST_av
   <int> <chr>  <chr>  <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
 1     1 CLE    WAS       94    84    22    26     NA       NA       NA       NA  
 2     2 MIA    BOS      120   107    25    24     NA       NA       NA       NA  
 3     3 LAL    DAL       91    99    24    22     NA       NA       NA       NA  
 4     4 PHI    DEN       84    75    18    19     NA       NA       NA       NA  
 5     5 CLE    IND       88    90    18    22     94       NA       22       NA  
 6     6 DET    HOU       96   105    21    28     NA       NA       NA       NA  
 7     7 CHI    SAC       93    87    21    14     NA       NA       NA       NA  
 8     8 DAL    WAS       95    99    26    22     99       84       22       26  
 9     9 UTA    DAL      113    94    24    20     NA       97       NA       24  
10    10 PHO    CLE       85    87    16    19     NA       91       NA       20  
11    11 POR    LAL      116   106    19    21     NA       91       NA       24  
12    12 WAS    OKC       86    84    27    18     91.5     NA       24       NA  
13    13 ORL    DEN      102    89    24    22     NA       75       NA       19  
14    14 CHA    IND       90    89    18    19     NA       90       NA       22  
15    15 BOS    MIL       88    99    22    26    107       NA       24       NA  
16    16 CHI    CLE       86   115    23    34     93       89.7     21       19.7
17    17 ATL    HOU      102   109    23    22     NA      105       NA       28  
18    18 DAL    MIA      104    84    27    18     96      120       22.7     25  
19    19 CLE    UTA       88    86    23    19     96.7    113       23.7     24  
20    20 WAS    DEN      111    88    25    16     89.7     82       25       20.5