##はじめに
大量の同じフォーマットのExcel,CSVファイルのデータをまとめて一つのグラフにしたい。直感的にわかりやすいようにGUIでファイルの選択、軸範囲の設定を行いたい。
##仕様
・「ファイルを選択」ボタンでグラフ化するファイルを選択する。※ファイルは複数選択可能
・選択可能なファイルの拡張子はxls,xlsx,csv
・処理するファイルのデータのヘッダー行数を指定する。ヘッダーがない場合は、0または空白を指定する
・グラフのx列、y列となる処理するファイルのデータの列数を指定する
・x列、y列の組み合わせは最大4つまで指定可能
・x列またはy列のどちらか一方でも空欄の場合はグラフ化を行わない
・保存するグラフのファイル名は「保存ファイル名」で指定可能。空欄の場合のファイル名は「現在時刻+条件番号」となる
・グラフのx軸の範囲は「x最小」,「x最大」,y軸の範囲は「y最小」,「y最大」で設定可能。空欄の条件は自動設定になる
・「実行ボタン」でグラフ化の処理が走る
・グラフ化したファイルはGUIのコードが存在するフォルダ内に保存される
##コード
import PySimpleGUI as sg
import pandas as pd
import matplotlib.pyplot as plt
import os
import numpy as np
import datetime
INPUT_BOX_SIZE = 5
HEADER_ROW = 1
SAVE_FILE_NAME_ROW = 2
OFFSET_DATA_NUM = 3
LINE_PER_DATA = 6
COND_LINE_NUM = 4
X_COL_OF_LINE = 0
Y_COL_OF_LINE = 1
X_MIN_OF_LINE = 2
X_MAX_OF_LINE = 3
Y_MIN_OF_LINE = 4
Y_MAX_OF_LINE = 5
def plotfig(axobj, x_data, y_data, filename, cmap, xlabel, ylabel, x_min, x_max, y_min, y_max):
##グラフ描画用の関数
#引数の説明
#axobj:グラフオブジェクト,x_data:x軸データ, y_data:y軸データ, filename:元データのファイル名
#cmap:グラフのカラー, xlabel:x軸ラベル, ylabel:y軸ラベル
#x_min:x軸最小値, x_max:x軸最大値, y_min:y軸最小値, y_max:y軸最大値
axobj.plot(x_data, y_data, label=filename, color=cmap, marker='o')
axobj.grid()
axobj.legend()
axobj.set_xlabel(xlabel)
axobj.set_ylabel(ylabel)
## 軸範囲の設定 ※GUIで指定されていない場合は自動で設定
# x軸
if x_min.strip() and x_max.strip():
axobj.set_xlim(left=int(x_min), right=int(x_max))
elif x_min.strip():
axobj.set_xlim(left=int(x_min))
elif x_max.strip():
axobj.set_xlim(right=int(x_max))
# y軸
if y_min.strip() and y_max.strip():
axobj.set_ylim(bottom=int(y_min), top=int(y_max))
elif y_min.strip():
axobj.set_ylim(bottom=int(y_min))
elif y_max.strip():
axobj.set_ylim(top=int(y_max))
# セクション1 - オプションの設定と標準レイアウト
sg.theme('Dark Blue 3')
layout = [
#1行目
[sg.InputText('ファイル一覧',enable_events=True,), sg.FilesBrowse('ファイルを選択', key='-FILES-', file_types=(("Excel ファイル", "*.xlsx"),("Excel ファイル", "*.xls"),("CSV ファイル", "*.csv")))],
#2行目
[sg.Text('ヘッダ行数', size=(10, 1)),sg.InputText('1', size=(INPUT_BOX_SIZE, 1)),sg.Text('保存ファイル名', size=(12, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE*5, 1))],
#3行目
[sg.Text('条件', size=(5, 1)),sg.Text('x列', size=(INPUT_BOX_SIZE, 1)),sg.Text('y列', size=(INPUT_BOX_SIZE, 1)),
sg.Text('x最小', size=(INPUT_BOX_SIZE-1, 1)), sg.Text('x最大', size=(INPUT_BOX_SIZE-1, 1)), sg.Text('y最小', size=(INPUT_BOX_SIZE-1, 1)), sg.Text('y最大', size=(INPUT_BOX_SIZE-1, 1))],
#4行目
[sg.Text('1',size=(5, 1)),sg.InputText('1', size=(INPUT_BOX_SIZE, 1)),sg.InputText('2', size=(INPUT_BOX_SIZE, 1)),
sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1))],
#5行目
[sg.Text('2',size=(5, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),
sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1))],
#6行目
[sg.Text('3',size=(5, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),
sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1))],
#7行目
[sg.Text('4',size=(5, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),sg.InputText(' ', size=(INPUT_BOX_SIZE, 1)),
sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1)), sg.InputText('', size=(INPUT_BOX_SIZE, 1))],
#8行目
[sg.Submit(button_text='実行ボタン')]
]
# セクション 2 - ウィンドウの生成
window = sg.Window('グラフ保存', layout)
# セクション 3 - イベントループ
while True:
event, values = window.read()
if event is None:
print('exit')
break
if event == '実行ボタン':
# ファイルのパスを抽出
files = values['-FILES-'].split(';')
# ファイルの数を抽出
files_num = len(files)
# 処理対象のデータのヘッダー行を抽出
header_row = values[HEADER_ROW]
# 画像の保存ファイル名を抽出
input_save_file_name = values[SAVE_FILE_NAME_ROW].strip()
d_temp={}
# グラフ処理
for k, v in values.items(): # 一度pd.Seriesに変換
# ヘッダー数を除いてデータフレームを作成
if type(k)==str:
continue
# GUIの入力データ以外は無視
if k < OFFSET_DATA_NUM:
continue
d_temp[k-1] = pd.Series(v)
df = pd.DataFrame(d_temp)
# DataFrameの行列をGUIの表示の形に成形
pd_cond = pd.DataFrame(df.values.reshape(COND_LINE_NUM, LINE_PER_DATA))
# グラフオブジェクトの作成
fig = np.zeros(COND_LINE_NUM)
ax = np.zeros(COND_LINE_NUM)
cmap = plt.get_cmap("tab10") # カラーマップを定義
fig_list = ['']*COND_LINE_NUM
ax_list = ['']*COND_LINE_NUM
for n in range(COND_LINE_NUM):
fig_list[n] = plt.figure()
ax_list[n] = fig_list[n].add_subplot(1,1,1)
# ファイルからデータ読み込み
for i, file in enumerate(files):
_, ext = os.path.splitext(file)
#Excelファイルの場合
if ext==".xls" or ext==".xlsx":
if header_row.strip() and int(header_row) > 0:
readdata = pd.read_excel(file, header=int(header_row)-1)#行数は1始まりを期待しいるため-1。0始まりなら-1は不要。
else:
readdata = pd.read_excel(file, header=None)
#CSVファイルの場合
elif ext==".csv":
if header_row.strip() and int(header_row) > 0:
readdata = pd.read_csv(file, header=int(header_row)-1)
else:
readdata = pd.read_csv(file, header=None)
#グラフの凡例につけるためのファイル名※拡張子は除いている
filename = os.path.splitext(os.path.basename(file))[0]
# 条件ごとに各ファイルのデータを重ねる
for j in range(COND_LINE_NUM):
#x列、y列が空欄の場合は処理を行わない
if not pd_cond.iloc[j,X_COL_OF_LINE].strip() or not pd_cond.iloc[j,Y_COL_OF_LINE].strip():
continue
# 処理対応データ格納列の情報を抽出してint化
x_col = int(pd_cond.iloc[j, X_COL_OF_LINE])-1 #GUIの条件は1始まりなので-1
y_col = int(pd_cond.iloc[j, Y_COL_OF_LINE])-1 #GUIの条件は1始まりなので-1
x_data = readdata.iloc[:,x_col]
y_data = readdata.iloc[:,y_col]
# x_col,y_colは空欄でないことが保証されているが、最大最小値は空欄の可能性があるためここではint化しない
x_min = pd_cond.iloc[j, X_MIN_OF_LINE]
x_max = pd_cond.iloc[j, X_MAX_OF_LINE]
y_min = pd_cond.iloc[j, Y_MIN_OF_LINE]
y_max = pd_cond.iloc[j, Y_MAX_OF_LINE]
# グラフ描画用の関数でグラフ描画
plotfig(ax_list[j], x_data, y_data, filename, cmap(i),
readdata.columns[x_col], readdata.columns[y_col],
x_min, x_max, y_min, y_max)
# ファイル名に現在時刻を付けて画像を保存
for j in range(COND_LINE_NUM):
#x列、y列が空欄の場合は処理を行わない
if not pd_cond.iloc[j,X_COL_OF_LINE].strip() or not pd_cond.iloc[j,Y_COL_OF_LINE].strip():
continue
if not input_save_file_name:
dt_now = datetime.datetime.now()
save_file_name = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒') + "_条件" + str(j+1)# GUIの条件は1始まりなので+1
else:
save_file_name = input_save_file_name + "_条件" + str(j+1)# GUIの条件は1始まりなので+1
fig_list[j].savefig(save_file_name)
# ポップアップ
sg.popup("終了~")
# セクション 4 - ウィンドウの破棄と終了
window.close()
注意点
importしているライブラリ以外にopenpyxlをインストールしておく必要有