• 2024/11/15 08:35

レースペース分析を始めるまでの手順

 昨今、FIAのリザルトとデータのページがアクセスできなくなっており、ラップタイムを入手するにはFIAのイベントページからPDFをダウンロードせざるを得なくなった。一方、9/13にはOpenAIから論理・数学知能に長けたOpenAI o1がリリースされた。

 そこで、当サイトでは、GPTs(GPT-4o)にPDFをエクセルに貼り付けやすい表形式に変換してもらい、o1にはエクセル内の作業を自動化するVBAマクロを書いてもらうことで、作業の効率化を図ることとした。

1. PDFから表へ

 以下のGPTsを使用する。

https://chatgpt.com/gpts/editor/g-bBZcBoHyy

 PDFを送れば、表を出力してくれる。GPT-4oの能力の限界の都合上、4人ずつの出力となり、「next」と送ると次の4人に進むことができる。なお、筆者の分析用エクセルシートに合わせて、Verstappen, Perez, Hamilton, Russel、次にLeclerc, Sainz, Norris, Piastri、次にGasly, Ocon, Tsunoda, Ricciardo、次にAlonso, Stroll, Albon, Colapinto、最後にBottas, Zhou, Hulkenberg, Magnussenの順となっている。こだわりがある方は、自分なりの順序をGPTに伝えれば。

 また、案内に記述してあるが、最初にPDFと共に

・最初に周回数が少ないドライバーについて、「フェルスタッペンとハミルトンは59周まで、ルクレールは41周までしかないことに気をつけてください。」のように最初に注意喚起。

・代役出走がある場合はその旨を指示。

の2点は押さえておく必要がある。特に後者は忘れるとハルシネーションに繋がる確率が高いだろう。ただし、そこまでやっても特に全周回数を走り切っていないドライバーのラップタイムを無理やり生成してくることは時折あるので、その部分だけ人間のチェックが必要だ。

 例えばアゼルバイジャンGPであれば、以下のように書けば良い。

“””
オコンとボッタスは50周まで、ペレスとサインツは49周まで、ストロールは45周まで、角田は14周までしかないことに気をつけてください。
また、マグヌッセンに代わってベアマンが出走していることにも気をつけてください。
よろしくお願いいたします。
“””

 時折、リタイア・周回遅れ組の注意書きに釣られて、その順番で出力してくることがあるので、一時停止して、Instructionに書いてある順番を守ってもらうよう注意喚起すると良いだろう。

 この表をスプレッドシートやエクセルに貼り付けて、ご使用いただける。

2. VBA

 表をエクセルシート(A列が周回数で、BからU列まで20人が並ぶ。4行目が1周目。)に貼り付けたら、10チームのレースペースグラフを予め作成しておく。グラフの縦軸(タイム)の最適な最大値・最小値はレースによって異なり、横軸(周回数)も当然異なるので、人力で調整するのは意外に面倒くさい。ここを自動化するのが以下のVBAプログラムだ。

〜〜〜コード1〜〜〜

Sub UpdateGraphs()

    Dim ws As Worksheet

    Dim lastRow As Long

    Dim chartObj As ChartObject

    Dim cht As Chart

    Dim ser As Series

    Dim i As Integer

    Dim colIndex As Integer

    Dim colLetter As String

    Dim s As Integer

    Dim minValue As Double

    Dim minValueTruncated As Double

    Dim maxValue As Double

    Dim dataRange As Range

    ‘ アクティブなワークシートを設定

    Set ws = ActiveSheet

    ‘ 列Bから列Uまでの最終行を取得(列Bから列Uの中で最も下にあるデータの行)

    lastRow = ws.Range(“B:U”).Find(What:=”*”, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

    ‘ データ範囲を設定(B4:U lastRow)

    Set dataRange = ws.Range(“B4:U” & lastRow)

    ‘ データ範囲内の最小値を取得

    minValue = WorksheetFunction.Min(dataRange)

    ‘ 最小値の小数点以下1秒未満を切り捨てて.000にする

    minValueTruncated = Int(minValue * 86400) / 86400

    ‘ 最大値を最小値+8秒に設定

    maxValue = minValueTruncated + (8 / 86400)

    ‘ グラフ1から10までループ

    For i = 1 To 10

        ‘ グラフオブジェクトを取得(グラフの名前を適宜変更してください)

        Set chartObj = ws.ChartObjects(“グラフ ” & i) ‘ グラフ名が “グラフ 1”, “グラフ 2”, … の場合

        ‘ グラフが存在する場合のみ処理を実行

        If Not chartObj Is Nothing Then

            Set cht = chartObj.Chart

            ‘ 系列の数を取得(各グラフには2つの系列があると仮定)

            For s = 1 To 2

                ‘ 系列を取得

                Set ser = cht.SeriesCollection(s)

                ‘ 系列のY値の列を取得

                ‘ 指定された対応に基づいて列インデックスを計算

                ‘ 列Bは2列目なので、Excelの列インデックスに合わせる

                colIndex = 2 * (i – 1) + s + 1 ‘ グラフ番号と系列番号から列インデックスを計算

                ‘ 列インデックスが21(列U、列インデックス21)を超えないようにする

                If colIndex <= 21 Then

                    colLetter = Split(ws.Cells(1, colIndex).Address, “$”)(1)

                    ‘ 系列のY値の範囲を設定

                    ser.Values = ws.Range(“$” & colLetter & “$3:$” & colLetter & “$” & lastRow)

                    ‘ X値の範囲を設定(例として列Aを使用)

                    ser.XValues = ws.Range(“$A$3:$A$” & lastRow)

                End If

            Next s

            ‘ グラフのY軸の最小値と最大値を設定

            With cht.Axes(xlValue)

                .MinimumScale = minValueTruncated

                .MaximumScale = maxValue

            End With

        End If

    Next i

End Sub

〜〜〜〜〜〜

 筆者は、ファステストラップの小数点以下を切り捨てた値を最小値に設定し、その8秒落ちを最大値としてきているため、このようになったが、この辺りは個人の好みで良いだろう。あるいは、ウェットレースなどで、自分で弄りながら決めたい際はグラフ 1のみ手動で行い、残り9つは以下のプログラムでグラフ 1と同じ設定にすれば良いだろう。

〜〜〜コード2〜〜〜

Sub SetAxisMinMax()

    Dim chart1 As ChartObject

    Dim chart1Axis As Axis

    Dim chart2to10 As ChartObject

    Dim i As Integer

    ‘ グラフ1の最小値と最大値を取得

    Set chart1 = ActiveSheet.ChartObjects(1)

    Set chart1Axis = chart1.Chart.Axes(xlValue, xlPrimary)

    minValue = chart1Axis.MinimumScale

    maxValue = chart1Axis.MaximumScale

    ‘ グラフ2から10の軸に最小値と最大値を設定

    For i = 2 To 10

        Set chart2to10 = ActiveSheet.ChartObjects(i)

        Set chartAxis = chart2to10.Chart.Axes(xlValue, xlPrimary)

        chartAxis.MinimumScale = minValue

        chartAxis.MaximumScale = maxValue

    Next i

End Sub

〜〜〜〜〜〜

 最後に、分析をする上で欠かせないのが、クリアエアとダーティエアの識別だ。これは前方にいるドライバーが2秒以内にいる際にそのラップのセルに色をつけるようにすれば良い。以下のプログラムで可能だ。

〜〜〜コード3〜〜〜

Sub HighlightCloseGapsAdvanced()

    Dim ws As Worksheet

    Dim lastRow As Long

    Dim lastCol As Long

    Dim i As Long, j As Long

    Dim driverCount As Long

    Dim lapTimes() As Variant

    Dim cumulativeTimes() As Double

    Dim positions() As Long

    Dim driverIndices() As Long

    Dim timeDiff As Double

    Dim sortedIndices() As Long

    Dim temp As Double

    Dim k As Long

    ‘ アクティブなワークシートを設定

    Set ws = ActiveSheet

    ‘ ドライバーの数(列数)を取得(列BからUまでの20列)

    driverCount = ws.Range(“B1:U1”).Columns.Count

    ‘ ドライバーの列インデックスを取得

    Dim driverCols() As Long

    ReDim driverCols(1 To driverCount)

    For j = 1 To driverCount

        driverCols(j) = ws.Range(“B1”).Column + j – 1

    Next j

    ‘ データの最終行を取得(列BからUの中で最も下にあるデータの行)

    lastRow = ws.Range(“B:U”).Find(What:=”*”, SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

    ‘ ラップごとに処理

    For i = 4 To lastRow

        ‘ 各ドライバーの累積タイムを計算

        ReDim cumulativeTimes(1 To driverCount)

        For j = 1 To driverCount

            cumulativeTimes(j) = 0

            For k = 4 To i

                If IsNumeric(ws.Cells(k, driverCols(j)).Value) Then

                    cumulativeTimes(j) = cumulativeTimes(j) + ws.Cells(k, driverCols(j)).Value

                Else

                    cumulativeTimes(j) = 0 ‘ データがない場合は0に設定

                    Exit For ‘ データがない場合は計算を中断

                End If

            Next k

        Next j

        ‘ ドライバーのインデックスを保持

        ReDim driverIndices(1 To driverCount)

        For j = 1 To driverCount

            driverIndices(j) = j

        Next j

        ‘ 累積タイムに基づいてドライバーをソート(バブルソートを使用)

        For j = 1 To driverCount – 1

            For k = j + 1 To driverCount

                If cumulativeTimes(j) > cumulativeTimes(k) Then

                    ‘ 累積タイムをスワップ

                    temp = cumulativeTimes(j)

                    cumulativeTimes(j) = cumulativeTimes(k)

                    cumulativeTimes(k) = temp

                    ‘ ドライバーのインデックスをスワップ

                    temp = driverIndices(j)

                    driverIndices(j) = driverIndices(k)

                    driverIndices(k) = temp

                End If

            Next k

        Next j

        ‘ 各ドライバーについてタイム差を計算し、ハイライト

        For j = 2 To driverCount

            Dim currentDriver As Long

            Dim previousDriver As Long

            currentDriver = driverIndices(j)

            previousDriver = driverIndices(j – 1)

            ‘ データが存在するか確認

            If cumulativeTimes(j) > 0 And cumulativeTimes(j – 1) > 0 Then

                timeDiff = (cumulativeTimes(j) – cumulativeTimes(j – 1)) * 86400 ‘ 秒に変換

                ‘ タイム差が0以上2.0秒以下の場合

                If timeDiff >= 0 And timeDiff <= 2 Then

                    ‘ セルをハイライト(例: 黄色に設定)

                    ws.Cells(i, driverCols(currentDriver)).Interior.Color = RGB(204, 193, 218)

                Else

                    ‘ ハイライトを解除

                    ws.Cells(i, driverCols(currentDriver)).Interior.ColorIndex = xlNone

                End If

            Else

                ‘ ハイライトを解除

                ws.Cells(i, driverCols(currentDriver)).Interior.ColorIndex = xlNone

            End If

        Next j

        ‘ 先頭のドライバーのハイライトを解除

        currentDriver = driverIndices(1)

        ws.Cells(i, driverCols(currentDriver)).Interior.ColorIndex = xlNone

    Next i

End Sub

〜〜〜〜〜〜

※管理人のシート上では時折ダーティエアにも関わらずハイライトされていないことがあるが、原因は不明。コードではなくシート側に問題がある可能性もあり。

 これにて、分析を始めるまでの段階の多く部分を自動化できた。筆者の分析スタイルに合ったものにはなってしまっているが、列や行の使い方、ドライバーの並び、ダーティエアの基準など、個々人でカスタマイズするなり、これをヒントにo1とのやり取りを通してより優れた手法を生み出していっていただければ本望だ。

Writer: Takumi