Обзор: Ниже приведен важный фрагмент кода R для моего сайта по баскетбольной статистике.На высоком уровне код R преобразует статистику состава, где каждая строка представляет уникальный состав (состав представляет собой комбинацию из 5 игроков, играющих вместе), в статистику включения / выключения, где каждая строка представляет общую статистику команды с определеннымигрок (а) на площадке или (б) вне площадки.
Я чувствовал, что небольшой фрагмент данных не будет работать для этого воспроизводимого примера, и поэтому я загрузилданные в Google Sheet и сделали этот лист общедоступным.Воспроизводимый код захватывает эти данные CSV, но вы также можете легко загрузить файл, посетив URL.
С учетом всего вышесказанного, вот тройной вложенный цикл, с которым я работаю, с которым я 'я сделал все возможное, чтобы прокомментировать:
# Raw Data Is Lineup Data - Each Row contains stats for a single lineup (combination of 5 basketball players)
sheets_url <- 'https://docs.google.com/spreadsheets/d/1GjDbWfZglwdwMwhNemWpX6uWjhmYfpQe-WNcCNE8EK4/export?format=csv&id=1GjDbWfZglwdwMwhNemWpX6uWjhmYfpQe-WNcCNE8EK4&gid=218640693'
raw.lineup.stats <- httr::content(httr::GET(url = sheets_url))
# Will contain the final output
on.off.stats <- c()
all_seasons <- c('1718', '1819')
# Loop each season
for(i in 1:length(all_seasons)) {
# Filter Lineup Data to include only lineups / stats from this season
this_season <- all_seasons[i]
season.lineup.stats <- raw.lineup.stats %>% dplyr::filter(season == this_season)
all_teams <- unique(season.lineup.stats$teamId)
# Loop each team that appeared in data for this season
for(j in 1:length(all_teams)) {
# Filter Lineup Data again to include only lineups / stats for this team
print(paste0(j, ': ', all_teams[j]))
this_team <- all_teams[j]
team.season.lineup.stats <- season.lineup.stats %>% dplyr::filter(teamId == this_team)
players_on_team <- unique(c(team.season.lineup.stats$onCtId1, team.season.lineup.stats$onCtId2, team.season.lineup.stats$onCtId3, team.season.lineup.stats$onCtId4, team.season.lineup.stats$onCtId5))
# Loop each player on team j
for(k in 1:length(players_on_team)) {
# Identify if player is on-court or off-court - is his ID one of the 5
this_player <- players_on_team[k]
this.players.teams.lineup.stats <- team.season.lineup.stats %>%
dplyr::mutate(isOnOrOff = ifelse(onCtId1 == this_player | onCtId2 == this_player | onCtId3 == this_player
| onCtId4 == this_player | onCtId5 == this_player, 'On Ct', 'Off Ct')) %>%
dplyr::mutate(playerId = this_player) %>%
dplyr::select(playerId, isOnOrOff, everything())
# Convert this team' lineup data into 2 Rows: 1 for team's stats w/ player on-court, and 1 for team's stats w/ player off-court
this.players.onoff.stats <- this.players.teams.lineup.stats %>%
dplyr::group_by(playerId, isOnOrOff) %>%
dplyr::mutate_at(vars(possessions:minutes), .funs = sum) %>%
dplyr::mutate_at(vars(fieldGoalsMade:oppDefensiveReboundPct), .funs = sum) %>%
dplyr::filter(!duplicated(isOnOrOff))
# If player played every minute for his team, nrow(this.players.onoff.stats) == 1. If so, create needed blank off-row
if(nrow(this.players.onoff.stats) == 1) {
off.row <- this.players.onoff.stats %>%
dplyr::ungroup() %>% dplyr::mutate(isOnOrOff = 'Off Ct') %>%
dplyr::mutate_at(vars(possessions:oppPersonalFoulsPer40), .funs = function(x) return(0)) %>%
dplyr::group_by(playerId, isOnOrOff)
this.players.onoff.stats <- this.players.onoff.stats %>% rbind(off.row)
}
# And Rbind to the main container
on.off.stats <- on.off.stats %>% base::rbind(this.players.onoff.stats)
}
}
}
Пожалуйста, дайте мне знать, если в этом примере нет ничего воспроизводимого.Сбор данных и циклы for, все работают на моем конце. Поток кода на высоком уровне (это все в комментариях к коду) делает это:
- Фильтрация данных очереди за один сезон
- Фильтрация данных очередидля одиночной команды
- Для каждого игрока в команде добавьте столбец индикатора
isOnOrOff
, в котором указано, является ли указанный игрок одним из 5 игроков в каждом составе / ряду. - Используйте столбец isOnOrOff с group_byчтобы преобразовать статистику состава команды сезона в статистику включения / выключения для конкретного игрока.
- Если игрок играл каждую минуту за свою команду, добавьте пустую строку «off».
- rbindСтатистика включения / выключения игрока на выходной фрейм данных.
Следуя комментариям при просмотре кода, мы надеемся прояснить, как происходит преобразование данных из статистики в линейке в статистику включения / выключения.
Текущая скорость / Будущие данные: Что касается текущей скорости, этот цикл занял 1,6 минуты в последний раз, когда я его запускал.Со всей статистикой (я удалил ~ 300 столбцов в данных примера), цикл занимает 3,5 минуты.Это данные о баскетболе в колледже, и в настоящее время я использовал только ~ 40 команд при создании своего сайта.Это скоро изменится до ~ 350 команд, и с этим изменением у каждой команды будет еще ~ 50% больше составов.В целом размер данных увеличится в ~ 15 раз.
Учитывая, что я использую цикл for, я ожидаю как минимум 15-кратного замедления, если не больше (15-кратных циклов, но каждый цикл может работать медленнее с большим общим набором данных) с полным набором данных,Я также должен вызывать этот цикл дважды при каждом запуске кода, а не один раз.В целом, я оцениваю время выполнения в 3,5 * 15 раз больше команд * 2 прогона кода == ~ 105 минут.Это слишком долго.Этот мой код нужно будет запускать ежедневно, и этот тройной цикл for является лишь небольшой частью гораздо большего сценария.
Закрыть: любая помощь в этом очень ценится.Я знаю, что это не самый простой цикл для векторизации, и я планирую увеличить этот пост и любые супер полезные ответы, если это будет необходимо.
РЕДАКТИРОВАТЬ: Быстрый общий доступдумал о моем подходе.Я чувствовал, что должен использовать этот подход с вложенным циклом, потому что очень важный group_by должен быть сделан только для статистики команды.Мне все равно, если игрок вне площадки, если состав для совершенно другой команды / для сезона, когда игрок даже не играл в баскетбол в колледже.
РЕДАКТИРОВАТЬ 2: Если бы я мог просто запустить код внутри цикла j
for для i
сезонов и j
команд одновременно (для каждого i
сезона, j
команды, определите игроков в этой командеЦикл игроков в команде, вычисление статистики включения / выключения каждого игрока, готово), что, вероятно, сделало бы работу, верно?