[Python] 230 複数のCSVファイル内検索 完全一致 その2

[Python] 229の続きです。

検索で複数ヒットした場合の絞り込み方法を追記しました。

馬名IDを絞り込めたら、次はレース検索に進みます。

# 前回コードの続き

if len(horseID_l) >= 2:
    print('該当する馬が複数います。番号を入力してください。')
    for i,data in enumerate(horseID_l):
        print(f'{i+1} {data}')
    num = input()
    id = horseID_l[int(num)-1][1]

elif len(horseID_l) == 0:
    print('該当する馬はいません')
    id = 'なし'

else:
    id = horseID_l[1]

print(f'検索馬のID {id}')
--------------------------------------------------

出力
--------------------------------------------------
検索したい馬名を入力してください
ヒシマサル
year 1989 index 8573
year 2014 index 6933
ヒシマサル [[1989, 198908574, 'ヒシマサル'], [2014, 201405934, 'ヒシマサル']]
該当する馬が複数います。番号を入力してください。
1 [1989, 198908574, 'ヒシマサル']
2 [2014, 201405934, 'ヒシマサル']
1
検索馬のID 198908574

[Python] 229 複数のCSVファイル内検索 完全一致

[Python]228のコードを完全一致で書いてみました。

検索先の馬名ファイルからは○地などの記号を削除しています。

import glob,csv,re
import pandas as pd

print('検索したい馬名を入力してください')
name = input()

horseID_l = list()
for year in range(1986,2019 +1):
    namefile = f'/Volumes/DATA_HR/horse_racing/horse_list/horse{year}.csv'

    df = pd.read_csv(namefile,encoding="shift_jis")

    # 馬名ファイルの馬名と検索馬名が完全一致した場合にTrueとする縦向き配列を作成
    b_array = df[df.columns[1]]==name

    # ブール値の横向き配列として取り出しリスト化
    b_array_v = b_array.values.tolist()

    # Trueのインデックス値を算出しhorseIDを取得
    try:
        i = b_array_v.index(True)
    except:
        pass
    else:
        print(f'year {year} index {i}')
        horseID = df.iloc[i,0]
        horsename = df.iloc[i,1]

        horseID_l.append([year,horseID,horsename])

print(f'{name} {horseID_l}')
--------------------------------------------------

出力
--------------------------------------------------
検索したい馬名を入力してください
ヒシマサル
year 1989 index 8573
year 2014 index 6933
ヒシマサル [[1989, 198908574, 'ヒシマサル'], [2014, 201405934, 'ヒシマサル']]

[Python] 228 複数のCSVファイル内検索 部分一致

1986年以降の馬データを対象とする検索コードを書いてみました。

出力されたindexとhorseIDの番号が1つずれていますがそういう仕様です。

今回は部分一致で実行しましたが、完全一致にしないと実用的ではないでしょう。

import glob,csv,re
import pandas as pd

print('検索したい馬名を入力してください')
name = input()

horseID_l = list()
for year in range(1986,2019 +1):
    namefile = f'/horse_racing/horse_list/horse{year}.csv'

    df = pd.read_csv(namefile,encoding="shift_jis")

    # 各行の馬名セルに検索馬名を含む場合にTrueとする縦向き配列を作成(部分一致)
    b_array = df[df.columns[1]].str.contains(name)

    # ブール値の横向き配列として取り出しリスト化
    b_array_v = b_array.values.tolist()

    # Trueのインデックス値を算出しhorseIDを取得
    try:
        i = b_array_v.index(True)
    except:
        pass
    else:
        print(f'year {year} index {i}')
        horseID = df.iloc[i,0]
        horsename = df.iloc[i,1]

        horseID_l.append([year,horseID,horsename])

print(f'{name} {horseID_l}')
--------------------------------------------------

出力
--------------------------------------------------
検索したい馬名を入力してください
ヒシマサル
year 1989 index 8573
year 1999 index 183
year 2014 index 6060
ヒシマサル [[1989, 198908574, '○外ヒシマサル'], [1999, 199900184, '□地ニシネヒシマサル'], [2014, 201405934, 'ヒシマサル']]

[Python] 227 CSVファイルを縦方向に結合する

CSVファイルを縦方向に結合するコードです。

縦方向に結合する場合は読み込み時にheader=Noneを付けて、書き込み時にheader=Falseを付けます。

headerがないと列インデックスが自動的に付加されるため、これに従ってデータが縦方向に並びます。

横方向に結合する場合は読み込み時、書き込み時ともにheaderオプションはいらないはずです。

なおformat文字列はかつての名残りです。

# filenameNとfilenamePを縦方向に結合する
# 最初にfilenameNのみのCSVファイルを作成し、結合したファイルで上書きする
# この方法ではfilenamePが存在しなくてもfilenameNのみのファイルが結合ファイルとして残る
# else以下は1つ上の階層に置く方が良いと思ったが、検証ではNGだったのでこれで仮確定させた

files = [filenameN , filenameP]

row_list = []
for file in files:
    try:
        row_list.append(pd.read_csv(file,header= None,encoding='Shift_JIS'))

    except FileNotFoundError:
        print('FileNotFoundError')
        print(f'ファイルがありません {file}')

    else:
        df = pd.concat(row_list, sort=False)

        filename = filename_horse(str(year),str(thousand),str('{0:05d}'.format(horse_number)))

        with open(filename, mode="w", encoding="cp932", errors="ignore") as f:
            df.to_csv(f,index= False,header= False)

[Python] 226 文字列に数字を含むかどうかの判定

文字列に数字を含むかどうかの条件分岐です。前記事コードの後半部分になります。

row[1]の値を1文字づつに分解してそれぞれ数字かどうかを判定し、1つでも数字が含まれていればTrueとするif文になります。

リスト内包表記のタプル版かと思ったのですが、タプル内包表記という用語があるんですね。

# row[1]の値に数字が含まれていればrow[0]と合わせてcsvファイルに書き込む
for row in csv.reader(f1):
    if any(chr.isdigit() for chr in row[1]):
        writer.writerow([row[0],row[1].replace('\u3000','')])

[Python] 225 ファイル作成日時の取得

ファイルのメタデータから作成日時を取得するコードを書きました。以前画像データ処理に関する記事でも取り上げています。

st_birthtimeはmacOSを含むUNIX系限定の機能です。

import glob,csv,datetime,pathlib

# 2017年から2019年のhorseファイルから仮馬名(誕生年を含む名前)を抽出する
for year in range(2017,2020):
    for f in glob.glob(f'/horse_racing/horse/horse{year}.csv'):

        # ファイルのメタデータから作成日時を取得
        p = pathlib.Path(f)
        dt = datetime.datetime.fromtimestamp(p.stat().st_birthtime)
        dt_str = dt.strftime('%y%m%d%H%M')

        # 新ファイルの先頭に元ファイルの作成日時を付ける
        file_new = f'/horse_racing/test/{dt_str}_horse{year}_check.csv'

        # 馬名に数字を含む場合、horseIDと仮馬名を抽出してCSVファイルを作成する
        with open (f, mode="r", encoding="shift_jis") as f1:
            with open(file_new, mode="w", encoding="shift_jis") as f2:
                writer = csv.writer(f2)

                writer.writerow(['horseID','仮馬名'])
                for row in csv.reader(f1):
                    if any(chr.isdigit() for chr in row[1]):
                        writer.writerow([row[0],row[1].replace('\u3000','')])

[Python] 223 フォルダを一括作成する

使い回ししやすいようにメモ書きしておきます。子フォルダ、孫フォルダの一括作成です。

macOSではディレクトリという表現が正しいようですが、字数が少ないのでフォルダを使っています。

f文字列による表記です。

import os

# 2021年各競馬場フォルダを各開催分作成する。競馬場コード、開催回は2桁表記。
l=[2,1,2,5,5,5,6,0,6,4]
for i,e in enumerate(l):
    os.mkdir(f'/horse_racing/race/2021/{i+1:02}')
    for c in range(e):
        os.mkdir(f'/horse_racing/race/2021/{i+1:02}/{c+1:02}')

[Python] 222 CSVファイルから改行コードを削除する

Windows10で作成したCSVファイルに余計な改行が含まれていたので下記コードで削除しました。

# file1,file2の設定は省略
with open(file1, 'r', encoding='shift_jis') as f1:
    with open(file2, 'w', encoding='shift_jis') as f2:
        rows = []
        reader = csv.reader(f1)
        for row in reader:
            for i,v in enumerate(row):
                row[i] = v.replace('\n', '')
            rows.append(row)

        writer = csv.writer(f2)
        for row in rows:
            writer.writerow(row)

[Python] 221 CSVファイルから値の一致する行を取り出す

レースファイルの馬名から馬名ファイルにあるhorseIDを取り出すコードを作成しました。

書いた本人にしかわからない内容ですが、pandasを扱って案の定苦戦したので記録を残しておきます。

手順としてはレースファイルから各着順の馬名を取り出し年齢から誕生年(満年齢の場合はレース年-年齢)を算出。誕生年の馬名リストからhorseIDを取得し、レースファイルの最後列に追記します。

データフレームはあくまでも配列であり一次元であってもリストとして扱わないことを今後は肝に銘じます。

import glob
import pandas as pd
import csv,re

# 1986年から2020年のレースファイルにhorseID列を追加する
for year in range(1986,2021):
    for f in glob.glob(f'/horse_racing/race_result_mas/{year}/*/*/*.csv'):
        file_new = f.split('race_result_mas/')[0] + 'race_result/' + f.split('race_result_mas/')[1]

        with open (f, mode="r", encoding="shift_jis") as f1:
            with open(file_new, mode="w", encoding="shift_jis") as f2:
                writer = csv.writer(f2)

                for i,row in enumerate(csv.reader(f1)):
                    if i != 0:
                        if year >=2001: # 満年齢
                            birthyear = int(year) - int(re.sub("\\D", "", row[4]))
                        else: # 2000年以前は数え年
                            birthyear = int(year) - int(re.sub("\\D", "", row[4])) + 1

                        # 誕生年馬名ファイル
                        namefile = f'/horse_racing/horse/horse{birthyear}.csv'
                        try:
                            df = pd.read_csv(namefile,encoding="shift_jis")
                        except:
                            horseID = '1985年以前'
                        else:
                            # 各行の馬名セルにレースファイルの馬名を含む場合にTrueとする縦向き配列を作成
                            b_array = df[df.columns[1]].str.contains(row[3])

                            # ブール値の横向き配列として取り出しリスト化
                            b_array_v = b_array.values.tolist()

                            # Trueのインデックス値を算出しhorseIDを取得
                            try:
                                i = b_array_v.index(True)
                            except:
                                horseID = '該当なし'
                            else:
                                print(f'index {i}')
                                horseID = df.iloc[i,0]

                        print(f'{row[3]} {birthyear} {horseID}')
                        rows = row[0:22] + [horseID]
                        writer.writerow(rows)

                    else: # タイトル行(0行目)
                        rows = row[0:22] + ['horseID']
                        writer.writerow(rows)