F1のレースを見ていると、「どのドライバーが安定して速いのか」「タイヤ交換後にどれくらいペースが上がったのか」など、ラップタイムの変化が気になることはありませんか?
こうした分析は、実はPythonを使えば誰でも簡単に行うことができます。
本記事では、F1公式のライブタイミングデータを扱えるライブラリ「FastF1」を使って、レース中のラップタイムを取得し、グラフで可視化する方法を解説します。
コードはすべてコピペで動かせるようにしているので、プログラミング初心者の方でも安心です。
この記事でできるようになることは以下の通りです。
- ドライバーごとのラップタイムを取得する
- レース中のペース変化をグラフで可視化する
- タイヤ交換やセーフティカーの影響を読み取る
それでは、実際にラップタイム分析を始めていきましょう。

注意事項
本記事で紹介している内容について、あらかじめ以下の点をご理解ください。
- 本記事は非公式の情報をもとに作成しており、FastF1およびF1公式とは一切関係ありません。
- 掲載しているコードは動作確認を行っていますが、環境やバージョンの違いにより正常に動作しない場合があります。
- 本記事のコードの作成・補助にはAIを使用しています。内容の正確性には配慮していますが、完全な保証はできません。
- 個別の環境に関するトラブルやエラーについては、対応できない場合があります。
▶︎ FastF1の全体像や学習手順はこちらの記事でまとめています⬇️
FastF1でレースのラップタイムをグラフ化する方法
ここからは、FastF1を使って実際にドライバーのラップタイムを取得する方法を解説します。本記事では、セッションのラップデータを読み込み、ドライバーごとのラップタイムをグラフで可視化する手順を順を追って紹介します。
掲載しているPythonコードはすべてコピペでそのまま実行できるようにしているため、プログラミング初心者の方でも安心して試すことができます。
また、FastF1を初めて使う方は、事前に「FastF1の使い方【初心者チュートリアル】」の記事を確認しておくと、よりスムーズに理解できます。
まずはセッションを読み込む
FastF1では、まず対象となるレースセッションを読み込む必要があります。以下は、日本GPのセッションを読み込む基本的な例です。
まずはFastF1を読み込み、セッションデータを取得します。
# ============================================================
# ① 準備パート:ライブラリの読み込み
# ============================================================
import fastf1
from fastf1 import plotting
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.ticker as ticker
import matplotlib.patches as mpatches
import pandas as pd
import numpy as np
import os
os.makedirs('cache', exist_ok=True)
fastf1.Cache.enable_cache('cache')
# FastF1のプロットスタイルを設定
plotting.setup_mpl(mpl_timedelta_support=True, color_scheme='fastf1')このコードでは、FastF1のライブラリをインポートし、キャッシュ機能を有効にしています。キャッシュを使うことで、一度取得したデータを再利用できるため、2回目以降の読み込みが高速になります。
また、Matplotlibの設定を行うことで、時間形式のデータを扱えるようになり、チームカラーを反映したグラフも作成できるようになります。
続いて、対象のセッションを指定してデータを読み込みます。
# ============================================================
# ② セッション読み込みパート
# ============================================================
session = fastf1.get_session(2025, "JAPAN", "R") # (年:2025, 開催国:日本, セッション:決勝)
session.load()これで、決勝セッションのラップデータを取得できる状態になります。
ドライバーを指定する
次に、ラップタイムを分析したいドライバーを指定します。FastF1では、各ドライバーを3文字の略称(例:HAM、VERなど)で指定します。
# ============================================================
# ③ ドライバー指定パート
# ============================================================
# 表示したいドライバーの略称を入力してください
DRIVERS = ('VER', 'NOR', 'LEC','TSU')このように、タプル形式で複数のドライバーを指定することで、複数人のラップタイムをまとめて比較することができます。例えば、トップ争いのドライバー同士を比較したり、チームメイト同士のペース差を分析する際に便利です。
なお、ドライバーの略称はF1公式の3文字コードに対応しており、FastF1でも同じ形式が使用されています。
関数を定義してデータを取得する
ここでは、ラップタイム分析に必要なデータをまとめて取得するための関数を定義します。
内容は少し長く見えますが、このパートは中身を理解する必要はありません。そのままコピペして使えるように用意しているので、まずは「動かすこと」を優先して進めていきましょう。
# ============================================================
# ④ データ取得パート(関数)
# ============================================================
# --- タイヤ色の定義 ---
COMPOUND_COLORS = {
'SOFT': '#FF3333',
'MEDIUM': '#FFD700',
'HARD': '#FFFFFF',
'INTERMEDIATE': '#39B54A',
'WET': '#0067FF',
}
COMPOUND_EDGE = {
'SOFT': '#CC0000',
'MEDIUM': '#CC9900',
'HARD': '#AAAAAA',
'INTERMEDIATE': '#007700',
'WET': '#0044BB',
}
# フラッグの色定義(テキスト色・背景塗りつぶし色・透明度)
FLAG_SETTINGS = [
# (対象ラップリスト用キー, 塗りつぶし色, ラベル, 透明度)
('yellow', 'yellow', 'YF', 0.25),
('vsc', 'cyan', 'VSC', 0.30),
('sc', 'orange', 'SC', 0.35),
]
FLAG_TEXT_COLORS = {
'yellow': '#FFE500',
'cyan': '#00DDFF',
'orange': '#FF8800',
}
def get_flag_laps_interval(session, laps, start_message, end_messages=('AllClear',)):
"""
SC・VSCなど「開始〜終了」で区間になるフラッグが
かかっているラップ番号のリストを返す関数。
"""
if not hasattr(session, 'track_status'):
return []
ts = session.track_status.copy()
ts['Time'] = ts['Time'].apply(pd.Timedelta)
# フラッグの開始・終了時刻をペアにまとめる
intervals = []
active_start = None
for _, row in ts.iterrows():
if row['Message'] == start_message:
active_start = row['Time']
elif active_start is not None and row['Message'] in end_messages:
intervals.append((active_start, row['Time']))
active_start = None
if active_start is not None:
intervals.append((active_start, ts['Time'].max()))
if not intervals:
return []
# 各ラップがフラッグ区間と重なるか確認
event_laps = []
valid_laps = laps[laps['LapTime'].notna()].copy()
for _, lap in valid_laps.iterrows():
lap_start = lap['LapStartTime']
lap_end = lap_start + lap['LapTime']
for (iv_start, iv_end) in intervals:
if lap_start <= iv_end and lap_end >= iv_start:
event_laps.append(lap['LapNumber'])
break
return sorted(set(event_laps))
def get_flag_laps_point(session, laps, status_message):
"""
イエローフラッグなど「瞬間的」なフラッグが
かかっているラップ番号のリストを返す関数。
"""
if not hasattr(session, 'track_status'):
return []
status_times = [
pd.Timedelta(t) for t in
session.track_status[
session.track_status['Message'] == status_message
]['Time'].values
]
event_laps = []
valid_laps = laps[laps['LapTime'].notna()].copy()
for _, lap in valid_laps.iterrows():
lap_start = lap['LapStartTime']
lap_end = lap_start + lap['LapTime']
for st in status_times:
if lap_start <= st <= lap_end:
event_laps.append(lap['LapNumber'])
break
return sorted(set(event_laps))
def get_pitin_laps(laps, driver):
"""ピットインしたラップ番号の集合を返す関数(PIT文字表示用)。"""
driver_laps = laps.pick_drivers(driver)
return set(driver_laps[driver_laps['PitInTime'].notna()]['LapNumber'].unique())
def get_pit_laps(laps, driver):
"""ピットイン・アウト両方のラップ番号の集合を返す関数(ラップタイム除外用)。"""
driver_laps = laps.pick_drivers(driver)
return set(
driver_laps[
driver_laps['PitInTime'].notna() | driver_laps['PitOutTime'].notna()
]['LapNumber'].unique()
)
def get_team_color(session, driver):
"""FastF1からドライバーのチームカラーを取得する関数。"""
try:
return plotting.get_driver_style(driver, style=['color'], session=session)['color']
except Exception:
return None
def group_consecutive(lap_list):
"""
連続するラップ番号をグループ化する関数。
例:[1,2,3,7,8] → [(1,3), (7,8)]
フラッグテキストを区間の中央に表示するために使用。
"""
groups = []
if not lap_list:
return groups
start = prev = lap_list[0]
for ln in lap_list[1:]:
if ln == prev + 1:
prev = ln
else:
groups.append((start, prev))
start = prev = ln
groups.append((start, prev))
return groups
# --- データ取得の実行 ---
laps = session.laps
sc_laps = get_flag_laps_interval(session, laps, 'SCDeployed', end_messages=('AllClear',))
vsc_laps = get_flag_laps_interval(session, laps, 'VSCDeployed', end_messages=('AllClear', 'SCDeployed'))
yellow_laps = get_flag_laps_point(session, laps, 'Yellow')
# フラッグ情報をキーつき辞書にまとめる(FLAG_SETTINGSのキーと対応)
flag_laps = {
'yellow': yellow_laps,
'vsc': vsc_laps,
'sc': sc_laps,
}
pitin_laps_per_driver = {d: get_pitin_laps(laps, d) for d in DRIVERS}
pit_laps_per_driver = {d: get_pit_laps(laps, d) for d in DRIVERS}
all_lap_nums = laps['LapNumber'].dropna().astype(int)
lap_min = int(all_lap_nums.min())
lap_max = int(all_lap_nums.max())グラフを作成する
ここまで準備できたら、いよいよラップタイムをグラフとして可視化します。
このパートもコードは少し長く見えますが、そのままコピペでOKです。実行すると、ラップタイムの推移とタイヤ戦略をまとめたグラフが表示されます。
# ============================================================
# ⑤ グラフ作成パート
# ============================================================
driver_colors = {}
n_drivers = len(DRIVERS)
# --- レイアウト:上がラップタイム、下がタイヤ帯 ---
fig = plt.figure(figsize=(14, 6 + n_drivers * 0.35))
gs = gridspec.GridSpec(
2, 1,
height_ratios=[6, n_drivers * 0.35],
hspace=0.08,
)
ax = fig.add_subplot(gs[0]) # ラップタイムグラフ
ax_tyre = fig.add_subplot(gs[1], sharex=ax) # タイヤ帯
# --- ラップタイム折れ線グラフ ---
for driver in DRIVERS:
driver_laps = laps.pick_drivers(driver).copy()
pit_lap_set = pit_laps_per_driver[driver]
all_laps_sorted = driver_laps.sort_values('LapNumber')
lap_nums = all_laps_sorted['LapNumber'].values
lap_times = all_laps_sorted['LapTime'].dt.total_seconds().values.copy()
for idx, row in enumerate(all_laps_sorted.itertuples()):
is_quick = getattr(row, 'IsAccurate', False) and not pd.isna(row.LapTime)
is_pit = row.LapNumber in pit_lap_set
if not is_quick or is_pit:
lap_times[idx] = np.nan
style = plotting.get_driver_style(
identifier=driver,
style=['color', 'linestyle'],
session=session
)
plot_kwargs = dict(
label=driver,
linewidth=1.5,
zorder=3,
marker='o',
markersize=3,
)
plot_kwargs.update(style)
line, = ax.plot(lap_nums, lap_times, **plot_kwargs)
driver_colors[driver] = line.get_color()
# --- フラッグ塗りつぶし(ラップタイムグラフ) ---
for key, color, label, alpha in FLAG_SETTINGS:
for lap in flag_laps[key]:
ax.axvspan(lap - 0.5, lap + 0.5, color=color, alpha=alpha, zorder=1)
# --- ラップタイムグラフの軸設定 ---
ax.set_xlim(lap_min - 0.5, lap_max + 0.5)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.xaxis.set_minor_locator(ticker.NullLocator())
ax.set_ylabel("Lap Time", fontsize=11)
ax.set_title(
f"{session.event['EventName']} {session.event.year} – Race Lap Times (Quick Laps)",
fontsize=13,
)
ax.yaxis.set_major_formatter(
ticker.FuncFormatter(lambda x, pos: f"{int(x // 60)}:{x % 60:04.1f}")
)
ax.grid(which='major', axis='y', linestyle='--', color='gray', alpha=0.5, zorder=0)
ax.grid(which='major', axis='x', linestyle=':', color='gray', alpha=0.3, zorder=0)
plt.setp(ax.get_xticklabels(), visible=False)
driver_handles, driver_labels = ax.get_legend_handles_labels()
ax.legend(handles=driver_handles, labels=driver_labels,
loc='upper right', fontsize=9, framealpha=0.8)
# --- フラッグテキスト(連続区間の中央に黒背景で表示) ---
fig.canvas.draw()
y_min, y_max = ax.get_ylim()
y_flag_text = y_max - (y_max - y_min) * 0.03
for key, color, label, alpha in FLAG_SETTINGS:
tc = FLAG_TEXT_COLORS.get(color, 'white')
for (grp_start, grp_end) in group_consecutive(flag_laps[key]):
ax.text(
(grp_start + grp_end) / 2, y_flag_text, label,
color=tc, fontsize=7.5, fontweight='bold', fontfamily='monospace',
ha='center', va='top', zorder=5, clip_on=True,
bbox=dict(facecolor='black', edgecolor='none', pad=1.5),
)
# --- PIT テキスト(ピットインラップに黒背景で表示) ---
for driver in DRIVERS:
driver_laps = laps.pick_drivers(driver).copy()
for _, lap in driver_laps.iterrows():
if lap['LapNumber'] not in pitin_laps_per_driver[driver]:
continue
# ピット前の最後の有効ラップタイムをY座標に使う
prev_laps = driver_laps[
driver_laps['LapNumber'] < lap['LapNumber']
].sort_values('LapNumber', ascending=False)
ref_y = None
for _, prev in prev_laps.iterrows():
if getattr(prev, 'IsAccurate', False) and pd.notna(prev['LapTime']):
ref_y = prev['LapTime'].total_seconds()
break
if ref_y is None:
ref_y = y_min + (y_max - y_min) * 0.1
ax.text(
lap['LapNumber'] + 0.55, ref_y, 'PIT',
color=driver_colors[driver],
fontsize=6.5, fontweight='bold', fontfamily='monospace',
ha='left', va='center', zorder=5, clip_on=True,
bbox=dict(facecolor='black', edgecolor='none', pad=1.0),
)
# --- タイヤ帯の描画 ---
row_h = 1.0
y_total = n_drivers * row_h
ax_tyre.set_xlim(lap_min - 0.5, lap_max + 0.5)
ax_tyre.set_ylim(0, y_total)
ax_tyre.set_yticks([i * row_h + row_h / 2 for i in range(n_drivers)])
ax_tyre.set_yticklabels(list(reversed(DRIVERS)), fontsize=9)
ax_tyre.set_ylabel("Tyre", fontsize=11)
ax_tyre.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax_tyre.xaxis.set_minor_locator(ticker.NullLocator())
ax_tyre.set_xlabel("Lap Number", fontsize=11)
ax_tyre.tick_params(axis='y', length=0)
if lap_max - lap_min > 40:
plt.setp(ax_tyre.get_xticklabels(), rotation=90, fontsize=7)
else:
plt.setp(ax_tyre.get_xticklabels(), fontsize=8)
# ドライバーの区切り線
for i in range(n_drivers + 1):
ax_tyre.axhline(i * row_h, color='gray', linewidth=0.5, zorder=2)
# タイヤ色の矩形を描画(タイヤ情報なし → 黒で塗りつぶし)
for i, driver in enumerate(reversed(DRIVERS)):
driver_laps = laps.pick_drivers(driver).copy()
y_bottom = i * row_h + 0.05
h = row_h - 0.10
for _, lap in driver_laps.iterrows():
compound = lap.get('Compound', None)
compound = 'UNKNOWN' if pd.isna(compound) or compound is None else str(compound).upper()
fc = COMPOUND_COLORS.get(compound, '#111111')
ec = COMPOUND_EDGE.get(compound, '#333333')
ax_tyre.add_patch(mpatches.Rectangle(
(lap['LapNumber'] - 0.48, y_bottom), 0.96, h,
facecolor=fc, edgecolor=ec, linewidth=0.4,
))
# --- 表示 ---
plt.show()このコードを実行すると、ドライバーのラップタイムに加えて、タイヤ情報やピットタイミングといった要素をグラフで確認できます。

このグラフを見ることで、次のようなポイントを分析できます。
- レース序盤・中盤・終盤のペース変化
- ドライバー同士のタイム差
- タイヤ劣化によるタイム低下
- タイヤ情報(ソフト:赤、ミディアム:黄、ハード:白)
例えば、このグラフは2025年の日本GPのデータをもとにしたものです。このレースではレッドブルのフェルスタッペンが優勝し、日本人ドライバーの角田裕毅がレッドブル昇格後初レースとして注目されました。
グラフを見ると、角田はフェルスタッペンと比較して一定のラップタイム差があることがわかります。後方スタートに加えて、追い抜きが難しい鈴鹿サーキットの特性もあり、トラフィックの影響で思うようにペースを上げられない状況でした。
一方で、レース後半になるにつれてラップタイム差が徐々に縮まっている点も読み取ることができます。
このように、レース映像だけでは順位や結果しか把握しにくい部分も、グラフで可視化することで「どれだけペース差があったのか」といった情報を直感的に理解できるようになります。
これまで感覚的に語られがちだったレース分析も、データに基づいて考察できるようになるのが、この手法の大きな魅力です。
【PR】F1ゲームをよりリアルに楽しめるハンコン
FastF1でドライバーのテレメトリーを分析していると、「実際に自分でもこの走りを体感してみたい」と思うことはありませんか? そんな方には、ステアリング型コントローラー(ハンコン)を使ったレースゲーム体験がぴったりです。
👉【PR】Logicool G29 ドライビングフォース(PS5 / PS4 / PC対応)

Logicool G29は、定番のハンコンモデルで、PS5・PS4・PCに対応。
フォースフィードバック機能により、路面の感触やマシンの挙動をリアルに再現してくれます。 ブレーキングやコーナリングの感覚を手元で感じられるので、テレメトリーで見たスピードやアクセル操作を自分で体験する感覚が味わえます。
まとめ
本記事では、FastF1を使ってレースのラップタイムを取得し、グラフで可視化する方法を解説しました。
今回の内容を振り返ると、以下のことができるようになりました。
- FastF1でレースセッションのデータを取得する
- ドライバーごとのラップタイムを抽出する
- ラップタイムをグラフとして可視化する
- タイヤ戦略やピットタイミング、フラッグの影響を読み取る
一見すると難しそうに見える分析も、FastF1を使えばコピペだけでここまで実現できます。
ラップタイムを可視化することで、レースの「流れ」や「駆け引き」がより深く理解できるようになり、F1観戦の楽しみ方が大きく広がります。
ぜひ、自分の好きなレースやドライバーで試してみてください。
─── 関連記事 ───
以下の記事では、2026年のF1・MotoGPの視聴方法をわかりやすくまとめています。日本の公式配信サービスに加え、VPNを使った海外配信サービスの利用ガイドも紹介しているので、モータースポーツ観戦の選択肢を広げたい方はぜひ参考にしてみてください。
▶︎ F1-DashやGP TEMPOなど、無料で使えるライブタイミングやテレメトリーデータの解析方法を紹介しています⬇️
F1公式のサブスクリプションサービス F1 TV Access では、レースのライブ視聴はできないものの、F1公式のライブタイミングやリアルタイムのテレメトリーデータを確認できます。さらに、過去のレースアーカイブにもアクセス可能です。
月額360円でどこまでの機能が使えるのか、メリットや注意点も含めて詳しくまとめています。
▶︎ F1 TV Accessでできること・できないことを解説した記事はこちら⬇️




