Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import math
from typing import List
from fastf1.core import Session, Lap
from DataHandler import DataHandler
class DataAnalyser(DataHandler):
# ===== Overtakes =====
def analyseRacesForOvertakes(self, races: List[Session]):
overtakesInRaces: List[List[int]] = [[]]
for race in races:
overtakesInRaces.append(self.analyseRaceForOvertakes(race))
return overtakesInRaces
def analyseRaceForOvertakes(self, race: Session):
# Collect grid positions
allLapPositions: List[dict[str, int]] = []
gridPositions: dict[str, int] = self.getGridPositions(race)
allLapPositions.append(gridPositions)
runningLapsPositions: List[dict[str, int]] = self.getRunningLapsPositions(race)
for runningLapPositions in runningLapsPositions:
allLapPositions.append(runningLapPositions)
# Count position changes between all lap pairs
overtakesInLaps: List[int] = self.countOvertakesInLaps(allLapPositions)
return overtakesInLaps
def getGridPositions(self, race: Session):
sessionResults = race.results
gridPositions: dict[str, int] = {} # driverId & lapNumber
for i in range(self.numberOfDrivers):
driverPosition = sessionResults['GridPosition'].iloc[i]
driverAbbreviation = sessionResults['Abbreviation'].iloc[i]
gridPositions[driverAbbreviation] = driverPosition
if self.activateDebugOvertakeAnalysis:
print(f"\nLap: 0")
for i in range(len(gridPositions)):
position: int = i + 1
driverAtPosition = self.getDriverByPositionFromMap(gridPositions, position)
print(f"P{position}: {driverAtPosition}")
return gridPositions
def getRunningLapsPositions(self, race: Session):
runningLapsPositions: List[dict[str, int]] = []
allRaceLaps = race.laps
for raceLapIndex in range(race.total_laps):
runningLapPositions: dict[str, int] = {} # driverId & lapNumber
for driver in race.drivers:
raceLap = allRaceLaps.pick_laps(raceLapIndex + 1) # Lap 0 doesn't exist
raceLap = raceLap.pick_drivers(driver)
try :
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
x = 0 # do nothing
runningLapsPositions.append(runningLapPositions)
# debug
if self.activateDebugOvertakeAnalysis:
for raceLapIndex in range(race.total_laps):
print(f"\nLap: {raceLapIndex + 1}")
runningLapPositions = runningLapsPositions[raceLapIndex]
for i in range(len(runningLapPositions)):
position: int = i + 1
driverAtPosition = self.getDriverByPositionFromMap(runningLapPositions, position)
print(f"P{position}: {driverAtPosition}")
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
return runningLapsPositions
def countOvertakesInLaps(self, allLapPositions: List[dict[str, int]]):
overtakesInLaps: List[int] = []
for i in range(len(allLapPositions) - 1):
overtakesInLap: int = self.countOvertakesInLap(allLapPositions[i], allLapPositions[i + 1])
overtakesInLaps.append(overtakesInLap)
if self.activateDebugOvertakeAnalysis: print(f"Overtakes in lap {i+1}: {overtakesInLap}")
return overtakesInLaps
def countOvertakesInLap(self, startOrder: dict[str, int], endOrder: dict[str, int]):
out: int = self.numberOfDrivers
orderToSort: List[int] = [self.numberOfDrivers] * self.numberOfDrivers
for driver in startOrder:
if driver not in endOrder: # add missing drivers for sorting/counting purposes
endOrder[driver] = out # heaviest weighting to drop driver to bottom, as they do irl when crashing or similar
if math.isnan(startOrder[driver]): startOrder[driver] = out
if math.isnan(endOrder[driver]): endOrder[driver] = out
driverStartPosition: int = int(startOrder[driver])
driverEndPosition: int = int(endOrder[driver])
orderToSort[driverStartPosition - 1] = driverEndPosition
if self.activateDebugOvertakeAnalysis:
#print("Weighting:")
for i in range(len(orderToSort)):
x=0
#print(orderToSort[i])
overtakes: int = 0
i: int = 0
while i < len(orderToSort) - 1:
if self.countOutPitstops:
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
i = -1
i += 1
def getDriverByPositionFromMap(self, positions: dict[str, int], position: int):
driver = list(positions.keys())[list(positions.values()).index(position)]
return driver
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# ===== Weather =====
def analyseRacesForWeather(self, races: List[Session]):
weatherInRaces: List[List[bool]] = [[]] # isRaining per lap per race
for race in races:
weatherInRace = self.analyseRaceForWeather(race)
weatherInRaces.append(weatherInRace)
return weatherInRaces
def analyseRaceForWeather(self, race: Session):
x = 0
# ===== Tire Changes =====
def getEarliestTireChanges(self, races: List[Session]):
earliestTireChanges: List[int] = [[]] # isRaining per lap per race
for race in races:
earliestTireChange = self.getEarliestTireChange(race)
earliestTireChanges.append(earliestTireChange)
return earliestTireChanges
# Returns -1 if no tire change occured
def getEarliestTireChange(self, race: Session):
earliestTireChangeLap: int = -1
compoundsPerLap: List[List[str]] = self.getCompoundsForRace(race)
compoundsPerLap[0] = compoundsPerLap[1] # presume grid tires same as 1st lap; races are only picked if weather change after first 10 laps anyways, so its ok
startingCompound: str = self.getPredominantCompound(compoundsPerLap[0])
earliestTireChangeLap = self.getFirstLapWithOppositeCompound(compoundsPerLap, startingCompound)
return earliestTireChangeLap
def getLatestTireChanges(self, races: List[Session]):
latestTireChanges: List[int] = [[]] # isRaining per lap per race
for race in races:
latestTireChange = self.getLatestTireChange(race)
latestTireChanges.append(latestTireChange)
return latestTireChanges
# Returns -1 if no tire change occured
def getLatestTireChange(self, race: Session):
latestTireChangeLap: int = -1
compoundsPerLap: List[List[str]] = self.getCompoundsForRace(race)
compoundsPerLap[0] = compoundsPerLap[1] # presume grid tires same as 1st lap; races are only picked if weather change after first 10 laps anyways, so its ok
startingCompound: str = self.getPredominantCompound(compoundsPerLap[0])
latestTireChangeLap = self.getFirstLapWithoutCompound(compoundsPerLap, startingCompound)
return latestTireChangeLap
def getFirstLapWithoutCompound(self, compoundsPerLap: List[List[str]], startingCompound: str):
currentLap = 0
filter = self.setFilter(startingCompound)
for compoundsThisLap in compoundsPerLap:
noStartingCompoundsLeft = True
for compound in compoundsThisLap:
if compound in filter:
noStartingCompoundsLeft = False
if noStartingCompoundsLeft: return currentLap
currentLap += 1
return -1 # no lap without compound found; all laps use same compound type
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def getCompoundsForRace(self, race: Session):
compoundsPerLap: List[List[str]] = [[]]
allRaceLaps = race.laps
for raceLapIndex in range(race.total_laps):
compoundsThisLap: List[str] = []
for driver in race.drivers:
raceLap = allRaceLaps.pick_laps(raceLapIndex + 1) # Lap 0 doesn't exist
raceLap = raceLap.pick_drivers(driver)
try:
driverAbbreviation = raceLap['Driver'].iloc[0]
compound = raceLap['Compound'].iloc[0]
compoundsThisLap.append(compound)
except: # triggered when not all drivers that took part reached lap, probably by crashing or being behind
x = 0 # do nothing
compoundsPerLap.append(compoundsThisLap)
return compoundsPerLap
def getPredominantCompound(self, compoundsThisLap: List[str]):
slickCounter = 0
interCounter = 0
wetCounter = 0
for compound in compoundsThisLap:
if compound in self.slickCompounds: slickCounter += 1
if compound == 'INTERMEDIATE': interCounter += 1
if compound == 'WET': wetCounter += 1
mostUsed = max(slickCounter, interCounter, wetCounter)
if slickCounter == mostUsed: return 'SLICK'
if interCounter == mostUsed: return 'INTERMEDIATE'
if wetCounter == mostUsed: return 'WET'
return 'error'
def getFirstLapWithOppositeCompound(self, compoundsPerLap: List[List[str]], startingCompound: str):
filter = self.setFilter(startingCompound)
currentLap = 0
for compoundsThisLap in compoundsPerLap:
for compound in compoundsThisLap:
if compound not in filter:
return currentLap
currentLap += 1
return -1 # no lap with opposite compound found; all laps use same compound type
def setFilter(self, startingCompound: str):
if startingCompound == 'SLICK': return self.slickCompounds
return startingCompound
# ===== Crashes =====
def analyseRacesForCrashes(self, races):
x = 0
# ===== Events =====
def analyseRacesForEvents(self, races):
x = 0