Skip to content
Snippets Groups Projects
Commit 9716bc71 authored by Lennard Geese's avatar Lennard Geese
Browse files

Setup infrastructure for importing only rain sessions from specific year(s)

parent 25ed4113
No related branches found
No related tags found
No related merge requests found
import pandas as pandas import pandas as pandas
from fastf1.core import Session, Lap, Laps from fastf1.core import Session, Lap, Laps
from DataHandler import DataHandler from DataHandler import DataHandler, SessionIdentifier
class DataAnalyser(DataHandler): class DataAnalyser(DataHandler):
'''
Analyses sessions by extrapolating existing or new data from them.
Any method of this class must be given a Session object or a list thereof. If the method does not require such an
object, it should not be part of this class.
'''
# ===== Overtakes ===== # ===== Overtakes =====
def analyseRacesForOvertakes(self, races: list[Session]): def getOvertakesPerLapForRaces(self, races: list[Session]):
overtakesInRaces: list[list[int]] = [[]] overtakesInRaces: list[list[int]] = [[]]
for race in races: for race in races:
overtakesInRaces.append(self.analyseRaceForOvertakes(race)) overtakesInRaces.append(self.getOvertakesPerLapForRace(race))
return overtakesInRaces return overtakesInRaces
def analyseRaceForOvertakes(self, race: Session): def getOvertakesPerLapForRace(self, race: Session):
if not race.session_info["Type"] == "Race": if not race.session_info["Type"] == "Race":
raise ValueError(f"Session must be a race session, not a {race.session_info["Type"]} session") raise ValueError(f"Session must be a race session, not a {race.session_info["Type"]} session")
overtakesInLaps: list[int] = self.countOvertakesPerLap(race) overtakesInLaps: list[int] = self.countOvertakesPerLap(race)
...@@ -112,16 +117,12 @@ class DataAnalyser(DataHandler): ...@@ -112,16 +117,12 @@ class DataAnalyser(DataHandler):
# ===== Weather ===== # ===== Weather =====
def analyseRacesForWeather(self, races: list[Session]): def filterForWetSessions(self, sessions: 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 x = 0
return 0
return sessions
......
...@@ -8,6 +8,7 @@ class DataHandler(ABC): ...@@ -8,6 +8,7 @@ class DataHandler(ABC):
self.activateDebugOvertakeAnalysis = False self.activateDebugOvertakeAnalysis = False
self.slickCompounds = ('SOFT', 'MEDIUM', 'HARD') self.slickCompounds = ('SOFT', 'MEDIUM', 'HARD')
self.countOutPitstops = True self.countOutPitstops = True
self.firstFastF1Year: int = 2018
class SessionIdentifier: class SessionIdentifier:
year: int year: int
......
...@@ -4,33 +4,69 @@ from abc import ABC ...@@ -4,33 +4,69 @@ from abc import ABC
from fastf1.core import Session from fastf1.core import Session
from fastf1.events import EventSchedule, Event from fastf1.events import EventSchedule, Event
from DataAnalyser import DataAnalyser
from DataHandler import DataHandler, SessionIdentifier from DataHandler import DataHandler, SessionIdentifier
class DataImporter(DataHandler, ABC): class DataImporter(DataHandler, ABC):
'''
Imports/loads sessions as specified by SessionIdentifiers and/or certain criteria such as rain.
Any method of this class must be given a SessionIdentifier, a time or a time period by which to select
sessions. If the method does not require one of these, it should not be part of this class.
'''
def importAllEventsFromYear(self, year: int): def importAllEventsFromYear(self, year: int):
races: list[Event] = [] races: list[Event] = []
schedule: EventSchedule = fastf1.get_event_schedule(year, include_testing = False) schedule: EventSchedule = fastf1.get_event_schedule(year, include_testing = False)
for raceIndex in schedule['RoundNumber']: for raceIndex in schedule['RoundNumber']:
races.append(schedule.get_event_by_round(raceIndex)) races.append(schedule.get_event_by_round(raceIndex))
return races return races
def importRaceWeather(self): def importSessionWeather(self, sessionIdentifier: SessionIdentifier):
x = 0 '''
Import a session containing only weather data.
:param sessionIdentifier: Session to import. Note that only races after 2017 can import weather, as session data
before 2018 is provided by the Ergast/Jolpica API, which does not contain weather data.
:return: Session object containing only weather data and basic session information.
'''
return self.importSession(sessionIdentifier, laps = False, telemetry = False, weather = True, messages = False)
def importSessions(self, sessionIdentifiers: list[SessionIdentifier], laps = True, telemetry = False, weather = False, messages = False):
def importSessions(self, sessionIdentifiers: list[SessionIdentifier], laps = True, telemetry = False, weather = True, messages = False):
sessions: list[Session] = [] sessions: list[Session] = []
for sessionIdentifier in sessionIdentifiers: for sessionIdentifier in sessionIdentifiers:
sessions.append(self.importSession(sessionIdentifier, laps, weather, messages, telemetry)) sessions.append(self.importSession(sessionIdentifier, laps, weather, messages, telemetry))
return sessions return sessions
def importSession(self, sessionIdentifier: SessionIdentifier, laps = True, telemetry = False, weather = False, messages = False):
if weather is True and sessionIdentifier.year < self.firstFastF1Year:
raise ValueError(f"Cannot fetch weather data for races before {self.firstFastF1Year} due to API limitations")
def importSession(self, sessionIdentifier: SessionIdentifier, laps = True, telemetry = False, weather = True, messages = False):
session = fastf1.get_session(sessionIdentifier.year, sessionIdentifier.event, sessionIdentifier.sessionType) session = fastf1.get_session(sessionIdentifier.year, sessionIdentifier.event, sessionIdentifier.sessionType)
session.load(laps = laps, telemetry = telemetry, weather = weather, messages = messages) session.load(laps = laps, telemetry = telemetry, weather = weather, messages = messages)
return session return session
\ No newline at end of file
def getRainRacesSince(self, firstYear: int):
rainRaces: list[Session] = []
for firstYear in range(firstYear, 2025): # FIXME: Add automatic upper bound for year so that it can at most be set to current year or year of latest f1 race (e.g. shortly after new years eve)
wetWeatherRacesInYear: list[Session] = self.getRainRacesIn(firstYear)
for wetWeatherRace in wetWeatherRacesInYear:
rainRaces.append(wetWeatherRace)
return rainRaces
def getRainRacesIn(self, year: int):
events: list[Event] = self.importAllEventsFromYear(year)
races: list[Session] = []
for event in events:
raceIdentifier: SessionIdentifier = SessionIdentifier(event.year, event["RoundNumber"], "R")
try:
raceSession: Session = self.importSessionWeather(raceIdentifier)
races.append(raceSession)
except ValueError: # Needed as long as weather data for races before 2018 hasn't been supplemented
print(f"Weather for {event["EventName"]} {event.year} could not be determined")
analyser: DataAnalyser = DataAnalyser()
rainRaces: list[Session] = analyser.filterForWetSessions(races)
return rainRaces
\ No newline at end of file
...@@ -10,4 +10,8 @@ ...@@ -10,4 +10,8 @@
- [ ] Cache data in CSV file for faster access / analysis - [ ] Cache data in CSV file for faster access / analysis
- [ ] Adjust for finding multiple weather changes in a race, not just one - [ ] Adjust for finding multiple weather changes in a race, not just one
- [ ] Read out number of drivers participating in session, rather than hardcoding number of drivers (since it might change from 20 to more (or less) in future) - [ ] Read out number of drivers participating in session, rather than hardcoding number of drivers (since it might change from 20 to more (or less) in future)
- [ ] Also read out direct weather data from API - [ ] Also read out direct weather data from API
\ No newline at end of file - [ ] Extrapolate weather conditions from lap times by difference between dry & wet laptimes in order to retrieve
weather / track conditions for races before 2018, as Ergast / Jolpica APIs do not provide this data
- Alternatively, get weather data from a dedicated weather API for the location & date/time the race was carried out
-
\ No newline at end of file
from fastf1.core import Session
from fastf1.events import Event from fastf1.events import Event
from DataAnalyser import DataAnalyser from DataAnalyser import DataAnalyser
...@@ -17,7 +18,9 @@ class Main: ...@@ -17,7 +18,9 @@ class Main:
def main(self): def main(self):
self.listWetWeatherRacesSince(2015) dataHandler: Main.DataHandlingPackage = Main.DataHandlingPackage()
rainRaces: list[Session] = dataHandler.importer.getRainRacesSince(2015) # FIXME: Change to all races _since_ 2015
racesToAnalyse = [ racesToAnalyse = [
SessionIdentifier(2022, "Imola", "R"), # Imola 2022 (DWR) SessionIdentifier(2022, "Imola", "R"), # Imola 2022 (DWR)
...@@ -27,14 +30,6 @@ class Main: ...@@ -27,14 +30,6 @@ class Main:
] ]
self.overtakeAnalysis(racesToAnalyse) self.overtakeAnalysis(racesToAnalyse)
def listWetWeatherRacesSince(self, firstYear: int):
dataHandler: Main.DataHandlingPackage = self.DataHandlingPackage()
events: list[Event] = []
for year in range(firstYear, 2025): # FIXME: Add automatic upper bound for year
events.append(dataHandler.importer.importAllEventsFromYear(year))
for event in events:
x = 0
# TODO: filter out dry races
def overtakeAnalysis(self, raceSessionIdentifiers: list[SessionIdentifier]): def overtakeAnalysis(self, raceSessionIdentifiers: list[SessionIdentifier]):
dataHandler: Main.DataHandlingPackage = self.DataHandlingPackage() dataHandler: Main.DataHandlingPackage = self.DataHandlingPackage()
...@@ -49,7 +44,7 @@ class Main: ...@@ -49,7 +44,7 @@ class Main:
# Analyse # Analyse
overtakesInRaces: list[int] = dataHandler.analyser.analyseRaceForOvertakes(raceSession) overtakesInRaces: list[int] = dataHandler.analyser.getOvertakesPerLapForRace(raceSession)
print("Overtake analysis done") print("Overtake analysis done")
# weatherInRaces = analyser.analyseRaceForWeather(raceSession) # weatherInRaces = analyser.analyseRaceForWeather(raceSession)
earliestTireChange: int = dataHandler.analyser.getEarliestTireChange(raceSession) # first lap where someone switched from slicks to non slicks or vice versa, denoted by lap number earliestTireChange: int = dataHandler.analyser.getEarliestTireChange(raceSession) # first lap where someone switched from slicks to non slicks or vice versa, denoted by lap number
...@@ -78,6 +73,4 @@ class Main: ...@@ -78,6 +73,4 @@ class Main:
if __name__ == '__main__': if __name__ == '__main__':
app = Main() app = Main()
# Call the get_overtakes_in_races method
app.main() app.main()
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment