[Python] 281 C言語実行ファイルの併用

前回記事で試みたCモジュールの導入は残念ながらうまくいきませんでしたが、次にPythonコードからターミナルコマンドでC言語実行ファイルを走らせてみたところ、こちらの方は難なく成功しました。

Pythonだけで処理するよりも10倍以上の速さです。

ファイルを介してデータのやり取りをするのであれば、この方法で問題ありません。

C言語のコードを書くのは初心者ゆえかなりしんどいものの、その凄まじい実力を知ってしまったら使わずにはいられないです。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	FILE *fp; // 誕生年競走馬リスト
	int horseID[9]; // 1 horseID
	char horse_name[50]; // 2 検索馬名
	char horse_name0[50]; // 3 馬名
	char status[10]; // 4 稼働
	char gender[10]; // 5 性別
	char hair[10]; // 6 毛色
	char birthday[50]; // 7 生年月日
	char trainer[50]; // 8 調教師
	char owner[50]; // 9 馬主
	char info[100]; // 10 募集情報
	char breeder[50]; // 11 生産者
	char area[50]; // 12 産地
	char price[50]; // 13 セリ取引価格
	char prize_money[50]; // 14 獲得賞金
	char result[50]; // 15 通算成績
	char winning_race[100]; // 16 主な勝鞍
	char relatives[100]; // 17 近親馬

	char horse_name_in[50]; // 検索馬名
	FILE *fp2; // 入力ファイル
	FILE *fp3; // 出力ファイル

	char buf[2000]; // fgets用
	char buf2[200]; // 検索馬名用

	int i=0;
	int strcmp(const char *s1, const char *s2);

	char fname[100];
	char fname2[] = "path_horse.txt"; // 誕生年競走馬リストのパスと競走馬名
	char fname3[] = "horseID.csv"; // 検索結果

	fp2 = fopen(fname2, "r");
	while(fgets(buf2,200, fp2) != NULL ) {
		sscanf(buf2,"%[^,],%s",fname,horse_name_in);

		fp = fopen(fname, "r");

		if(fp == NULL) {
		printf("%s file not open!\n", fname);
		return -1;
		}

		while(fgets( buf,2000, fp ) != NULL ) {
			sscanf(buf, " %9s, %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %s",horseID,horse_name,horse_name0,status,gender,hair,birthday,trainer,owner,info,breeder,area,price,prize_money,result,winning_race,relatives ) ;

			if (i != 0){
				if (strcmp(horse_name,horse_name_in)==0){
					fp3 = fopen(fname3, "a");
					fprintf(fp3, "%s,%9s\n", horse_name,horseID);
					fclose(fp3);
					break;
				}
			}
			i ++ ;
		}
		fp3 = fopen(fname3, "a");
        // ヒットしなかった馬名には仮番号100000000を付ける
		fprintf(fp3, "%s,100000000\n", horse_name_in);
		fclose(fp3);
		fclose(fp);
	}
	fclose(fp2);
	printf("C言語実行しました");
}

[Python] 280 C言語モジュールの性能検証

競走馬名からIDを検索する部分をC言語で作成し、Python用にモジュール化して走らせてみました。約25000件の処理になります。

速度は6倍になりましたが、検索漏れのエラーが多発して今のところ使えない状態です(エラー率6%)。

丸1日掛けて過去最高レベルで苦労したにも関わらず、残念な結果となりました。

シェルスクリプトのワンライナーでも試してみましたが、処理は正確なものの時間は2倍掛かりました。やはり大量処理には向かないようです。

馬名を都度Cモジュールに渡して検索する方式から一括渡しのバッチ式に変更してさらに検証を進める予定です。

それでも改善しなければ、観念して全編C言語で作成することになるかもしれません。なるべく回避したいところです。

21/7/12追記 バッチ式で検証したところC言語ではOK、モジュール化するとNGでした。モジュール化の方法に問題があるようです。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	FILE *fp; // horse_listファイル
	int horseID[9]; // 1 horseID
	char horse_name[50]; // 2 馬名
	char horse_name0[50]; // 3 馬名0 (外),(地)等を付加した馬名
	char status[10]; // 4 稼働状態
	char gender[10]; // 5 性別
	char hair[10]; // 6 毛色
	char birthday[50]; // 7 生年月日
	char trainer[50]; // 8 調教師
	char owner[50]; // 9 馬主
	char breeder[50]; // 10 生産者
	char area[50]; // 11 産地
	char price[50]; // 12 セリ取引価格
	char prize_money[50]; // 13 獲得賞金
	char result[50]; // 14 通算成績
	char winning_race[100]; // 15 主な勝鞍

	char horse_name_in[50]; // 検索馬名
	FILE *fp2; // 入出力用一時ファイル

	char buf[600]; // fgets用
	char buf2[100]; // 検索馬名用

	int ret;
	int i=0;
	int strcmp(const char *s1, const char *s2);

	char fname[100];
	char fname2[] = "horseID.txt";

	fp2 = fopen(fname2, "r");
	while(fgets(buf2,100, fp2) != NULL ) {
		sscanf(buf2,"%[^,],%s",fname,horse_name_in);
		printf("%s,%s\n",fname,horse_name_in);
	fclose(fp2);
	}

	fp = fopen(fname, "r");

	if(fp == NULL) {
		printf("%s file not open!\n", fname);
		return -1;
	}

	while( fgets( buf,600, fp ) != NULL ) {
		ret = sscanf(buf, " %9s, %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %[^,], %s",horseID,horse_name,horse_name0,status,gender,hair,birthday,trainer,owner,breeder,area,price,prize_money,result,winning_race ) ;

		if (i != 0){
			if (strcmp(horse_name,horse_name_in)==0){
				fp2 = fopen(fname2, "w");
				fprintf(fp2, "%9s\n", horseID);
				fclose(fp2);
			}
		}
		i ++ ;
	}
	fclose(fp);
}

[Python] 278 pyinstallerによるアプリ作成失敗事例

ささいなことでハマったのでメモ書きしておきます。

コードにprint関数による標準出力が含まれているにもかかわらず引数に–noconsoleを付けたために、作成したアプリが起動しませんでした。

アプリが標準出力したいのにコンソールが立ち上がらないのでは当然エラーになります。

標準出力の箇所は削除し、以下コマンドで再度作成しました。

pyinstaller pythonファイル名 --onefile --noconsole

[Python] 277 CSVファイルのデータ型を整数に変換

CSVファイルのデータ型を小数の文字列から整数に変換するコードの一例です。画像上・中は表計算ソフトNumbersで変換前後をファイル表示したものです。

小数の文字列から整数への直接変換は仕様のため不可なので、一旦浮動小数点数に変換してから整数にします。

<コードの一部>
with open (file, mode="r", encoding="shift_jis") as f1:
    with open(file_convert, mode="a", encoding="shift_jis") as f2:
        writer = csv.writer(f2)
        for i,row in enumerate(csv.reader(f1)):
            if i == 0: # ヘッダ行
                rows = row[0:23]
                writer.writerow(rows)
            else:
                try: # 着順 "中(止)"や"除(外)"への対応
                    col0 = int(float(row[0]))
                except:
                    col0 = row[0]
                try: # 人気 空欄への対応
                    col13 = int(float(row[13]))
                except:
                    col13 = row[13]
                rows2 = [col0] + [int(float(row[1]))] + [int(float(row[2]))] + row[3:13] + [col13] + row[14:23]
                writer.writerow(rows2)


Excelでは変換前後で見た目変わらず。この種の作業には全く向きません。Numbersの優秀さが際立ちます。

[Python] 276 MySQLをpipでPythonのマイクロバージョン毎にインストール

[macOS Catalina 10.15.7]

pyenvでpipを使ったMySQLモジュールのインストールコマンド例は以下のようになります。

モジュール名がmysqlではなくてmysql-connector-pythonなので注意が必要です。

Python 3.9.X毎にライブラリを管理する場合
(pythonコマンドをマイクロバージョン毎に変える必要がある)

/Users/xxx/.pyenv/versions/3.9.5/bin/python -m pip install mysql-connector-python

<インストール先>
/Users/xxx/.pyenv/versions/3.9.5/lib/python3.9/site-packages

Python 3.9でまとめて管理する場合
(1.which python → /Users/xxx/.pyenv/shims/python
2.python –version → Python3.9.X を事前確認する)

python -m pip install mysql-connector-python

<インストール先>
/Users/xxx/.local/lib/python3.9/site-packages

[Python] 275 MySQLでIF EXISTSが機能しない

MySQLでテーブルを一旦削除してから新たに作成する操作をする際、テーブルが存在しなくてもエラーにならないようSQL文に”IF EXISTS”を付けたのですが機能しません。

SQL文作成はf文字列でもformatメソッドでもNGでした。

仕方がないので、エラーを無視してtry-except-finallyでテーブルを作成するようにしました。

原因は不明ですが、mysql.connectorモジュールがおかしいのでしょうか。

import csv,mysql.connector,glob
import pandas as pd

<中略>

# sqlのリストを作成
sql_l = list()
for table in table_l:
    sql = f"DROP TABLE IF EXISTS horse_race_name.{table}"
    sql_l.append(sql)

sql_l2 = list()
for table in table_l:
    sql = f"CREATE TABLE horse_race_name.{table} {column_l_str}"
    sql_l2.append(sql)

# mysqlに接続
conn = mysql.connector.connect(**config)
cur = conn.cursor()

# データベースhorse_race_nameにtableを作成する
for file,table,sql,sql2 in zip(file_l,table_l,sql_l,sql_l2):
    try:
        cur.execute(sql)
    except Exception as e:
        print(e)
    finally:
        try:
            cur.execute(sql2)
        except Exception as e:
             print(e)
        else:
            cur.execute('BEGIN')

            # CSVファイルを読み込み、各行をtableに挿入する
            with open(file, 'rt', encoding='Shift-JIS') as f:
                reader = csv.reader(f)
                for i,row in enumerate(reader):
                    if i != 0:
                        row_str = str(row).replace('[','(').replace(']',')')
                        sql3 = f'INSERT INTO horse_race_name.{table} VALUES {row_str}'
                        cur.execute(sql3)

            cur.execute('COMMIT')

conn.close()

[Python] 274 CSVファイル完全一致検索の高速化検討

誕生年単位の馬名ファイルとCSVファイル内の馬名との完全一致検索を実行するコードです。[Python] 229にも似たようなコードを書いています。

実行時間が結構かかり、改善の余地ありです。C言語でモジュールを書いたらどれだけ早くなるか、試してみたいです。

import csv
import pandas as pd

<中略>

with open (file_new_pre, mode="r", encoding="shift_jis") as f3:
    with open (file_new, mode="a", encoding="shift_jis") as f4:
        writer = csv.writer(f4)

        for i,row in enumerate(csv.reader(f3)):
            if i != 0:
                if int(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{birthyear}.csv'
                
                try:
                    df = pd.read_csv(namefile,encoding="shift_jis")
                except:
                    horseID = '198500000'
                else:
                    # 馬名ファイル各行の馬名とレースファイルの馬名が完全一致する場合にTrueとする配列を作成
                    b_array = df[df.columns[1]]==row[3]

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

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

                rows = row[0:22] + [horseID]
                writer.writerow(rows)

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

[Python] 273 macOSのデスクトップ通知

処理に時間がかかる場合は、待っている間に他のことができるよう通知設定しています。

目立たせるため、関数にしました。

import os

# Macのデスクトップ通知
def notification():
    os.system("osascript -e 'display notification \"〇〇チェック\nコード実行終了\"'")

notification()

[Python] 272 辞書型データをCSVファイルに変換他

辞書型データからのvalue取得、キーの任意並べ替え(valueがない場合にも対応)、データフレームとの相互変換、CSVファイルへの変換が含まれたコードです。

import pandas as pd

<中略>

df = pd.read_csv(path,header=None,encoding='shift_jis')
df2 = df.drop_duplicates(subset=[0])

# データフレームを辞書型データに変換
dict = {str(x) : str(y) for x,y in zip(df2[0],df2[1])}

# 15項目の辞書型データを作成(valueがない場合はnanを入力)
dict2 = {'馬名':dict.get('馬名','nan'),'稼働':dict.get('稼働','nan'), '性別':dict.get('性別','nan'), '毛色':dict.get('毛色','nan'), '生年月日':dict.get('生年月日','nan'), '調教師':dict.get('調教師','nan'), '馬主':dict.get('馬主','nan'),'募集情報':dict.get('募集情報','nan'),'生産者':dict.get('生産者','nan'), '産地':dict.get('産地','nan'), 'セリ取引価格':dict.get('セリ取引価格','nan'), '獲得賞金':dict.get('獲得賞金','nan'), '通算成績':dict.get('通算成績','nan'), '主な勝鞍': dict.get('主な勝鞍','nan'), '近親馬':dict.get('近親馬','nan')}

# 辞書型データをデータフレームに変換
df3 = pd.json_normalize(dict2)

# df3の行と列を転置してCSVファイルを作成(T属性を使う)
with open(path,mode="w",encoding="cp932", errors="ignore") as f:
    df3.T.to_csv(f,header=False)