[Python] 282 C言語実行ファイルの併用 その2 内容説明

前回の続きです。

C言語実行から作成したデータを処理する流れを説明します。

実行ファイルはsubprocessモジュールで走らせて、処理の終了は実行ファイルの最後に仕込んだ標準出力の受け取りで判断します。

作成されたデータ内容は下図のようになっています。データベースにヒットした馬名は2行になり、ヒットしなかった馬名は1行になります。

あとはpandasなどを使ってデータ加工するのですが、重複行の削除には一工夫必要でした。馬名は複数回登場することがあるのでpandasのduplicatedメソッドは使えず、馬名列要素のリストと1要素分スライドしたリストを比較し重複する所のインデックス番号をリスト化して処理しました。

重たい処理はC言語にさせて、出来上がったラフなデータをPythonで加工する。データベースを扱うに際し私にとって最強の組み合わせになりそうです。

<該当箇所のみ>

# C言語実行ファイル
proc = subprocess.run(C言語実行ファイルのパス, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
print(proc.stdout.decode('UTF-8'))

# horseID.csvのデータフレーム化
df = pd.read_csv(horseID_file,names=['馬名2','horseID'],encoding='UTF-8')

# "馬名2"列リストと1要素スライドしたリストを作成(最後の'A'は数合わせ)
list_horseA = df['馬名2'].tolist()
list_horseB = list_horseA[1:] + ['A']

# 重複行のインデックス番号を取得してリスト化
list_num = []
i = 1
for nameA,nameB in zip(list_horseA,list_horseB):
    if nameA == nameB:
        list_num.append(i)
    i = i + 1

# 重複行を削除
df2 = df.drop(df.index[list_num])

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

[JavaScript] 01 標準入力取得

Javaを本格的に学び始めて3週間が経過し、初級者なりのコードが書けるようになりました。

次はこれまで無意識に遠ざけていたJavaScriptの学習に着手します。Python、Javaに次ぐ私の第3言語にしたいと考えています。WordPressやMySQLの管理で役に立ちそうなPHPまであわよくば手を伸ばして4言語を駆使するようになるのが当面の目標です。

学習内容はJavaと同じく競馬DBを使った検索アプリの作成です。

まずは標準入力の取得です。

PythonやJavaと比較しながらマイペースで進めていきます。

console.log("競走馬名を入力してください");

var input = require("fs").readFileSync("/dev/stdin", "utf8");
console.log(input)

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