Skip to main content
Understanding tire degradation is crucial in F1 strategy. This tutorial shows how to visualize how different tire compounds lose performance as they age during a race. Tire degradation chart showing compound performance over tire life

What is Tire Degradation?

Tire degradation is the gradual loss of grip and increase in lap times as tires wear out. Different compounds (Soft, Medium, Hard) have different degradation profiles:
  • Soft: Fastest initially but degrades quickly
  • Medium: Balanced performance and durability
  • Hard: Slower but lasts longer with minimal degradation

Loading Race Data

We’ll analyze the 2024 Hungarian Grand Prix to see how tires performed:
import tif1
import matplotlib.pyplot as plt
import numpy as np
from tif1.plotting import setup_mpl

# Setup plotting style with FastF1 dark background
setup_mpl(color_scheme="fastf1")

# Load race session
session = tif1.get_session(2024, "Hungarian Grand Prix", "Race")
laps = session.laps

# Convert LapTime to seconds for calculations
laps["LapTimeSeconds"] = laps["LapTime"].dt.total_seconds()

# Clean data: remove outliers and first lap
clean_laps = laps[
    (laps["LapNumber"] > 1) &
    (laps["LapTimeSeconds"] < laps["LapTimeSeconds"].min() * 1.07)
].copy()

Fuel-Corrected Lap Times

Cars get lighter as they burn fuel, making them faster. To see true tire degradation, we need to correct for this:
# Estimate fuel effect: ~0.03s per lap improvement
max_lap = clean_laps["LapNumber"].max()
clean_laps["FuelCorrectedTime"] = (
    clean_laps["LapTimeSeconds"] -
    (0.03 * (max_lap - clean_laps["LapNumber"]))
)

Smoothing Data with Rolling Median

To reduce noise in the degradation curves, we’ll use a rolling median:
def rolling_median(data, window=5):
    """Simple rolling median for smoothing."""
    result = []
    for i in range(len(data)):
        start = max(0, i - window // 2)
        end = min(len(data), i + window // 2 + 1)
        result.append(np.median(data[start:end]))
    return np.array(result)

Visualizing Degradation by Compound

Let’s plot how each compound performs over its lifetime with both raw data and smoothed trends:
fig, ax = plt.subplots(figsize=(14, 8))

# Compound colors and styling
compounds = {
    "SOFT": {"color": "red", "alpha": 0.3},
    "MEDIUM": {"color": "gold", "alpha": 0.3},
    "HARD": {"color": "lightgray", "alpha": 0.3},
}

for compound, style in compounds.items():
    compound_laps = clean_laps[clean_laps["Compound"] == compound]

    if len(compound_laps) > 10:
        # Scatter plot of raw data
        ax.scatter(
            compound_laps["TyreLife"],
            compound_laps["FuelCorrectedTime"],
            color=style["color"],
            alpha=style["alpha"],
            s=30,
            zorder=1
        )

        # Group by tire life and get median
        grouped = compound_laps.groupby("TyreLife")["FuelCorrectedTime"].median()
        tire_life = grouped.index.values
        lap_times = grouped.values

        # Apply rolling median for smoothing
        if len(lap_times) > 5:
            smoothed_times = rolling_median(lap_times, window=5)
        else:
            smoothed_times = lap_times

        # Plot smoothed line
        ax.plot(
            tire_life,
            smoothed_times,
            color=style["color"],
            linewidth=4,
            label=compound,
            zorder=2
        )

        # Add annotation at end of line
        ax.annotate(
            compound,
            xy=(tire_life[-1], smoothed_times[-1]),
            xytext=(tire_life[-1] + 2, smoothed_times[-1]),
            fontsize=14,
            fontweight="bold",
            color=style["color"],
            va="center"
        )

# Styling
ax.set_xlabel("Tire Life (Laps)", fontsize=16, fontweight="bold")
ax.set_ylabel("Fuel-Corrected Lap Time (s)", fontsize=16, fontweight="bold")
ax.set_title(
    f"Tire Degradation Analysis - {session.event['EventName']} {session.event['EventDate'].year}",
    fontsize=18,
    fontweight="bold",
    pad=20
)

# Grid and legend
ax.grid(True, alpha=0.3, linestyle="--")
ax.legend(fontsize=14, loc="upper left", framealpha=0.9)

# Set reasonable y-axis limits using quantiles
y_min = clean_laps["FuelCorrectedTime"].quantile(0.01)
y_max = clean_laps["FuelCorrectedTime"].quantile(0.99)
ax.set_ylim(y_min - 0.5, y_max + 0.5)

plt.tight_layout()
plt.show()
The visualization now shows:
  • Raw data points as semi-transparent scatter plots
  • Smoothed trend lines using rolling median to reduce noise
  • Compound labels annotated at the end of each line
  • Dynamic y-axis limits that exclude extreme outliers

Analyzing Specific Driver Performance

Compare how different drivers manage the same compound:
import numpy as np

# Compare two drivers on Medium tires
drivers = ["VER", "HAM"]
fig, ax = plt.subplots(figsize=(12, 8))

for driver in drivers:
    driver_laps = clean_laps[
        (clean_laps["Driver"] == driver) &
        (clean_laps["Compound"] == "MEDIUM")
    ]

    if len(driver_laps) > 5:
        # Calculate degradation rate using numpy polyfit
        coeffs = np.polyfit(driver_laps["TyreLife"],
                           driver_laps["FuelCorrectedTime"], 1)
        slope = coeffs[0]

        ax.scatter(driver_laps["TyreLife"],
                  driver_laps["FuelCorrectedTime"],
                  label=f"{driver} ({slope:.3f}s/lap)",
                  alpha=0.6, s=50)

        # Add trend line
        x_trend = np.linspace(driver_laps["TyreLife"].min(),
                             driver_laps["TyreLife"].max(), 100)
        y_trend = np.polyval(coeffs, x_trend)
        ax.plot(x_trend, y_trend, linestyle="--", alpha=0.5)

ax.set_xlabel("Tire Life (Laps)", fontsize=14)
ax.set_ylabel("Fuel-Corrected Lap Time (s)", fontsize=14)
ax.set_title("Driver Tire Management Comparison - Medium Compound",
            fontsize=16, fontweight="bold")
ax.legend(fontsize=12)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Understanding Degradation Rates

The slope of the trend line tells you the degradation rate:
  • 0.00 - 0.05 s/lap: Excellent tire management
  • 0.05 - 0.10 s/lap: Normal degradation
  • > 0.10 s/lap: High degradation (aggressive driving or setup issues)

Finding the “Cliff”

The “cliff” is when tires suddenly lose significant performance:
# Detect sudden performance drops
for compound in ["SOFT", "MEDIUM", "HARD"]:
    compound_laps = clean_laps[clean_laps["Compound"] == compound]

    if len(compound_laps) > 15:
        degradation = compound_laps.groupby("TyreLife")["FuelCorrectedTime"].median()

        # Calculate lap-to-lap delta
        deltas = degradation.diff()

        # Find where degradation exceeds threshold
        cliff_point = deltas[deltas > 0.15].index.min() if any(deltas > 0.15) else None

        if cliff_point:
            print(f"{compound}: Cliff at lap {cliff_point}")
        else:
            print(f"{compound}: No cliff detected")

Summary

With tif1, you can:
  • Analyze tire degradation patterns across compounds
  • Compare driver tire management skills
  • Identify optimal stint lengths before the “cliff”
  • Make data-driven strategy decisions
This analysis is fundamental to understanding race strategy and predicting optimal pit stop windows.
Last modified on March 6, 2026