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

Update diagnosis function and Gradio UI for improved clarity and error handling

Browse files
Files changed (1) hide show
  1. app.py +52 -54
app.py CHANGED
@@ -2,7 +2,7 @@ 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,38 +15,37 @@ def judge_status(value, ll, l, h, hh):
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():
@@ -61,63 +60,59 @@ def diagnose_process_range(csv_file, excel_file, process_name, datetime_str, win
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 ---
@@ -126,26 +121,29 @@ with gr.Blocks() as demo:
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__":
151
- demo.launch(server_name="0.0.0.0", share=False, ssr_mode=False)
 
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
+ # --- 閾値診断処理 ---
19
  def diagnose_process_range(csv_file, excel_file, process_name, datetime_str, window_minutes):
20
  try:
21
+ # CSV読み込み
22
  df = pd.read_csv(csv_file.name, header=[0, 1, 2])
23
  timestamp_col = df.iloc[:, 0]
24
  df = df.drop(df.columns[0], axis=1)
25
  df.insert(0, "timestamp", timestamp_col)
26
  df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
27
 
28
+ # 閾値テーブル読み込み
29
  thresholds_df = pd.read_excel(excel_file.name)
30
  thresholds_df["Important"] = thresholds_df["Important"].astype(str).str.upper().map({"TRUE": True, "FALSE": False})
31
  for col in ["LL", "L", "H", "HH"]:
32
  if col in thresholds_df.columns:
33
  thresholds_df[col] = pd.to_numeric(thresholds_df[col], errors="coerce")
34
 
35
+ # --- 入力日時処理 ---
36
  target_time = pd.to_datetime(datetime_str)
37
  start_time = target_time - pd.Timedelta(minutes=window_minutes)
38
  end_time = target_time
 
 
39
  df_window = df[(df["timestamp"] >= start_time) & (df["timestamp"] <= end_time)]
40
+
41
  if df_window.empty:
42
+ return None, "⚠ 指定時間幅にデータなし", None
43
 
 
44
  proc_thresholds = thresholds_df[thresholds_df["ProcessNo_ProcessName"] == process_name]
45
  if proc_thresholds.empty:
46
+ return None, f"⚠ プロセス {process_name} の閾値がありません", None
47
 
48
+ # --- 判定処理 ---
49
  all_results = []
50
  for _, row in df_window.iterrows():
51
  for _, thr in proc_thresholds.iterrows():
 
60
  "重要項目": bool(thr.get("Important", False))
61
  })
62
 
63
+ if not all_results:
64
+ return None, f"⚠ データ不足", None
65
+
66
+ # --- 集計(全項目) ---
67
  status_counts = pd.Series([r["判定"] for r in all_results]).value_counts().reindex(
68
  ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
69
  )
70
+ status_ratio = (status_counts / len(all_results) * 100).round(1)
71
  result_df_all = pd.DataFrame({"状態": status_counts.index, "件数": status_counts.values, "割合(%)": status_ratio.values})
72
 
73
+ # --- 集計(重要項目全体) ---
74
  important_results = [r for r in all_results if r["重要項目"]]
75
  if 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 / len(important_results) * 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 set([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
  status_counts_item = pd.Series([r["判定"] for r in item_results]).value_counts().reindex(
91
  ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
92
  )
93
+ status_ratio_item = (status_counts_item / len(item_results) * 100).round(1)
94
  for s, c, r in zip(status_counts_item.index, status_counts_item.values, status_ratio_item.values):
95
+ result_per_item.append({"ItemName": item, "状態": s, "件数": int(c), "割合(%)": float(r)})
96
  result_df_imp_items = pd.DataFrame(result_per_item)
97
 
98
+ # --- JSON出力(集計のみ) ---
 
 
 
 
 
 
 
 
 
 
99
  summary_stats = {
100
+ "全項目割合": {k: float(v) for k, v in status_ratio.to_dict().items()},
101
+ "重要項目全体割合": {k: float(v) for k, v in status_ratio_imp.to_dict().items()} if not result_df_imp.empty else {},
102
  "重要項目ごと割合": result_per_item
103
  }
104
  result_json = json.dumps({"集計結果": summary_stats}, ensure_ascii=False, indent=2)
105
 
106
+ # --- サマリー ---
107
+ summary = (
108
+ f"✅ {process_name} の診断完了({start_time} ~ {end_time})\n"
109
+ + "[全項目] " + " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio.items()])
110
+ )
111
+
112
  return result_df_all, result_df_imp, result_df_imp_items, summary, result_json
113
 
114
  except Exception as e:
115
+ return None, None, None, f"❌ エラー: {str(e)}", None
116
 
117
 
118
  # --- Gradio UI ---
 
121
 
122
  with gr.Row():
123
  csv_input = gr.File(label="CSVファイルをアップロード", type="filepath")
124
+ excel_input = gr.File(label="閾値テーブルExcelをアップロード", type="filepath")
125
 
126
+ process_name = gr.Textbox(label="プロセス名", value="E018-A012_除害RO")
127
+ datetime_str = gr.Textbox(label="日時 (例: 2025/8/1 1:05)")
128
+ window_minutes = gr.Number(label="さかのぼる間幅 ()", value=60)
129
+
130
+ run_button = gr.Button("診断を実行")
131
+
132
+ with gr.Tab("全項目集計"):
133
+ result_all = gr.Dataframe(label="全項目の状態集計結果")
134
+ with gr.Tab("重要項目全体集計"):
135
+ result_imp = gr.Dataframe(label="重要項目全体の状態集計結果")
136
+ with gr.Tab("重要項目ごと集計"):
137
+ result_imp_items = gr.Dataframe(label="重要項目ごとの状態集計結果")
138
+
139
+ summary_output = gr.Textbox(label="サマリー")
140
+ json_output = gr.JSON(label="JSON出力")
141
+
142
+ run_button.click(
143
+ fn=diagnose_process_range,
144
+ inputs=[csv_input, excel_input, process_name, datetime_str, window_minutes],
145
+ outputs=[result_all, result_imp, result_imp_items, summary_output, json_output]
146
  )
147
 
148
  if __name__ == "__main__":
149
+ demo.launch(server_name="0.0.0.0", share=False)