[Java] 69 Swing 09 PythonによるCSVファイル集計 条件毎の着別度数

芝とダートの着別度数を算出し、プロフィール隣のJTextAreaに表示しました。あと馬場状態や距離の着別度数は必須でしょう。

芝とダートで活躍した実力馬といえばホクトベガですが地方競馬のデータがこちらにないので、中央の芝とダートにデータがあるナリタハヤブサを検索例にしました。ここまで書いたところでアグネスデジタルを思い出しました。

import glob,csv
import pandas as pd

paths = glob.glob('/*.csv')
print(paths)

paths2 = sorted(paths)
print(paths2)

csvfile = paths2[-1]
print(csvfile)

df = pd.read_csv(csvfile,encoding='UTF-8')
print(df)

prize = df['賞金'].sum(skipna=True).round(1)
print(f"獲得賞金 {prize}")

race_count = len(df)
print("レース数 " + str(len(df)))

list_着順 = df['着順'].tolist()
着順1 = len([i for i in list_着順 if i == 1])
着順2 = len([i for i in list_着順 if i == 2])
着順3 = len([i for i in list_着順 if i == 3])
着順外 = len([i for i in list_着順 if i > 3])

着別度数 = f"{着順1}-{着順2}-{着順3}-{着順外}"
print("着別度数 " + 着別度数)

list_馬場 = df['馬場状態'].tolist()

list_turf_order = list()
list_dirt_order = list()
for course,order in zip(list_馬場,list_着順):
    if "芝" in course:
        list_turf_order.append(order)
    else:
        list_dirt_order.append(order)

着順1_t = len([i for i in list_turf_order if i == 1])
着順2_t = len([i for i in list_turf_order if i == 2])
着順3_t = len([i for i in list_turf_order if i == 3])
着順外_t = len([i for i in list_turf_order if i > 3])

着別度数_t = f"{着順1_t}-{着順2_t}-{着順3_t}-{着順外_t}"
print("芝着別度数 " + 着別度数_t)

着順1_d = len([i for i in list_dirt_order if i == 1])
着順2_d = len([i for i in list_dirt_order if i == 2])
着順3_d = len([i for i in list_dirt_order if i == 3])
着順外_d = len([i for i in list_dirt_order if i > 3])

着別度数_d = f"{着順1_d}-{着順2_d}-{着順3_d}-{着順外_d}"
print("ダ着別度数 " + 着別度数_d)

list_output = [{"獲得賞金":prize,"着別度数":着別度数,"芝":着別度数_t,"ダ":着別度数_d}]

# 出力ファイル名作成
filename = csvfile.split(".")[0] + "_agg.csv"

field_name = ['獲得賞金','着別度数','芝','ダ']
with open(filename,'w',encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames = field_name)
    writer.writeheader()
    writer.writerows(list_output)

[Java] 68 Swing 08 外部プログラムの選択 改良版

前回の続きです。switch文の重複部分が気になったので書き直しました。こちらの方がメンテナンス性も優れており断然スマートです。

import java.util.ArrayList;

public class ProcessExecutor {
    public static void exec(String type) throws Exception {
    	System.out.println("ProcessExecutor");

    	// 分類用リスト作成 集計:agg,分析:ana,血統:blo 以降随時追加
    	ArrayList<String> types = new ArrayList<String>();
    		types.add("agg");
    		types.add("ana");

		// cmdリスト作成
		ArrayList<String> cmds = new ArrayList<String>();
    		cmds.add("python HS_csv_agg.py ; echo 'コマンド完了'");
    		cmds.add("python HS_csv_ana.py ; echo 'コマンド完了'");

    	// index番号取得
    	int type_num = types.indexOf(type);
    	System.out.println("index番号 " + type_num);

    	// コマンド選択
    	String cmd = cmds.get(type_num);

		ProcessBuilder p = new ProcessBuilder("sh", "-c", cmd);
        p.redirectErrorStream(true);
        // プロセス開始
        Process process = p.start();
        // プロセス終了まで待機
        process.waitFor();
        int result = process.exitValue();
        System.out.printf("result = %d%n", result);
    }
}

[Java] 67 Swing 07 外部プログラムの選択(switch文)

Java-Swingアプリの集計、分析等はPythonで処理していますが、処理内容によってコードを使い分けるようにしました。

import java.util.ArrayList;

public class ProcessExecutor {
    public static void exec(String type) throws Exception {
    	System.out.println("ProcessExecutor");

    	// 分類用リスト作成 集計:agg,分析:ana,血統:blo 以降随時追加
    	ArrayList<String> types = new ArrayList<String>();
    		types.add("agg");
    		types.add("ana");
    		types.add("blo");

    	// case番号を付番
    	int type_num = types.indexOf(type);
    	System.out.println("case番号 " + type_num);

    	switch(type_num) {
			case 0: // 集計
				String cmd0 = "python HS_csv_agg.py ; echo 'コマンド完了'";
				ProcessBuilder p0 = new ProcessBuilder("sh", "-c", cmd0);
		        p0.redirectErrorStream(true);
		        // プロセス開始
		        Process process0 = p0.start();
		        // プロセス終了まで待機
		        process0.waitFor();
		        int result = process0.exitValue();
		        System.out.printf("result = %d%n", result);
		        break;
			case 1: // 分析
				String cmd1 = "python HS_csv_ana.py ; echo 'コマンド完了'";
				ProcessBuilder p1 = new ProcessBuilder("sh", "-c", cmd1);
		        p1.redirectErrorStream(true);
		        // プロセス開始
		        Process process1 = p1.start();
		        // プロセス終了まで待機
		        process1.waitFor();
		        int result1 = process1.exitValue();
		        System.out.printf("result = %d%n", result1);
		        break;
    	}
    }
}

[Python] 297 数値を丸める(偶数丸め,四捨五入)

Java-Swingアプリの外部プログラムとしてPythonコードを走らせ、pandasにて合計を計算したところ変な小数が発生しました。

原因は不明ですが、放置もできないので数値を丸めました。pandasのround()メソッドは四捨五入ではなく偶数丸めです。偶数丸めとは、丸めるところが5の場合すぐ上の桁が偶数であれば切り捨て、奇数であれば切り上げる処理です。

数値の丸めはJIS Z 8401で規定されていて、規則Aが偶数丸め、規則Bが四捨五入です。つまり、誤差がより小さい偶数丸めの方を推奨しています。銀行が好んで使うため銀行丸めとも呼ばれています。

# 小数点以下の桁数は1桁に設定
prize = df['賞金'].sum(skipna=True).round(1)
print(f"獲得賞金 {prize}")

[Java] 66 Swing 06 PythonによるCSVファイル集計

CSVファイルの内容集計は外部プログラムのPython(pandas)に任せています。

集計コードは以下の通りです。Javaとの格闘で一時げんなりしていましたが、直感的にコーディングできるPythonによって息を吹き返しました。リスト内包表記を書くといつも爽快な気分になります。引き続き頑張っていけそうです。

import glob,csv
import pandas as pd

paths = glob.glob('/*.csv')
print(paths)

paths2 = sorted(paths)
print(paths2)

csvfile = paths2[-1]
print(csvfile)

df = pd.read_csv(csvfile,encoding='UTF-8')
print(df)

prize = df['賞金'].sum()
print(f"獲得賞金 {prize}")

race_count = len(df)
print("レース数 " + str(len(df)))

list = df['着順'].tolist()
一着回数 = len([i for i in list if i == 1])
二着回数 = len([i for i in list if i == 2])
三着回数 = len([i for i in list if i == 3])
着外回数 = len([i for i in list if i > 3])

着別度数 = f"{一着回数}-{二着回数}-{三着回数}-{着外回数}"
print("着別度数 " + 着別度数)

list_output = [{"獲得賞金":prize,"着別度数":着別度数}]

# 集計ファイル名作成(集計:aggregate)
filename = csvfile.split(".")[0] + "_agg.csv"

field_name = ['獲得賞金','着別度数']
with open(filename,'w',encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames = field_name)
    writer.writeheader()
    writer.writerows(list_output)

[Java] 65 外部コマンドの実行

Javaにおける配列、リスト、CSVファイルの取り扱いが私にとってはあまりに煩雑なので、外部コマンドにてPythonスクリプトを走らせることにします。

昨日はArrayListをCSVファイルに変換するのに散々痛い目にあって懲りました。JavaでCSVファイルの内容を読み込んで集計するなんてPythonユーザーの私にはかなりの苦行です。

JavaからPythonスクリプトを直接呼び出せないのでコンソールコマンドを使います。戻り値のやりとりはできないため、CSVファイル等を介してデータを出し入れします。

コンソールコマンドを実行するクラスは以下の通りです。

Javaとの実用面での関わりはGUI作成限定になりそうです。好きな言語ではありますが、コーディング快適性や開発速度を考慮しての結論です。

JavaはC言語と同様、実装に使うかどうかはともかく学習対象であることは変わりません。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class ProcessExecutor {
    public static void main() throws Exception {
    	System.out.println("ProcessExecutor");

        ProcessBuilder p = new ProcessBuilder("sh", "-c", "python test.py ; echo 'コマンド完了'");

        p.redirectErrorStream(true);

        // コマンドを実行する
        Process process = p.start();

        // 結果を受け取る
        try (BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.defaultCharset()))) {
            String line;
            while ((line = r.readLine()) != null) {
                System.out.println(line);
            }
        }
        process.waitFor();
        int result = process.exitValue();
        System.out.printf("result = %d%n", result);
    }
}

[Java] 64 ネストしたArrayListのCSVファイル化 桁区切りカンマ対応

リストをCSVファイルにする際、リストの要素にカンマが含まれているとそこでも区切られてしまいリストの内容が正確に反映されません。

対策としてArrayListをフラット化すると同時に各要素をreplace関数でカンマ削除処理し、平滑化した配列から入れ子Listに組み直してCSVファイルにしました。平滑化配列から入れ子配列にするやり方がうまくいかなくて、どうにも野暮ったい方法を選択せざるを得ませんでした。

Pythonのように、CSVファイルにしてからpandasを使って簡単に加工したりすることができないのでかっちりとしたListを作らなければならず、かなり手間取りました。

CollectionUtils.chunkという自製メソッド(リストの要素を同じ数で入れ子にする)をネット情報を参考に作成し、なんとか形にできたような感じです。

Java8で追加されたStream API等がなかったら、Javaを第2言語から降格させていたかもしれません。ベテランユーザーの方々は相当苦労されていたと想像します。

今のところPythonと比較して開発速度は1/10から1/5位で、かなり遅いです。さらにスキルアップしてどこまで速くできるでしょう。

// raceリストを配列に変換   
        String[][] raceListConSorts_arrays = new String[raceListConSorts.size()][];
        int i = 0;
        for (ArrayList<String> ele:raceListConSorts) {
        	String print_list = String.format("raceListConSorts リスト%d個目 ",i);
        	System.out.println(print_list + ele);

        	String[] array = ele.toArray(new String[ele.size()]);
        	System.out.println("array " + Arrays.toString(array));

        	raceListConSorts_arrays[i] = array;
        	i = i + 1;
        }

        String[] arrayA = new String[21*raceListConSorts.size()];
        int i2 = 0;
        for (String[] ele:raceListConSorts_arrays) {
		  	System.out.println("arrays作成後array確認 " + Arrays.toString(ele));
		  	for (String ele2:ele) {
		  		ele2 = ele2.replace(",","");
		  		arrayA[i2] = ele2;
		  		i2 = i2 + 1;
		  	}
		  }

        System.out.println("arrayA " + Arrays.toString(arrayA));
        List<String> arrayA_list = Arrays.asList(arrayA);

        // 平滑化リストから入れ子リスト(各リスト要素数21)を作成
        List<List<String>>  listB = CollectionUtils.chunk(arrayA_list,21);
        System.out.println("listB " + listB);

        String[][] listB_array = new String[listB.size()][];
        int i3 = 0;
        for (List<String> ele:listB) {
            String[] array = ele.toArray(new String[ele.size()]);
            System.out.println("array " + i3 + " " + Arrays.toString(array));

            listB_array[i3] = array;
            i3 = i3 + 1;
        }

        List<String> list = Arrays.stream(listB_array)
        		.map(line -> String.join(",",line))
        		.collect(Collectors.toList());

        // csvファイル名を作成
        String[] filename_split = filename.split(Pattern.quote("."));
        System.out.println("filename_split " + filename_split);

        String csvname = filename_split[0] + ".csv";
        System.out.println("csvname " + csvname);

        try {
        	  Files.write(Paths.get(csvname), list, StandardOpenOption.CREATE);
       	}
        catch (IOException e) {
        	    e.printStackTrace();
    	}

[Java] 63 ネストしたArrayList→2次元配列→CSVファイル

ネストしたArrayListを2次元配列に変換し、CSVファイルにしました。確認のための出力はそのままにしています。

またしても文字列をドットにて分割するところでワナにかかりました。まだ2週間しかたっていないのに情けない。

こういったコードを書くとPythonのありがたさが身に染みます。文字列やリストを簡単に扱える自製ライブラリを作りたくなります。最初のfor文は結構手こずりました。appendを使えると大分違うのですが。出力時にtoStringメソッドを使うのも面倒です。

try文のcatchの直前に波カッコを書くことに違和感があるため、今のところ行が増えるのも構わず分けて書いています。審美的に難ありと思ったのですが、少数派でしょうね。

String[][] raceListConSorts_arrays = new String[raceListConSorts.size()][];
int i = 0;
for (ArrayList<String> ele:raceListConSorts) {
    String print_list = String.format("raceListConSorts リスト%d個目 ",i);
    System.out.println(print_list + ele);

    String[] array = ele.toArray(new String[ele.size()]);
    System.out.println("array " + Arrays.toString(array));

    raceListConSorts_arrays[i] = array;
    i = i + 1;
}

for (String[] ele:raceListConSorts_arrays) {
   	System.out.println("arrays作成後array確認 " + Arrays.toString(ele));
}

// 作成した2次元配列をCSV仕様に変換
List<String> list = Arrays.stream(raceListConSorts_arrays)
    .map(line -> String.join(",",line))
    .collect(Collectors.toList());

// HTMLファイル名からCSVファイル名を作成
String[] filename_split = filename.split(Pattern.quote("."));
System.out.println("filename_split " + filename_split);

String csvname = filename_split[0] + ".csv";
System.out.println("csvname " + csvname);

// CSVファイルを作成
try {
    Files.write(Paths.get(csvname), list, StandardOpenOption.CREATE);
}
catch (IOException e) {
    e.printStackTrace();
}

[Java] 62 Swing 05 switch文のbreak,文字列から数字抽出

Pythonにはなぜかswitch文がありません。

開発中のアプリでボタンのコマンドに付番してswitch文で条件分岐しようとしたら、ちょいハマりしました。

各caseに定数で分岐するのにも少し戸惑いましたが、最後にbreakを入れないと次のcaseも読み込んでしまうというのを知りませんでした。

そして以下のエラー発生。
“Exception in thread “AWT-EventQueue-0” java.lang.NumberFormatException: For input string: “”

For input string: “”を見て他のJTextFieldへの読み込みを一瞬疑ったものの、それは後回し。System.out.printlnを随所に入れてチェックしまくりました。

まさかと思いながら、次のcaseの先頭にもSystem.out.printlnを挿入して実行するとあっけなく出力されてようやく原因判明。初心者あるあるでしょうか。

まあこのトラブル対応に伴い様々な小技を覚えたので良しとします。

public void actionPerformed(ActionEvent e) {
    // 文字列(Button1)から数字(1)を抽出してデータ型を整数に変換(今回覚えた小技の一つ)
		String cmd = e.getActionCommand();

		String cmd_i = cmd.replaceAll("[^\\d]", "");
		Integer cmd_i2 = Integer.parseInt(cmd_i);

		switch(cmd_i2) {
			case 1:
      <中略>
      break;

[Java] 61 Swing 04 メソッドの追加 mySQL検索

50日ぶりにGUIフレームワークのSwingをいじりました。第1言語のPythonはともかく第2言語のJavaはしばらく書かないと文法を思い出すのに少々時間がかかります。

アプリの機能拡充が手付かずになっていたので、もっとこまめに手を入れるようにしたいです。

Javaの習いはじめは記述がくどくどしくてあまり好きではなかったのですが、慣れてくると独特の心地よさがあります。

情報へのアクセス性が高く、多機能性に優れ、Pythonと文法が比較的似ていて、Pythonがこうだからこうなるのでは?といった類推が大体当たります。Pythonとの連携が弱いのが数少ない欠点の一つでしょうか。JythonのPython3版開発に期待です。

// profメソッド
	public static ArrayList<String> profSearch(String ID) {
		System.out.println("profメソッド内出力 " + ID);

		String birthyear = ID.substring(0,4);
		ArrayList<String> profList = new ArrayList<String>();
		try {
        	Connection conn = DriverManager.getConnection(url, user, password);
            String sql = String.format("SELECT horseID,検索馬名,馬名,稼働,性別,毛色,生年月日,調教師,馬主,募集情報,生産者,産地,セリ取引価格,近親馬 FROM horse_list.horse%S WHERE horseID = '%S'",birthyear,ID);
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();

            String horseID = null;
            String 検索馬名 = null;
            String 馬名 = null;
            String 稼働 = null;
            String 性別 = null;
            String 毛色 = null;
            String 生年月日 = null;
            String 調教師 = null;
            String 馬主 = null;
            String 募集情報 = null;
            String 生産者 = null;
            String 産地 = null;
            String セリ取引価格 = null;
            String 近親馬 = null;

            while (rs.next()) {
                horseID = rs.getString("horseID");
                検索馬名 = rs.getString("検索馬名");
                馬名 = rs.getString("馬名");
                稼働 = rs.getString("稼働");
                性別 = rs.getString("性別");
                毛色 = rs.getString("毛色");
                生年月日 = rs.getString("生年月日");
                調教師 = rs.getString("調教師");
                馬主 = rs.getString("馬主");
                募集情報 = rs.getString("募集情報");
                生産者 = rs.getString("生産者");
                産地 = rs.getString("産地");
                セリ取引価格 = rs.getString("セリ取引価格");
                近親馬 = rs.getString("近親馬");

                profList.add("horseID " + horseID);
                profList.add("検索馬名 " + 検索馬名);
                profList.add("馬名 " + 馬名 );
                profList.add("稼働 " + 稼働);
                profList.add("性別 " + 性別);
                profList.add("毛色 " + 毛色);
                profList.add("生年月日 " + 生年月日);
                profList.add("調教師 " + 調教師);
                profList.add("馬主 " + 馬主);
                profList.add("募集情報 " + 募集情報);
                profList.add("生産者 " + 生産者);
                profList.add("産地 " + 産地);
                profList.add("セリ取引価格 " + セリ取引価格);
                profList.add("近親馬 " + 近親馬);
            }
        }
        catch (Exception e) {
                e.printStackTrace();
        }
		return profList;
	}