cwadayi commited on
Commit
951dff8
·
verified ·
1 Parent(s): c8c480c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -100
app.py CHANGED
@@ -6,9 +6,9 @@ from dateutil import tz
6
  import gradio as gr
7
  import pandas as pd
8
  import matplotlib.pyplot as plt
9
- import numpy as np # 隨機示範資料
 
10
 
11
- # grafanalib:用來「定義」Grafana Dashboard JSON(不在這裡畫圖)
12
  from grafanalib.core import (
13
  Dashboard, Graph, Row, Target, YAxis, YAxes, Time
14
  )
@@ -20,34 +20,29 @@ TAIPEI = tz.gettz("Asia/Taipei")
20
  # Demo / Data loading
21
  # -----------------------------
22
  def make_demo_dataframe() -> pd.DataFrame:
23
- """產生隨機示範資料(1 小時、每分鐘一筆),每次呼叫都不同"""
24
  t0 = datetime.now(tz=TAIPEI) - timedelta(minutes=60)
25
  times = [t0 + timedelta(minutes=i) for i in range(61)]
26
- amp = np.random.rand(len(times)) # 0~1 浮點數
27
- cnt = np.random.randint(0, 11, size=len(times)) # 0~10 整數
28
- df = pd.DataFrame({"time": times, "amplitude": amp, "count": cnt})
29
- return df
30
 
31
 
32
  def load_csv(file: gr.File | None) -> pd.DataFrame:
33
- """讀 CSV(需含時間欄);若未上傳則回傳隨機示範資料。"""
34
  if file is None:
35
  df = make_demo_dataframe()
36
  else:
37
  df = pd.read_csv(file.name)
38
- time_col_candidates = ["time", "timestamp", "datetime", "date"]
39
- time_col = next((c for c in time_col_candidates if c in df.columns), None)
40
  if time_col is None:
41
  raise ValueError("CSV 檔需包含時間欄位,例如:time / timestamp / datetime / date")
42
  df[time_col] = pd.to_datetime(df[time_col])
43
- df.rename(columns={time_col: "time"}, inplace=True)
44
-
45
- # 統一時區
46
  if getattr(df["time"].dt, "tz", None) is None:
47
  df["time"] = df["time"].dt.tz_localize(TAIPEI)
48
  else:
49
  df["time"] = df["time"].dt.tz_convert(TAIPEI)
50
-
51
  return df.sort_values("time").reset_index(drop=True)
52
 
53
 
@@ -55,18 +50,11 @@ def load_csv(file: gr.File | None) -> pd.DataFrame:
55
  # grafanalib JSON builder
56
  # -----------------------------
57
  def build_grafanalib_dashboard(series_columns: list[str], dual_axis: bool, rolling_window: int) -> dict:
58
- """
59
- 產生 Grafana Dashboard JSON:
60
- - Panel 1:Line(第一個數值欄位)
61
- - Panel 2:Bar 或 Mixed(若 dual_axis=True 則同面板加上第一欄折線)
62
- - Panel 3:Line(第一個數值欄位的 rolling mean)
63
- """
64
  panels = []
65
 
66
- # Panel 1:折線
67
  panels.append(
68
  Graph(
69
- title=f"Time Series - {series_columns[0]}",
70
  dataSource="(example)",
71
  targets=[Target(expr=f"{series_columns[0]}", legendFormat=series_columns[0])],
72
  lines=True, bars=False, points=False,
@@ -74,19 +62,13 @@ def build_grafanalib_dashboard(series_columns: list[str], dual_axis: bool, rolli
74
  )
75
  )
76
 
77
- # Panel 2:柱狀(若有第二欄);dual_axis=True 時,同面板加入第一欄折線
78
  if len(series_columns) > 1:
79
  targets = [Target(expr=f"{series_columns[1]}", legendFormat=series_columns[1])]
80
- lines = False
81
- bars = True
82
- title = f"Event Count (Bar) - {series_columns[1]}"
83
-
84
  if dual_axis:
85
  targets.append(Target(expr=f"{series_columns[0]}", legendFormat=f"{series_columns[0]} (line)"))
86
- lines = True
87
- bars = True
88
- title = f"Bar+Line (Mixed) - {series_columns[1]} / {series_columns[0]}"
89
-
90
  panels.append(
91
  Graph(
92
  title=title,
@@ -97,10 +79,9 @@ def build_grafanalib_dashboard(series_columns: list[str], dual_axis: bool, rolli
97
  )
98
  )
99
 
100
- # Panel 3:Rolling Mean(第一欄)
101
  panels.append(
102
  Graph(
103
- title=f"Rolling Mean ({rolling_window}) - {series_columns[0]}",
104
  dataSource="(example)",
105
  targets=[Target(expr=f"{series_columns[0]}_rolling{rolling_window}",
106
  legendFormat=f"{series_columns[0]}_rolling{rolling_window}")],
@@ -109,52 +90,72 @@ def build_grafanalib_dashboard(series_columns: list[str], dual_axis: bool, rolli
109
  )
110
  )
111
 
112
- dash = Dashboard(
113
- title="Seismology Demo Dashboard (grafanalib + Gradio)",
114
  rows=[Row(panels=panels)],
115
  timezone="browser",
116
  time=Time("now-1h", "now"),
117
- )
118
- return dash.to_json_data()
119
 
120
 
121
  # -----------------------------
122
- # Matplotlib rendering
123
  # -----------------------------
 
 
 
 
 
 
 
 
 
 
 
 
124
  def render_line(df: pd.DataFrame, col: str):
125
- fig = plt.figure()
126
- plt.plot(df["time"], df[col])
127
- plt.title(f"Time Series - {col}")
128
- plt.xlabel("Time")
129
- plt.ylabel(col)
130
- plt.xticks(rotation=20)
131
- plt.tight_layout()
132
  return fig
133
 
134
 
 
 
 
 
 
 
 
 
 
135
  def render_bar_or_dual(df: pd.DataFrame, second_col: str, first_col: str, dual_axis: bool):
136
- fig, ax = plt.subplots()
137
- ax.bar(df["time"], df[second_col], label=second_col)
138
- ax.set_xlabel("Time")
139
- ax.set_ylabel(second_col)
140
- title = f"Event Count (Bar) - {second_col}"
 
 
141
 
142
  if dual_axis:
143
  ax2 = ax.twinx()
144
- ax2.plot(df["time"], df[first_col], label=f"{first_col} (line)")
145
- ax2.set_ylabel(first_col)
146
- title = f"Bar+Line (Dual Axis) - {second_col} / {first_col}"
147
-
148
- # 合併圖例
149
  h1, l1 = ax.get_legend_handles_labels()
150
  h2, l2 = ax2.get_legend_handles_labels()
151
- ax.legend(h1 + h2, l1 + l2, loc="upper left")
152
  else:
153
- ax.legend(loc="upper left")
154
 
155
- plt.title(title)
156
- plt.xticks(rotation=20)
157
- plt.tight_layout()
158
  return fig
159
 
160
 
@@ -162,13 +163,13 @@ def render_rolling(df: pd.DataFrame, col: str, window: int = 5):
162
  roll_col = f"{col}_rolling{window}"
163
  if roll_col not in df.columns:
164
  df[roll_col] = df[col].rolling(window=window, min_periods=1).mean()
165
- fig = plt.figure()
166
- plt.plot(df["time"], df[roll_col])
167
- plt.title(f"Rolling Mean ({window}) - {col}")
168
- plt.xlabel("Time")
169
- plt.ylabel(roll_col)
170
- plt.xticks(rotation=20)
171
- plt.tight_layout()
172
  return fig, df
173
 
174
 
@@ -178,43 +179,32 @@ def render_rolling(df: pd.DataFrame, col: str, window: int = 5):
178
  def pipeline(file, series_choice, dual_axis, rolling_window):
179
  """
180
  1) 讀 CSV(或示範)
181
- 2) 決定欄位(第一欄做折線+rolling;第二欄做柱狀或雙軸混合)
182
  3) 產出 grafanalib Dashboard JSON
183
  4) 回傳三張圖、dashboard.json 路徑、資料表(含 rolling)、demo.csv 路徑
184
  """
185
  df = load_csv(file)
186
-
187
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
188
  if not numeric_cols:
189
  raise ValueError("未找到可繪圖的數值欄位。請提供至少一個數值欄位(除了 time 以外)。")
190
 
191
- chosen = series_choice or numeric_cols[:2]
192
- chosen = [c for c in chosen if c in numeric_cols]
193
  if not chosen:
194
  chosen = numeric_cols[:2]
195
 
196
  # Dashboard JSON
197
  dash_json = build_grafanalib_dashboard(chosen, bool(dual_axis), int(rolling_window))
198
  dash_json_str = json.dumps(dash_json, ensure_ascii=False, indent=2, default=str)
199
-
200
- # 寫入暫存檔給 gr.File
201
  with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w", encoding="utf-8") as f:
202
  f.write(dash_json_str)
203
  json_path = f.name
204
 
205
- # 圖形
206
  fig1 = render_line(df, chosen[0])
207
- if len(chosen) > 1:
208
- fig2 = render_bar_or_dual(df, chosen[1], chosen[0], bool(dual_axis))
209
- else:
210
- fig2 = plt.figure()
211
- plt.text(0.5, 0.5, "第二張圖:未選第二數值欄", ha="center", va="center", fontsize=12)
212
- plt.axis("off")
213
- plt.tight_layout()
214
-
215
  fig3, df_with_roll = render_rolling(df.copy(), chosen[0], window=int(rolling_window))
216
 
217
- # 產生 demo CSV 檔案(每次呼叫都更新內容 → 下載也會不同)
218
  demo_df = make_demo_dataframe()
219
  with tempfile.NamedTemporaryFile(delete=False, suffix=".csv", mode="w", encoding="utf-8") as f:
220
  demo_df.to_csv(f, index=False)
@@ -223,11 +213,19 @@ def pipeline(file, series_choice, dual_axis, rolling_window):
223
  return fig1, fig2, fig3, dash_json_str, json_path, df_with_roll, demo_csv_path
224
 
225
 
 
 
 
 
 
 
 
 
226
  # -----------------------------
227
  # Regenerate demo helper
228
  # -----------------------------
229
  def regenerate_demo(series_choice, dual_axis, rolling_window):
230
- """忽略上傳檔案,直接用『全新隨機示範資料』跑一次。"""
231
  return pipeline(file=None, series_choice=series_choice, dual_axis=dual_axis, rolling_window=rolling_window)
232
 
233
 
@@ -236,14 +234,14 @@ def regenerate_demo(series_choice, dual_axis, rolling_window):
236
  # -----------------------------
237
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
238
  gr.Markdown(
239
- "## grafanalib + Gradio 隨機示範\n"
240
- "🔁 你可以點「**重新產生示範資料**」來快速換一批**全新隨機**數據。"
241
  )
242
 
243
  with gr.Row():
244
  file_in = gr.File(label="上傳 CSV(可空,會用隨機示範資料)", file_types=[".csv"])
245
  series_multiselect = gr.CheckboxGroup(
246
- label="數值欄位選擇(第一欄→折線與 Rolling;第二欄→柱狀/雙軸,選配)",
247
  choices=[]
248
  )
249
 
@@ -253,12 +251,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
253
 
254
  with gr.Row():
255
  run_btn = gr.Button("用『上傳/當前資料』產生 Dashboard")
256
- regen_btn = gr.Button("🔁 重新產生示範資料(隨機)") # ← 新增
257
 
258
  with gr.Row():
259
- plot1 = gr.Plot(label="1:Line")
260
- plot2 = gr.Plot(label="圖 2:Bar / Dual Axis")
261
- plot3 = gr.Plot(label="圖 3Rolling Mean")
 
 
262
 
263
  json_box = gr.Code(label="grafanalib Dashboard JSON", language="json")
264
  json_file = gr.File(label="下載 dashboard.json")
@@ -269,14 +269,9 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
269
  def probe_columns(file):
270
  df = load_csv(file)
271
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
272
- default_select = numeric_cols[:2]
273
- return gr.CheckboxGroup(choices=numeric_cols, value=default_select), df
274
 
275
- file_in.change(
276
- probe_columns,
277
- inputs=[file_in],
278
- outputs=[series_multiselect, df_view],
279
- )
280
 
281
  # 初次載入:以隨機示範資料跑一次並更新欄位清單
282
  def initial_load():
@@ -284,10 +279,9 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
284
  file=None, series_choice=[], dual_axis=False, rolling_window="5"
285
  )
286
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
287
- default_select = numeric_cols[:2]
288
  return (
289
  f1, f2, f3, dash_json, json_path, df, demo_csv_path,
290
- gr.CheckboxGroup(choices=numeric_cols, value=default_select),
291
  False, "5"
292
  )
293
 
 
6
  import gradio as gr
7
  import pandas as pd
8
  import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ import matplotlib.dates as mdates # 時間軸格式化
11
 
 
12
  from grafanalib.core import (
13
  Dashboard, Graph, Row, Target, YAxis, YAxes, Time
14
  )
 
20
  # Demo / Data loading
21
  # -----------------------------
22
  def make_demo_dataframe() -> pd.DataFrame:
23
+ """隨機示範資料:1 小時、每分鐘 1 筆;每次呼叫都不同"""
24
  t0 = datetime.now(tz=TAIPEI) - timedelta(minutes=60)
25
  times = [t0 + timedelta(minutes=i) for i in range(61)]
26
+ amp = np.random.rand(len(times)) # 0~1 浮點數
27
+ cnt = np.random.randint(0, 11, size=len(times)) # 0~10 整數
28
+ return pd.DataFrame({"time": times, "amplitude": amp, "count": cnt})
 
29
 
30
 
31
  def load_csv(file: gr.File | None) -> pd.DataFrame:
32
+ """讀 CSV(需含時間欄);若未上傳則回傳隨機示範資料並排序、統一時區。"""
33
  if file is None:
34
  df = make_demo_dataframe()
35
  else:
36
  df = pd.read_csv(file.name)
37
+ time_col = next((c for c in ["time", "timestamp", "datetime", "date"] if c in df.columns), None)
 
38
  if time_col is None:
39
  raise ValueError("CSV 檔需包含時間欄位,例如:time / timestamp / datetime / date")
40
  df[time_col] = pd.to_datetime(df[time_col])
41
+ df = df.rename(columns={time_col: "time"})
 
 
42
  if getattr(df["time"].dt, "tz", None) is None:
43
  df["time"] = df["time"].dt.tz_localize(TAIPEI)
44
  else:
45
  df["time"] = df["time"].dt.tz_convert(TAIPEI)
 
46
  return df.sort_values("time").reset_index(drop=True)
47
 
48
 
 
50
  # grafanalib JSON builder
51
  # -----------------------------
52
  def build_grafanalib_dashboard(series_columns: list[str], dual_axis: bool, rolling_window: int) -> dict:
 
 
 
 
 
 
53
  panels = []
54
 
 
55
  panels.append(
56
  Graph(
57
+ title=f"{series_columns[0]}",
58
  dataSource="(example)",
59
  targets=[Target(expr=f"{series_columns[0]}", legendFormat=series_columns[0])],
60
  lines=True, bars=False, points=False,
 
62
  )
63
  )
64
 
 
65
  if len(series_columns) > 1:
66
  targets = [Target(expr=f"{series_columns[1]}", legendFormat=series_columns[1])]
67
+ lines, bars, title = False, True, f"{series_columns[1]} (bar)"
 
 
 
68
  if dual_axis:
69
  targets.append(Target(expr=f"{series_columns[0]}", legendFormat=f"{series_columns[0]} (line)"))
70
+ lines, bars = True, True
71
+ title = f"{series_columns[1]} (bar) + {series_columns[0]} (line)"
 
 
72
  panels.append(
73
  Graph(
74
  title=title,
 
79
  )
80
  )
81
 
 
82
  panels.append(
83
  Graph(
84
+ title=f"{series_columns[0]} rolling({rolling_window})",
85
  dataSource="(example)",
86
  targets=[Target(expr=f"{series_columns[0]}_rolling{rolling_window}",
87
  legendFormat=f"{series_columns[0]}_rolling{rolling_window}")],
 
90
  )
91
  )
92
 
93
+ return Dashboard(
94
+ title="Grafana-like Demo (grafanalib + Gradio)",
95
  rows=[Row(panels=panels)],
96
  timezone="browser",
97
  time=Time("now-1h", "now"),
98
+ ).to_json_data()
 
99
 
100
 
101
  # -----------------------------
102
+ # Matplotlib helpers(統一外觀)
103
  # -----------------------------
104
+ def _style_time_axis(ax):
105
+ """時間軸:精簡刻度 + 自動格式化,適合手機寬度"""
106
+ locator = mdates.AutoDateLocator(minticks=3, maxticks=6)
107
+ formatter = mdates.ConciseDateFormatter(locator)
108
+ ax.xaxis.set_major_locator(locator)
109
+ ax.xaxis.set_major_formatter(formatter)
110
+ ax.tick_params(axis="x", labelrotation=20, labelsize=9)
111
+ ax.tick_params(axis="y", labelsize=9)
112
+ ax.grid(True, which="major", alpha=0.25)
113
+ plt.margins(x=0.02, y=0.05) # 留一點白邊
114
+
115
+
116
  def render_line(df: pd.DataFrame, col: str):
117
+ fig, ax = plt.subplots(figsize=(6.5, 3.6))
118
+ ax.plot(df["time"], df[col], linewidth=1.6)
119
+ ax.set_title(col, fontsize=12, pad=8)
120
+ ax.set_xlabel("Time", fontsize=10)
121
+ ax.set_ylabel(col, fontsize=10)
122
+ _style_time_axis(ax)
123
+ fig.tight_layout()
124
  return fig
125
 
126
 
127
+ def _infer_bar_width_days(times: pd.Series) -> float:
128
+ """依時間間距估算柱寬(單位:天)。避免整片藍牆。"""
129
+ if len(times) < 2:
130
+ return 60 / 86400 # 60秒
131
+ deltas = (times.iloc[1:].values - times.iloc[:-1].values) / np.timedelta64(1, 's')
132
+ med = np.median(deltas) if len(deltas) else 60
133
+ return max(10, med * 0.8) / 86400.0 # 取 80% 的中位數,至少 10 秒
134
+
135
+
136
  def render_bar_or_dual(df: pd.DataFrame, second_col: str, first_col: str, dual_axis: bool):
137
+ fig, ax = plt.subplots(figsize=(6.5, 3.6))
138
+ width = _infer_bar_width_days(df["time"])
139
+ x = mdates.date2num(df["time"].to_pydatetime())
140
+ ax.bar(x, df[second_col], width=width, align="center", label=second_col)
141
+ ax.set_xlabel("Time", fontsize=10)
142
+ ax.set_ylabel(second_col, fontsize=10)
143
+ title = f"{second_col} (bar)"
144
 
145
  if dual_axis:
146
  ax2 = ax.twinx()
147
+ ax2.plot(df["time"], df[first_col], linewidth=1.6, label=f"{first_col} (line)")
148
+ ax2.set_ylabel(first_col, fontsize=10)
149
+ title = f"{second_col} (bar) + {first_col} (line)"
 
 
150
  h1, l1 = ax.get_legend_handles_labels()
151
  h2, l2 = ax2.get_legend_handles_labels()
152
+ ax.legend(h1 + h2, l1 + l2, loc="upper left", fontsize=9)
153
  else:
154
+ ax.legend(loc="upper left", fontsize=9)
155
 
156
+ ax.set_title(title, fontsize=12, pad=8)
157
+ _style_time_axis(ax)
158
+ fig.tight_layout()
159
  return fig
160
 
161
 
 
163
  roll_col = f"{col}_rolling{window}"
164
  if roll_col not in df.columns:
165
  df[roll_col] = df[col].rolling(window=window, min_periods=1).mean()
166
+ fig, ax = plt.subplots(figsize=(6.5, 3.6))
167
+ ax.plot(df["time"], df[roll_col], linewidth=1.6)
168
+ ax.set_title(f"{col} rolling({window})", fontsize=12, pad=8)
169
+ ax.set_xlabel("Time", fontsize=10)
170
+ ax.set_ylabel(roll_col, fontsize=10)
171
+ _style_time_axis(ax)
172
+ fig.tight_layout()
173
  return fig, df
174
 
175
 
 
179
  def pipeline(file, series_choice, dual_axis, rolling_window):
180
  """
181
  1) 讀 CSV(或示範)
182
+ 2) 第一欄→折線與 rolling;第二欄→柱狀/雙軸
183
  3) 產出 grafanalib Dashboard JSON
184
  4) 回傳三張圖、dashboard.json 路徑、資料表(含 rolling)、demo.csv 路徑
185
  """
186
  df = load_csv(file)
 
187
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
188
  if not numeric_cols:
189
  raise ValueError("未找到可繪圖的數值欄位。請提供至少一個數值欄位(除了 time 以外)。")
190
 
191
+ chosen = [c for c in (series_choice or numeric_cols[:2]) if c in numeric_cols]
 
192
  if not chosen:
193
  chosen = numeric_cols[:2]
194
 
195
  # Dashboard JSON
196
  dash_json = build_grafanalib_dashboard(chosen, bool(dual_axis), int(rolling_window))
197
  dash_json_str = json.dumps(dash_json, ensure_ascii=False, indent=2, default=str)
 
 
198
  with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w", encoding="utf-8") as f:
199
  f.write(dash_json_str)
200
  json_path = f.name
201
 
202
+ # Figures
203
  fig1 = render_line(df, chosen[0])
204
+ fig2 = render_bar_or_dual(df, chosen[1], chosen[0], bool(dual_axis)) if len(chosen) > 1 else _placeholder_fig()
 
 
 
 
 
 
 
205
  fig3, df_with_roll = render_rolling(df.copy(), chosen[0], window=int(rolling_window))
206
 
207
+ # demo.csv(每次都用全新隨機)
208
  demo_df = make_demo_dataframe()
209
  with tempfile.NamedTemporaryFile(delete=False, suffix=".csv", mode="w", encoding="utf-8") as f:
210
  demo_df.to_csv(f, index=False)
 
213
  return fig1, fig2, fig3, dash_json_str, json_path, df_with_roll, demo_csv_path
214
 
215
 
216
+ def _placeholder_fig():
217
+ fig, ax = plt.subplots(figsize=(6.5, 3.6))
218
+ ax.text(0.5, 0.5, "未選第二數值欄", ha="center", va="center", fontsize=12)
219
+ ax.axis("off")
220
+ fig.tight_layout()
221
+ return fig
222
+
223
+
224
  # -----------------------------
225
  # Regenerate demo helper
226
  # -----------------------------
227
  def regenerate_demo(series_choice, dual_axis, rolling_window):
228
+ """忽略上傳檔案,使用全新隨機示範資料。"""
229
  return pipeline(file=None, series_choice=series_choice, dual_axis=dual_axis, rolling_window=rolling_window)
230
 
231
 
 
234
  # -----------------------------
235
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
236
  gr.Markdown(
237
+ "## Grafana-like 隨機示範\n"
238
+ "為手機優化:精簡時間軸、控制刻度、加上網格與間距。"
239
  )
240
 
241
  with gr.Row():
242
  file_in = gr.File(label="上傳 CSV(可空,會用隨機示範資料)", file_types=[".csv"])
243
  series_multiselect = gr.CheckboxGroup(
244
+ label="數值欄位(第一欄→折線/rolling;第二欄→柱狀/雙軸)",
245
  choices=[]
246
  )
247
 
 
251
 
252
  with gr.Row():
253
  run_btn = gr.Button("用『上傳/當前資料』產生 Dashboard")
254
+ regen_btn = gr.Button("🔁 重新產生示範資料(隨機)")
255
 
256
  with gr.Row():
257
+ plot1 = gr.Plot(label="1:Line", scale=1)
258
+ with gr.Row():
259
+ plot2 = gr.Plot(label="2Bar / Dual Axis", scale=1)
260
+ with gr.Row():
261
+ plot3 = gr.Plot(label="3:Rolling Mean", scale=1)
262
 
263
  json_box = gr.Code(label="grafanalib Dashboard JSON", language="json")
264
  json_file = gr.File(label="下載 dashboard.json")
 
269
  def probe_columns(file):
270
  df = load_csv(file)
271
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
272
+ return gr.CheckboxGroup(choices=numeric_cols, value=numeric_cols[:2]), df
 
273
 
274
+ file_in.change(probe_columns, inputs=[file_in], outputs=[series_multiselect, df_view])
 
 
 
 
275
 
276
  # 初次載入:以隨機示範資料跑一次並更新欄位清單
277
  def initial_load():
 
279
  file=None, series_choice=[], dual_axis=False, rolling_window="5"
280
  )
281
  numeric_cols = [c for c in df.columns if c != "time" and pd.api.types.is_numeric_dtype(df[c])]
 
282
  return (
283
  f1, f2, f3, dash_json, json_path, df, demo_csv_path,
284
+ gr.CheckboxGroup(choices=numeric_cols, value=numeric_cols[:2]),
285
  False, "5"
286
  )
287