Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
F1 Overtake Analyser
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Lennard Geese
F1 Overtake Analyser
Commits
34441452
Commit
34441452
authored
1 month ago
by
Lennard Geese
Browse files
Options
Downloads
Patches
Plain Diff
Reimplemented overtake counting with list of laps rather than integer arrays
parent
73b4248d
No related branches found
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
DataAnalyser.py
+81
-22
81 additions, 22 deletions
DataAnalyser.py
DataHandler.py
+0
-1
0 additions, 1 deletion
DataHandler.py
ImportantNotes.md
+22
-0
22 additions, 0 deletions
ImportantNotes.md
Todos.md
+3
-1
3 additions, 1 deletion
Todos.md
with
106 additions
and
24 deletions
DataAnalyser.py
+
81
−
22
View file @
34441452
...
...
@@ -19,13 +19,10 @@ class DataAnalyser(DataHandler):
return
overtakesInRaces
def
analyseRaceForOvertakes
(
self
,
race
:
Session
):
laps
:
Laps
=
race
.
laps
overtakesInLaps
:
List
[
int
]
=
self
.
countOvertakesInLaps
(
laps
)
overtakesInLaps
:
List
[
int
]
=
self
.
countOvertakesPerLap
(
race
)
return
overtakesInLaps
'''
OLD
def
analyseRaceForOvertakesOld
(
self
,
race
:
Session
):
# Collect grid positions
allLapPositions
:
List
[
dict
[
str
,
int
]]
=
[]
...
...
@@ -37,25 +34,85 @@ class DataAnalyser(DataHandler):
allLapPositions
.
append
(
runningLapPositions
)
# Count position changes between all lap pairs
overtakesInLaps: List[int] = self.countOvertakes
In
Lap
s
(allLapPositions)
overtakesInLaps
:
List
[
int
]
=
self
.
countOvertakes
Per
Lap
(
allLapPositions
)
return
overtakesInLaps
'''
def
countOvertakesInLaps
(
self
,
laps
:
Laps
):
overtakes
:
list
[
int
]
for
lapIndex
in
range
(
len
(
laps
)
-
1
):
currentLap
:
Laps
=
laps
.
pick_lap
(
lapIndex
)
nextLap
:
Laps
=
laps
.
pick_laps
(
lapIndex
+
1
)
overtakes
.
append
(
self
.
countOvertakesInLap
(
currentLap
,
nextLap
))
def
countOvertakesPerLap
(
self
,
race
:
Session
):
overtakes
:
list
[
int
]
=
[]
# TODO: Implement overtake counting for starting lap (lap 1) (necessary?)
for
lapNumber
in
range
(
1
,
race
.
total_laps
+
1
):
# in this context, index 1 = lap 1
overtakes
.
append
(
self
.
countOvertakesInLap
(
lapNumber
,
race
))
return
overtakes
def
countOvertakesInLap
(
self
,
currentLap
:
Laps
,
nextLap
:
Laps
):
# TODO: Implement
# NOTE: Left off here
'''
-
def
prepareWeightedOrderFromLap
(
self
,
lapNumber
:
int
,
race
:
Session
):
'''
Prepare a list from specific lap & race, that can be sorted via bubble sort to determine the number of
overtakes that occured in that lap.
:param lapNumber: Which lap to prepare from the given race. Note that value of 1 will return a list ordered by
starting grid, as there is no previous lap.
:param race: Race from which to pull the given lap from.
:return: list[(Lap, int)]: A list with pairs of every driver
'
s lap and their position at the end of the lap. Entries are
sorted by the driver
'
s positions at the start of the lap. If an invalid lap number (below 1 or above the number
of laps the race had), all laps in the list will be either None objects or empty Panda Dataframes.
'''
# TODO: Implement returning starting grid for lapNumber = 0
previousLaps
:
Laps
=
race
.
laps
.
pick_laps
(
lapNumber
-
1
)
currentLaps
:
Laps
=
race
.
laps
.
pick_laps
(
lapNumber
)
out
:
int
=
self
.
numberOfDrivers
# weighting for drivers that did not complete the lap (they are "out" of the lap/race)
weightedOrder
:
list
[(
Lap
,
int
)]
=
[(
None
,
out
)]
*
20
# data from start of lap + weighting for end of lap; initialize to add laps in lap's starting order
# Put every driver's laps in a sortable array & apply weighting based on position at end of lap
for
driver
in
race
.
drivers
:
driverCurrentLap
:
Laps
=
previousLaps
.
pick_drivers
(
driver
)
# should only get 1 lap, but data type shenanigans
driverNextLap
:
Laps
=
currentLaps
.
pick_drivers
(
driver
)
startPosition
:
int
=
out
endPosition
:
int
=
out
try
:
startPosition
=
int
(
driverCurrentLap
[
'
Position
'
].
iloc
[
0
])
endPosition
=
int
(
driverNextLap
[
'
Position
'
].
iloc
[
0
])
except
ValueError
:
if
self
.
activateDebugOvertakeAnalysis
:
print
(
f
"
Could not fetch positions from lap; driver %d likely didn
'
t finish lap %d or %d
"
,
driver
,
lapNumber
,
(
lapNumber
+
1
))
except
IndexError
:
if
self
.
activateDebugOvertakeAnalysis
:
print
(
"
[
'
Position
'
].iloc[0] was out of bounds; lap was likely empty because driver previously left the race
"
)
weightedOrder
[
startPosition
-
1
]
=
(
driverCurrentLap
,
endPosition
)
return
weightedOrder
def
countOvertakesInLap
(
self
,
lapNumber
:
int
,
race
:
Session
):
orderToSort
:
list
[(
Lap
,
int
)]
=
self
.
prepareWeightedOrderFromLap
(
lapNumber
,
race
)
overtakes
:
int
=
0
i
:
int
=
0
while
i
<
len
(
orderToSort
)
-
1
:
# do not change to for-loop, seems to not like resetting the index
weightedDriverAhead
:
list
[(
Lap
,
int
)]
=
orderToSort
[
i
]
weightedDriverBehind
:
list
[(
Lap
,
int
)]
=
orderToSort
[
i
+
1
]
if
weightedDriverAhead
[
1
]
>
weightedDriverBehind
[
1
]:
temp
:
int
=
orderToSort
[
i
]
orderToSort
[
i
]
=
orderToSort
[
i
+
1
]
orderToSort
[
i
+
1
]
=
temp
i
=
-
1
# reset to first index; -1 because loop will set it to 0
if
not
(
# don't count overtake if driver nonexistent or if one of them is on an in-lap
weightedDriverAhead
[
0
]
is
None
or
weightedDriverBehind
[
0
]
is
None
or
self
.
isInLap
(
weightedDriverAhead
[
0
])
or
self
.
isInLap
(
weightedDriverBehind
[
0
])
):
overtakes
+=
1
i
+=
1
return
overtakes
def
isInLap
(
self
,
lap
:
Lap
):
# TODO: Implement :)
return
False
def
getGridPositions
(
self
,
race
:
Session
):
sessionResults
=
race
.
results
...
...
@@ -88,7 +145,7 @@ class DataAnalyser(DataHandler):
driverAbbreviation
=
raceLap
[
'
Driver
'
].
iloc
[
0
]
driverPosition
=
raceLap
[
'
Position
'
].
iloc
[
0
]
runningLapPositions
[
driverAbbreviation
]
=
driverPosition
except
:
# triggered when not all drivers that took part reached lap, probably by crashing or being behind
except
:
# triggered when not all drivers that took part reached lap
end (i.e. when value is NaN)
, probably by crashing or being behind
x
=
0
# do nothing
runningLapsPositions
.
append
(
runningLapPositions
)
...
...
@@ -105,6 +162,7 @@ class DataAnalyser(DataHandler):
return
runningLapsPositions
'''
def countOvertakesInLaps(self, allLapPositions: List[dict[str, int]]):
overtakesInLaps: List[int] = []
for i in range(len(allLapPositions) - 1):
...
...
@@ -136,21 +194,22 @@ class DataAnalyser(DataHandler):
overtakes: int = 0
i: int = 0
while i < len(orderToSort) - 1:
if
self
.
countOutPitstops
:
if self.countOutPitstops:
# TODO: Remove? This was never fully implemented here, new implementation elsewhere
driverPittedThisLap: bool = False
currentDriversEndPosition = orderToSort[i]
driver = self.getDriverByPositionFromMap(endOrder, currentDriversEndPosition)
if orderToSort[i] > orderToSort[i + 1]:
temp: int = orderToSort[i]
orderToSort[i] = orderToSort[i + 1]
orderToSort[i + 1] = temp
overtakes += 1
i
=
-
1
i = -1 # reset to first index; -1 because loop will set it to 0
i += 1
return overtakes
'''
def
getDriverByPositionFromMap
(
self
,
positions
:
dict
[
str
,
int
],
position
:
int
):
driver
=
list
(
positions
.
keys
())[
list
(
positions
.
values
()).
index
(
position
)]
...
...
This diff is collapsed.
Click to expand it.
DataHandler.py
+
0
−
1
View file @
34441452
...
...
@@ -5,7 +5,6 @@ class DataHandler(ABC):
def
__init__
(
self
):
self
.
numberOfDrivers
=
20
self
.
invalidDriverId
=
"
NO_DRIVER
"
self
.
enableTestingMode
=
False
# only partially import data to speed up testing, because HTTP 429 limits import speed
self
.
activateDebugOvertakeAnalysis
=
False
self
.
slickCompounds
=
(
'
SOFT
'
,
'
MEDIUM
'
,
'
HARD
'
)
self
.
countOutPitstops
=
True
\ No newline at end of file
This diff is collapsed.
Click to expand it.
ImportantNotes.md
0 → 100644
+
22
−
0
View file @
34441452
# Lap position counting in F1
When a driver pits, his position for the lap he just drove is counted when he exits the pit lane. E.g. when the
driver in P1 pits at the end of the 1st lap, and he gets overtaken by 2 cars during his pitstop, meaning he exits
the pit as the driver in P3, his position for lap 1 will be counted as P3. This also applies if he gets overtaken by
a 3rd car while he is on the pit exit road, but already out of pitlane.
This explanation seems to make the most sense for fair and reliable counting. However, a source which explains this
could not be found.
There is no way to tell, if a switch of position occured during the lap or the pitstop. Statistically, although not
proven, it is assumed to be more likely, that a switch of position occurs during a pitstop, rather than during the
lap. For this reason, overtakes made on or by a driver on an in-lap are not counted, since the actual overtakes
missed by this should be negligible compared to the error caused by the pitstops which this method of counting negates.
# FastF1 querks
## Laps
In the FastF1 API, a lap is driven by only one driver. This means that if you want to get every drivers position for
a given lap, you will have to access a unique lap for every driver-lapnumber combination.
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Todos.md
+
3
−
1
View file @
34441452
...
...
@@ -12,4 +12,6 @@
# Done
-
Automatically title graph by race name (no more hardcoding the graph name)
\ No newline at end of file
-
Automatically title graph by race name (no more hardcoding the graph name)
-
Migrate counting of overtakes functionality from integer-array based to list
\[
(Lap, int)] based so that in-laps
can be accounted for
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment