Ken-INOUE commited on
Commit
5ac3d72
·
1 Parent(s): b6dd650

Refactor diagnosis logic and enhance Gradio UI for threshold evaluation application

Browse files
Files changed (1) hide show
  1. app.py +111 -106
app.py CHANGED
@@ -1,125 +1,130 @@
1
- import os
2
  import gradio as gr
3
  import pandas as pd
4
- import numpy as np
5
  import json
6
- from sklearn.linear_model import LinearRegression
7
-
8
- # =========================================================
9
- # データロード部分(共通)
10
- # =========================================================
11
- csv_path = r"sample.csv" # あなたのCSVパスに置き換え
12
- excel_path = r"thresholds.xlsx" # あなたの閾値テーブルExcelパスに置き換え
13
- lag_excel_path = r"lag_table.xlsx" # タイムラグ表Excelパスに置き換え
14
-
15
- # CSV(3行ヘッダー)
16
- df = pd.read_csv(csv_path, header=[0, 1, 2])
17
- timestamp_col = df.iloc[:, 0]
18
- df = df.drop(df.columns[0], axis=1)
19
- df.insert(0, "timestamp", timestamp_col)
20
- df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
21
-
22
- # 閾値テーブル
23
- thresholds_df = pd.read_excel(excel_path)
24
- thresholds_df["Important"] = thresholds_df["Important"].astype(str).str.upper().map({"TRUE": True, "FALSE": False})
25
- for col in ["LL", "L", "H", "HH"]:
26
- if col in thresholds_df.columns:
27
- thresholds_df[col] = pd.to_numeric(thresholds_df[col], errors="coerce")
28
-
29
- # タイムラグ表
30
- lag_matrix = pd.read_excel(lag_excel_path, index_col=0)
31
-
32
-
33
- # =========================================================
34
- # 閾値診断関数
35
- # =========================================================
36
- def diagnose(process_name, datetime_str, window_minutes):
 
 
 
37
  try:
38
  target_time = pd.to_datetime(datetime_str)
39
  except Exception:
40
- return None, "⚠ 日時が不正です", None
 
 
 
 
 
41
 
42
- start_time = target_time - pd.Timedelta(minutes=int(window_minutes))
43
- df_window = df[(df["timestamp"] >= start_time) & (df["timestamp"] <= target_time)]
44
  if df_window.empty:
45
- return None, "⚠ データなし", None
46
 
 
47
  proc_thresholds = thresholds_df[thresholds_df["ProcessNo_ProcessName"] == process_name]
48
- results = []
49
- for _, thr in proc_thresholds.iterrows():
50
- col_tuple = (thr["ColumnID"], thr["ItemName"], thr["ProcessNo_ProcessName"])
51
- if col_tuple not in df.columns:
52
- continue
53
- series = df_window[col_tuple].dropna()
54
- if series.empty:
55
- continue
56
-
57
- value = series.iloc[-1]
58
- status = "OK"
59
- if pd.notna(thr["LL"]) and value < thr["LL"]:
60
- status = "LOW-LOW"
61
- elif pd.notna(thr["L"]) and value < thr["L"]:
62
- status = "LOW"
63
- elif pd.notna(thr["HH"]) and value > thr["HH"]:
64
- status = "HIGH-HIGH"
65
- elif pd.notna(thr["H"]) and value > thr["H"]:
66
- status = "HIGH"
67
-
68
- results.append({
69
- "ItemName": thr["ItemName"],
70
- "値": value,
71
- "判定": status,
72
- "重要項目": bool(thr.get("Important", False))
73
- })
74
-
75
- result_df = pd.DataFrame(results)
76
- result_json = json.dumps(results, ensure_ascii=False, indent=2, default=lambda x: x.item() if hasattr(x, "item") else x)
77
- summary = f"✅ {process_name} の診断完了({start_time}~{target_time})"
78
- return result_df, summary, result_json
79
-
80
-
81
- # =========================================================
82
- # Gradio UI 構築
83
- # =========================================================
 
 
 
 
 
 
 
 
 
 
 
 
84
  with gr.Blocks() as demo:
85
- gr.Markdown("## 水処理データ解析アプリ")
86
 
87
- with gr.Tab("閾値診断アプリ"):
88
- process_in = gr.Textbox(label="プロセス名", value="E018-A012_除害RO")
89
- datetime_in = gr.Textbox(label="基準日時 (例: 2025/8/1 0:05)", value="2025/8/1 0:05")
90
- window_in = gr.Number(label="時間幅(分)", value=60)
91
 
92
- run_btn = gr.Button("診断実行")
93
- summary_out = gr.Textbox(label="サマリー")
94
- table_out = gr.Dataframe(label="診断結果", interactive=False)
95
- json_out = gr.Textbox(label="JSON出力")
96
 
97
- def run_diagnose(process, dt, win):
98
- df_out, summary, js = diagnose(process, dt, win)
99
- return summary, df_out, js
100
 
101
- run_btn.click(
102
- fn=run_diagnose,
103
- inputs=[process_in, datetime_in, window_in],
104
- outputs=[summary_out, table_out, json_out]
105
- )
106
 
 
 
 
 
 
107
 
108
- # =========================================================
109
- # 実行部分
110
- # =========================================================
111
  if __name__ == "__main__":
112
- if os.getenv("USE_MCP", "0") == "1":
113
- # Hugging Face 用: MCP + UI
114
- demo.launch(
115
- mcp_server=True,
116
- server_name="0.0.0.0",
117
- share=False
118
- )
119
  else:
120
- # ローカル用: UI のみ
121
- demo.launch(
122
- server_name="0.0.0.0",
123
- share=False,
124
- ssr_mode=False
125
- )
 
 
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"
9
+ elif pd.notna(l) and value < l:
10
+ return "LOW"
11
+ elif pd.notna(hh) and value > hh:
12
+ return "HIGH-HIGH"
13
+ elif pd.notna(h) and value > h:
14
+ return "HIGH"
15
+ else:
16
+ return "OK"
17
+
18
+ # --- 診断処理 ---
19
+ def diagnose_app(csv_file, excel_file, process_name, datetime_str, window_minutes):
20
+ if csv_file is None or excel_file is None:
21
+ return "⚠ CSVとExcelファイルを両方アップロードしてください", None, None
22
+
23
+ # CSV読み込み(3行ヘッダー維持)
24
+ df = pd.read_csv(csv_file.name, header=[0, 1, 2])
25
+ timestamp_col = df.iloc[:, 0]
26
+ df = df.drop(df.columns[0], axis=1)
27
+ df.insert(0, "timestamp", timestamp_col)
28
+ df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")
29
+
30
+ # 閾値テーブル読み込み
31
+ thresholds_df = pd.read_excel(excel_file.name)
32
+ thresholds_df["Important"] = thresholds_df["Important"].astype(str).str.upper().map({"TRUE": True, "FALSE": False})
33
+ for col in ["LL", "L", "H", "HH"]:
34
+ if col in thresholds_df.columns:
35
+ thresholds_df[col] = pd.to_numeric(thresholds_df[col], errors="coerce")
36
+
37
+ # 入力日時
38
  try:
39
  target_time = pd.to_datetime(datetime_str)
40
  except Exception:
41
+ return f"⚠ 入力した日時 {datetime_str} 無効です", None, None
42
+
43
+ # 時間幅のデータ抽出
44
+ start_time = target_time - pd.Timedelta(minutes=window_minutes)
45
+ end_time = target_time
46
+ df_window = df[(df["timestamp"] >= start_time) & (df["timestamp"] <= end_time)]
47
 
 
 
48
  if df_window.empty:
49
+ return "⚠ 指定した時間幅にデータがありません", None, None
50
 
51
+ # プロセスの閾値行を抽出
52
  proc_thresholds = thresholds_df[thresholds_df["ProcessNo_ProcessName"] == process_name]
53
+ if proc_thresholds.empty:
54
+ return f"⚠ プロセス {process_name} の閾��が見つかりません", None, None
55
+
56
+ # 判定実行
57
+ all_results = []
58
+ for _, row in df_window.iterrows():
59
+ for _, thr in proc_thresholds.iterrows():
60
+ col_tuple = (thr["ColumnID"], thr["ItemName"], thr["ProcessNo_ProcessName"])
61
+ if col_tuple not in df.columns:
62
+ continue
63
+ value = row[col_tuple]
64
+ status = judge_status(value, thr.get("LL"), thr.get("L"), thr.get("H"), thr.get("HH"))
65
+ all_results.append({
66
+ "ColumnID": thr["ColumnID"],
67
+ "ItemName": thr["ItemName"],
68
+ "": value,
69
+ "判定": status,
70
+ "重要項目": bool(thr.get("Important", False)),
71
+ "時刻": row["timestamp"]
72
+ })
73
+
74
+ # 集計(件数と割合)
75
+ total = len(all_results)
76
+ if total == 0:
77
+ return f" プロセス {process_name} の診断結果なし", None, None
78
+
79
+ status_counts = pd.Series([r["判定"] for r in all_results]).value_counts().reindex(
80
+ ["LOW-LOW", "LOW", "OK", "HIGH", "HIGH-HIGH"], fill_value=0
81
+ )
82
+ status_ratio = (status_counts / total * 100).round(1)
83
+
84
+ result_df = pd.DataFrame({
85
+ "状態": status_counts.index,
86
+ "件数": status_counts.values,
87
+ "割合(%)": status_ratio.values
88
+ })
89
+
90
+ summary = (
91
+ f"✅ {process_name} の診断完了({start_time} ~ {end_time})\n"
92
+ + " / ".join([f"{s}:{r:.1f}%" for s, r in status_ratio.items()])
93
+ )
94
+
95
+ result_json = json.dumps(all_results, ensure_ascii=False, indent=2, default=str)
96
+
97
+ return summary, result_df, result_json
98
+
99
+
100
+ # --- Gradio UI ---
101
  with gr.Blocks() as demo:
102
+ gr.Markdown("## 閾値診断アプリ")
103
 
104
+ with gr.Row():
105
+ csv_input = gr.File(label="CSVファイルをアップロード", type="file")
106
+ excel_input = gr.File(label="閾値テーブルをアップロード", type="file")
 
107
 
108
+ process_name = gr.Textbox(label="プロセス名", placeholder="例: E018-A012_除害RO")
109
+ datetime_str = gr.Textbox(label="基準日時 (例: 2025/8/1 0:05)")
110
+ window_minutes = gr.Number(label="さかのぼり時間幅 (分)", value=60)
 
111
 
112
+ run_btn = gr.Button("診断実行")
 
 
113
 
114
+ summary_out = gr.Textbox(label="サマリー")
115
+ table_out = gr.Dataframe(label="診断結果(集計表)")
116
+ json_out = gr.JSON(label="JSON出力")
 
 
117
 
118
+ run_btn.click(
119
+ diagnose_app,
120
+ inputs=[csv_input, excel_input, process_name, datetime_str, window_minutes],
121
+ outputs=[summary_out, table_out, json_out]
122
+ )
123
 
 
 
 
124
  if __name__ == "__main__":
125
+ import os
126
+ # Hugging Face / ローカル両対応
127
+ if os.getenv("SPACE_ID"):
128
+ demo.launch(server_name="0.0.0.0")
 
 
 
129
  else:
130
+ demo.launch()