gr-analytics 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/PKG-INFO +26 -5
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/README.md +23 -4
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics/__init__.py +30 -23
- gr_analytics-0.4.0/gr_analytics/data/driver_data.csv +265 -0
- gr_analytics-0.4.0/gr_analytics/get_positions.py +119 -0
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics.egg-info/PKG-INFO +26 -5
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics.egg-info/SOURCES.txt +1 -1
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics.egg-info/requires.txt +3 -0
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics.egg-info/top_level.txt +0 -2
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/pyproject.toml +6 -1
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/tests/test_eight_race_average.py +27 -15
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/tests/test_scoring.py +55 -0
- gr_analytics-0.3.2/gr_analytics/data/driver_data.csv +0 -232
- gr_analytics-0.3.2/gr_analytics/data/race_results.csv +0 -89
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/gr_analytics.egg-info/dependency_links.txt +0 -0
- {gr_analytics-0.3.2 → gr_analytics-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gr_analytics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Scoring and salary calculation for GridRival fantasy F1
|
|
5
5
|
Author: nce8
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,6 +14,8 @@ Description-Content-Type: text/markdown
|
|
|
14
14
|
Requires-Dist: pandas
|
|
15
15
|
Requires-Dist: scipy
|
|
16
16
|
Requires-Dist: numpy
|
|
17
|
+
Provides-Extra: fastf1
|
|
18
|
+
Requires-Dist: fastf1; extra == "fastf1"
|
|
17
19
|
|
|
18
20
|
# gr_analytics
|
|
19
21
|
|
|
@@ -39,7 +41,8 @@ scenario = pd.read_csv("my_race.csv")
|
|
|
39
41
|
# Score the event (defaults to the latest round in driver_data)
|
|
40
42
|
result = score_event(scenario)
|
|
41
43
|
|
|
42
|
-
# Or score a specific round
|
|
44
|
+
# Or score a specific race: pass the round one less than the race
|
|
45
|
+
# (round=1 is the post-race-1 state, so this scores race 2). See "Driver Data".
|
|
43
46
|
result = score_event(scenario, round=1)
|
|
44
47
|
|
|
45
48
|
# Score your specific team selection
|
|
@@ -135,19 +138,31 @@ Bundled driver data (`gr_analytics/data/driver_data.csv`) contains starting sala
|
|
|
135
138
|
- `round=0` — pre-season (before Australia 2026)
|
|
136
139
|
- `round=1` — post-Australia 2026
|
|
137
140
|
|
|
141
|
+
Each `round=N` row holds the state **after** race N: the post-race salaries,
|
|
142
|
+
the finishing position from race N, and the 8-race average including race N.
|
|
143
|
+
|
|
144
|
+
**Round semantics for `score_event`:** the `round` argument is that
|
|
145
|
+
post-race state, so `score_event(scenario, round=N)` scores the race that
|
|
146
|
+
*follows* it. Pass the round **one less** than the race you're scoring:
|
|
147
|
+
`round=0` scores race 1 (off the pre-season seeds), `round=1` scores race 2,
|
|
148
|
+
and so on. Improvement points therefore compare each finish to the 8-race
|
|
149
|
+
average *going into* that race (`calculate_eight_race_averages(through_round=round)`).
|
|
150
|
+
|
|
138
151
|
## Eight-Race Average
|
|
139
152
|
|
|
140
153
|
GridRival's "8 race average" can be computed instead of entered by hand.
|
|
141
154
|
GridRival seeds the season with 8 slots holding a hard-coded initial
|
|
142
155
|
average (the `round=0` values in driver_data); each race replaces one
|
|
143
156
|
slot with the driver's classified finishing position, and the displayed
|
|
144
|
-
value is the **ceiling** of the slot mean.
|
|
145
|
-
in `
|
|
157
|
+
value is the **ceiling** of the slot mean. Each driver's finishing
|
|
158
|
+
position per race is recorded in the `finishing_position` column of
|
|
159
|
+
`driver_data.csv` (the `round=N` row holds the finish from race N), so
|
|
160
|
+
the average is derived entirely from `driver_data`.
|
|
146
161
|
|
|
147
162
|
```python
|
|
148
163
|
from gr_analytics import calculate_eight_race_averages, eight_race_average
|
|
149
164
|
|
|
150
|
-
# All drivers, after the latest round
|
|
165
|
+
# All drivers, after the latest round with recorded finishing positions
|
|
151
166
|
calculate_eight_race_averages()
|
|
152
167
|
|
|
153
168
|
# All drivers, after round 2
|
|
@@ -160,6 +175,12 @@ eight_race_average(1, [6, 16])
|
|
|
160
175
|
This reproduces GridRival's displayed values exactly for all rounds so
|
|
161
176
|
far (verified in `tests/test_eight_race_average.py`).
|
|
162
177
|
|
|
178
|
+
`score_event` uses this computed average for improvement points (the value
|
|
179
|
+
*going into* the race, i.e. `calculate_eight_race_averages(through_round=round)`)
|
|
180
|
+
rather than the stored `eight_race_average` column. That column is kept only as
|
|
181
|
+
GridRival's transcribed ground-truth/test oracle and may be blank for recent
|
|
182
|
+
rounds; scoring no longer depends on it.
|
|
183
|
+
|
|
163
184
|
## Lineup Optimisation
|
|
164
185
|
|
|
165
186
|
`optimal_lineup` uses mixed-integer linear programming (via `scipy.optimize.milp`) to find the best 5-driver + 1-constructor lineup within a salary budget.
|
|
@@ -22,7 +22,8 @@ scenario = pd.read_csv("my_race.csv")
|
|
|
22
22
|
# Score the event (defaults to the latest round in driver_data)
|
|
23
23
|
result = score_event(scenario)
|
|
24
24
|
|
|
25
|
-
# Or score a specific round
|
|
25
|
+
# Or score a specific race: pass the round one less than the race
|
|
26
|
+
# (round=1 is the post-race-1 state, so this scores race 2). See "Driver Data".
|
|
26
27
|
result = score_event(scenario, round=1)
|
|
27
28
|
|
|
28
29
|
# Score your specific team selection
|
|
@@ -118,19 +119,31 @@ Bundled driver data (`gr_analytics/data/driver_data.csv`) contains starting sala
|
|
|
118
119
|
- `round=0` — pre-season (before Australia 2026)
|
|
119
120
|
- `round=1` — post-Australia 2026
|
|
120
121
|
|
|
122
|
+
Each `round=N` row holds the state **after** race N: the post-race salaries,
|
|
123
|
+
the finishing position from race N, and the 8-race average including race N.
|
|
124
|
+
|
|
125
|
+
**Round semantics for `score_event`:** the `round` argument is that
|
|
126
|
+
post-race state, so `score_event(scenario, round=N)` scores the race that
|
|
127
|
+
*follows* it. Pass the round **one less** than the race you're scoring:
|
|
128
|
+
`round=0` scores race 1 (off the pre-season seeds), `round=1` scores race 2,
|
|
129
|
+
and so on. Improvement points therefore compare each finish to the 8-race
|
|
130
|
+
average *going into* that race (`calculate_eight_race_averages(through_round=round)`).
|
|
131
|
+
|
|
121
132
|
## Eight-Race Average
|
|
122
133
|
|
|
123
134
|
GridRival's "8 race average" can be computed instead of entered by hand.
|
|
124
135
|
GridRival seeds the season with 8 slots holding a hard-coded initial
|
|
125
136
|
average (the `round=0` values in driver_data); each race replaces one
|
|
126
137
|
slot with the driver's classified finishing position, and the displayed
|
|
127
|
-
value is the **ceiling** of the slot mean.
|
|
128
|
-
in `
|
|
138
|
+
value is the **ceiling** of the slot mean. Each driver's finishing
|
|
139
|
+
position per race is recorded in the `finishing_position` column of
|
|
140
|
+
`driver_data.csv` (the `round=N` row holds the finish from race N), so
|
|
141
|
+
the average is derived entirely from `driver_data`.
|
|
129
142
|
|
|
130
143
|
```python
|
|
131
144
|
from gr_analytics import calculate_eight_race_averages, eight_race_average
|
|
132
145
|
|
|
133
|
-
# All drivers, after the latest round
|
|
146
|
+
# All drivers, after the latest round with recorded finishing positions
|
|
134
147
|
calculate_eight_race_averages()
|
|
135
148
|
|
|
136
149
|
# All drivers, after round 2
|
|
@@ -143,6 +156,12 @@ eight_race_average(1, [6, 16])
|
|
|
143
156
|
This reproduces GridRival's displayed values exactly for all rounds so
|
|
144
157
|
far (verified in `tests/test_eight_race_average.py`).
|
|
145
158
|
|
|
159
|
+
`score_event` uses this computed average for improvement points (the value
|
|
160
|
+
*going into* the race, i.e. `calculate_eight_race_averages(through_round=round)`)
|
|
161
|
+
rather than the stored `eight_race_average` column. That column is kept only as
|
|
162
|
+
GridRival's transcribed ground-truth/test oracle and may be blank for recent
|
|
163
|
+
rounds; scoring no longer depends on it.
|
|
164
|
+
|
|
146
165
|
## Lineup Optimisation
|
|
147
166
|
|
|
148
167
|
`optimal_lineup` uses mixed-integer linear programming (via `scipy.optimize.milp`) to find the best 5-driver + 1-constructor lineup within a salary budget.
|
|
@@ -34,16 +34,6 @@ def driver_data() -> pd.DataFrame:
|
|
|
34
34
|
return pd.read_csv(_DATA_DIR / "driver_data.csv")
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def race_results() -> pd.DataFrame:
|
|
38
|
-
"""
|
|
39
|
-
Load and return race_results.csv as a DataFrame.
|
|
40
|
-
|
|
41
|
-
One row per driver per round with the official classified finishing
|
|
42
|
-
position (DNF/DNS drivers receive their classified position).
|
|
43
|
-
"""
|
|
44
|
-
return pd.read_csv(_DATA_DIR / "race_results.csv")
|
|
45
|
-
|
|
46
|
-
|
|
47
37
|
# ---------------------------------------------------------------------------
|
|
48
38
|
# Lookup tables — Drivers
|
|
49
39
|
# ---------------------------------------------------------------------------
|
|
@@ -372,16 +362,19 @@ def calculate_eight_race_averages(through_round: int = None) -> pd.Series:
|
|
|
372
362
|
"""
|
|
373
363
|
Compute every driver's eight-race average after ``through_round``.
|
|
374
364
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
round-N row holds the
|
|
378
|
-
|
|
365
|
+
Reads everything from driver_data.csv: the round-0 ``eight_race_average``
|
|
366
|
+
seeds and the per-race ``finishing_position`` recorded on each driver's
|
|
367
|
+
round row (the round-N row holds the finishing position from race N,
|
|
368
|
+
parallel to ``points_from_last_race``). Matches the convention of
|
|
369
|
+
driver_data, where the round-N row holds the state *after* race N (so
|
|
370
|
+
the returned values are what GridRival displays going into race N+1).
|
|
379
371
|
|
|
380
372
|
Parameters
|
|
381
373
|
----------
|
|
382
374
|
through_round : int, optional
|
|
383
375
|
Include races 1 through this round. Defaults to the latest round
|
|
384
|
-
in
|
|
376
|
+
whose finishing positions are recorded in driver_data. Pass 0 to
|
|
377
|
+
get the season-start seeds.
|
|
385
378
|
|
|
386
379
|
Returns
|
|
387
380
|
-------
|
|
@@ -389,20 +382,20 @@ def calculate_eight_race_averages(through_round: int = None) -> pd.Series:
|
|
|
389
382
|
Eight-race average indexed by driver_abbr.
|
|
390
383
|
"""
|
|
391
384
|
dd = driver_data()
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
385
|
+
drivers = dd[dd["type"] == "driver"]
|
|
386
|
+
seeds = drivers[drivers["round"] == 0].set_index("driver_abbr")[
|
|
387
|
+
"eight_race_average"
|
|
388
|
+
]
|
|
396
389
|
|
|
397
|
-
|
|
390
|
+
finishes = drivers[(drivers["round"] >= 1) & drivers["finishing_position"].notna()]
|
|
398
391
|
if through_round is None:
|
|
399
|
-
through_round =
|
|
400
|
-
|
|
392
|
+
through_round = int(finishes["round"].max())
|
|
393
|
+
finishes = finishes[finishes["round"] <= through_round].sort_values("round")
|
|
401
394
|
|
|
402
395
|
averages = {
|
|
403
396
|
abbr: eight_race_average(
|
|
404
397
|
seed,
|
|
405
|
-
|
|
398
|
+
finishes.loc[finishes["driver_abbr"] == abbr, "finishing_position"],
|
|
406
399
|
)
|
|
407
400
|
for abbr, seed in seeds.items()
|
|
408
401
|
}
|
|
@@ -531,6 +524,11 @@ def score_event(scenario: pd.DataFrame, round: int = None) -> pd.DataFrame:
|
|
|
531
524
|
round = dd["round"].max()
|
|
532
525
|
dd = dd[dd["round"] == round].copy()
|
|
533
526
|
|
|
527
|
+
# `finishing_position` stored in driver_data is the historical race result
|
|
528
|
+
# (used only for the eight-race average); the race being scored is supplied
|
|
529
|
+
# by `scenario`, so drop the stored column to avoid a merge collision.
|
|
530
|
+
dd = dd.drop(columns=["finishing_position"], errors="ignore")
|
|
531
|
+
|
|
534
532
|
drivers_dd = dd[dd["type"] == "driver"].copy()
|
|
535
533
|
teams_dd = dd[dd["type"] == "team"].copy()
|
|
536
534
|
|
|
@@ -562,6 +560,15 @@ def score_event(scenario: pd.DataFrame, round: int = None) -> pd.DataFrame:
|
|
|
562
560
|
how="left",
|
|
563
561
|
).rename(columns={"race_position": "finishing_position"})
|
|
564
562
|
|
|
563
|
+
# Improvement points compare each driver's finish to GridRival's eight-race
|
|
564
|
+
# average *going into* this race — i.e. the average as of the end of `round`,
|
|
565
|
+
# the state the scenario is scored against (round=0 scores race 1 off the
|
|
566
|
+
# season-start seeds). Compute it rather than trust the stored
|
|
567
|
+
# eight_race_average column, which is left blank for rounds whose GridRival
|
|
568
|
+
# value hasn't been transcribed.
|
|
569
|
+
avgs = calculate_eight_race_averages(through_round=round)
|
|
570
|
+
drivers_merged["eight_race_average"] = drivers_merged["driver_abbr"].map(avgs)
|
|
571
|
+
|
|
565
572
|
df = pd.concat([drivers_merged, teams_dd], ignore_index=True)
|
|
566
573
|
|
|
567
574
|
drivers = df[df["type"] == "driver"].copy()
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
type,driver_abbr,driver_name,driver_team,round,starting_salary,eight_race_average,points_from_last_race,finishing_position
|
|
2
|
+
driver,VER,M. Verstappen,RBR,0,30,1,,
|
|
3
|
+
driver,RUS,G. Russell,MER,0,28.7,2,,
|
|
4
|
+
driver,NOR,L. Norris,MCL,0,27.4,3,,
|
|
5
|
+
driver,PIA,O. Piastri,MCL,0,26.1,4,,
|
|
6
|
+
driver,ANT,K. Antonelli,MER,0,24.8,5,,
|
|
7
|
+
driver,LEC,C. Leclerc,FER,0,23.5,6,,
|
|
8
|
+
driver,ALO,F. Alonso,AMR,0,22.2,7,,
|
|
9
|
+
driver,HAM,L. Hamilton,FER,0,20.9,8,,
|
|
10
|
+
driver,HAD,I. Hadjar,RBR,0,19.6,9,,
|
|
11
|
+
driver,GAS,P. Gasly,ALP,0,18.3,10,,
|
|
12
|
+
driver,STR,L. Stroll,AMR,0,17,11,,
|
|
13
|
+
driver,SAI,C. Sainz Jr,WIL,0,15.7,12,,
|
|
14
|
+
driver,LAW,L. Lawson,RBS,0,14.4,13,,
|
|
15
|
+
driver,ALB,A. Albon,WIL,0,13.1,14,,
|
|
16
|
+
driver,HUL,N. Hulkenberg,AUD,0,11.8,15,,
|
|
17
|
+
driver,BOR,G. Bortoleto,AUD,0,10.5,16,,
|
|
18
|
+
driver,BEA,O. Bearman,HAS,0,9.2,17,,
|
|
19
|
+
driver,OCO,E. Ocon,HAS,0,7.9,18,,
|
|
20
|
+
driver,BOT,V. Bottas,CAD,0,4.7,19,,
|
|
21
|
+
driver,PER,S. Perez,CAD,0,4.7,19,,
|
|
22
|
+
driver,COL,F. Colapinto,ALP,0,4.7,19,,
|
|
23
|
+
driver,LIN,A. Lindblad,RBS,0,4.6,19,,
|
|
24
|
+
team,MER,MER,,0,28.5,1,,
|
|
25
|
+
team,MCL,MCL,,0,28.5,2,,
|
|
26
|
+
team,RBR,RBR,,0,25,3,,
|
|
27
|
+
team,FER,FER,,0,22.5,4,,
|
|
28
|
+
team,AMR,AMR,,0,20,5,,
|
|
29
|
+
team,WIL,WIL,,0,17.5,6,,
|
|
30
|
+
team,RBS,RBS,,0,15,7,,
|
|
31
|
+
team,ALP,ALP,,0,12.5,8,,
|
|
32
|
+
team,AUD,AUD,,0,10,9,,
|
|
33
|
+
team,CAD,CAD,,0,7.5,10,,
|
|
34
|
+
team,HAS,HAS,,0,5,11,,
|
|
35
|
+
driver,VER,M. Verstappen,RBR,1,28.2,2,,6
|
|
36
|
+
driver,RUS,G. Russell,MER,1,29.6,2,,1
|
|
37
|
+
driver,NOR,L. Norris,MCL,1,26.7,4,,5
|
|
38
|
+
driver,PIA,O. Piastri,MCL,1,24.1,7,,21
|
|
39
|
+
driver,ANT,K. Antonelli,MER,1,25.9,5,,2
|
|
40
|
+
driver,LEC,C. Leclerc,FER,1,24.5,6,,3
|
|
41
|
+
driver,ALO,F. Alonso,AMR,1,20.2,9,,18
|
|
42
|
+
driver,HAM,L. Hamilton,FER,1,22.1,8,,4
|
|
43
|
+
driver,HAD,I. Hadjar,RBR,1,17.6,11,,20
|
|
44
|
+
driver,GAS,P. Gasly,ALP,1,18.3,10,,10
|
|
45
|
+
driver,STR,L. Stroll,AMR,1,15,12,,17
|
|
46
|
+
driver,SAI,C. Sainz Jr,WIL,1,13.9,13,,15
|
|
47
|
+
driver,LAW,L. Lawson,RBS,1,14.5,13,,13
|
|
48
|
+
driver,ALB,A. Albon,WIL,1,13.9,14,,12
|
|
49
|
+
driver,HUL,N. Hulkenberg,AUD,1,9.8,16,,22
|
|
50
|
+
driver,BOR,G. Bortoleto,AUD,1,12.5,16,,9
|
|
51
|
+
driver,BEA,O. Bearman,HAS,1,11.2,16,,7
|
|
52
|
+
driver,OCO,E. Ocon,HAS,1,9.9,18,,11
|
|
53
|
+
driver,BOT,V. Bottas,CAD,1,4.5,19,,19
|
|
54
|
+
driver,PER,S. Perez,CAD,1,6.4,19,,16
|
|
55
|
+
driver,COL,F. Colapinto,ALP,1,6.7,19,,14
|
|
56
|
+
driver,LIN,A. Lindblad,RBS,1,6.6,18,,8
|
|
57
|
+
team,MER,MER,,1,28.8,,,
|
|
58
|
+
team,MCL,MCL,,1,25.7,,,
|
|
59
|
+
team,RBR,RBR,,1,22.4,,,
|
|
60
|
+
team,FER,FER,,1,23.7,,,
|
|
61
|
+
team,AMR,AMR,,1,17,,,
|
|
62
|
+
team,WIL,WIL,,1,16.1,,,
|
|
63
|
+
team,RBS,RBS,,1,17.4,,,
|
|
64
|
+
team,ALP,ALP,,1,14.2,,,
|
|
65
|
+
team,AUD,AUD,,1,9.8,,,
|
|
66
|
+
team,CAD,CAD,,1,7.3,,,
|
|
67
|
+
team,HAS,HAS,,1,8,,,
|
|
68
|
+
driver,RUS,G. Russell,MER,2,29.5,2,171,2
|
|
69
|
+
driver,ANT,K. Antonelli,MER,2,27.5,5,174,1
|
|
70
|
+
driver,VER,M. Verstappen,RBR,2,26.2,4,132,16
|
|
71
|
+
driver,LEC,C. Leclerc,FER,2,24.8,6,164,4
|
|
72
|
+
driver,NOR,L. Norris,MCL,2,24.7,6,107,20
|
|
73
|
+
driver,HAM,L. Hamilton,FER,2,24.1,7,169,3
|
|
74
|
+
driver,PIA,O. Piastri,MCL,2,22.1,8,50,19
|
|
75
|
+
driver,GAS,P. Gasly,ALP,2,19.8,10,143,6
|
|
76
|
+
driver,ALO,F. Alonso,AMR,2,18.2,10,78,17
|
|
77
|
+
driver,HAD,I. Hadjar,RBR,2,18.1,11,118,8
|
|
78
|
+
driver,LAW,L. Lawson,RBS,2,16.5,13,142,7
|
|
79
|
+
driver,SAI,C. Sainz Jr,WIL,2,15.9,12,123,9
|
|
80
|
+
driver,BEA,O. Bearman,HAS,2,13.2,15,186,5
|
|
81
|
+
driver,STR,L. Stroll,AMR,2,13,13,75,18
|
|
82
|
+
driver,ALB,A. Albon,WIL,2,11.9,15,68,22
|
|
83
|
+
driver,HUL,N. Hulkenberg,AUD,2,11.8,16,81,11
|
|
84
|
+
driver,OCO,E. Ocon,HAS,2,10.7,17,124,14
|
|
85
|
+
driver,BOR,G. Bortoleto,AUD,2,10.5,16,90,21
|
|
86
|
+
driver,COL,F. Colapinto,ALP,2,8.7,18,130,10
|
|
87
|
+
driver,LIN,A. Lindblad,RBS,2,8.6,17,143,12
|
|
88
|
+
driver,PER,S. Perez,CAD,2,7.3,19,102,15
|
|
89
|
+
driver,BOT,V. Bottas,CAD,2,6.5,19,94,13
|
|
90
|
+
team,MER,MER,,2,29.1,,177,
|
|
91
|
+
team,FER,FER,,2,24.6,,163,
|
|
92
|
+
team,MCL,MCL,,2,22.7,,77,
|
|
93
|
+
team,RBR,RBR,,2,21.7,,110,
|
|
94
|
+
team,RBS,RBS,,2,17.3,,123,
|
|
95
|
+
team,ALP,ALP,,2,16.8,,121,
|
|
96
|
+
team,AMR,AMR,,2,15.7,,72,
|
|
97
|
+
team,WIL,WIL,,2,13.8,,78,
|
|
98
|
+
team,HAS,HAS,,2,11,,125,
|
|
99
|
+
team,AUD,AUD,,2,9.7,,80,
|
|
100
|
+
team,CAD,CAD,,2,9,,83,
|
|
101
|
+
driver,RUS,G. Russell,MER,3,29.5,3,164,4
|
|
102
|
+
driver,ANT,K. Antonelli,MER,3,28.7,4,173,1
|
|
103
|
+
driver,LEC,C. Leclerc,FER,3,26.3,5,162,3
|
|
104
|
+
driver,NOR,L. Norris,MCL,3,24.7,6,118,5
|
|
105
|
+
driver,VER,M. Verstappen,RBR,3,24.6,5,133,8
|
|
106
|
+
driver,PIA,O. Piastri,MCL,3,24.1,8,91,2
|
|
107
|
+
driver,HAM,L. Hamilton,FER,3,23.4,7,158,6
|
|
108
|
+
driver,GAS,P. Gasly,ALP,3,21.7,10,143,7
|
|
109
|
+
driver,LAW,L. Lawson,RBS,3,18,12,140,9
|
|
110
|
+
driver,HAD,I. Hadjar,RBR,3,17.7,11,117,12
|
|
111
|
+
driver,ALO,F. Alonso,AMR,3,16.2,11,79,18
|
|
112
|
+
driver,SAI,C. Sainz Jr,WIL,3,14.9,13,115,15
|
|
113
|
+
driver,HUL,N. Hulkenberg,AUD,3,13.3,15,95,11
|
|
114
|
+
driver,OCO,E. Ocon,HAS,3,12.7,16,130,10
|
|
115
|
+
driver,BOR,G. Bortoleto,AUD,3,11.5,16,98,13
|
|
116
|
+
driver,BEA,O. Bearman,HAS,3,11.2,15,142,22
|
|
117
|
+
driver,STR,L. Stroll,AMR,3,11,14,69,21
|
|
118
|
+
driver,ALB,A. Albon,WIL,3,9.9,16,70,20
|
|
119
|
+
driver,LIN,A. Lindblad,RBS,3,9.7,17,131,14
|
|
120
|
+
driver,COL,F. Colapinto,ALP,3,9,17,117,16
|
|
121
|
+
driver,PER,S. Perez,CAD,3,7.5,18,97,17
|
|
122
|
+
driver,BOT,V. Bottas,CAD,3,6.2,19,87,19
|
|
123
|
+
team,MER,MER,,3,29.3,,175,
|
|
124
|
+
team,FER,FER,,3,24.6,,161,
|
|
125
|
+
team,MCL,MCL,,3,23.8,,106,
|
|
126
|
+
team,RBR,RBR,,3,21.8,,116,
|
|
127
|
+
team,ALP,ALP,,3,17.5,,120,
|
|
128
|
+
team,RBS,RBS,,3,17.3,,120,
|
|
129
|
+
team,AMR,AMR,,3,12.8,,69,
|
|
130
|
+
team,WIL,WIL,,3,12.7,,80,
|
|
131
|
+
team,HAS,HAS,,3,11.2,,114,
|
|
132
|
+
team,AUD,AUD,,3,10.8,,92,
|
|
133
|
+
team,CAD,CAD,,3,8.4,,80,
|
|
134
|
+
driver,ANT,K. Antonelli,MER,4,28.8,4,176,1
|
|
135
|
+
driver,RUS,G. Russell,MER,4,28.7,3,165,4
|
|
136
|
+
driver,NOR,L. Norris,MCL,4,26.7,6,136,2
|
|
137
|
+
driver,LEC,C. Leclerc,FER,4,25.9,6,161,8
|
|
138
|
+
driver,PIA,O. Piastri,MCL,4,25.7,8,114,3
|
|
139
|
+
driver,VER,M. Verstappen,RBR,4,25.3,5,144,5
|
|
140
|
+
driver,HAM,L. Hamilton,FER,4,23.3,7,157,6
|
|
141
|
+
driver,GAS,P. Gasly,ALP,4,19.7,11,130,21
|
|
142
|
+
driver,SAI,C. Sainz Jr,WIL,4,16.4,12,122,9
|
|
143
|
+
driver,LAW,L. Lawson,RBS,4,16,13,126,20
|
|
144
|
+
driver,HAD,I. Hadjar,RBR,4,15.7,13,100,22
|
|
145
|
+
driver,ALO,F. Alonso,AMR,4,15.1,12,85,15
|
|
146
|
+
driver,OCO,E. Ocon,HAS,4,13.2,15,127,13
|
|
147
|
+
driver,BEA,O. Bearman,HAS,4,12.9,15,140,11
|
|
148
|
+
driver,BOR,G. Bortoleto,AUD,4,12.7,15,105,12
|
|
149
|
+
driver,ALB,A. Albon,WIL,4,11.9,15,87,10
|
|
150
|
+
driver,HUL,N. Hulkenberg,AUD,4,11.3,16,91,19
|
|
151
|
+
driver,COL,F. Colapinto,ALP,4,11,16,135,7
|
|
152
|
+
driver,LIN,A. Lindblad,RBS,4,10.5,16,125,14
|
|
153
|
+
driver,STR,L. Stroll,AMR,4,10.4,15,74,17
|
|
154
|
+
driver,PER,S. Perez,CAD,4,8.1,18,98,16
|
|
155
|
+
driver,BOT,V. Bottas,CAD,4,6,19,85,18
|
|
156
|
+
team,MER,MER,,4,29.4,,174,
|
|
157
|
+
team,MCL,MCL,,4,24.7,,120,
|
|
158
|
+
team,FER,FER,,4,24.6,,158,
|
|
159
|
+
team,RBR,RBR,,4,20,,111,
|
|
160
|
+
team,ALP,ALP,,4,18,,118,
|
|
161
|
+
team,RBS,RBS,,4,15.3,,113,
|
|
162
|
+
team,WIL,WIL,,4,15,,90,
|
|
163
|
+
team,HAS,HAS,,4,12.6,,113,
|
|
164
|
+
team,AMR,AMR,,4,11.3,,74,
|
|
165
|
+
team,AUD,AUD,,4,11,,92,
|
|
166
|
+
team,CAD,CAD,,4,7.3,,80,
|
|
167
|
+
driver,ANT,K. Antonelli,MER,5,30.1,,180,1
|
|
168
|
+
driver,RUS,G. Russell,MER,5,26.7,,156,19
|
|
169
|
+
driver,VER,M. Verstappen,RBR,5,26.2,,150,3
|
|
170
|
+
driver,LEC,C. Leclerc,FER,5,25.9,,163,4
|
|
171
|
+
driver,HAM,L. Hamilton,FER,5,25.3,,163,2
|
|
172
|
+
driver,NOR,L. Norris,MCL,5,24.7,,133,18
|
|
173
|
+
driver,PIA,O. Piastri,MCL,5,24.6,,121,11
|
|
174
|
+
driver,GAS,P. Gasly,ALP,5,19.3,,132,8
|
|
175
|
+
driver,SAI,C. Sainz Jr,WIL,5,18,,128,9
|
|
176
|
+
driver,LAW,L. Lawson,RBS,5,18,,135,7
|
|
177
|
+
driver,HAD,I. Hadjar,RBR,5,17.7,,113,5
|
|
178
|
+
driver,BEA,O. Bearman,HAS,5,14.5,,140,10
|
|
179
|
+
driver,ALO,F. Alonso,AMR,5,13.1,,80,20
|
|
180
|
+
driver,COL,F. Colapinto,ALP,5,13,,145,6
|
|
181
|
+
driver,HUL,N. Hulkenberg,AUD,5,12.5,,98,12
|
|
182
|
+
driver,BOR,G. Bortoleto,AUD,5,12.5,,107,13
|
|
183
|
+
driver,OCO,E. Ocon,HAS,5,12.4,,124,14
|
|
184
|
+
driver,STR,L. Stroll,AMR,5,9.9,,81,15
|
|
185
|
+
driver,ALB,A. Albon,WIL,5,9.9,,81,21
|
|
186
|
+
driver,LIN,A. Lindblad,RBS,5,8.5,,110,22
|
|
187
|
+
driver,PER,S. Perez,CAD,5,7.4,,96,17
|
|
188
|
+
driver,BOT,V. Bottas,CAD,5,6.2,,89,16
|
|
189
|
+
team,MER,MER,,5,28.3,,168,
|
|
190
|
+
team,FER,FER,,5,25.9,,158,
|
|
191
|
+
team,MCL,MCL,,5,23.5,,120,
|
|
192
|
+
team,RBR,RBR,,5,21.8,,120,
|
|
193
|
+
team,ALP,ALP,,5,19,,121,
|
|
194
|
+
team,WIL,WIL,,5,14.2,,90,
|
|
195
|
+
team,RBS,RBS,,5,13.8,,108,
|
|
196
|
+
team,HAS,HAS,,5,13,,111,
|
|
197
|
+
team,AUD,AUD,,5,12.5,,96,
|
|
198
|
+
team,AMR,AMR,,5,9.5,,74,
|
|
199
|
+
team,CAD,CAD,,5,7.2,,79,
|
|
200
|
+
driver,ANT,K. Antonelli,MER,6,29.9,,179,1
|
|
201
|
+
driver,HAM,L. Hamilton,FER,6,27,,165,2
|
|
202
|
+
driver,PIA,O. Piastri,MCL,6,25.3,,130,5
|
|
203
|
+
driver,RUS,G. Russell,MER,6,24.7,,150,12
|
|
204
|
+
driver,VER,M. Verstappen,RBR,6,24.2,,139,22
|
|
205
|
+
driver,LEC,C. Leclerc,FER,6,23.9,,153,17
|
|
206
|
+
driver,NOR,L. Norris,MCL,6,22.7,,125,19
|
|
207
|
+
driver,LAW,L. Lawson,RBS,6,20,,140,6
|
|
208
|
+
driver,GAS,P. Gasly,ALP,6,19.7,,134,3
|
|
209
|
+
driver,HAD,I. Hadjar,RBR,6,19.7,,126,4
|
|
210
|
+
driver,SAI,C. Sainz Jr,WIL,6,16,,122,16
|
|
211
|
+
driver,ALO,F. Alonso,AMR,6,14.7,,90,10
|
|
212
|
+
driver,OCO,E. Ocon,HAS,6,14.4,,128,9
|
|
213
|
+
driver,BOR,G. Bortoleto,AUD,6,13.8,,110,11
|
|
214
|
+
driver,HUL,N. Hulkenberg,AUD,6,12.6,,99,13
|
|
215
|
+
driver,BEA,O. Bearman,HAS,6,12.5,,127,20
|
|
216
|
+
driver,COL,F. Colapinto,ALP,6,12.3,,137,14
|
|
217
|
+
driver,ALB,A. Albon,WIL,6,11.9,,94,8
|
|
218
|
+
driver,LIN,A. Lindblad,RBS,6,10.5,,121,7
|
|
219
|
+
driver,PER,S. Perez,CAD,6,8.4,,97,15
|
|
220
|
+
driver,STR,L. Stroll,AMR,6,8.4,,80,18
|
|
221
|
+
driver,BOT,V. Bottas,CAD,6,4.8,,83,21
|
|
222
|
+
team,MER,MER,,6,28.7,,165,
|
|
223
|
+
team,FER,FER,,6,26.2,,155,
|
|
224
|
+
team,MCL,MCL,,6,22.6,,121,
|
|
225
|
+
team,RBR,RBR,,6,21.9,,122,
|
|
226
|
+
team,ALP,ALP,,6,18.5,,121,
|
|
227
|
+
team,RBS,RBS,,6,16.5,,113,
|
|
228
|
+
team,WIL,WIL,,6,14.2,,94,
|
|
229
|
+
team,AUD,AUD,,6,12.4,,98,
|
|
230
|
+
team,HAS,HAS,,6,12.1,,108,
|
|
231
|
+
team,AMR,AMR,,6,8.8,,76,
|
|
232
|
+
team,CAD,CAD,,6,6.4,,79,
|
|
233
|
+
driver,HAM,L. Hamilton,FER,7,28.7,,168,1
|
|
234
|
+
driver,ANT,K. Antonelli,MER,7,27.9,,169,16
|
|
235
|
+
driver,RUS,G. Russell,MER,7,26.6,,154,2
|
|
236
|
+
driver,PIA,O. Piastri,MCL,7,25.8,,132,5
|
|
237
|
+
driver,VER,M. Verstappen,RBR,7,25.4,,142,4
|
|
238
|
+
driver,NOR,L. Norris,MCL,7,24.7,,132,3
|
|
239
|
+
driver,LEC,C. Leclerc,FER,7,21.9,,146,15
|
|
240
|
+
driver,HAD,I. Hadjar,RBR,7,21.2,,129,6
|
|
241
|
+
driver,GAS,P. Gasly,ALP,7,20.8,,135,7
|
|
242
|
+
driver,LAW,L. Lawson,RBS,7,20.3,,139,8
|
|
243
|
+
driver,SAI,C. Sainz Jr,WIL,7,16.1,,121,12
|
|
244
|
+
driver,BOR,G. Bortoleto,AUD,7,14.8,,112,11
|
|
245
|
+
driver,OCO,E. Ocon,HAS,7,14.1,,125,13
|
|
246
|
+
driver,COL,F. Colapinto,ALP,7,14.1,,135,10
|
|
247
|
+
driver,ALO,F. Alonso,AMR,7,12.7,,88,19
|
|
248
|
+
driver,LIN,A. Lindblad,RBS,7,12.5,,123,9
|
|
249
|
+
driver,BEA,O. Bearman,HAS,7,11.5,,121,17
|
|
250
|
+
driver,HUL,N. Hulkenberg,AUD,7,11.2,,96,20
|
|
251
|
+
driver,ALB,A. Albon,WIL,7,10.3,,91,18
|
|
252
|
+
driver,PER,S. Perez,CAD,7,9.2,,99,14
|
|
253
|
+
driver,STR,L. Stroll,AMR,7,6.4,,75,22
|
|
254
|
+
driver,BOT,V. Bottas,CAD,7,4.1,,78,21
|
|
255
|
+
team,MER,MER,,7,27.8,,162,
|
|
256
|
+
team,FER,FER,,7,25.2,,153,
|
|
257
|
+
team,MCL,MCL,,7,24.4,,126,
|
|
258
|
+
team,RBR,RBR,,7,23.2,,126,
|
|
259
|
+
team,ALP,ALP,,7,18.2,,122,
|
|
260
|
+
team,RBS,RBS,,7,17.2,,116,
|
|
261
|
+
team,WIL,WIL,,7,13,,94,
|
|
262
|
+
team,AUD,AUD,,7,12.9,,99,
|
|
263
|
+
team,HAS,HAS,,7,12.1,,106,
|
|
264
|
+
team,AMR,AMR,,7,7.6,,74,
|
|
265
|
+
team,CAD,CAD,,7,6.4,,78,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Fetch official F1 classified finishing positions via FastF1.
|
|
3
|
+
|
|
4
|
+
These feed the ``finishing_position`` column of ``driver_data.csv`` (the
|
|
5
|
+
round-N row holds each driver's finishing position in race N), which
|
|
6
|
+
``calculate_eight_race_averages()`` rolls into GridRival's eight-race average.
|
|
7
|
+
|
|
8
|
+
FastF1 reads the official F1 timing data, so ``Position`` is the *classified*
|
|
9
|
+
finishing position — DNF/DNS drivers keep the position they are classified in,
|
|
10
|
+
matching GridRival's convention. Driver codes are FastF1's three-letter
|
|
11
|
+
``Abbreviation`` values, which are the FIA codes used as ``driver_abbr`` in
|
|
12
|
+
driver_data.csv (VER, RUS, ...).
|
|
13
|
+
|
|
14
|
+
``fastf1`` is an optional dependency (``pip install fastf1``) and is imported
|
|
15
|
+
lazily, so importing :mod:`gr_analytics` never requires it. Fetching a session
|
|
16
|
+
needs network access; results are cached so repeat calls are offline-fast.
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
>>> from gr_analytics.get_positions import race_finishing_positions
|
|
21
|
+
>>> race_finishing_positions(2026, 1) # round 1 (season opener)
|
|
22
|
+
driver_abbr
|
|
23
|
+
RUS 1
|
|
24
|
+
ANT 2
|
|
25
|
+
...
|
|
26
|
+
Name: finishing_position, dtype: int64
|
|
27
|
+
|
|
28
|
+
>>> # Long-format rows for new rounds, shaped like driver_data's per-race data
|
|
29
|
+
>>> from gr_analytics.get_positions import finishing_positions_frame
|
|
30
|
+
>>> finishing_positions_frame(2026, [5, 6])
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
import pandas as pd
|
|
36
|
+
|
|
37
|
+
# Cache outside the repo so fetched timing data isn't committed or shipped.
|
|
38
|
+
_DEFAULT_CACHE = Path.home() / ".cache" / "fastf1"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _enable_cache(cache_dir=None) -> None:
|
|
42
|
+
"""Point FastF1 at a cache directory, creating it if needed."""
|
|
43
|
+
import fastf1
|
|
44
|
+
|
|
45
|
+
cache_dir = Path(cache_dir) if cache_dir is not None else _DEFAULT_CACHE
|
|
46
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
fastf1.Cache.enable_cache(str(cache_dir))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def race_finishing_positions(year: int, rnd: int, cache_dir=None) -> pd.Series:
|
|
51
|
+
"""
|
|
52
|
+
Classified finishing positions for a single race.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
year : int
|
|
57
|
+
Season, e.g. ``2026``.
|
|
58
|
+
rnd : int
|
|
59
|
+
Championship round number (1 = season opener).
|
|
60
|
+
cache_dir : path-like, optional
|
|
61
|
+
FastF1 cache location. Defaults to ``~/.cache/fastf1``.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
pandas.Series
|
|
66
|
+
Finishing position (int) indexed by three-letter ``driver_abbr``,
|
|
67
|
+
sorted by position. Drivers without a classified position (e.g. DNS
|
|
68
|
+
with no time) are dropped. Series name is ``finishing_position`` to
|
|
69
|
+
match driver_data.csv.
|
|
70
|
+
"""
|
|
71
|
+
import fastf1
|
|
72
|
+
|
|
73
|
+
_enable_cache(cache_dir)
|
|
74
|
+
|
|
75
|
+
session = fastf1.get_session(year, rnd, "R")
|
|
76
|
+
# Results are always loaded; skip the heavy laps/telemetry/weather data.
|
|
77
|
+
session.load(laps=False, telemetry=False, weather=False, messages=False)
|
|
78
|
+
|
|
79
|
+
positions = (
|
|
80
|
+
session.results.set_index("Abbreviation")["Position"]
|
|
81
|
+
.dropna()
|
|
82
|
+
.astype(int)
|
|
83
|
+
.sort_values()
|
|
84
|
+
)
|
|
85
|
+
positions.index.name = "driver_abbr"
|
|
86
|
+
positions.name = "finishing_position"
|
|
87
|
+
return positions
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def finishing_positions_frame(year: int, rounds, cache_dir=None) -> pd.DataFrame:
|
|
91
|
+
"""
|
|
92
|
+
Long-format finishing positions for one or more rounds.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
year : int
|
|
97
|
+
Season, e.g. ``2026``.
|
|
98
|
+
rounds : int or iterable of int
|
|
99
|
+
Round number(s) to fetch.
|
|
100
|
+
cache_dir : path-like, optional
|
|
101
|
+
FastF1 cache location. Defaults to ``~/.cache/fastf1``.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
pandas.DataFrame
|
|
106
|
+
Columns ``round``, ``driver_abbr``, ``finishing_position`` — the shape
|
|
107
|
+
of the per-race data recorded in driver_data.csv, handy for filling in
|
|
108
|
+
new rounds.
|
|
109
|
+
"""
|
|
110
|
+
if isinstance(rounds, int):
|
|
111
|
+
rounds = [rounds]
|
|
112
|
+
|
|
113
|
+
frames = []
|
|
114
|
+
for rnd in rounds:
|
|
115
|
+
frame = race_finishing_positions(year, rnd, cache_dir=cache_dir).reset_index()
|
|
116
|
+
frame.insert(0, "round", rnd)
|
|
117
|
+
frames.append(frame)
|
|
118
|
+
|
|
119
|
+
return pd.concat(frames, ignore_index=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gr_analytics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Scoring and salary calculation for GridRival fantasy F1
|
|
5
5
|
Author: nce8
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,6 +14,8 @@ Description-Content-Type: text/markdown
|
|
|
14
14
|
Requires-Dist: pandas
|
|
15
15
|
Requires-Dist: scipy
|
|
16
16
|
Requires-Dist: numpy
|
|
17
|
+
Provides-Extra: fastf1
|
|
18
|
+
Requires-Dist: fastf1; extra == "fastf1"
|
|
17
19
|
|
|
18
20
|
# gr_analytics
|
|
19
21
|
|
|
@@ -39,7 +41,8 @@ scenario = pd.read_csv("my_race.csv")
|
|
|
39
41
|
# Score the event (defaults to the latest round in driver_data)
|
|
40
42
|
result = score_event(scenario)
|
|
41
43
|
|
|
42
|
-
# Or score a specific round
|
|
44
|
+
# Or score a specific race: pass the round one less than the race
|
|
45
|
+
# (round=1 is the post-race-1 state, so this scores race 2). See "Driver Data".
|
|
43
46
|
result = score_event(scenario, round=1)
|
|
44
47
|
|
|
45
48
|
# Score your specific team selection
|
|
@@ -135,19 +138,31 @@ Bundled driver data (`gr_analytics/data/driver_data.csv`) contains starting sala
|
|
|
135
138
|
- `round=0` — pre-season (before Australia 2026)
|
|
136
139
|
- `round=1` — post-Australia 2026
|
|
137
140
|
|
|
141
|
+
Each `round=N` row holds the state **after** race N: the post-race salaries,
|
|
142
|
+
the finishing position from race N, and the 8-race average including race N.
|
|
143
|
+
|
|
144
|
+
**Round semantics for `score_event`:** the `round` argument is that
|
|
145
|
+
post-race state, so `score_event(scenario, round=N)` scores the race that
|
|
146
|
+
*follows* it. Pass the round **one less** than the race you're scoring:
|
|
147
|
+
`round=0` scores race 1 (off the pre-season seeds), `round=1` scores race 2,
|
|
148
|
+
and so on. Improvement points therefore compare each finish to the 8-race
|
|
149
|
+
average *going into* that race (`calculate_eight_race_averages(through_round=round)`).
|
|
150
|
+
|
|
138
151
|
## Eight-Race Average
|
|
139
152
|
|
|
140
153
|
GridRival's "8 race average" can be computed instead of entered by hand.
|
|
141
154
|
GridRival seeds the season with 8 slots holding a hard-coded initial
|
|
142
155
|
average (the `round=0` values in driver_data); each race replaces one
|
|
143
156
|
slot with the driver's classified finishing position, and the displayed
|
|
144
|
-
value is the **ceiling** of the slot mean.
|
|
145
|
-
in `
|
|
157
|
+
value is the **ceiling** of the slot mean. Each driver's finishing
|
|
158
|
+
position per race is recorded in the `finishing_position` column of
|
|
159
|
+
`driver_data.csv` (the `round=N` row holds the finish from race N), so
|
|
160
|
+
the average is derived entirely from `driver_data`.
|
|
146
161
|
|
|
147
162
|
```python
|
|
148
163
|
from gr_analytics import calculate_eight_race_averages, eight_race_average
|
|
149
164
|
|
|
150
|
-
# All drivers, after the latest round
|
|
165
|
+
# All drivers, after the latest round with recorded finishing positions
|
|
151
166
|
calculate_eight_race_averages()
|
|
152
167
|
|
|
153
168
|
# All drivers, after round 2
|
|
@@ -160,6 +175,12 @@ eight_race_average(1, [6, 16])
|
|
|
160
175
|
This reproduces GridRival's displayed values exactly for all rounds so
|
|
161
176
|
far (verified in `tests/test_eight_race_average.py`).
|
|
162
177
|
|
|
178
|
+
`score_event` uses this computed average for improvement points (the value
|
|
179
|
+
*going into* the race, i.e. `calculate_eight_race_averages(through_round=round)`)
|
|
180
|
+
rather than the stored `eight_race_average` column. That column is kept only as
|
|
181
|
+
GridRival's transcribed ground-truth/test oracle and may be blank for recent
|
|
182
|
+
rounds; scoring no longer depends on it.
|
|
183
|
+
|
|
163
184
|
## Lineup Optimisation
|
|
164
185
|
|
|
165
186
|
`optimal_lineup` uses mixed-integer linear programming (via `scipy.optimize.milp`) to find the best 5-driver + 1-constructor lineup within a salary budget.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
pyproject.toml
|
|
3
3
|
gr_analytics/__init__.py
|
|
4
|
+
gr_analytics/get_positions.py
|
|
4
5
|
gr_analytics.egg-info/PKG-INFO
|
|
5
6
|
gr_analytics.egg-info/SOURCES.txt
|
|
6
7
|
gr_analytics.egg-info/dependency_links.txt
|
|
7
8
|
gr_analytics.egg-info/requires.txt
|
|
8
9
|
gr_analytics.egg-info/top_level.txt
|
|
9
10
|
gr_analytics/data/driver_data.csv
|
|
10
|
-
gr_analytics/data/race_results.csv
|
|
11
11
|
tests/test_eight_race_average.py
|
|
12
12
|
tests/test_scoring.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gr_analytics"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Scoring and salary calculation for GridRival fantasy F1"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -18,11 +18,16 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
dependencies = ["pandas", "scipy", "numpy"]
|
|
20
20
|
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
# Only needed for gr_analytics.get_positions (fetching finishing positions).
|
|
23
|
+
fastf1 = ["fastf1"]
|
|
24
|
+
|
|
21
25
|
[project.urls]
|
|
22
26
|
Homepage = "https://github.com/nce8/gr_analytics"
|
|
23
27
|
|
|
24
28
|
[tool.setuptools.packages.find]
|
|
25
29
|
where = ["."]
|
|
30
|
+
include = ["gr_analytics*"]
|
|
26
31
|
|
|
27
32
|
[tool.setuptools.package-data]
|
|
28
33
|
gr_analytics = ["data/*.csv"]
|
|
@@ -13,7 +13,6 @@ from gr_analytics import (
|
|
|
13
13
|
calculate_eight_race_averages,
|
|
14
14
|
driver_data,
|
|
15
15
|
eight_race_average,
|
|
16
|
-
race_results,
|
|
17
16
|
)
|
|
18
17
|
|
|
19
18
|
_TESTS_DIR = Path(__file__).parent
|
|
@@ -69,8 +68,8 @@ def _rounds_with_recorded_averages():
|
|
|
69
68
|
"""Rounds whose driver rows have GridRival eight_race_average values.
|
|
70
69
|
|
|
71
70
|
Later rounds may be entered with only salary/points (eight_race_average
|
|
72
|
-
left blank until the corresponding
|
|
73
|
-
check rounds where GridRival's own values exist to compare against.
|
|
71
|
+
left blank until the corresponding finishing positions are added), so we
|
|
72
|
+
only check rounds where GridRival's own values exist to compare against.
|
|
74
73
|
"""
|
|
75
74
|
dd = driver_data()
|
|
76
75
|
drivers = dd[dd["type"] == "driver"]
|
|
@@ -99,7 +98,11 @@ class TestAgreementWithGridRival:
|
|
|
99
98
|
)
|
|
100
99
|
|
|
101
100
|
def test_default_round_is_latest(self):
|
|
102
|
-
|
|
101
|
+
drivers = driver_data()
|
|
102
|
+
drivers = drivers[drivers["type"] == "driver"]
|
|
103
|
+
latest = int(
|
|
104
|
+
drivers.loc[drivers["finishing_position"].notna(), "round"].max()
|
|
105
|
+
)
|
|
103
106
|
pd.testing.assert_series_equal(
|
|
104
107
|
calculate_eight_race_averages(),
|
|
105
108
|
calculate_eight_race_averages(through_round=latest),
|
|
@@ -107,25 +110,34 @@ class TestAgreementWithGridRival:
|
|
|
107
110
|
|
|
108
111
|
|
|
109
112
|
# ---------------------------------------------------------------------------
|
|
110
|
-
#
|
|
113
|
+
# Recorded finishing-position data integrity (driver_data.csv)
|
|
111
114
|
# ---------------------------------------------------------------------------
|
|
112
115
|
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
def _finishes_by_round():
|
|
118
|
+
"""driver_data finishing positions, one frame per round that records them."""
|
|
119
|
+
drivers = driver_data()
|
|
120
|
+
drivers = drivers[(drivers["type"] == "driver") & drivers["finishing_position"].notna()]
|
|
121
|
+
return {rnd: group for rnd, group in drivers.groupby("round")}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TestFinishingPositionData:
|
|
125
|
+
@pytest.mark.parametrize("rnd", sorted(_finishes_by_round()))
|
|
126
|
+
def test_positions_complete_each_round(self, rnd):
|
|
127
|
+
group = _finishes_by_round()[rnd]
|
|
128
|
+
positions = sorted(group["finishing_position"].astype(int))
|
|
129
|
+
assert positions == list(range(1, len(group) + 1)), (
|
|
130
|
+
f"Round {rnd} finishing positions are not a complete 1..n sequence"
|
|
131
|
+
)
|
|
122
132
|
|
|
123
133
|
def test_round_one_matches_australia_fixture(self):
|
|
124
134
|
"""Race 1 positions must agree with the Australia scenario fixture."""
|
|
125
135
|
australia = pd.read_csv(_TESTS_DIR / "test_australia.csv").set_index(
|
|
126
136
|
"driver_abbr"
|
|
127
137
|
)
|
|
128
|
-
|
|
129
|
-
round_one =
|
|
138
|
+
drivers = driver_data()
|
|
139
|
+
round_one = drivers[
|
|
140
|
+
(drivers["type"] == "driver") & (drivers["round"] == 1)
|
|
141
|
+
].set_index("driver_abbr")
|
|
130
142
|
for abbr, expected in australia["finishing_position"].items():
|
|
131
143
|
assert round_one.at[abbr, "finishing_position"] == expected
|
|
@@ -13,6 +13,8 @@ import pytest
|
|
|
13
13
|
from gr_analytics import (
|
|
14
14
|
_score_constructors,
|
|
15
15
|
_score_drivers,
|
|
16
|
+
calculate_eight_race_averages,
|
|
17
|
+
driver_data,
|
|
16
18
|
optimal_lineup,
|
|
17
19
|
score_event,
|
|
18
20
|
)
|
|
@@ -415,6 +417,59 @@ class TestAustraliaRound0:
|
|
|
415
417
|
assert _get_row(australia_result, abbr)["salary_change"] == exp_change
|
|
416
418
|
|
|
417
419
|
|
|
420
|
+
# ---------------------------------------------------------------------------
|
|
421
|
+
# score_event derives the eight-race average from driver_data
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class TestScoreEventEightRaceAverage:
|
|
426
|
+
"""score_event computes the eight-race average from the recorded
|
|
427
|
+
finishing positions (calculate_eight_race_averages) rather than trusting
|
|
428
|
+
the hard-coded eight_race_average column. So it works for rounds whose
|
|
429
|
+
GridRival value was never transcribed (the column is blank), and uses the
|
|
430
|
+
average going *into* the race being scored (through_round == round)."""
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def _scenario_from_finishes(rnd):
|
|
434
|
+
d = driver_data()
|
|
435
|
+
d = d[(d["type"] == "driver") & (d["round"] == rnd)][
|
|
436
|
+
["driver_abbr", "finishing_position"]
|
|
437
|
+
].copy()
|
|
438
|
+
d["finishing_position"] = d["finishing_position"].astype(int)
|
|
439
|
+
return pd.DataFrame(
|
|
440
|
+
{
|
|
441
|
+
"driver_abbr": d["driver_abbr"],
|
|
442
|
+
"qualifying_position": d["finishing_position"],
|
|
443
|
+
"race_position": d["finishing_position"],
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
@pytest.mark.parametrize("rnd", [1, 2, 3, 4, 5, 6, 7])
|
|
448
|
+
def test_uses_average_through_round(self, rnd):
|
|
449
|
+
"""The average score_event applies equals calculate_eight_race_averages
|
|
450
|
+
through that round — the state going into the race scored at `round`."""
|
|
451
|
+
result = score_event(self._scenario_from_finishes(rnd), round=rnd)
|
|
452
|
+
used = (
|
|
453
|
+
result[result["type"] == "driver"]
|
|
454
|
+
.set_index("driver_abbr")["eight_race_average"]
|
|
455
|
+
.astype(int)
|
|
456
|
+
.sort_index()
|
|
457
|
+
)
|
|
458
|
+
expected = (
|
|
459
|
+
calculate_eight_race_averages(through_round=rnd).astype(int).sort_index()
|
|
460
|
+
)
|
|
461
|
+
pd.testing.assert_series_equal(used, expected, check_names=False)
|
|
462
|
+
|
|
463
|
+
@pytest.mark.parametrize("rnd", [5, 6, 7])
|
|
464
|
+
def test_blank_column_rounds_score_finite(self, rnd):
|
|
465
|
+
"""Rounds whose eight_race_average column is blank must still score —
|
|
466
|
+
before the average was computed, this raised on NaN improvement."""
|
|
467
|
+
result = score_event(self._scenario_from_finishes(rnd), round=rnd)
|
|
468
|
+
drivers = result[result["type"] == "driver"]
|
|
469
|
+
assert drivers["pts_improvement"].notna().all()
|
|
470
|
+
assert drivers["points_earned"].notna().all()
|
|
471
|
+
|
|
472
|
+
|
|
418
473
|
# ---------------------------------------------------------------------------
|
|
419
474
|
# optimal_lineup star_salary_cap tests
|
|
420
475
|
# ---------------------------------------------------------------------------
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
type,driver_abbr,driver_name,driver_team,round,starting_salary,eight_race_average,points_from_last_race
|
|
2
|
-
driver,VER,M. Verstappen,RBR,0,30,1,
|
|
3
|
-
driver,RUS,G. Russell,MER,0,28.7,2,
|
|
4
|
-
driver,NOR,L. Norris,MCL,0,27.4,3,
|
|
5
|
-
driver,PIA,O. Piastri,MCL,0,26.1,4,
|
|
6
|
-
driver,ANT,K. Antonelli,MER,0,24.8,5,
|
|
7
|
-
driver,LEC,C. Leclerc,FER,0,23.5,6,
|
|
8
|
-
driver,ALO,F. Alonso,AMR,0,22.2,7,
|
|
9
|
-
driver,HAM,L. Hamilton,FER,0,20.9,8,
|
|
10
|
-
driver,HAD,I. Hadjar,RBR,0,19.6,9,
|
|
11
|
-
driver,GAS,P. Gasly,ALP,0,18.3,10,
|
|
12
|
-
driver,STR,L. Stroll,AMR,0,17,11,
|
|
13
|
-
driver,SAI,C. Sainz Jr,WIL,0,15.7,12,
|
|
14
|
-
driver,LAW,L. Lawson,RBS,0,14.4,13,
|
|
15
|
-
driver,ALB,A. Albon,WIL,0,13.1,14,
|
|
16
|
-
driver,HUL,N. Hulkenberg,AUD,0,11.8,15,
|
|
17
|
-
driver,BOR,G. Bortoleto,AUD,0,10.5,16,
|
|
18
|
-
driver,BEA,O. Bearman,HAS,0,9.2,17,
|
|
19
|
-
driver,OCO,E. Ocon,HAS,0,7.9,18,
|
|
20
|
-
driver,BOT,V. Bottas,CAD,0,4.7,19,
|
|
21
|
-
driver,PER,S. Perez,CAD,0,4.7,19,
|
|
22
|
-
driver,COL,F. Colapinto,ALP,0,4.7,19,
|
|
23
|
-
driver,LIN,A. Lindblad,RBS,0,4.6,19,
|
|
24
|
-
team,MER,MER,,0,28.5,1,
|
|
25
|
-
team,MCL,MCL,,0,28.5,2,
|
|
26
|
-
team,RBR,RBR,,0,25,3,
|
|
27
|
-
team,FER,FER,,0,22.5,4,
|
|
28
|
-
team,AMR,AMR,,0,20,5,
|
|
29
|
-
team,WIL,WIL,,0,17.5,6,
|
|
30
|
-
team,RBS,RBS,,0,15,7,
|
|
31
|
-
team,ALP,ALP,,0,12.5,8,
|
|
32
|
-
team,AUD,AUD,,0,10,9,
|
|
33
|
-
team,CAD,CAD,,0,7.5,10,
|
|
34
|
-
team,HAS,HAS,,0,5,11,
|
|
35
|
-
driver,VER,M. Verstappen,RBR,1,28.2,2,
|
|
36
|
-
driver,RUS,G. Russell,MER,1,29.6,2,
|
|
37
|
-
driver,NOR,L. Norris,MCL,1,26.7,4,
|
|
38
|
-
driver,PIA,O. Piastri,MCL,1,24.1,7,
|
|
39
|
-
driver,ANT,K. Antonelli,MER,1,25.9,5,
|
|
40
|
-
driver,LEC,C. Leclerc,FER,1,24.5,6,
|
|
41
|
-
driver,ALO,F. Alonso,AMR,1,20.2,9,
|
|
42
|
-
driver,HAM,L. Hamilton,FER,1,22.1,8,
|
|
43
|
-
driver,HAD,I. Hadjar,RBR,1,17.6,11,
|
|
44
|
-
driver,GAS,P. Gasly,ALP,1,18.3,10,
|
|
45
|
-
driver,STR,L. Stroll,AMR,1,15,12,
|
|
46
|
-
driver,SAI,C. Sainz Jr,WIL,1,13.9,13,
|
|
47
|
-
driver,LAW,L. Lawson,RBS,1,14.5,13,
|
|
48
|
-
driver,ALB,A. Albon,WIL,1,13.9,14,
|
|
49
|
-
driver,HUL,N. Hulkenberg,AUD,1,9.8,16,
|
|
50
|
-
driver,BOR,G. Bortoleto,AUD,1,12.5,16,
|
|
51
|
-
driver,BEA,O. Bearman,HAS,1,11.2,16,
|
|
52
|
-
driver,OCO,E. Ocon,HAS,1,9.9,18,
|
|
53
|
-
driver,BOT,V. Bottas,CAD,1,4.5,19,
|
|
54
|
-
driver,PER,S. Perez,CAD,1,6.4,19,
|
|
55
|
-
driver,COL,F. Colapinto,ALP,1,6.7,19,
|
|
56
|
-
driver,LIN,A. Lindblad,RBS,1,6.6,18,
|
|
57
|
-
team,MER,MER,,1,28.8,,
|
|
58
|
-
team,MCL,MCL,,1,25.7,,
|
|
59
|
-
team,RBR,RBR,,1,22.4,,
|
|
60
|
-
team,FER,FER,,1,23.7,,
|
|
61
|
-
team,AMR,AMR,,1,17,,
|
|
62
|
-
team,WIL,WIL,,1,16.1,,
|
|
63
|
-
team,RBS,RBS,,1,17.4,,
|
|
64
|
-
team,ALP,ALP,,1,14.2,,
|
|
65
|
-
team,AUD,AUD,,1,9.8,,
|
|
66
|
-
team,CAD,CAD,,1,7.3,,
|
|
67
|
-
team,HAS,HAS,,1,8,,
|
|
68
|
-
driver,RUS,G. Russell,MER,2,29.5,2,171
|
|
69
|
-
driver,ANT,K. Antonelli,MER,2,27.5,5,174
|
|
70
|
-
driver,VER,M. Verstappen,RBR,2,26.2,4,132
|
|
71
|
-
driver,LEC,C. Leclerc,FER,2,24.8,6,164
|
|
72
|
-
driver,NOR,L. Norris,MCL,2,24.7,6,107
|
|
73
|
-
driver,HAM,L. Hamilton,FER,2,24.1,7,169
|
|
74
|
-
driver,PIA,O. Piastri,MCL,2,22.1,8,50
|
|
75
|
-
driver,GAS,P. Gasly,ALP,2,19.8,10,143
|
|
76
|
-
driver,ALO,F. Alonso,AMR,2,18.2,10,78
|
|
77
|
-
driver,HAD,I. Hadjar,RBR,2,18.1,11,118
|
|
78
|
-
driver,LAW,L. Lawson,RBS,2,16.5,13,142
|
|
79
|
-
driver,SAI,C. Sainz Jr,WIL,2,15.9,12,123
|
|
80
|
-
driver,BEA,O. Bearman,HAS,2,13.2,15,186
|
|
81
|
-
driver,STR,L. Stroll,AMR,2,13,13,75
|
|
82
|
-
driver,ALB,A. Albon,WIL,2,11.9,15,68
|
|
83
|
-
driver,HUL,N. Hulkenberg,AUD,2,11.8,16,81
|
|
84
|
-
driver,OCO,E. Ocon,HAS,2,10.7,17,124
|
|
85
|
-
driver,BOR,G. Bortoleto,AUD,2,10.5,16,90
|
|
86
|
-
driver,COL,F. Colapinto,ALP,2,8.7,18,130
|
|
87
|
-
driver,LIN,A. Lindblad,RBS,2,8.6,17,143
|
|
88
|
-
driver,PER,S. Perez,CAD,2,7.3,19,102
|
|
89
|
-
driver,BOT,V. Bottas,CAD,2,6.5,19,94
|
|
90
|
-
team,MER,MER,,2,29.1,,177
|
|
91
|
-
team,FER,FER,,2,24.6,,163
|
|
92
|
-
team,MCL,MCL,,2,22.7,,77
|
|
93
|
-
team,RBR,RBR,,2,21.7,,110
|
|
94
|
-
team,RBS,RBS,,2,17.3,,123
|
|
95
|
-
team,ALP,ALP,,2,16.8,,121
|
|
96
|
-
team,AMR,AMR,,2,15.7,,72
|
|
97
|
-
team,WIL,WIL,,2,13.8,,78
|
|
98
|
-
team,HAS,HAS,,2,11,,125
|
|
99
|
-
team,AUD,AUD,,2,9.7,,80
|
|
100
|
-
team,CAD,CAD,,2,9,,83
|
|
101
|
-
driver,RUS,G. Russell,MER,3,29.5,3,164
|
|
102
|
-
driver,ANT,K. Antonelli,MER,3,28.7,4,173
|
|
103
|
-
driver,LEC,C. Leclerc,FER,3,26.3,5,162
|
|
104
|
-
driver,NOR,L. Norris,MCL,3,24.7,6,118
|
|
105
|
-
driver,VER,M. Verstappen,RBR,3,24.6,5,133
|
|
106
|
-
driver,PIA,O. Piastri,MCL,3,24.1,8,91
|
|
107
|
-
driver,HAM,L. Hamilton,FER,3,23.4,7,158
|
|
108
|
-
driver,GAS,P. Gasly,ALP,3,21.7,10,143
|
|
109
|
-
driver,LAW,L. Lawson,RBS,3,18,12,140
|
|
110
|
-
driver,HAD,I. Hadjar,RBR,3,17.7,11,117
|
|
111
|
-
driver,ALO,F. Alonso,AMR,3,16.2,11,79
|
|
112
|
-
driver,SAI,C. Sainz Jr,WIL,3,14.9,13,115
|
|
113
|
-
driver,HUL,N. Hulkenberg,AUD,3,13.3,15,95
|
|
114
|
-
driver,OCO,E. Ocon,HAS,3,12.7,16,130
|
|
115
|
-
driver,BOR,G. Bortoleto,AUD,3,11.5,16,98
|
|
116
|
-
driver,BEA,O. Bearman,HAS,3,11.2,15,142
|
|
117
|
-
driver,STR,L. Stroll,AMR,3,11,14,69
|
|
118
|
-
driver,ALB,A. Albon,WIL,3,9.9,16,70
|
|
119
|
-
driver,LIN,A. Lindblad,RBS,3,9.7,17,131
|
|
120
|
-
driver,COL,F. Colapinto,ALP,3,9,17,117
|
|
121
|
-
driver,PER,S. Perez,CAD,3,7.5,18,97
|
|
122
|
-
driver,BOT,V. Bottas,CAD,3,6.2,19,87
|
|
123
|
-
team,MER,MER,,3,29.3,,175
|
|
124
|
-
team,FER,FER,,3,24.6,,161
|
|
125
|
-
team,MCL,MCL,,3,23.8,,106
|
|
126
|
-
team,RBR,RBR,,3,21.8,,116
|
|
127
|
-
team,ALP,ALP,,3,17.5,,120
|
|
128
|
-
team,RBS,RBS,,3,17.3,,120
|
|
129
|
-
team,AMR,AMR,,3,12.8,,69
|
|
130
|
-
team,WIL,WIL,,3,12.7,,80
|
|
131
|
-
team,HAS,HAS,,3,11.2,,114
|
|
132
|
-
team,AUD,AUD,,3,10.8,,92
|
|
133
|
-
team,CAD,CAD,,3,8.4,,80
|
|
134
|
-
driver,ANT,K. Antonelli,MER,4,28.8,4,176
|
|
135
|
-
driver,RUS,G. Russell,MER,4,28.7,3,165
|
|
136
|
-
driver,NOR,L. Norris,MCL,4,26.7,6,136
|
|
137
|
-
driver,LEC,C. Leclerc,FER,4,25.9,6,161
|
|
138
|
-
driver,PIA,O. Piastri,MCL,4,25.7,8,114
|
|
139
|
-
driver,VER,M. Verstappen,RBR,4,25.3,5,144
|
|
140
|
-
driver,HAM,L. Hamilton,FER,4,23.3,7,157
|
|
141
|
-
driver,GAS,P. Gasly,ALP,4,19.7,11,130
|
|
142
|
-
driver,SAI,C. Sainz Jr,WIL,4,16.4,12,122
|
|
143
|
-
driver,LAW,L. Lawson,RBS,4,16,13,126
|
|
144
|
-
driver,HAD,I. Hadjar,RBR,4,15.7,13,100
|
|
145
|
-
driver,ALO,F. Alonso,AMR,4,15.1,12,85
|
|
146
|
-
driver,OCO,E. Ocon,HAS,4,13.2,15,127
|
|
147
|
-
driver,BEA,O. Bearman,HAS,4,12.9,15,140
|
|
148
|
-
driver,BOR,G. Bortoleto,AUD,4,12.7,15,105
|
|
149
|
-
driver,ALB,A. Albon,WIL,4,11.9,15,87
|
|
150
|
-
driver,HUL,N. Hulkenberg,AUD,4,11.3,16,91
|
|
151
|
-
driver,COL,F. Colapinto,ALP,4,11,16,135
|
|
152
|
-
driver,LIN,A. Lindblad,RBS,4,10.5,16,125
|
|
153
|
-
driver,STR,L. Stroll,AMR,4,10.4,15,74
|
|
154
|
-
driver,PER,S. Perez,CAD,4,8.1,18,98
|
|
155
|
-
driver,BOT,V. Bottas,CAD,4,6,19,85
|
|
156
|
-
team,MER,MER,,4,29.4,,174
|
|
157
|
-
team,MCL,MCL,,4,24.7,,120
|
|
158
|
-
team,FER,FER,,4,24.6,,158
|
|
159
|
-
team,RBR,RBR,,4,20,,111
|
|
160
|
-
team,ALP,ALP,,4,18,,118
|
|
161
|
-
team,RBS,RBS,,4,15.3,,113
|
|
162
|
-
team,WIL,WIL,,4,15,,90
|
|
163
|
-
team,HAS,HAS,,4,12.6,,113
|
|
164
|
-
team,AMR,AMR,,4,11.3,,74
|
|
165
|
-
team,AUD,AUD,,4,11,,92
|
|
166
|
-
team,CAD,CAD,,4,7.3,,80
|
|
167
|
-
driver,ANT,K. Antonelli,MER,5,30.1,,180
|
|
168
|
-
driver,RUS,G. Russell,MER,5,26.7,,156
|
|
169
|
-
driver,VER,M. Verstappen,RBR,5,26.2,,150
|
|
170
|
-
driver,LEC,C. Leclerc,FER,5,25.9,,163
|
|
171
|
-
driver,HAM,L. Hamilton,FER,5,25.3,,163
|
|
172
|
-
driver,NOR,L. Norris,MCL,5,24.7,,133
|
|
173
|
-
driver,PIA,O. Piastri,MCL,5,24.6,,121
|
|
174
|
-
driver,GAS,P. Gasly,ALP,5,19.3,,132
|
|
175
|
-
driver,SAI,C. Sainz Jr,WIL,5,18,,128
|
|
176
|
-
driver,LAW,L. Lawson,RBS,5,18,,135
|
|
177
|
-
driver,HAD,I. Hadjar,RBR,5,17.7,,113
|
|
178
|
-
driver,BEA,O. Bearman,HAS,5,14.5,,140
|
|
179
|
-
driver,ALO,F. Alonso,AMR,5,13.1,,80
|
|
180
|
-
driver,COL,F. Colapinto,ALP,5,13,,145
|
|
181
|
-
driver,HUL,N. Hulkenberg,AUD,5,12.5,,98
|
|
182
|
-
driver,BOR,G. Bortoleto,AUD,5,12.5,,107
|
|
183
|
-
driver,OCO,E. Ocon,HAS,5,12.4,,124
|
|
184
|
-
driver,STR,L. Stroll,AMR,5,9.9,,81
|
|
185
|
-
driver,ALB,A. Albon,WIL,5,9.9,,81
|
|
186
|
-
driver,LIN,A. Lindblad,RBS,5,8.5,,110
|
|
187
|
-
driver,PER,S. Perez,CAD,5,7.4,,96
|
|
188
|
-
driver,BOT,V. Bottas,CAD,5,6.2,,89
|
|
189
|
-
team,MER,MER,,5,28.3,,168
|
|
190
|
-
team,FER,FER,,5,25.9,,158
|
|
191
|
-
team,MCL,MCL,,5,23.5,,120
|
|
192
|
-
team,RBR,RBR,,5,21.8,,120
|
|
193
|
-
team,ALP,ALP,,5,19,,121
|
|
194
|
-
team,WIL,WIL,,5,14.2,,90
|
|
195
|
-
team,RBS,RBS,,5,13.8,,108
|
|
196
|
-
team,HAS,HAS,,5,13,,111
|
|
197
|
-
team,AUD,AUD,,5,12.5,,96
|
|
198
|
-
team,AMR,AMR,,5,9.5,,74
|
|
199
|
-
team,CAD,CAD,,5,7.2,,79
|
|
200
|
-
driver,ANT,K. Antonelli,MER,6,29.9,,179
|
|
201
|
-
driver,HAM,L. Hamilton,FER,6,27,,165
|
|
202
|
-
driver,PIA,O. Piastri,MCL,6,25.3,,130
|
|
203
|
-
driver,RUS,G. Russell,MER,6,24.7,,150
|
|
204
|
-
driver,VER,M. Verstappen,RBR,6,24.2,,139
|
|
205
|
-
driver,LEC,C. Leclerc,FER,6,23.9,,153
|
|
206
|
-
driver,NOR,L. Norris,MCL,6,22.7,,125
|
|
207
|
-
driver,LAW,L. Lawson,RBS,6,20,,140
|
|
208
|
-
driver,GAS,P. Gasly,ALP,6,19.7,,134
|
|
209
|
-
driver,HAD,I. Hadjar,RBR,6,19.7,,126
|
|
210
|
-
driver,SAI,C. Sainz Jr,WIL,6,16,,122
|
|
211
|
-
driver,ALO,F. Alonso,AMR,6,14.7,,90
|
|
212
|
-
driver,OCO,E. Ocon,HAS,6,14.4,,128
|
|
213
|
-
driver,BOR,G. Bortoleto,AUD,6,13.8,,110
|
|
214
|
-
driver,HUL,N. Hulkenberg,AUD,6,12.6,,99
|
|
215
|
-
driver,BEA,O. Bearman,HAS,6,12.5,,127
|
|
216
|
-
driver,COL,F. Colapinto,ALP,6,12.3,,137
|
|
217
|
-
driver,ALB,A. Albon,WIL,6,11.9,,94
|
|
218
|
-
driver,LIN,A. Lindblad,RBS,6,10.5,,121
|
|
219
|
-
driver,PER,S. Perez,CAD,6,8.4,,97
|
|
220
|
-
driver,STR,L. Stroll,AMR,6,8.4,,80
|
|
221
|
-
driver,BOT,V. Bottas,CAD,6,4.8,,83
|
|
222
|
-
team,MER,MER,,6,28.7,,165
|
|
223
|
-
team,FER,FER,,6,26.2,,155
|
|
224
|
-
team,MCL,MCL,,6,22.6,,121
|
|
225
|
-
team,RBR,RBR,,6,21.9,,122
|
|
226
|
-
team,ALP,ALP,,6,18.5,,121
|
|
227
|
-
team,RBS,RBS,,6,16.5,,113
|
|
228
|
-
team,WIL,WIL,,6,14.2,,94
|
|
229
|
-
team,AUD,AUD,,6,12.4,,98
|
|
230
|
-
team,HAS,HAS,,6,12.1,,108
|
|
231
|
-
team,AMR,AMR,,6,8.8,,76
|
|
232
|
-
team,CAD,CAD,,6,6.4,,79
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
round,driver_abbr,finishing_position
|
|
2
|
-
1,RUS,1
|
|
3
|
-
1,ANT,2
|
|
4
|
-
1,LEC,3
|
|
5
|
-
1,HAM,4
|
|
6
|
-
1,NOR,5
|
|
7
|
-
1,VER,6
|
|
8
|
-
1,BEA,7
|
|
9
|
-
1,LIN,8
|
|
10
|
-
1,BOR,9
|
|
11
|
-
1,GAS,10
|
|
12
|
-
1,OCO,11
|
|
13
|
-
1,ALB,12
|
|
14
|
-
1,LAW,13
|
|
15
|
-
1,COL,14
|
|
16
|
-
1,SAI,15
|
|
17
|
-
1,PER,16
|
|
18
|
-
1,STR,17
|
|
19
|
-
1,ALO,18
|
|
20
|
-
1,BOT,19
|
|
21
|
-
1,HAD,20
|
|
22
|
-
1,PIA,21
|
|
23
|
-
1,HUL,22
|
|
24
|
-
2,ANT,1
|
|
25
|
-
2,RUS,2
|
|
26
|
-
2,HAM,3
|
|
27
|
-
2,LEC,4
|
|
28
|
-
2,BEA,5
|
|
29
|
-
2,GAS,6
|
|
30
|
-
2,LAW,7
|
|
31
|
-
2,HAD,8
|
|
32
|
-
2,SAI,9
|
|
33
|
-
2,COL,10
|
|
34
|
-
2,HUL,11
|
|
35
|
-
2,LIN,12
|
|
36
|
-
2,BOT,13
|
|
37
|
-
2,OCO,14
|
|
38
|
-
2,PER,15
|
|
39
|
-
2,VER,16
|
|
40
|
-
2,ALO,17
|
|
41
|
-
2,STR,18
|
|
42
|
-
2,PIA,19
|
|
43
|
-
2,NOR,20
|
|
44
|
-
2,BOR,21
|
|
45
|
-
2,ALB,22
|
|
46
|
-
3,ANT,1
|
|
47
|
-
3,PIA,2
|
|
48
|
-
3,LEC,3
|
|
49
|
-
3,RUS,4
|
|
50
|
-
3,NOR,5
|
|
51
|
-
3,HAM,6
|
|
52
|
-
3,GAS,7
|
|
53
|
-
3,VER,8
|
|
54
|
-
3,LAW,9
|
|
55
|
-
3,OCO,10
|
|
56
|
-
3,HUL,11
|
|
57
|
-
3,HAD,12
|
|
58
|
-
3,BOR,13
|
|
59
|
-
3,LIN,14
|
|
60
|
-
3,SAI,15
|
|
61
|
-
3,COL,16
|
|
62
|
-
3,PER,17
|
|
63
|
-
3,ALO,18
|
|
64
|
-
3,BOT,19
|
|
65
|
-
3,ALB,20
|
|
66
|
-
3,STR,21
|
|
67
|
-
3,BEA,22
|
|
68
|
-
4,ANT,1
|
|
69
|
-
4,NOR,2
|
|
70
|
-
4,PIA,3
|
|
71
|
-
4,RUS,4
|
|
72
|
-
4,VER,5
|
|
73
|
-
4,HAM,6
|
|
74
|
-
4,COL,7
|
|
75
|
-
4,LEC,8
|
|
76
|
-
4,SAI,9
|
|
77
|
-
4,ALB,10
|
|
78
|
-
4,BEA,11
|
|
79
|
-
4,BOR,12
|
|
80
|
-
4,OCO,13
|
|
81
|
-
4,LIN,14
|
|
82
|
-
4,ALO,15
|
|
83
|
-
4,PER,16
|
|
84
|
-
4,STR,17
|
|
85
|
-
4,BOT,18
|
|
86
|
-
4,HUL,19
|
|
87
|
-
4,LAW,20
|
|
88
|
-
4,GAS,21
|
|
89
|
-
4,HAD,22
|
|
File without changes
|
|
File without changes
|