【问题标题】:How do I make my for loop properly calculate means over time?如何使我的 for 循环随着时间的推移正确计算平均值?
【发布时间】:2020-06-06 08:27:07
【问题描述】:

我拥有自 2003 年以来发生的所有 NCAA 篮球比赛的数据。我正在尝试实现一个 for 循环,该循环将计算某个时间点每次统计数据的平均值。这是我的 for 循环:

library(data.table)

roll_season_team_stats <- NULL

for (i in 0:max(stats_DT$DayNum)) {
  stats <- stats_DT[DayNum < i]
  roll_stats <- dcast(stats_DT, TeamID+Season~.,fun=mean,na.rm=T,value.var = c('FGM', 'FGA', 'FGM3', 'FGA3', 'FTM', 'FTA', 'OR', 'DR', 'TO'))
  roll_stats$DayNum <- i + 1
  roll_season_team_stats <- rbind(roll_season_team_stats, roll_stats)
}

这是 dput 的输出:

structure(list(Season = c(2003L, 2003L, 2003L, 2003L, 2003L, 
2003L, 2003L, 2003L, 2003L, 2003L), DayNum = c(10L, 10L, 11L, 
11L, 11L, 11L, 12L, 12L, 12L, 12L), TeamID = c(1104L, 1272L, 
1266L, 1296L, 1400L, 1458L, 1161L, 1186L, 1194L, 1458L), FGM = c(27L, 
26L, 24L, 18L, 30L, 26L, 23L, 28L, 28L, 32L), FGA = c(58L, 62L, 
58L, 38L, 61L, 57L, 55L, 62L, 58L, 67L), FGM3 = c(3L, 8L, 8L, 
3L, 6L, 6L, 2L, 4L, 5L, 5L), FGA3 = c(14L, 20L, 18L, 9L, 14L, 
12L, 8L, 14L, 11L, 17L), FTM = c(11L, 10L, 17L, 17L, 11L, 23L, 
32L, 15L, 10L, 15L), FTA = c(18L, 19L, 29L, 31L, 13L, 27L, 39L, 
21L, 18L, 19L), OR = c(14L, 15L, 17L, 6L, 17L, 12L, 13L, 13L, 
9L, 14L), DR = c(24L, 28L, 26L, 19L, 22L, 24L, 18L, 35L, 22L, 
22L), TO = c(23L, 13L, 10L, 12L, 14L, 9L, 17L, 19L, 17L, 6L)), row.names = c(NA, 
-10L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x102004ae0>)

循环成功运行,但没有产生正确的输出。它不是显示团队随时间推移的平均值,而是每天给我相同的数字(我假设是每个统计数据的总体平均值)。任何想法我的循环有什么问题?谢谢!

【问题讨论】:

  • 您没有使用您在第一行循环中分配的stats。在dcast 中使用stats 而不是stats_DT
  • 如果我将 stats_DT 更改为 stats 我会收到以下错误:
  • dcast.data.table 中的错误(stats, TeamID + Season ~ ., fun = mean, na.rm = T, : Can not cast an empty data.table

标签: r loops for-loop debugging data.table


【解决方案1】:

如果我理解正确,OP 想要计算每个团队和每个赛季的一些变量的累积平均值显示团队随时间变化的平均值”。

虽然 OP 使用术语“roll”,例如 roll_statsroll_season_team_stats,但他的代码表明他不是在使用滚动均值,而是希望从第一个开始计算累积均值DayNum on,例如:

stats <- stats_DT[DayNum < i]

但是,可以直接计算累积均值,而无需在 for 循环中分段创建结果,也无需在 lapply() 之后再组合这些部分。

不幸的是,OP 提供的示例数据集确实包含许多不同团队的行,但没有历史,即连续几天没有同一团队的数据。因此,我修改了示例数据集进行演示:

# create new sample data set
stats_DT2 <- copy(stats_DT)[, TeamID := c(1:2, 1:4, 1:4)][]
stats_DT2
    Season DayNum TeamID FGM FGA FGM3 FGA3 FTM FTA OR DR TO
 1:   2003     10      1  27  58    3   14  11  18 14 24 23
 2:   2003     10      2  26  62    8   20  10  19 15 28 13
 3:   2003     11      1  24  58    8   18  17  29 17 26 10
 4:   2003     11      2  18  38    3    9  17  31  6 19 12
 5:   2003     11      3  30  61    6   14  11  13 17 22 14
 6:   2003     11      4  26  57    6   12  23  27 12 24  9
 7:   2003     12      1  23  55    2    8  32  39 13 18 17
 8:   2003     12      2  28  62    4   14  15  21 13 35 19
 9:   2003     12      3  28  58    5   11  10  18  9 22 17
10:   2003     12      4  32  67    5   17  15  19 14 22  6

现在,由于每个团队有 2 到 3 行,因此可以通过以下方式计算累积均值:

# define function for cummulative mean
cummean <- function(x) cumsum(x) / seq_along(x)
# define variables to compute on
cols <- c('FGM', 'FGA', 'FGM3', 'FGA3', 'FTM', 'FTA', 'OR', 'DR', 'TO')
# compute aggregates 
stats_DT2[order(DayNum), c(.(DayNum = DayNum), lapply(.SD, cummean)), 
          .SDcols = cols, by = .(TeamID, Season)][]
    TeamID Season DayNum   FGM  FGA  FGM3  FGA3  FTM   FTA    OR    DR    TO
 1:      1   2003     10 27.00 58.0 3.000 14.00 11.0 18.00 14.00 24.00 23.00
 2:      1   2003     11 25.50 58.0 5.500 16.00 14.0 23.50 15.50 25.00 16.50
 3:      1   2003     12 24.67 57.0 4.333 13.33 20.0 28.67 14.67 22.67 16.67
 4:      2   2003     10 26.00 62.0 8.000 20.00 10.0 19.00 15.00 28.00 13.00
 5:      2   2003     11 22.00 50.0 5.500 14.50 13.5 25.00 10.50 23.50 12.50
 6:      2   2003     12 24.00 54.0 5.000 14.33 14.0 23.67 11.33 27.33 14.67
 7:      3   2003     11 30.00 61.0 6.000 14.00 11.0 13.00 17.00 22.00 14.00
 8:      3   2003     12 29.00 59.5 5.500 12.50 10.5 15.50 13.00 22.00 15.50
 9:      4   2003     11 26.00 57.0 6.000 12.00 23.0 27.00 12.00 24.00  9.00
10:      4   2003     12 29.00 62.0 5.500 14.50 19.0 23.00 13.00 23.00  7.50

或者,可以附加累积均值:

# append cumulative columns
stats_DT2[order(DayNum), paste0("cm_", cols) := lapply(.SD, cummean), 
          .SDcols = cols, by = .(TeamID, Season)][]
    Season DayNum TeamID FGM FGA FGM3 FGA3 FTM FTA OR DR TO cm_FGM cm_FGA cm_FGM3 cm_FGA3 cm_FTM cm_FTA cm_OR cm_DR cm_TO
 1:   2003     10      1  27  58    3   14  11  18 14 24 23  27.00   58.0   3.000   14.00   11.0  18.00 14.00 24.00 23.00
 2:   2003     10      2  26  62    8   20  10  19 15 28 13  26.00   62.0   8.000   20.00   10.0  19.00 15.00 28.00 13.00
 3:   2003     11      1  24  58    8   18  17  29 17 26 10  25.50   58.0   5.500   16.00   14.0  23.50 15.50 25.00 16.50
 4:   2003     11      2  18  38    3    9  17  31  6 19 12  22.00   50.0   5.500   14.50   13.5  25.00 10.50 23.50 12.50
 5:   2003     11      3  30  61    6   14  11  13 17 22 14  30.00   61.0   6.000   14.00   11.0  13.00 17.00 22.00 14.00
 6:   2003     11      4  26  57    6   12  23  27 12 24  9  26.00   57.0   6.000   12.00   23.0  27.00 12.00 24.00  9.00
 7:   2003     12      1  23  55    2    8  32  39 13 18 17  24.67   57.0   4.333   13.33   20.0  28.67 14.67 22.67 16.67
 8:   2003     12      2  28  62    4   14  15  21 13 35 19  24.00   54.0   5.000   14.33   14.0  23.67 11.33 27.33 14.67
 9:   2003     12      3  28  58    5   11  10  18  9 22 17  29.00   59.5   5.500   12.50   10.5  15.50 13.00 22.00 15.50
10:   2003     12      4  32  67    5   17  15  19 14 22  6  29.00   62.0   5.500   14.50   19.0  23.00 13.00 23.00  7.50

【讨论】:

  • 这正是我想要的。非常感谢!!
【解决方案2】:

避免在循环中增长对象,这会导致内存中的过度复制。相反,构建一个数据框列表以在循环外一次进行行绑定。

dt_list <- lapply(0:max(stats_DT$DayNum), function(i)
              tryCatch(
                  dcast(stats_DT[DayNum < i], 
                        TeamID + Season ~ ., fun=mean, na.rm=TRUE,
                        value.var = c('FGM', 'FGA', 'FGM3', 'FGA3', 
                                      'FTM', 'FTA', 'OR', 'DR', 'TO')
                       )[, DayNum := i + 1],
                       error = function(e) NULL)
           )        

roll_season_team_stats <- data.table::rbindlist(dt_list)

事实上,您可以在 base R 中使用 aggregate 在数据帧上执行此操作:

stats_DF <- data.frame(stats_DT)

df_list <- lapply(0:max(stats_DT$DayNum), function(i)
              tryCatch(
                 transform(aggregate(cbind(FGM, FGA, FGM3, FGA3, 
                                           FTM, FTA, OR, DR) ~ TeamID + Season, 
                                     stats_DF[stats_DF$DayNum < i,],
                                     FUN = mean,
                                     na.rm = TRUE),
                           DayNum = i + 1),
                       error = function(e) NULL)
           )    

roll_season_team_stats <- do.call(rbind, df_list)

Online Demo

【讨论】:

  • 首先,请删除过去的cmets。见更新。由于每个分组的第一天没有返回任何行,因此您只需将循环代码包装在 tryCatch 中。包括基本 R 解决方案的在线演示。此外,data.table 版本针对这些语义进行了调整。
猜你喜欢
  • 2016-10-08
  • 2020-03-28
  • 2016-12-22
  • 2021-06-21
  • 2021-08-05
  • 2021-02-02
  • 1970-01-01
  • 2011-04-15
  • 1970-01-01
相关资源
最近更新 更多