xlsx_comparison / app.py
juyam's picture
moved diff graph upper
9671197 verified
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import io
import PIL.Image
def process_excel_files(file1, file2):
ファイル1 = pd.read_excel(file1.name)
ファイル2 = pd.read_excel(file2.name)
比較列名1 = ファイル1.columns[3]
比較列名2 = ファイル2.columns[3]
IDs1 = ファイル1.iloc[:, 0].values
ラベル1 = ファイル1.iloc[:, 1].values
データ1 = ファイル1.iloc[:, 3].values
IDs2 = ファイル2.iloc[:, 0].values
ラベル2 = ファイル2.iloc[:, 1].values
データ2 = ファイル2.iloc[:, 3].values
加工後df1 = pd.DataFrame({'id': IDs1,'label': ラベル1,'value': データ1 })
加工後df2 = pd.DataFrame({'id': IDs2,'label': ラベル2,'value': データ2 })
共通ID = set(IDs1).intersection(set(IDs2))
if not 共通ID:
return PIL.Image.new('RGB', (400, 200), color='white'), "エラー: 両ファイル間に共通のIDがありません。"
ソート後df1 = 加工後df1[加工後df1['id'].isin(共通ID)].sort_values('id')
ソート後df2 = 加工後df2[加工後df2['id'].isin(共通ID)].sort_values('id')
# 同じIDの順序でデータを整列
合体後df = pd.merge(ソート後df1, ソート後df2, on='id', suffixes=('_1', '_2'))
# データを取得
IDs = 合体後df['id'].values
ラベル = 合体後df['label_1'].values # ファイル1のラベルを使用
1 = 合体後df['value_1'].values
2 = 合体後df['value_2'].values
差分 = 値2 - 値1
# グラフ用にラベルを短縮(長すぎる場合)
short_labels = [str(label)[:15] + '...' if len(str(label)) > 15 else str(label) for label in ラベル]
# 可視化
図表, ( ax3,ax1, ax2) = plt.subplots(3, 1, figsize=(12, 18))
# X軸インデックス
x = np.arange(len(IDs))
width = 0.8
########################################################################################### 増減の可視化
colors = ['red' if x < 0 else 'green' for x in 差分]
bars3 = ax3.bar(x, 差分, width, color=colors, alpha=0.7)
ax3.set_title('diff : file1 >> file2')
ax3.set_ylabel('Difference')
ax3.set_xticks(x)
# ラベルを垂直(90度)に変更
ax3.set_xticklabels(short_labels, rotation=90, ha='center', va='top')
ax3.axhline(y=0, color='black', linestyle='-', alpha=0.5)
ax3.grid(True, linestyle='--', alpha=0.7, axis='y')
# 値をバーの上または下に表示
for bar in bars3:
height = bar.get_height()
y_pos = height + 0.1 if height >= 0 else height - 0.3
ax3.text(bar.get_x() + bar.get_width()/2., y_pos,
f'{height:.2f}', ha='center', va='bottom', rotation=0, fontsize=8)
########################################################################################### # ファイル1のデータ(元データ)
bars1 = ax1.bar(x, 値1, width, color='blue', alpha=0.7)
ax1.set_title(f'file1')
ax1.set_ylabel('Value')
ax1.set_xticks(x)
# ラベルを垂直(90度)に変更
ax1.set_xticklabels(short_labels, rotation=90, ha='center', va='top')
ax1.grid(True, linestyle='--', alpha=0.7, axis='y')
# 値をバーの上に表示
for bar in bars1:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{height:.2f}', ha='center', va='bottom', rotation=0, fontsize=8)
########################################################################################### ファイル2のデータ
bars2 = ax2.bar(x, 値2, width, color='green', alpha=0.7)
ax2.set_title(f'file2')
ax2.set_ylabel('Value')
ax2.set_xticks(x)
# ラベルを垂直(90度)に変更
ax2.set_xticklabels(short_labels, rotation=90, ha='center', va='top')
ax2.grid(True, linestyle='--', alpha=0.7, axis='y')
# 値をバーの上に表示
for bar in bars2:
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
f'{height:.2f}', ha='center', va='bottom', rotation=0, fontsize=8)
# 下部に余白を追加して、垂直ラベルのためのスペースを確保
plt.subplots_adjust(bottom=0.15)
plt.tight_layout()
# 画像をバイト列に変換してPIL Imageに変換
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=100)
buf.seek(0)
画像 = PIL.Image.open(buf)
統計 = {
"比較した項目数": len(IDs),
"増加した項目数": sum(0<差分),
"減少した項目数": sum(差分<0),
"最大の増加": int(np.max(差分)*100)/100 if len(差分[0<差分]) > 0 else 0,
"最大の減少": int(np.min(差分)*100)/100 if len(差分[差分<0]) > 0 else 0,
}
変化量データ = [{"id": IDs[i], "ラベル": ラベル[i], "変化量": 差分[i]}for i in range(len(IDs))]
変化量プラス = sorted([item for item in 変化量データ if item["変化量"] > 0],key=lambda x: x["変化量"], reverse=True)
変化量マイナス = sorted([item for item in 変化量データ if item["変化量"] < 0],key=lambda x: x["変化量"])
分析結果 = "\n".join([f"{k}: {v}" for k, v in 統計.items()])
分析結果 += "\n\n最も変化の大きいプラス 5件:\n"
for item in 変化量プラス[:5]: 分析結果 += f"{item['変化量']:.2f} : {item['ラベル']} \n"
分析結果 += "\n最も変化の大きいマイナス 5件:\n"
for item in 変化量マイナス[:5]: 分析結果 += f"{item['変化量']:.2f} : {item['ラベル']} \n"
plt.close(図表) # 図を閉じてメモリリーク防止
return 画像, 分析結果
# Gradioインターフェースの作成
with gr.Blocks(title="Excelファイル比較アプリ") as app:
gr.Markdown("# Excelファイル比較ツール")
gr.Markdown("2つのExcelファイルをアップロードし、4列目のデータの増減を可視化します。")
with gr.Row():
比較元 = gr.File(label="比較元Excelファイル")
比較先 = gr.File(label="比較先Excelファイル")
with gr.Row():
画像出力 = gr.Image(label="比較結果グラフ")
統計出力 = gr.Textbox(label="統計情報", lines=12)
比較開始ボタン = gr.Button("比較開始")
比較開始ボタン.click( fn=process_excel_files, inputs=[比較元, 比較先], outputs=[画像出力, 統計出力] )
if __name__ == "__main__":
app.launch(share=True) # 公開リンクを生成するためにshare=Trueを設定