もっとコラボでGO!(5)主キー?なにそれ食べられるの?レースコードを振るの巻

勝手なイメージ

テイモン!今度一緒に雪山行こうよ。今年の雪は去年より期待できそうだよ

ボクは山よりも海とか川の方が好きなんだけど。。。

あれ?流氷の上でゴロゴロしたりしてるでしょ?どちらかと言うと夏よりも冬の方が好きだと思ってた。

(そういう写真を撮られることが多いだけなんだけどな…)

主キーとはなんぞや?

ボートレースは年中無休、冬でもレースがあります。三国(福井)や桐生(群馬)は特にしばれるだろうな、、、レーサーの皆さまお疲れさまです。

さて、番組表には様々なデータが含まれていますが、番組表だけを使って分析してもあまり意味がありません。ボートレースの結果を予想するには、番組表と競走成績を紐づける必要があります。

2つのテーブル(データを縦と横に並べたもの)を紐づけるには、それぞれのテーブルにレースを特定する情報が必要です。レースを特定するには、レース日・レース場・レース回の3つのデータがあれば良いのですが、幸い番組表にも競走成績にもこれらのデータが存在します。

しかし、3つのデータは列が別れているため扱いづらいです。そこで、これらのデータから「レースコード」を作成し、それを番組表と競走成績の両方のテーブルに持たせることで、簡単にデータを結びつけられるようにします。

このレースコードは、テーブルの各レコードを重複のない1つのデータとして示すものです。これを主キー(プライマリキー項目)と呼びます。主キーは表の中で重複しない項目と覚えておきましょう。

主キーの役割

主キーは、データをまとめるために存在します。別の言い方をすると、データを集計するために設定するものです。

コラボでGO!(6)では、Pivotテーブルを利用して競走成績のファイルからレース場ごとに1-2-3ボックスの発生率を計算しましたが、これはレース場を主キーと見立てて集計したものです。

主キーは、このように1:多の関係(1つ項目に複数の項目が紐づく)を持たせることが本来の役割です。今回は番組表と競走成績それぞれに主キーが存在するため、1:1の関係になります。

ちなみに、スプレッドシートのPivotテーブルは、データを集計するツールとして大変便利なのですが、データの範囲として設定できるのは1つのテーブルだけです。

2つ以上のテーブルを紐づけて集計するには、テーブル間にリレーションシップを設定する必要があります。ExcelではPower Pivotで実現できますが、これについてはまた別の機会にご紹介します。

レースコードの形式

それでは、実際にどのようなレースコードを振るのが良いでしょうか。2020年6月1日の芦屋の第1レースにコードを振ると仮定して考えてみましょう。

レース日レース場レース回
候補1YYYYMMDD場コードNN202006012101
候補2YYYYMMDD3レターコードNN20200601ASY01

上の2つの候補は、レース場の表記の仕方だけが異なります。レース場には場コードというものが設定されており、これを利用したものが候補1です。場コードの代わりに場ごとに3レターコードを設定したものが候補2になります。

既に知られているコードという点では、候補1の方が良さそうです。しかし、数字の羅列だけではこれが芦屋のレースであることをすぐに認識できませんね。

テイモンは見てすぐ分かるコードをオススメします。候補2であればパッと見て、レース日・レース場・レース回が認識できそうです。3レターコードは以下の通りに振ります。

場コードレース場3レターコード場コードレース場3レターコード場コードレース場3レターコード
01桐生KRY09TSU17宮島MYJ
02戸田TDA10三国MKN18徳山TKY
03江戸川EDG11びわこBWK19下関SMS
04平和島HWJ12住之江SME20若松WKM
05多摩川TMG13尼崎AMG21芦屋ASY
06浜名湖HMN14鳴門NRT22福岡FKO
07蒲郡GMG15丸亀MRG23唐津KRT
08常滑TKN16児島KJM24大村OMR

Pythonでレースコードを振る

それではスクリプトに手を加えていきます。

日付はdate変数に代入した値、レース回はrace_round変数に代入した値をそれぞれ流用できますので、これらの変数からレースコードに利用する部分を抜き出します。

レース場を3レターコードに変換するには辞書を定義します。キーにレース場、値に3レターコードをそれぞれ設定します。

# レースコードを生成
dict_stadium = {'桐生': 'KRY', '戸田': 'TDA', '江戸川': 'EDG', '平和島': 'HWJ',
                '多摩川': 'TMG', '浜名湖': 'HMN', '蒲郡': 'GMG', '常滑': 'TKN',
                '津': 'TSU', '三国': 'MKN', '琵琶湖': 'BWK', '住之江': 'SME',
                '尼崎': 'AMG', '鳴門': 'NRT', '丸亀': 'MRG', '児島': 'KJM',
                '宮島': 'MYJ', '徳山': 'TKY', '下関': 'SMS', '若松': 'WKM',
                '芦屋': 'ASY', '福岡': 'FKO', '唐津': 'KRT', '大村': 'OMR'
                }

race_code = date[0:4] + date[5:7] + date[8:10] + dict_stadium[stadium] + race_round[0:2]

レースコードは今後よく使うことになるので、最初の列に挿入しましょう。ヘッダー情報に加えることも忘れずに。

Pythonスクリプトの記述(番組表)

それでは完成版の番組表です。コラボに貼り付けて実行してみましょう。

# 解凍したテキストファイルの格納先を指定
TEXT_FILE_DIR = "drive/My Drive/timetable_txt/"

# CSVファイルの保存先を指定
CSV_FILE_DIR = "drive/My Drive/timetable_csv_racecode/"

# CSVファイルの名前を指定 ※YYYYMMDDには対象期間を入力
CSV_FILE_NAME = "timetable_YYYYMMDD-YYYYMMDD.csv"

# CSVファイルのヘッダーを指定
CSV_FILE_HEADER = "レースコード,タイトル,日次,レース日,レース場,レース回,レース名,距離(m),電話投票締切予定,\
1枠_艇番,1枠_登録番号,1枠_選手名,1枠_年齢,1枠_支部,1枠_体重,1枠_級別,\
1枠_全国勝率,1枠_全国2連対率,1枠_当地勝率,1枠_当地2連対率,\
1枠_モーター番号,1枠_モーター2連対率,1枠_ボート番号,1枠_ボート2連対率,\
1枠_今節成績_1-1,1枠_今節成績_1-2,1枠_今節成績_2-1,1枠_今節成績_2-2,1枠_今節成績_3-1,1枠_今節成績_3-2,\
1枠_今節成績_4-1,1枠_今節成績_4-2,1枠_今節成績_5-1,1枠_今節成績_5-2,1枠_今節成績_6-1,1枠_今節成績_6-2,1枠_早見,\
2枠_艇番,2枠_登録番号,2枠_選手名,2枠_年齢,2枠_支部,2枠_体重,2枠_級別,\
2枠_全国勝率,2枠_全国2連対率,2枠_当地勝率,2枠_当地2連対率,\
2枠_モーター番号,2枠_モーター2連対率,2枠_ボート番号,2枠_ボート2連対率,\
2枠_今節成績_1-1,2枠_今節成績_1-2,2枠_今節成績_2-1,2枠_今節成績_2-2,2枠_今節成績_3-1,2枠_今節成績_3-2,\
2枠_今節成績_4-1,2枠_今節成績_4-2,2枠_今節成績_5-1,2枠_今節成績_5-2,2枠_今節成績_6-1,2枠_今節成績_6-2,2枠_早見,\
3枠_艇番,3枠_登録番号,3枠_選手名,3枠_年齢,3枠_支部,3枠_体重,3枠_級別,\
3枠_全国勝率,3枠_全国2連対率,3枠_当地勝率,3枠_当地2連対率,\
3枠_モーター番号,3枠_モーター2連対率,3枠_ボート番号,3枠_ボート2連対率,\
3枠_今節成績_1-1,3枠_今節成績_1-2,3枠_今節成績_2-1,3枠_今節成績_2-2,3枠_今節成績_3-1,3枠_今節成績_3-2,\
3枠_今節成績_4-1,3枠_今節成績_4-2,3枠_今節成績_5-1,3枠_今節成績_5-2,3枠_今節成績_6-1,3枠_今節成績_6-2,3枠_早見,\
4枠_艇番,4枠_登録番号,4枠_選手名,4枠_年齢,4枠_支部,4枠_体重,4枠_級別,\
4枠_全国勝率,4枠_全国2連対率,4枠_当地勝率,4枠_当地2連対率,\
4枠_モーター番号,4枠_モーター2連対率,4枠_ボート番号,4枠_ボート2連対率,\
4枠_今節成績_1-1,4枠_今節成績_1-2,4枠_今節成績_2-1,4枠_今節成績_2-2,4枠_今節成績_3-1,4枠_今節成績_3-2,\
4枠_今節成績_4-1,4枠_今節成績_4-2,4枠_今節成績_5-1,4枠_今節成績_5-2,4枠_今節成績_6-1,4枠_今節成績_6-2,4枠_早見,\
5枠_艇番,5枠_登録番号,5枠_選手名,5枠_年齢,5枠_支部,5枠_体重,5枠_級別,\
5枠_全国勝率,5枠_全国2連対率,5枠_当地勝率,5枠_当地2連対率,\
5枠_モーター番号,5枠_モーター2連対率,5枠_ボート番号,5枠_ボート2連対率,\
5枠_今節成績_1-1,5枠_今節成績_1-2,5枠_今節成績_2-1,5枠_今節成績_2-2,5枠_今節成績_3-1,5枠_今節成績_3-2,\
5枠_今節成績_4-1,5枠_今節成績_4-2,5枠_今節成績_5-1,5枠_今節成績_5-2,5枠_今節成績_6-1,5枠_今節成績_6-2,5枠_早見,\
6枠_艇番,6枠_登録番号,6枠_選手名,6枠_年齢,6枠_支部,6枠_体重,6枠_級別,\
6枠_全国勝率,6枠_全国2連対率,6枠_当地勝率,6枠_当地2連対率,\
6枠_モーター番号,6枠_モーター2連対率,6枠_ボート番号,6枠_ボート2連対率,\
6枠_今節成績_1-1,6枠_今節成績_1-2,6枠_今節成績_2-1,6枠_今節成績_2-2,6枠_今節成績_3-1,6枠_今節成績_3-2,\
6枠_今節成績_4-1,6枠_今節成績_4-2,6枠_今節成績_5-1,6枠_今節成績_5-2,6枠_今節成績_6-1,6枠_今節成績_6-2,6枠_早見\n"

# OSの機能を利用するパッケージ os をインポート
import os

# 正規表現をサポートするモジュール re をインポート
import re


# テキストファイルからデータを抽出し、CSVファイルに書き込む関数 get_data を定義
def get_data(text_file):
    # CSVファイルを追記モードで開く
    csv_file = open(CSV_FILE_DIR + CSV_FILE_NAME, "a", encoding="shift_jis")

    # テキストファイルから中身を順に取り出す
    for contents in text_file:

        trans_asc = str.maketrans('1234567890R: ', '1234567890R: ')

        # キーワード「番組表」を見つけたら(rは正規表現でraw文字列を指定するおまじない)
        if re.search(r"番組表", contents):
            # 1行スキップ
            text_file.readline()

            # タイトルを格納
            line = text_file.readline()
            title = line[:-1].strip()

            # 1行スキップ
            text_file.readline()

            # 日次・レース日・レース場を格納
            line = text_file.readline()
            day = line[3:7].translate(trans_asc).replace(' ', '')
            date = line[17:28].translate(trans_asc).replace(' ', '0')
            stadium = line[52:55].replace(' ', '')

        # キーワード「電話投票締切予定」を見つけたら
        if re.search(r"電話投票締切予定", contents):

            # キーワードを見つけた行を格納
            line = contents

            # レース名にキーワード「進入固定」が割り込んだ際の補正(「進入固定戦隊」は除くためHまで含めて置換)
            if re.search(r"進入固定", line):
                line = line.replace('進入固定 H', '進入固定     H')

            # レース回・レース名・距離(m)・電話投票締切予定を格納
            race_round = line[0:3].translate(trans_asc).replace(' ', '0')
            race_name = line[5:21].replace(' ', '')
            distance = line[22:26].translate(trans_asc)
            post_time = line[37:42].translate(trans_asc)

            # 4行スキップ(ヘッダー部分)
            text_file.readline()
            text_file.readline()
            text_file.readline()
            text_file.readline()

            # 選手データを格納する変数を定義
            racer_data = ""

            # 選手データを読み込む行(開始行)を格納
            line = text_file.readline()

            # 空行またはキーワード「END」まで処理を繰り返す = 1~6艇分の選手データを取得
            while line != "\n":

                if re.search(r"END", line):
                    break

                # 選手データを格納(行末にカンマが入らないように先頭にカンマを入れる)
                racer_data += "," + line[0] + "," + line[2:6] + "," + line[6:10] + "," + line[10:12] \
                              + "," + line[12:14] + "," + line[14:16] + "," + line[16:18] \
                              + "," + line[19:23] + "," + line[24:29] + "," + line[30:34] \
                              + "," + line[35:40] + "," + line[41:43] + "," + line[44:49] \
                              + "," + line[50:52] + "," + line[53:58] + "," + line[59:60] \
                              + "," + line[60:61] + "," + line[61:62] + "," + line[62:63] \
                              + "," + line[63:64] + "," + line[64:65] + "," + line[65:66] \
                              + "," + line[66:67] + "," + line[67:68] + "," + line[68:69] \
                              + "," + line[69:70] + "," + line[70:71] + "," + line[71:73]

                # 次の行を読み込む
                line = text_file.readline()

            # レースコードを生成
            dict_stadium = {'桐生': 'KRY', '戸田': 'TDA', '江戸川': 'EDG', '平和島': 'HWJ',
                            '多摩川': 'TMG', '浜名湖': 'HMN', '蒲郡': 'GMG', '常滑': 'TKN',
                            '津': 'TSU', '三国': 'MKN', '琵琶湖': 'BWK', '住之江': 'SME',
                            '尼崎': 'AMG', '鳴門': 'NRT', '丸亀': 'MRG', '児島': 'KJM',
                            '宮島': 'MYJ', '徳山': 'TKY', '下関': 'SMS', '若松': 'WKM',
                            '芦屋': 'ASY', '福岡': 'FKO', '唐津': 'KRT', '大村': 'OMR'
                            }

            race_code = date[0:4] + date[5:7] + date[8:10] + dict_stadium[stadium] + race_round[0:2]

            # 抽出したデータをCSVファイルに書き込む
            csv_file.write(race_code + "," + title + "," + day + "," + date + "," + stadium + "," + race_round
                           + "," + race_name + "," + distance + "," + post_time + racer_data + "\n")
    # CSVファイルを閉じる
    csv_file.close()


# 開始合図
print("作業を開始します")

# CSVファイルを保存するフォルダを作成
os.makedirs(CSV_FILE_DIR, exist_ok=True)

# CSVファイルを作成しヘッダ情報を書き込む
csv_file = open(CSV_FILE_DIR + CSV_FILE_NAME, "w", encoding="shift_jis")
csv_file.write(CSV_FILE_HEADER)
csv_file.close()

# テキストファイルのリストを取得
text_file_list = os.listdir(TEXT_FILE_DIR)

# リストからファイル名を順に取り出す
for text_file_name in text_file_list:

    # 拡張子が TXT のファイルに対してのみ実行
    if re.search(".TXT", text_file_name):
        # テキストファイルを開く
        text_file = open(TEXT_FILE_DIR + text_file_name, "r", encoding="shift_jis")

        # 関数 get_data にファイル(オブジェクト)を渡す
        get_data(text_file)

        # テキストファイルを閉じる
        text_file.close()

print(CSV_FILE_DIR + CSV_FILE_NAME + " を作成しました")

# 終了合図
print("作業を終了しました")

うまくいけば、下図のように先頭の列にレースコードが挿入されます。

Pythonスクリプトの記述(競走成績)

最後に前回のシリーズで作成した競走成績のファイルでも、レースコードを出力できるように調整を加えましょう。ファイルの保存先、ヘッダー情報、レースコードの生成・出力などを加えました。

# 解凍したテキストファイルの格納先を指定
TEXT_FILE_DIR = "drive/My Drive/results_txt/"

# CSVファイルの保存先を指定
CSV_FILE_DIR = "drive/My Drive/results_csv_racecode/"

# CSVファイルの名前を指定 ※YYYYMMDDには対象期間を入力
CSV_FILE_NAME = "results_YYYYMMDD-YYYYMMDD.csv"

# CSVファイルのヘッダーを指定
CSV_FILE_HEADER = "レースコード,タイトル,日次,レース日,レース場,レース回,\
3連単_組番,3連単_払戻金,3連複_組番,3連複_払戻金,2連単_組番,2連単_払戻金,2連複_組番,2連複_払戻金\n"

# OSの機能を利用するパッケージ os をインポート
import os

# 正規表現をサポートするモジュール re をインポート
import re


# テキストファイルからデータを抽出し、CSVファイルに書き込む関数 get_data を定義
def get_data(text_file):
    # CSVファイルを追記モードで開く
    csv_file = open(CSV_FILE_DIR + CSV_FILE_NAME, "a", encoding="shift_jis")

    # テキストファイルから中身を順に取り出す
    for contents in text_file:

        # キーワード「競争成績」を見つけたら(rは正規表現でraw文字列を指定するおまじない)
        if re.search(r"競走成績", contents):
            # 1行スキップ
            text_file.readline()

            # タイトルを格納
            line = text_file.readline()
            title = line[:-1].strip()

            # 1行スキップ
            text_file.readline()

            # 日次・レース日・レース場を格納
            line = text_file.readline()
            day = line[3:7].replace(' ', '')
            date = line[17:27].replace(' ', '0')
            stadium = line[62:65].replace(' ', '')

        # キーワード「払戻金」を見つけたら
        if re.search(r"払戻金", contents):

            # レース結果を読み込む行(開始行)を格納
            line = text_file.readline()

            # 空行まで処理を繰り返す = 1〜12レースまでの結果を取得
            while line != "\n":
                # レース結果を格納
                results = line[10:13].replace(' ', '0') + "," \
                          + line[15:20] + "," + line[21:28].strip() + "," \
                          + line[32:37] + "," + line[38:45].strip() + "," \
                          + line[49:52] + "," + line[53:60].strip() + "," \
                          + line[64:67] + "," + line[68:75].strip()

                # レースコードを生成
                dict_stadium = {'桐生': 'KRY', '戸田': 'TDA', '江戸川': 'EDG', '平和島': 'HWJ',
                                '多摩川': 'TMG', '浜名湖': 'HMN', '蒲郡': 'GMG', '常滑': 'TKN',
                                '津': 'TSU', '三国': 'MKN', '琵琶湖': 'BWK', '住之江': 'SME',
                                '尼崎': 'AMG', '鳴門': 'NRT', '丸亀': 'MRG', '児島': 'KJM',
                                '宮島': 'MYJ', '徳山': 'TKY', '下関': 'SMS', '若松': 'WKM',
                                '芦屋': 'ASY', '福岡': 'FKO', '唐津': 'KRT', '大村': 'OMR'
                                }

                race_round = line[10:12].replace(' ','0')
                race_code = date[0:4] + date[5:7] + date[8:10] + dict_stadium[stadium] + race_round[0:2]

                # 抽出したデータをCSVファイルに書き込む
                csv_file.write(race_code + "," + title + "," + day + "," + date + "," + stadium + "," + results + "\n")

                # 次の行を読み込む
                line = text_file.readline()

    # CSVファイルを閉じる
    csv_file.close()


# 開始合図
print("作業を開始します")

# CSVファイルを格納するフォルダを作成
os.makedirs(CSV_FILE_DIR, exist_ok=True)

# CSVファイルを作成しヘッダ情報を書き込む
csv_file = open(CSV_FILE_DIR + CSV_FILE_NAME, "w", encoding="shift_jis")
csv_file.write(CSV_FILE_HEADER)
csv_file.close()

# テキストファイルのリストを取得
text_file_list = os.listdir(TEXT_FILE_DIR)

# リストからファイル名を順に取り出す
for text_file_name in text_file_list:

    # 拡張子が TXT のファイルに対してのみ実行
    if re.search(".TXT", text_file_name):
        # テキストファイルを開く
        text_file = open(TEXT_FILE_DIR + text_file_name, "r", encoding="shift_jis")

        # 関数 get_data にファイル(オブジェクト)を渡す
        get_data(text_file)

        # テキストファイルを閉じる
        text_file.close()

print(CSV_FILE_DIR + CSV_FILE_NAME + " を作成しました")

# 終了合図
print("作業を終了しました")

これで番組表と競走成績にレースコードを振ることができました。次回はこの2つのファイルを結合して分析してみましょう。

タイトルとURLをコピーしました