Ken-INOUE commited on
Commit
dc4f35e
·
1 Parent(s): 7fb00e2

Refactor diagnosis function to streamline data processing and enhance Gradio UI for improved user experience

Browse files
Files changed (1) hide show
  1. app.py +111 -112
app.py CHANGED
@@ -1,8 +1,8 @@
 
1
  import pandas as pd
2
  import json
3
- import gradio as gr
4
 
5
- # --- 状態判定関数 ---
6
  def judge_status(value, ll, l, h, hh):
7
  if pd.notna(ll) and value < ll:
8
  return "LOW-LOW"
@@ -15,137 +15,136 @@ def judge_status(value, ll, l, h, hh):
15
  else:
16
  return "OK"
17
 
18
- # --- 診断関数 ---
19
- def diagnose_process_range(process_name, datetime_str, window_minutes, df, thresholds_df):
20
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  target_time = pd.to_datetime(datetime_str)
22
- except Exception:
23
- return (pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
24
- f"⚠ 入力した日時 {datetime_str} が無効です。", "")
25
-
26
- start_time = target_time - pd.Timedelta(minutes=window_minutes)
27
- end_time = target_time
28
- df_window = df[(df["timestamp"] >= start_time) & (df["timestamp"] <= end_time)]
29
-
30
- if df_window.empty:
31
- return (pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
32
- "⚠ 指定した時間幅にデータが見つかりません。", "")
33
-
34
- proc_thresholds = thresholds_df[thresholds_df["ProcessNo_ProcessName"] == process_name]
35
- if proc_thresholds.empty:
36
- return (pd.DataFrame(), pd.DataFrame(), pd.DataFrame(),
37
- f"⚠ プロセス {process_name} の閾値が設定されていません。", "")
38
-
39
- all_results = []
40
- for _, row in df_window.iterrows():
41
- for _, thr in proc_thresholds.iterrows():
42
- col_tuple = (thr["ColumnID"], thr["ItemName"], thr["ProcessNo_ProcessName"])
43
- if col_tuple not in df.columns:
44
- continue
45
- value = row[col_tuple]
46
- status = judge_status(value, thr.get("LL"), thr.get("L"), thr.get("H"), thr.get("HH"))
47
- all_results.append({
48
- "ColumnID": thr["ColumnID"],
49
- "ItemName": thr["ItemName"],
50
- "値": value,
51
- "LL": thr.get("LL"),
52
- "L": thr.get("L"),
53
- "H": thr.get("H"),
54
- "HH": thr.get("HH"),
55
- "判定": status,
56
- "重要項目": bool(thr.get("Important", False)),
57
- "時刻": str(row["timestamp"])
58
- })
59
-
60
- # 集計(全項目)
61
- total = len(all_results)
62
- status_counts = pd.Series([r["判定"] for r in all_results]).value_counts().reindex(
63
- ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
64
- )
65
- status_ratio = (status_counts / total * 100).round(1)
66
- result_df_all = pd.DataFrame({"状態": status_counts.index, "件数": status_counts.values, "割合(%)": status_ratio.values})
67
-
68
- # 集計(重要項目全体)
69
- important_results = [r for r in all_results if r["重要項目"]]
70
- if important_results:
71
- total_imp = len(important_results)
72
- status_counts_imp = pd.Series([r["判定"] for r in important_results]).value_counts().reindex(
73
- ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
74
- )
75
- status_ratio_imp = (status_counts_imp / total_imp * 100).round(1)
76
- result_df_imp = pd.DataFrame({"状態": status_counts_imp.index, "件数": status_counts_imp.values, "割合(%)": status_ratio_imp.values})
77
- else:
78
- result_df_imp = pd.DataFrame(columns=["状態", "件数", "割合(%)"])
79
-
80
- # 集計(重要項目ごと)
81
- result_per_item = []
82
- for item in [r["ItemName"] for r in important_results]:
83
- item_results = [r for r in important_results if r["ItemName"] == item]
84
- if not item_results:
85
- continue
86
- total_item = len(item_results)
87
- status_counts_item = pd.Series([r["判定"] for r in item_results]).value_counts().reindex(
88
  ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
89
  )
90
- status_ratio_item = (status_counts_item / total_item * 100).round(1)
91
- for s, c, r in zip(status_counts_item.index, status_counts_item.values, status_ratio_item.values):
92
- result_per_item.append({"ItemName": item, "状態": s, "件数": c, "割合(%)": r})
93
- result_df_imp_items = pd.DataFrame(result_per_item)
94
-
95
- # サマリー
96
- summary = (
97
- f" {process_name} の診断完了({start_time} {end_time})\n"
98
- + "[全項目] " + " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio.items()]) + "\n"
99
- + "[重要項目全体] " + (
100
- " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio_imp.items()])
101
- if not result_df_imp.empty else "対象データなし"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  )
103
- )
104
 
105
- result_json = json.dumps(all_results, ensure_ascii=False, indent=2,
106
- default=lambda x: x.item() if hasattr(x, "item") else x)
 
 
 
 
 
107
 
108
- return result_df_all, result_df_imp, result_df_imp_items, summary, result_json
109
 
110
- # --- Gradio用ラッパ ---
111
- def run_diagnosis(csv_file, excel_file, process_name, datetime_str, window_minutes):
112
- df = pd.read_csv(csv_file.name, header=[0, 1, 2])
113
- timestamp_col = df.iloc[:, 0]
114
- df = df.drop(df.columns[0], axis=1)
115
- df.insert(0, "timestamp", timestamp_col)
116
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
117
 
118
- thresholds_df = pd.read_excel(excel_file.name)
119
- thresholds_df["Important"] = thresholds_df["Important"].astype(str).str.upper().map({"TRUE": True, "FALSE": False})
120
- for col in ["LL", "L", "H", "HH"]:
121
- if col in thresholds_df.columns:
122
- thresholds_df[col] = pd.to_numeric(thresholds_df[col], errors="coerce")
123
-
124
- return diagnose_process_range(process_name, datetime_str, int(window_minutes), df, thresholds_df)
125
 
126
  # --- Gradio UI ---
127
  with gr.Blocks() as demo:
128
  gr.Markdown("## 閾値診断アプリ")
129
 
130
  with gr.Row():
131
- csv_input = gr.File(label="CSVファイル", type="filepath")
132
- excel_input = gr.File(label="Excel閾値テーブル", type="filepath")
133
- process_name_input = gr.Textbox(label="プロセス名", value="E018-A012_除害RO")
134
- datetime_input = gr.Textbox(label="基準日時 (例: 2025/8/1 1:05)")
135
- window_input = gr.Number(label="さかのぼり時間幅)", value=60)
 
 
136
 
137
  run_btn = gr.Button("診断実行")
138
 
 
 
 
139
  summary_out = gr.Textbox(label="診断サマリー")
140
- all_df_out = gr.DataFrame(label="全項目の状態集計結果")
141
- imp_df_out = gr.DataFrame(label="重要項目全体の状態集計結果")
142
- imp_items_out = gr.DataFrame(label="重要項目ごとの状態集計結果")
143
- json_out = gr.Code(label="JSON出力", language="json")
144
 
145
  run_btn.click(
146
- fn=run_diagnosis,
147
- inputs=[csv_input, excel_input, process_name_input, datetime_input, window_input],
148
- outputs=[all_df_out, imp_df_out, imp_items_out, summary_out, json_out],
149
  )
150
 
151
  if __name__ == "__main__":
 
1
+ import gradio as gr
2
  import pandas as pd
3
  import json
 
4
 
5
+ # --- 閾値診断関数 ---
6
  def judge_status(value, ll, l, h, hh):
7
  if pd.notna(ll) and value < ll:
8
  return "LOW-LOW"
 
15
  else:
16
  return "OK"
17
 
18
+ def diagnose_process_range(csv_file, excel_file, process_name, datetime_str, window_minutes):
 
19
  try:
20
+ # CSV読み込み(3行ヘッダー)
21
+ df = pd.read_csv(csv_file.name, header=[0, 1, 2])
22
+ timestamp_col = df.iloc[:, 0]
23
+ df = df.drop(df.columns[0], axis=1)
24
+ df.insert(0, "timestamp", timestamp_col)
25
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
26
+
27
+ # 閾値テーブル
28
+ thresholds_df = pd.read_excel(excel_file.name)
29
+ thresholds_df["Important"] = thresholds_df["Important"].astype(str).str.upper().map({"TRUE": True, "FALSE": False})
30
+ for col in ["LL", "L", "H", "HH"]:
31
+ if col in thresholds_df.columns:
32
+ thresholds_df[col] = pd.to_numeric(thresholds_df[col], errors="coerce")
33
+
34
+ # 入力日時
35
  target_time = pd.to_datetime(datetime_str)
36
+ start_time = target_time - pd.Timedelta(minutes=window_minutes)
37
+ end_time = target_time
38
+
39
+ # 対象期間の抽出
40
+ df_window = df[(df["timestamp"] >= start_time) & (df["timestamp"] <= end_time)]
41
+ if df_window.empty:
42
+ return None, None, None, f"⚠ 指定時間幅にデータが見つかりません。", "{}"
43
+
44
+ # 閾値対象
45
+ proc_thresholds = thresholds_df[thresholds_df["ProcessNo_ProcessName"] == process_name]
46
+ if proc_thresholds.empty:
47
+ return None, None, None, f"⚠ プロセス {process_name} の閾値が設定されていません。", "{}"
48
+
49
+ # 判定処理
50
+ all_results = []
51
+ for _, row in df_window.iterrows():
52
+ for _, thr in proc_thresholds.iterrows():
53
+ col_tuple = (thr["ColumnID"], thr["ItemName"], thr["ProcessNo_ProcessName"])
54
+ if col_tuple not in df.columns:
55
+ continue
56
+ value = row[col_tuple]
57
+ status = judge_status(value, thr.get("LL"), thr.get("L"), thr.get("H"), thr.get("HH"))
58
+ all_results.append({
59
+ "ItemName": thr["ItemName"],
60
+ "判定": status,
61
+ "重要項目": bool(thr.get("Important", False))
62
+ })
63
+
64
+ # 全項目集計
65
+ total = len(all_results)
66
+ status_counts = pd.Series([r["判定"] for r in all_results]).value_counts().reindex(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
68
  )
69
+ status_ratio = (status_counts / total * 100).round(1)
70
+ result_df_all = pd.DataFrame({"状態": status_counts.index, "件数": status_counts.values, "割合(%)": status_ratio.values})
71
+
72
+ # 重要項目全体集計
73
+ important_results = [r for r in all_results if r["重要項目"]]
74
+ if important_results:
75
+ total_imp = len(important_results)
76
+ status_counts_imp = pd.Series([r["判定"] for r in important_results]).value_counts().reindex(
77
+ ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
78
+ )
79
+ status_ratio_imp = (status_counts_imp / total_imp * 100).round(1)
80
+ result_df_imp = pd.DataFrame({"状態": status_counts_imp.index, "件数": status_counts_imp.values, "割合(%)": status_ratio_imp.values})
81
+ else:
82
+ result_df_imp = pd.DataFrame(columns=["状態", "件数", "割合(%)"])
83
+
84
+ # 重要項目ごと集計
85
+ result_per_item = []
86
+ for item in [r["ItemName"] for r in important_results]:
87
+ item_results = [r for r in important_results if r["ItemName"] == item]
88
+ if not item_results:
89
+ continue
90
+ total_item = len(item_results)
91
+ status_counts_item = pd.Series([r["判定"] for r in item_results]).value_counts().reindex(
92
+ ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
93
+ )
94
+ status_ratio_item = (status_counts_item / total_item * 100).round(1)
95
+ for s, c, r in zip(status_counts_item.index, status_counts_item.values, status_ratio_item.values):
96
+ result_per_item.append({"ItemName": item, "状態": s, "件数": c, "割合(%)": r})
97
+ result_df_imp_items = pd.DataFrame(result_per_item)
98
+
99
+ # サマリー
100
+ summary = (
101
+ f"✅ {process_name} の診断完了({start_time} ~ {end_time})\n"
102
+ + "[全項目] " + " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio.items()]) + "\n"
103
+ + "[重要項目全体] " + (
104
+ " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio_imp.items()])
105
+ if not result_df_imp.empty else "対象データなし"
106
+ )
107
  )
 
108
 
109
+ # --- 軽量 JSON(集計結果のみ) ---
110
+ summary_stats = {
111
+ "全項目割合": status_ratio.to_dict(),
112
+ "重要項目全体割合": status_ratio_imp.to_dict() if not result_df_imp.empty else {},
113
+ "重要項目ごと割合": result_per_item
114
+ }
115
+ result_json = json.dumps({"集計結果": summary_stats}, ensure_ascii=False, indent=2)
116
 
117
+ return result_df_all, result_df_imp, result_df_imp_items, summary, result_json
118
 
119
+ except Exception as e:
120
+ return None, None, None, f"❌ エラー: {str(e)}", "{}"
 
 
 
 
 
121
 
 
 
 
 
 
 
 
122
 
123
  # --- Gradio UI ---
124
  with gr.Blocks() as demo:
125
  gr.Markdown("## 閾値診断アプリ")
126
 
127
  with gr.Row():
128
+ csv_input = gr.File(label="CSVファイルをアップロード", type="filepath")
129
+ excel_input = gr.File(label="閾値テーブル (Excel)", type="filepath")
130
+
131
+ with gr.Row():
132
+ process_input = gr.Textbox(label="工程名例: E018-A012_除害RO)")
133
+ datetime_input = gr.Textbox(label="診断日時 (YYYY/MM/DD HH:MM)")
134
+ window_input = gr.Number(label="時間幅 (分)", value=60)
135
 
136
  run_btn = gr.Button("診断実行")
137
 
138
+ result_all = gr.Dataframe(label="全項目の状態集計結果")
139
+ result_imp = gr.Dataframe(label="重要項目全体の状態集計結果")
140
+ result_imp_items = gr.Dataframe(label="重要項目ごとの状態集計結果")
141
  summary_out = gr.Textbox(label="診断サマリー")
142
+ json_out = gr.JSON(label="集計結果 JSON")
 
 
 
143
 
144
  run_btn.click(
145
+ diagnose_process_range,
146
+ inputs=[csv_input, excel_input, process_input, datetime_input, window_input],
147
+ outputs=[result_all, result_imp, result_imp_items, summary_out, json_out]
148
  )
149
 
150
  if __name__ == "__main__":