F1 Data Analysis - Time Penalties in a Season

F1 Data Analysis - Time Penalties in a Season

In today's post, let's look at an example of how to use the FastF1 Python library to find all the time penalties from the 2023 F1 season. You could Google this, but it's more fun to do it ourselves, right? Plus, this way, we can easily look up penalties from other seasons too.

If you're not already familiar with the FastF1 or Python in general, check out my introductory post below, which will get you up to speed in no time.

F1 Data Analysis with Python - the Basics
There’s this great library called ‘FastF1’ that can really simplify things for you. It’s designed to make accessing and analysing F1 data a lot more straightforward and less time-consuming.

Get All Race Control Messages from a Specific Race

To begin, the following Python code demonstrates how to use the fastf1 Python library to retrieve race control messages from a specific Formula 1 race, in this case, the 2023 Abu Dhabi Grand Prix.

import fastf1
import pandas as pd

pd.set_option('display.max_rows', None)
fastf1.Cache.enable_cache('cache_dir')

session = fastf1.get_session(2023,'Abu Dhabi','R')
session.load()

messages = session.race_control_messages.loc[:,['Time', 'Message']]
print(messages)
  • Importing Libraries: The code begins by importing the necessary libraries, fastf1 for accessing F1 data and pandas (aliased as pd) for data manipulation and analysis.
  • Setting Display Option: pd.set_option('display.max_rows', None) configures pandas to display all rows of the DataFrame without truncation. This is useful for viewing all race control messages without missing any due to output limits.
  • Enabling Cache: fastf1.Cache.enable_cache('cache_dir') sets up a cache directory. This improves performance by storing downloaded data locally, reducing the need for repeated data downloads from the API.
  • Retrieving Session Data: The session = fastf1.get_session(2023, 'Abu Dhabi', 'R') line fetches the session data for the 2023 Abu Dhabi Grand Prix race ('R' stands for Race). session.load() loads the data into memory.
  • Accessing Race Control Messages: session.race_control_messages is a DataFrame containing all race control messages for the selected session. These messages include information about penalties, track conditions, and other race-related notifications.
  • Using loc for Data Selection: messages = session.race_control_messages.loc[:, ['Time', 'Message']] uses the .loc method to select specific columns ('Time' and 'Message') from the race control messages DataFrame. The colon : indicates that all rows are included, while ['Time', 'Message'] specifies the columns to be selected.
  • Output: The print(messages) statement displays the DataFrame, which includes the time each message was issued and the message content.

                  Time                                            Message
0  2023-11-26 12:10:01                        GREEN LIGHT - PIT EXIT OPEN
1  2023-11-26 12:20:01                                    PIT EXIT CLOSED
2  2023-11-26 12:23:35                   DOUBLE YELLOW IN TRACK SECTOR 12
3  2023-11-26 12:23:38                           CLEAR IN TRACK SECTOR 12
4  2023-11-26 12:45:08                     RISK OF RAIN FOR F1 RACE IS 0%
5  2023-11-26 12:57:05                                       DRS DISABLED
6  2023-11-26 13:03:26                        GREEN LIGHT - PIT EXIT OPEN
7  2023-11-26 13:06:30                                        DRS ENABLED
8  2023-11-26 13:11:34  CAR 3 (RIC) TIME 1:31.326 DELETED - TRACK LIMI...
9  2023-11-26 13:14:08  TURN 7 INCIDENT INVOLVING CARS 24 (ZHO) AND 23...
10 2023-11-26 13:14:56  TURN 7 INCIDENT INVOLVING CARS 2 (SAR) AND 20 ...
11 2023-11-26 13:15:56  CAR 23 (ALB) TIME 1:33.560 DELETED - TRACK LIM...
12 2023-11-26 13:15:57  CAR 2 (SAR) TIME 1:33.582 DELETED - TRACK LIMI...
13 2023-11-26 13:16:05  CAR 55 (SAI) TIME 1:31.175 DELETED - TRACK LIM...
14 2023-11-26 13:17:24  FIA STEWARDS: TURN 7 INCIDENT INVOLVING CARS 2...
15 2023-11-26 13:18:08  CAR 4 (NOR) TIME 1:30.613 DELETED - TRACK LIMI...
16 2023-11-26 13:18:43  CAR 16 (LEC) TIME 1:30.609 DELETED - TRACK LIM...
17 2023-11-26 13:19:10  FIA STEWARDS: TURN 7 INCIDENT INVOLVING CARS 2...
18 2023-11-26 13:20:02  CAR 2 (SAR) TIME 1:32.038 DELETED - TRACK LIMI...
19 2023-11-26 13:20:28  CAR 27 (HUL) TIME 1:32.002 DELETED - TRACK LIM...
20 2023-11-26 13:21:59  CAR 24 (ZHO) TIME 1:32.298 DELETED - TRACK LIM...
21 2023-11-26 13:24:53  CAR 44 (HAM) TIME 1:31.432 DELETED - TRACK LIM...
22 2023-11-26 13:24:59  CAR 18 (STR) TIME 1:31.693 DELETED - TRACK LIM...
23 2023-11-26 13:25:11  CAR 23 (ALB) TIME 1:31.655 DELETED - TRACK LIM...
24 2023-11-26 13:26:41  CAR 81 (PIA) TIME 1:30.649 DELETED - TRACK LIM...
25 2023-11-26 13:27:04  TURN 6 INCIDENT INVOLVING CARS 44 (HAM) AND 10...
26 2023-11-26 13:27:40  CAR 22 (TSU) TIME 1:30.901 DELETED - TRACK LIM...
27 2023-11-26 13:27:52  CAR 22 (TSU) TIME 1:31.081 DELETED - TRACK LIM...
28 2023-11-26 13:28:10  FIA STEWARDS: TURN 6 INCIDENT INVOLVING CARS 4...
29 2023-11-26 13:29:23  CAR 22 (TSU) TIME 1:31.228 DELETED - TRACK LIM...
30 2023-11-26 13:34:24  FIA STEWARDS: TURN 6 INCIDENT INVOLVING CARS 4...
31 2023-11-26 13:38:31  CAR 1 (VER) TIME 1:29.634 DELETED - TRACK LIMI...
32 2023-11-26 13:39:28  CAR 20 (MAG) TIME 1:51.422 DELETED - TRACK LIM...
33 2023-11-26 13:47:53  CAR 16 (LEC) TIME 1:29.556 DELETED - TRACK LIM...
34 2023-11-26 13:49:31  CAR 55 (SAI) TIME 1:29.700 DELETED - TRACK LIM...
35 2023-11-26 13:50:43  CAR 81 (PIA) TIME 1:30.082 DELETED - TRACK LIM...
36 2023-11-26 13:51:01  CAR 3 (RIC) TIME 1:32.484 DELETED - TRACK LIMI...
37 2023-11-26 13:51:51  CAR 4 (NOR) TIME 1:30.010 DELETED - TRACK LIMI...
38 2023-11-26 13:52:23  CAR 18 (STR) TIME 1:29.831 DELETED - TRACK LIM...
39 2023-11-26 13:53:47  CAR 14 (ALO) TIME 1:30.426 DELETED - TRACK LIM...
40 2023-11-26 13:59:26  CAR 23 (ALB) TIME 1:30.555 DELETED - TRACK LIM...
41 2023-11-26 13:59:51  CAR 23 (ALB) TIME 1:30.350 DELETED - TRACK LIM...
42 2023-11-26 13:59:59  BLACK AND WHITE FLAG FOR CAR 23 (ALB) - TRACK ...
43 2023-11-26 14:02:08  TURN 4 INCIDENT INVOLVING CARS 14 (ALO) AND 44...
44 2023-11-26 14:04:43  FIA STEWARDS: TURN 4 INCIDENT INVOLVING CARS 1...
45 2023-11-26 14:05:31  CAR 11 (PER) TIME 1:29.195 DELETED - TRACK LIM...
46 2023-11-26 14:08:35  INCIDENT INVOLVING CAR 44 (HAM) NOTED - PIT ST...
47 2023-11-26 14:09:13  CAR 10 (GAS) TIME 1:29.621 DELETED - TRACK LIM...
48 2023-11-26 14:11:07  CAR 10 (GAS) TIME 1:29.717 DELETED - TRACK LIM...
49 2023-11-26 14:11:35  FIA STEWARDS: INCIDENT INVOLVING CAR 44 (HAM) ...
50 2023-11-26 14:13:23  INCIDENT INVOLVING CAR 10 (GAS) NOTED - PIT ST...
51 2023-11-26 14:13:31  FIA STEWARDS: INCIDENT INVOLVING CAR 10 (GAS) ...
52 2023-11-26 14:14:22  TURN 6 INCIDENT INVOLVING CARS 4 (NOR) AND 11 ...
53 2023-11-26 14:14:57  CAR 4 (NOR) TIME 1:28.568 DELETED - TRACK LIMI...
54 2023-11-26 14:15:37  FIA STEWARDS: TURN 6 INCIDENT INVOLVING CARS 4...
55 2023-11-26 14:18:33  INCIDENT INVOLVING CARS 1 (VER), 2 (SAR), 24 (...
56 2023-11-26 14:19:14  FIA STEWARDS: 5 SECOND TIME PENALTY FOR CAR 11...
57 2023-11-26 14:19:51  FIA STEWARDS: INCIDENT INVOLVING CARS 1 (VER),...
58 2023-11-26 14:22:01  WAVED BLUE FLAG FOR CAR 20 (MAG) TIMED AT 18:2...
59 2023-11-26 14:23:22  WAVED BLUE FLAG FOR CAR 77 (BOT) TIMED AT 18:2...
60 2023-11-26 14:24:58  CAR 63 (RUS) TIME 1:30.038 DELETED - TRACK LIM...
61 2023-11-26 14:30:15  WAVED BLUE FLAG FOR CAR 24 (ZHO) TIMED AT 18:3...
62 2023-11-26 14:30:29                                     CHEQUERED FLAG
63 2023-11-26 14:40:35  INCIDENT INVOLVING CAR 23 (ALB) NOTED - PIT ST...
64 2023-11-26 14:40:42  FIA STEWARDS: INCIDENT INVOLVING CAR 23 (ALB) ...

By running this script, users can obtain a detailed log of all race control messages for a particular F1 race.

We Only Need Time Penalties

Since we're only interested in time penalties from the race control messages, we need to filter the messages to include only those relevant to our analysis. Let's add two more lines to the code.

import fastf1
import pandas as pd

pd.set_option('display.max_rows', None)
fastf1.Cache.enable_cache('cache_dir')

session = fastf1.get_session(2023,'Abu Dhabi','R')
session.load()

messages = session.race_control_messages.loc[:,['Time', 'Message']]
specific_string = 'SECOND TIME PENALTY'
filtered_df = messages[messages['Message'].str.contains(specific_string, na=False)]

print(filtered_df)
  • specific_string = 'SECOND TIME PENALTY': This line creates a variable named specific_string and assigns it the value '5 SECOND TIME PENALTY'. This string will be used to identify messages that contain time penalties.
  • filtered_df = messages[messages['Message'].str.contains(specific_string, na=False)]: This line filters the messages DataFrame. We use the str.contains method to check each message for the presence of our specific_string. The na=False parameter ensures that any NaN (Not a Number) values in the 'Message' column are treated as False during the filtering process. As a result, filtered_df is a new DataFrame containing only the rows from messages where the 'Message' column contains the string 'SECOND TIME PENALTY'.
Time                                            Message
56 2023-11-26 14:19:14  FIA STEWARDS: 5 SECOND TIME PENALTY FOR CAR 11...

By adding these lines to our script, we can effectively isolate just the time penalties from the race control messages.

Find All the Penalties Across a Season

In this section, we expand our script to find all the time penalties across the entire 2023 F1 season. This script iterates through each race event in the season and filters out the time penalty messages, similar to what we did for a single race but now on a season-wide scale.

import fastf1
import pandas as pd
import logging

logging.getLogger('fastf1').setLevel(logging.CRITICAL)
pd.set_option('display.max_rows', None)
fastf1.Cache.enable_cache('cache_dir')

schedule = fastf1.get_event_schedule(2023)

for index, row in schedule.iterrows():
    track = row['Location']
    session = fastf1.get_session(2023,track,'R')
    session.load()
    session_name = session.event['EventName']
    print(session_name)

    messages = session.race_control_messages

    specific_string = 'SECOND TIME PENALTY'
    filtered_df = messages[messages['Message'].str.contains(specific_string, na=False)]

    for index, row in filtered_df.iterrows():
        message = row['Message'].replace('FIA STEWARDS: ', '')
        print(message)
    print('\n')
  • Setting Logging Level: The script begins by setting the logging level for fastf1 to CRITICAL with logging.getLogger('fastf1').setLevel(logging.CRITICAL). This is done to minimize unnecessary logging output from the fastf1 library.
  • Retrieving Season Schedule: schedule = fastf1.get_event_schedule(2023) fetches the schedule for all events in the 2023 F1 season. This schedule includes information about each race, like location and date.
  • Iterating Over Each Event: The script uses a for loop (for index, row in schedule.iterrows():) to iterate over each event in the season schedule. For each event, it retrieves the race session data using session = fastf1.get_session(2023, track, 'R') and session.load(). Here, 'R' stands for the Race session.
  • Printing Time Penalty Messages: Finally, for each event, the script iterates over the filtered DataFrame containing penalty messages. It prints each message, having removed the prefix 'FIA STEWARDS: ' for clarity, using message = row['Message'].replace('FIA STEWARDS: ', '') and print(message).
  • Separating Events: print('\n') at the end of the outer loop adds a newline after processing each event, making the output more readable by separating the penalties for each race.

By running this script, you can comprehensively extract and list all time penalties issued during each race of the 2023 F1 season, providing a season-wide overview of penalties in an organized and readable format. Here is the output.

Bahrain Grand Prix
5 SECOND TIME PENALTY FOR CAR 31 (OCO) - INACCURATE GRID POSITION
10 SECOND TIME PENALTY FOR CAR 31 (OCO) - SERVING TIME PENALTY INCORRECTLY 
5 SECOND TIME PENALTY FOR CAR 31 (OCO) - SPEEDING IN THE PIT LANE
5 SECOND TIME PENALTY FOR CAR 27 (HUL) - MULTIPLE TRACK LIMITS
10 SECOND TIME PENALTY FOR CAR 27 (HUL) - MULTIPLE TRACK LIMITS


Saudi Arabian Grand Prix
5 SECOND TIME PENALTY FOR CAR 14 (ALO) - INCORRECT STARTING LOCATION
10 SECOND TIME PENALTY FOR CAR 14 (ALO) - SERVING PENALTY INCORRECTLY 


Australian Grand Prix
5 SECOND TIME PENALTY FOR CAR 55 (SAI) - CAUSING A COLLISION


Azerbaijan Grand Prix


Miami Grand Prix
5 SECOND TIME PENALTY FOR CAR 55 (SAI) - SPEEDING IN THE PIT LANE


Monaco Grand Prix
5 SECOND TIME PENALTY FOR CAR 27 (HUL) - CAUSING A COLLISION
5 SECOND TIME PENALTY FOR CAR 2 (SAR) - SPEEDING IN THE PIT LANE
5 SECOND TIME PENALTY FOR CAR 63 (RUS) - UNSAFE REJOIN
10 SECOND TIME PENALTY FOR CAR 27 (HUL) - SERVING PENALTY INCORRECTLY 


Spanish Grand Prix
5 SECOND TIME PENALTY FOR CAR 22 (TSU) - FORCING ANOTHER DRIVER OFF THE TRACK


Canadian Grand Prix
5 SECOND TIME PENALTY FOR CAR 4 (NOR) - UNSPORTSMANLIKE BEHAVIOUR 


Austrian Grand Prix
5 SECOND TIME PENALTY FOR CAR 44 (HAM) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 22 (TSU) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 31 (OCO) - UNSAFE RELEASE
5 SECOND TIME PENALTY FOR CAR 55 (SAI) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 23 (ALB) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 21 (DEV) - FORCING ANOTHER DRIVER OFF THE TRACK
5 SECOND TIME PENALTY FOR CAR 10 (GAS) - TRACK LIMITS
10 SECOND TIME PENALTY FOR CAR 22 (TSU) - 7 TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 2 (SAR) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 20 (MAG) - TRACK LIMITS


British Grand Prix
5 SECOND TIME PENALTY FOR CAR 18 (STR) - CAUSING A COLLISION


Hungarian Grand Prix
5 SECOND TIME PENALTY FOR CAR 24 (ZHO) - CAUSING A COLLISION
5 SECOND TIME PENALTY FOR CAR 16 (LEC) - SPEEDING IN THE PIT LANE


Belgian Grand Prix


Dutch Grand Prix
10 SECOND TIME PENALTY FOR CAR 40 (LAW) - IMPEDING
5 SECOND TIME PENALTY FOR CAR 10 (GAS) - SPEEDING IN THE PIT LANE
5 SECOND TIME PENALTY FOR CAR 22 (TSU) - CAUSING A COLLISION
5 SECOND TIME PENALTY FOR CAR 11 (PER) - SPEEDING IN THE PIT LANE


Italian Grand Prix
5 SECOND TIME PENALTY FOR CAR 63 (RUS) - LEAVING THE TRACK AND GAINING AN ADVANTAGE
5 SECOND TIME PENALTY FOR CAR 44 (HAM) - CAUSING A COLLISION
5 SECOND TIME PENALTY FOR CAR 2 (SAR) - CAUSING A COLLISION


Singapore Grand Prix
5 SECOND TIME PENALTY FOR CAR 14 (ALO) - FAILING TO FOLLOW RACE DIRECTORS INSTRUCTIONS – CROSSING THE LINE AT PIT ENTRY


Japanese Grand Prix
5 SECOND TIME PENALTY FOR CAR 2 (SAR) - CAUSING A COLLISION
5 SECOND TIME PENALTY FOR CAR 11 (PER) - SAFETY CAR INFRINGEMENT
5 SECOND TIME PENALTY FOR CAR 11 (PER) - CAUSING A COLLISION


Qatar Grand Prix
10 SECOND TIME PENALTY FOR CAR 27 (HUL) - INCORRECT STARTING LOCATION
5 SECOND TIME PENALTY FOR CAR 11 (PER) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 11 (PER) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 18 (STR) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 10 (GAS) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 10 (GAS) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 23 (ALB) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 23 (ALB) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 18 (STR) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 10 (GAS) - TRACK LIMITS
5 SECOND TIME PENALTY FOR CAR 11 (PER) - TRACK LIMITS


United States Grand Prix
5 SECOND TIME PENALTY FOR CAR 23 (ALB) - TRACK LIMITS


Mexico City Grand Prix


São Paulo Grand Prix


Las Vegas Grand Prix
LAP 1 TURN 1 INCIDENT 5 SECOND TIME PENALTY FOR CAR 1 (VER) - FORCING ANOTHER DRIVER OFF THE TRACK
5 SECOND TIME PENALTY FOR CAR 63 (RUS) - CAUSING A COLLISION


Abu Dhabi Grand Prix
5 SECOND TIME PENALTY FOR CAR 11 (PER) - CAUSING A COLLISION

Closing Up

In conclusion, by using the fastf1 Python library, we've successfully navigated through the process of extracting time penalties from the 2023 Formula 1 season. This method not only gives us a detailed view of penalties in individual races but also offers a season-wide perspective, all with a few lines of code.