具有自定义时间片的时间序列数据的平均值 window

Average over time series data with custom time slice window

我正在使用 R 中的一些网球排名数据,它给出了 ATP 巡回赛中所有球员的网球排名随时间的演变。

可以在此处找到我使用的数据示例,给出了 2000 年的排名数据:https://github.com/JeffSackmann/tennis_atp/blob/master/atp_rankings_00s.csv

清理数据:

rankings <- read_csv("data/atp/atp_rankings_00s.csv")
rankings = rankings %>% 
  mutate(rankingDate = lubridate::ymd(ranking_date) ) %>% 
  select(-ranking_date)

现在,假设我想追踪每个玩家在整个十年中的时间演变,并计算他们在此期间的平均排名。然后我可以写:

rankings %>%  
  group_by(player) %>% 
  summarise(
    meanRanking = mean(rank, na.rm = TRUE),
  ) 

但是,假设我想要更多东西。我想沿时间轴切分这些数据,并计算这些切分的平均排名。因此,对于 start=01-01-2000, end=01-01-2008, skip=2 years 之类的东西,我可以表示从 2000 年 1 月 1 日到 2008 年 1 月 1 日期间超过 2 年时间 windows 的排名。如何在 R 中编写这样的“时间切片”代码?

试用 zoo 包中的滚动功能

library(zoo)
set.seed(2137)
my_data <- rnorm(100)
zoo::rollmean(my_data, 10)

根据数据类型和数据的时间频率,它可能看起来像这样

rankings %>%  
  group_by(player) %>% 
  summarise(
    meanRanking = zoo::rollmean(rank, k = 2, na.rm = TRUE),
) 

以下是我如何获取 two-year 数据切片:

rankings %>%
  mutate(floor = 2 * floor(lubridate::year(rankingDate) / 2),
         slice = paste(floor, floor + 1, sep = '-')) %>%
  select(-floor) %>%
  group_by(player, slice) %>%
  summarize(average_ranking = mean(rank)) 
#> # A tibble: 13,537 x 3
#> # Groups:   player [5,711]
#>    player slice     average_ranking
#>     <int> <chr>               <dbl>
#>  1 100149 2000-2001           1332 
#>  2 100149 2004-2005           1052 
#>  3 100183 2004-2005           1477 
#>  4 100236 2000-2001           1106.
#>  5 100236 2002-2003            780 
#>  6 100236 2004-2005            779.
#>  7 100428 2000-2001            939.
#>  8 100428 2002-2003            976 
#>  9 100474 2000-2001           1298.
#> 10 100474 2002-2003            981 

我们可以用它来查看在任何 two-year 时期平均排名前 5 的球员的轨迹,观察明星们进入和退出竞争。

rankings %>%
  mutate(floor = 2 * round(lubridate::year(rankingDate) / 2),
         slice = paste(floor, floor + 1, sep = '-')) %>%
  select(-floor) %>%
  group_by(player, slice) %>%
  summarize(average_ranking = mean(rank)) %>%
  filter(any(average_ranking < 5)) %>%
  ggplot(aes(slice, average_ranking, group = player, color = factor(player))) + 
  geom_line(size = 1) +
  coord_cartesian(ylim = c(0, 100)) +
  theme_light(base_size = 16) +
  theme(legend.position = 'none')

您的数据相当大,data.table 可以大大提高速度。这是一个非常快速的方法,它使用了一个灵活的函数f(s,e,p,u),它允许你传入任何开始(s)或结束(e)日期,一个整数周期(例如 2 表示 2 年),以及时间单位 (u),其取值 "y""m""d",分别代表年、月、日

f <- function(s,e,p, u=c("y","m","d")) {
  u=match.arg(u)
  uf = list("y"=years,"m"=months,"d"=days)
  data.table(s = seq(as.Date(s), as.Date(e),by=paste(p,u)))[,`:=`(e=s %m+% uf[[u]](p), period=1:.N)]
}

然后您只需将该函数应用于non-equi join

中的排名
setDT(rankings)
rankings[f("2000-01-01", "2008-01-01",2), on=.(ranking_date>=s, ranking_date<=e)] %>% 
  .[,.(ranking=mean(rank,na.rm=T)), by=.(player,period )]

输出:

       player period     ranking
    1: 101736      1    2.769231
    2: 102338      1    5.211538
    3: 101948      1    4.730769
    4: 103017      1   23.259615
    5: 102856      1    2.538462
   ---                          
13543: 105996      5 1780.500000
13544: 105050      5 1665.333333
13545: 105757      5 1781.000000
13546: 121555      5 1328.500000
13547: 106018      5 1508.000000

3 个月 windows,只需使用 p=3 和 u="m"

调用 f
rankings[f("2000-01-01", "2008-01-01",3, "m"), on=.(ranking_date>=s, ranking_date<=e)] %>% 
  .[,.(ranking=mean(rank,na.rm=T)), by=.(player,period )]

产出(3个月windows)

       player period     ranking
    1: 101736      1    1.000000
    2: 102338      1    2.666667
    3: 101948      1    2.333333
    4: 103017      1    4.000000
    5: 102856      1    5.500000
   ---                          
62491: 105517     33 1502.000000
62492: 104405     33 1588.000000
62493: 104576     33 1588.000000
62494: 105500     33 1679.000000
62495: 108698     33 1844.000000