[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)

[Python] 220 ファイル削除ツールのexe化

ファイルやフォルダを一括削除するツールを作成しました。

[Python] 206で紹介したJSON CSV相互コンバータのGUIを流用しています。

削除したいファイルのあるフォルダのパスを入力するだけです。フォルダかファイルを選択できます。下層ファイルも削除するのでフォルダだけの骨格が残ります。

コードは以下の通りです。PyInstallerでexeファイルを作成しました。

import glob,os,shutil
import sys,json
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

root = tk.Tk()
root.title("FILE REMOVER")
root.geometry("300x80")
root.configure(bg='#FFFACD')

# 設定
my_font = font.Font(root,family="System",size=18,weight="normal")

# フレームの作成・配置
frame = tk.Frame(root,background = '#FFFACD')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)

# ラベルの作成・配置
label = tk.Label(frame,text='PATH',background = '#FFFACD',foreground = '#8b0000',font=my_font)
label.grid(row=0, column=0)

# パス入力エントリの作成・配置
entry = tk.Entry(frame,width=15,background = '#98fb98',foreground = '#8b0000',font=my_font)
entry.grid(row=0,column=1)

# 実行ボタンの作成・配置
var_act = tk.IntVar()
btn = tk.Button(frame, text="削除",command=lambda:var_act.set(1),width=2,font=my_font)
btn.grid(row=0,column=2,padx=2)

# クリアボタンの作成・配置
btn2 = tk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=2,font=my_font)
btn2.grid(row=1,column=2,padx=2)

# ラジオボタンの作成・配置
var = tk.IntVar()
rb1 = ttk.Radiobutton(frame,text='フォルダ',value=1,variable=var)
rb1.grid(row=1,column=1,padx=2,pady=5,sticky=tk.W)

rb2 = ttk.Radiobutton(frame,text='ファイル',value=2,variable=var)
rb2.grid(row=1,column=1,padx=2,pady=5,sticky=tk.E)

for i in range(100): # 100回処理可能
    # フォルダパスの入力を待機
    btn.wait_variable(var_act)

    # 入力したフォルダパスを取得
    dirpath = entry.get()

    # ラジオボタンのvalueを取得
    v = var.get()

    if v == 1:
        path = dirpath + '/*' # 直下のフォルダを削除
        for f in glob.glob(path):
            if os.path.isdir(f):
                shutil.rmtree(f)
    else:
        path = dirpath + '/**' # 再帰的処理により下層ファイルも削除
        for f in glob.glob(path, recursive=True):
            if os.path.isfile(f):
                os.remove(f)

root.mainloop()

[Python] 219 CSVファイルの行と列を入れ替える

CSVファイルの行と列の入れ替えはpandasやnumpyでもできますが、今回はCSVモジュールでやってみました。

読み込んだ行データを列毎に分割、リスト化して行データにする手法です。そのようなメソッドがないため手作りするしかありません。

pandasでは列インデックスや行インデックスについても考慮しなければならないので、CSVモジュールの方が私には楽です。

コード例
各馬のデータを年毎にまとめる。

import glob
import csv

# 1986年から2020年のhorseファイルを年毎にまとめる
for year in range(1986,2021):
    for f in glob.glob(f'/horse_racing/horse_mas/{year}/*/*.csv'):
        file_new = f.split('horse_mas/')[0] + 'horse/' + f'horse{year}' + '.csv'

        with open (f, mode="r", encoding="shift_jis") as f1:
            with open(file_new, mode="a", encoding="shift_jis") as f2:
                writer = csv.writer(f2)
                row1_pre = []
                row2_pre = []
                for row in csv.reader(f1):
                    row1_pre.append(row[0])
                    row2_pre.append(row[1])

                row1 = ['horseID'] + row1_pre # 列タイトル
                row2 = [f[-13:-4]] + row2_pre # 馬データ

                if f[-9:-4] == '00001': # 各年1番の馬には列タイトルをつける
                    writer.writerow(row1[0:15])
                    writer.writerow(row2[0:15])
                else:
                    writer.writerow(row2[0:15])

[Python] 218 フォルダ(ファイル)をまとめて消去する

備忘のためメモ書きしておきます。

コード例
年フォルダ内のフォルダ(ファイル含)を全て消去

import glob,os,shutil

for year in range(1986,2021):
    for f in glob.glob(f'/horse_racing/race_name/{year}/*'):
        if os.path.isdir(f):
            shutil.rmtree(f)

年フォルダ内のファイルを全て消去

import glob,os

for year in range(1986,2021):
    for f in glob.glob(f'/Volumes/DATA_HR/horse_racing/race_name/{year}/*'):
        if os.path.isfile(f):
            os.remove(f)

[Python] 217 複数のCSVファイルから一部を抽出してまとめる

複数のCSVファイルからある行を抽出し、1つのファイルにまとめるコードです。

with文を追記モード(mode = ‘a’)にします。

一連のCSVファイル処理でCSVモジュールの使いやすさを認識しました。

数値データの配列ではなく単なる表として扱うのであれば、pandasよりもこちらの方が適しているように思います。

コード例は[Python]215の図にあるレースファイルから図右下のレース名他を抽出します。

import glob,csv

# 1986年から2020年のレースファイルからレース名他を抽出して競馬場毎にまとめる
for year in range(1986,2021):
    for f in glob.glob(f'/horse_racing/race/{year}/*/*/*.csv', recursive=True):
        file_new = f.split('race/')[0] + 'race_name/' + f.split('race/')[1][:-26] + f'race_n_{year}' + f.split('race/')[1][-26:-24] + '.csv'

        with open (f, mode="r", encoding="shift_jis") as f1:
            with open(file_new, mode="a", encoding="shift_jis") as f2: # 追記モード
                writer = csv.writer(f2)
                for i,row in enumerate(csv.reader(f1)):
                    if i != 0: # 行0の列インデックスは削除
                        if '010101' == f[-10:-4]: # 1回1日1Rだけ列タイトルを書き込み
                            if i == 1: # 列タイトル
                                rows = [e[2:] for e in row[21:30]] + ['raceID']
                                writer.writerow(rows)

                        if '年' in row[21]: # 列21に'年'がある行を書き込み
                            rows = row[21:30] + [f[-20:-4]]
                            writer.writerow(rows)