Fast-F1
Fast-F1 copied to clipboard
[BUG] Personal best times need to be kept for each qualifying segment individually
Discussed in https://github.com/theOehrly/Fast-F1/discussions/164
Originally posted by blakehinsey April 26, 2022 Has anyone noticed the behaviour of the pick_fastest() with Imola data for Qualifying?
I've looked into the code, and the only_by_time=True helps.

Does this mean that Q1 and Q2 laps were not counted as personal bests? It is a strange one as Q1/2 were dry and Q3 times were wet.
I will admit, my coding is not excellent, so maybe I have goofed something up.
It's "BRRRAKE" from Twitter btw!
The API resets personal best for every quali section. Only a reference to the last received personal best time is kept in the API parser. Therefore, Q3 overrides Q2 here despite the times being slower. This could actually happen in some dry sessions too, which is not good at all.
Working around this is non-trivial.The API parser needs to keep track of the order of personal best times to kind of create a ranking of official best times, while still detecting lap time deletions. Additionally, it needs to be possible to split the laps in session.laps into Q1, Q2 and Q3 laps.
Seeing as no one has picked this up, I'm going to try and take a look at this.
Please correct me if I'm wrong - but it seems like the issue is that after each Qualifying session, the personal bests are reset. So, even if a time in Q2 was faster, .pick_fastest() will return the fastest time from Q3, correct? In this case the lap_time may actually not be the fastest "legal" lap time set.
If we set .pick_fastest(only_by_time=True) does that return the fastest lap, regardless of whether it was a legal lap or not?
it seems like the issue is that after each Qualifying session, the personal bests are reset. So, even if a time in Q2 was faster,
.pick_fastest()will return the fastest time from Q3, correct? In this case the lap_time may actually not be the fastest "legal" lap time set.
Yes, exactly. The API provides the current personal best lap time for each driver, but without any reference to the lap number. That's a bit problematic. Currently, the API parser code only keeps the last personal best time that is ever received, which will be the last personal best in Q3 (or Q2/Q1 if a driver is eliminated earlier). Therefore, the information is lost for any drivers earlier Q sessions. Then the each drivers lap times are compared with the last received personal best time and the first lap that matches the personal best time is marked as fastest lap.
Generally, a new personal best is only received from the API if a driver goes faster than their previous time. Or, if a personal best lap is deleted, the personal best time from before that is sent again. For example, if we receive as personal best times
1:16:150
1:16:050
1:15:900 < deleted
1:16:050
we can assume that the third personal best time was deleted. We have no information about deleted laps, if they were not personal best lap times. (In theory, we could try to extract that information from race control messages. I'm not sure how reliable that would be)
If we set
.pick_fastest(only_by_time=True)does that return the fastest lap, regardless of whether it was a legal lap or not?
Yes, it simply returns the lap with the minimum time without any further logic being involved.
~~Thank you, that cleared up a lot. Is there any documentation on the internal attributes, for example - what is isAccurate in the lap object?~~ I found it in the class.
https://github.com/theOehrly/Fast-F1/blob/e4b3778215a3e80ce9154b1a6e1d0ccb2fb5e42e/fastf1/api.py#L601
It seems this is the line of code or logic, where we get the raw lap data for each driver, and figure out the personal best time. I see that we override the personal_best_lap_time everytime we get a new entry which has a BestLapTime. However, I don't see any identifier in the resp dictionary that could help us identify which session it is (Q1, Q2, Q3 etc.) - any ideas?
Yes, this is a bit challenging. We likely won't be able to determine the individual qualifying sessions from the data here. I was thinking of creating a ranking of personal best times, maybe. Like fastest, second fastest, ... and so on. But not based on the lap time value, but the order in which the personal best values were received. The individual qualifying sessions can then be separated at a later point using the session status data.
I am still iterating the best approach, but for now I have added an additional column to the data -> AllPersonalBests, so we can keep track of the personal best time.

We can get the Time value for rows corresponding to AllPersonalBests.
The session_status df looks something like this -

I am assuming (couldn't find definitions in the docs) that 'Finished' indicates when a Qualifying (Q1, Q2) session has finished - and for Q3 this is marked by Finalized (classifications complete). We can get the bounds of Qualifying times and then split the AllPersonalBests times by Qualifying. Is this what you were alluding to?
I also looked into whether we can use the race control messages, but I was confused by the Time column there. It's not in pd.timedelta format, and the time doesn't seem to match the session start time for Qualifying which was 17:00, it looks like the times here start from 15:00. Any idea about this? (Screenshot below). Thank you for taking the time to answer my Qs, this is a lot of work you have put into this project!

We could simply keep a track of all the "personal bests" and return the fastest personal best when calling session.laps.pick_fastest(). That way we don't have to split the laps by Qualifying. That can be a separate feature, to get the fastest lap for a particular Qualifying session. What do you think? @theOehrly
I think adding a separate column like you did with the AllPersonalBests is good. I was thinking more like numbering them instead of duplicating the lap time into that column. But your way might probably make it easier to work with the data.
We could simply keep a track of all the "personal bests" and return the fastest personal best when calling session.laps.pick_fastest(). That way we don't have to split the laps by Qualifying. That can be a separate feature, to get the fastest lap for a particular Qualifying session.
Agreed. That's what I more or less had in mind. We could (potentially as a separate issue/feature) add another column that indicates Q1/2/3 for each lap. And a ready made method to select all laps from a specific quali section.
There is no further information given about the session status data in the docs. I haven't analyzed it in detail and written down how it works (should probably). It's fairly self explanatory though usually.
Regarding the timestamps of the race control messages, I need some time to look at that.
Whenever you want me to take a look at your code, feel free to open a PR, maybe as draft.
I created a PR (#184) for only the "return fastest legal lap" change, since it was only a small change. I'll work on the feature for returning laps by Qualifying session in a different PR.
Hey, I have seen this issue now and it looks pretty interesting. I don't know what's exactly the situation now, if you guys have found out how to return the personal best of each qualifying round. If not, maybe I can have a look, I don't have a lot of experience with Python but I'm ready to learn.
I believe this is still an open issue. I'm not going to have some free time soon to complete this, so feel free to work on it! There's lot of discussion in this thread that maybe helpful.
Thanks! I have a little question by the way. If I have downloaded the source code, and have made changes to the core.py file, how can I test those changes that I have made locally?
I mean, in a normal script, If I use import fastf1 the original module of fastf1 is loaded, without my changes on core.py
I know it sounds weird but I'm not familiar with github etc so...
@hash55 please try to read through the contribution guidelines: https://theoehrly.github.io/Fast-F1/contributing/index.html. You're questions will probably at least partially be answered there. If you have any further questions after that, please open a separate discussion. Then I can try to help you with whatever is still unclear.
Hi! I have made a Pull Request with the changes I've done on my forked repo. It was really my first pull request, so let me know if there is any mistake.
About the changes:
-pick_fastest() (without arguments) now returns the oficially fastest lap of the driver, no matter the qualifying round.
If you really want to know which was the fastest of Qn round, you can call pick_fastest("Q1") so the fastest lap of only that round would be returned, since that is the only round that would be taken into account.
-To verify that the lap was not deleted I created the method Lap.isValid() that compares that lap with the race control messages and checks if it was deleted due to track limits
-I was able to get qualifying rounds (Q1,Q2 and Q3) thanks to the session status data. The method Session.get_quali_rounds() returns a dictionary with the timestamps of beginning and end of each Q1 Q2 and Q3
I don't know if this was exactly what you were looking for, but as far as I am concerned it works pretty decent, but some tests need to be made.
I left appart the isPersonalBest attribute since I didn't figure out how it worked.
I know that the code have been written in a more "correct" way, but this is just the beginning. Please, feel free to have a look at it and tell me what you think about it!
I have now implemented this feature and it will be included in the next major release. Thank you for the very constructive discussion through which all relevant parts had already been discussed. This made it much easier now to actually implement this.