ОП попросил оптимизировать несколько каскадных sqldf
операторов (до редактирования ОП). К сожалению, ФП не объяснил устно, какие агрегаты он реализовал. Таким образом, потребовалось значительное количество реверс-инжиниринга.
Во всяком случае, вот , что я бы сделал , используя data.table
для достижения тех же результатов. Время выполнения уменьшено с 16 секунд для кода OP sqldf
до менее 0,2 секунды для версий data.table
.
data.table
версии отредактированного примера
ОП отредактировал вопрос, чтобы уменьшить количество sqldf
утверждений. Теперь вычисляется только один агрегат.
Новый столбец winner_overall_wins
в data2
- это количество всех матчей, выигранных победителем до фактического турнира. Этот номер прикрепляется ко всем матчам реального турнира, которые выиграл победитель. (Обратите внимание, что это агрегирование отличается от количества матчей, которые были выиграны до фактического матча).
Начиная с версии 1.9.8 (в CRAN 25 ноября 2016 г.), data.table
способен выполнять неравных объединений . Кроме того, fread()
можно посоветовать читать только выбранные столбцы, что еще больше ускоряет ввод / вывод.
library(data.table) # v1.11.2
urls <- sprintf(
"https://raw.githubusercontent.com/JeffSackmann/tennis_atp/master/atp_matches_%i.csv",
2000:2018)
selected_cols <- c("tourney_name", "tourney_date", "match_num",
"winner_id", "winner_name",
"loser_id", "loser_name")
# read only selected columns from files & combine into one data object
matches <- rbindlist(lapply(urls, fread, select = selected_cols))
# non-equi join to compute aggregate, second join to append, order result
system.time({
result_nej <- matches[
unique(matches[matches, on = .(winner_id, tourney_date < tourney_date),
.(winner_overall_wins = .N), by = .EACHI]),
on = .(winner_id, tourney_date)][
order(-tourney_date, tourney_name, -match_num)]
})
Два соединения data.table
и последующее упорядочение заняли в моей системе около 0,15 с против 16–19 с для различных прогонов кода sqldf
ОП.
История конкретного игрока может быть восстановлена с помощью
p_name <- "Federer"; result_nej[winner_name %like% p_name | loser_id %like% p_name]
tourney_name tourney_date match_num winner_id winner_name loser_id loser_name winner_overall_wins
1: Australian Open 20180115 701 103819 Roger Federer 105227 Marin Cilic 1128
2: Australian Open 20180115 602 103819 Roger Federer 111202 Hyeon Chung 1128
3: Australian Open 20180115 504 103819 Roger Federer 104607 Tomas Berdych 1128
4: Australian Open 20180115 408 103819 Roger Federer 105916 Marton Fucsovics 1128
5: Australian Open 20180115 316 103819 Roger Federer 104755 Richard Gasquet 1128
---
1131: Marseille 20000207 3 103819 Roger Federer 102179 Antony Dupuis 4
1132: Davis Cup WG R1: SUI vs AUS 20000204 2 103819 Roger Federer 102882 Mark Philippoussis 3
1133: Australian Open 20000117 90 103819 Roger Federer 102466 Jan Kroslak 1
1134: Australian Open 20000117 52 103819 Roger Federer 102021 Michael Chang 1
1135: Adelaide 20000103 2 103819 Roger Federer 102533 Jens Knippschild 0
Существует альтернативное и более быстрое решение с использованием cumsum()
и shift()
:
system.time({
# cumumlative operations require ordered data
setorder(matches, tourney_date, tourney_name, match_num)
# add tourney id for convenience and conciseness
matches[, t_id := rleid(tourney_date, tourney_name)]
# aggregate by player and tourney
p_t_hist <- matches[, .(winner_won = .N), by = .(winner_id, t_id)]
# compute cumulative sum for each player and
# lag to show only matches of previous tourneys
tmp <- p_t_hist[order(t_id),
.(t_id, winner_overall_wins = shift(cumsum(winner_won))),
by = winner_id]
# append new column & order result
result_css <- matches[tmp, on = .(t_id, winner_id)][order(-t_id)]
})
p_name <- "Federer"; result_css[winner_name %like% p_name | loser_id %like% p_name]
В моей системе истекшее время составляет около 0,05 с, что в 3 раза быстрее, чем вариант с неравным соединением, и на величины быстрее, чем подход OP.