Rooobert commited on
Commit
57ce1fd
·
verified ·
1 Parent(s): fffa948

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +353 -51
app.py CHANGED
@@ -104,11 +104,25 @@ class SurveyAnalyzer:
104
  }
105
  }
106
 
107
- def plot_satisfaction_scores(self, df: pd.DataFrame):
108
- """📊 各項滿意度平均分數圖表"""
109
- # 準備數據
110
- satisfaction_means = [df[col].mean() for col in self.satisfaction_columns]
111
- satisfaction_stds = [df[col].std() for col in self.satisfaction_columns]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  # 創建數據框
114
  satisfaction_df = pd.DataFrame({
@@ -117,37 +131,96 @@ class SurveyAnalyzer:
117
  '標準差': satisfaction_stds
118
  })
119
 
 
 
 
 
 
 
 
 
 
 
120
  # 繪製條形圖
121
  fig = px.bar(
122
  satisfaction_df,
123
  x='滿意度項目',
124
  y='平均分數',
125
  error_y='標準差',
126
- title='📊 各項滿意度平均分數與標準差',
127
  color='平均分數',
128
- color_continuous_scale='Viridis',
129
- text='平均分數'
 
 
 
 
 
130
  )
131
 
132
  # 調整圖表佈局
133
  fig.update_layout(
134
- font=dict(size=16),
135
- title_font=dict(size=24),
 
136
  xaxis_title="滿意度項目",
137
  yaxis_title="平均分數",
138
- yaxis_range=[1, 5], # 假設評分範圍是 1-5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  )
140
 
141
  # 調整文字格式
142
  fig.update_traces(
143
  texttemplate='%{y:.2f}',
144
- textposition='outside'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  )
146
 
147
  st.plotly_chart(fig, use_container_width=True)
148
 
149
- def plot_gender_distribution(self, df: pd.DataFrame, venues=None, month=None):
150
- """🟠 性別分佈圓餅圖��使用藍色和紅色)"""
151
  # 過濾數據
152
  filtered_df = df.copy()
153
  if venues and '全部' not in venues:
@@ -156,6 +229,13 @@ class SurveyAnalyzer:
156
  # 假設有一個月份欄位,如果沒有請調整
157
  filtered_df = filtered_df[filtered_df['月份'] == month]
158
 
 
 
 
 
 
 
 
159
  gender_counts = filtered_df['1. 性別'].value_counts().reset_index()
160
  gender_counts.columns = ['性別', '人數']
161
 
@@ -164,14 +244,27 @@ class SurveyAnalyzer:
164
  gender_counts['百分比'] = (gender_counts['人數'] / total * 100).round(1)
165
  gender_counts['標籤'] = gender_counts.apply(lambda x: f"{x['性別']}: {x['人數']}人 ({x['百分比']}%)", axis=1)
166
 
167
- # 設定顏色映射 - 男性藍色,女性紅色
168
- color_map = {'男性': '#2171b5', '女性': '#cb181d'}
 
 
 
 
 
 
 
 
 
169
 
 
 
 
 
170
  fig = px.pie(
171
  gender_counts,
172
  names='性別',
173
  values='人數',
174
- title='🟠 受訪者性別分布',
175
  color='性別',
176
  color_discrete_map=color_map,
177
  hover_data=['人數', '百分比'],
@@ -179,20 +272,144 @@ class SurveyAnalyzer:
179
  custom_data=['標籤']
180
  )
181
 
182
- # 更新悬停信息
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  fig.update_traces(
184
  textinfo='percent+label',
185
- hovertemplate='%{customdata[0]}'
 
 
 
 
186
  )
187
 
188
  st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  # 🎨 Streamlit UI
191
  def main():
192
- st.set_page_config(page_title="問卷調查分析", layout="wide")
 
 
 
 
193
 
194
- st.title("📊 問卷調查分析報告")
195
- st.write("本頁面展示問卷調查數據的分析結果,包括統計信息與視覺化圖表。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
  # 讀取數據
198
  df = read_google_sheet(sheet_id, gid)
@@ -200,52 +417,137 @@ def main():
200
  if df is not None:
201
  analyzer = SurveyAnalyzer()
202
 
203
- # 新增場域和月份篩選器
204
- st.sidebar.header("🔍 數據篩選")
 
 
 
 
 
 
 
 
 
 
 
205
 
206
- # 假設數據有「場域名稱」欄位,如果名稱不同請調整
207
  if '場域名稱' in df.columns:
208
  venues = ['全部'] + sorted(df['場域名稱'].unique().tolist())
209
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
210
  else:
211
  # 如果沒有場域欄位,創建10個虛擬場域供選擇
212
- venues = ['全部'] + [f'場域{i+1}' for i in range(10)]
213
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
 
 
 
 
214
 
215
- # 假設數據有「月份」欄位,如果沒有請調整
 
 
 
 
 
 
 
216
  if '月份' in df.columns:
217
  months = ['全部'] + sorted(df['月份'].unique().tolist())
218
- selected_month = st.sidebar.selectbox("選擇月份", months)
219
  else:
220
  # 如果沒有月份欄位,可以創建虛擬月份選項
221
- months = ['全部'] + [f'{i+1}月' for i in range(12)]
222
- selected_month = st.sidebar.selectbox("選擇月份", months)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  # 📌 基本統計數據
225
  st.sidebar.header("📌 選擇數據分析")
226
  selected_analysis = st.sidebar.radio("選擇要查看的分析",
227
  ["📋 問卷統計報告", "📊 滿意度統計", "🟠 性別分佈"])
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  if selected_analysis == "📋 問卷統計報告":
230
- st.header("📋 問卷統計報告")
231
- report = analyzer.generate_report(df)
232
- for category, stats in report.items():
233
- with st.expander(f"🔍 {category}", expanded=True):
234
- for key, value in stats.items():
235
- if key == '各項滿意度':
236
- st.write(f"**{key}:**")
237
- for item, item_stats in value.items():
238
- st.write(f" - **{item}**: {', '.join([f'{k}: {v}' for k, v in item_stats.items()])}")
239
- else:
240
- st.write(f"**{key}**: {value}")
241
-
242
- elif selected_analysis == "📊 滿意度統計":
243
- st.header("📊 滿意度統計")
244
- analyzer.plot_satisfaction_scores(df)
245
-
246
- elif selected_analysis == "🟠 性別分佈":
247
- st.header("🟠 性別分佈")
248
- analyzer.plot_gender_distribution(df, selected_venues, selected_month)
 
 
 
 
 
 
249
 
250
  if __name__ == "__main__":
251
  main()
 
104
  }
105
  }
106
 
107
+ def plot_satisfaction_scores(self, df: pd.DataFrame, venues=None, month=None, age_range=None):
108
+ """📊 各項滿意度平均分數圖表 - 美化版"""
109
+ # 過濾數據
110
+ filtered_df = df.copy()
111
+ if venues and '全部' not in venues:
112
+ filtered_df = filtered_df[filtered_df['場域名稱'].isin(venues)]
113
+ if month and month != '全部':
114
+ # 假設有一個月份欄位,如果沒有請調整
115
+ filtered_df = filtered_df[filtered_df['月份'] == month]
116
+
117
+ # 年齡篩選
118
+ if age_range:
119
+ ages = self.calculate_age(filtered_df['2.出生年(民國__年)'])
120
+ age_mask = (ages >= age_range[0]) & (ages <= age_range[1])
121
+ filtered_df = filtered_df[age_mask]
122
+
123
+ # 計算過濾後數據的平均和標準差
124
+ satisfaction_means = [filtered_df[col].mean() for col in self.satisfaction_columns]
125
+ satisfaction_stds = [filtered_df[col].std() for col in self.satisfaction_columns]
126
 
127
  # 創建數據框
128
  satisfaction_df = pd.DataFrame({
 
131
  '標準差': satisfaction_stds
132
  })
133
 
134
+ # 排序結果(可選)
135
+ satisfaction_df = satisfaction_df.sort_values(by='平均分數', ascending=False)
136
+
137
+ # 建立顏色漸變映射
138
+ color_scale = [
139
+ [0, '#90CAF9'], # 淺藍色
140
+ [0.5, '#2196F3'], # 中藍色
141
+ [1, '#1565C0'] # 深藍色
142
+ ]
143
+
144
  # 繪製條形圖
145
  fig = px.bar(
146
  satisfaction_df,
147
  x='滿意度項目',
148
  y='平均分數',
149
  error_y='標準差',
150
+ title='📊 各項滿意度平均分數與標準差分析',
151
  color='平均分數',
152
+ color_continuous_scale=color_scale,
153
+ text='平均分數',
154
+ hover_data={
155
+ '滿意度項目': True,
156
+ '平均分數': ':.2f',
157
+ '標準差': ':.2f'
158
+ }
159
  )
160
 
161
  # 調整圖表佈局
162
  fig.update_layout(
163
+ font=dict(family="Arial", size=16),
164
+ title_font=dict(family="Arial Black", size=24),
165
+ title_x=0.5, # 標題置中
166
  xaxis_title="滿意度項目",
167
  yaxis_title="平均分數",
168
+ yaxis_range=[0, 5], # 評分範圍從0開始,視覺上更明顯
169
+ plot_bgcolor='rgba(240,240,240,0.8)', # 淺灰色背景
170
+ paper_bgcolor='white',
171
+ xaxis_tickangle=-25, # 斜角標籤,避免重疊
172
+ margin=dict(l=40, r=40, t=80, b=60),
173
+ legend_title_text="平均分數",
174
+ shapes=[
175
+ # 添加參考線 - 例如4分
176
+ dict(
177
+ type='line',
178
+ yref='y', y0=4, y1=4,
179
+ xref='paper', x0=0, x1=1,
180
+ line=dict(color='rgba(220,20,60,0.5)', width=2, dash='dash')
181
+ )
182
+ ],
183
+ annotations=[
184
+ # 參考線標籤
185
+ dict(
186
+ x=0.02, y=4.1,
187
+ xref='paper', yref='y',
188
+ text='優良標準 (4分)',
189
+ showarrow=False,
190
+ font=dict(size=14, color='rgba(220,20,60,0.8)')
191
+ )
192
+ ]
193
  )
194
 
195
  # 調整文字格式
196
  fig.update_traces(
197
  texttemplate='%{y:.2f}',
198
+ textposition='outside',
199
+ marker_line_color='rgb(8,48,107)',
200
+ marker_line_width=1.5,
201
+ opacity=0.85
202
+ )
203
+
204
+ # 添加受訪人數標註
205
+ num_respondents = len(filtered_df)
206
+ fig.add_annotation(
207
+ x=0.5,
208
+ xref='paper',
209
+ yref='paper',
210
+ text=f'受訪人數: {num_respondents}人',
211
+ showarrow=False,
212
+ font=dict(size=16),
213
+ bgcolor='rgba(255,255,255,0.8)',
214
+ bordercolor='rgba(0,0,0,0.2)',
215
+ borderwidth=1,
216
+ borderpad=4,
217
+ y=-0.2
218
  )
219
 
220
  st.plotly_chart(fig, use_container_width=True)
221
 
222
+ def plot_gender_distribution(self, df: pd.DataFrame, venues=None, month=None, age_range=None):
223
+ """🟠 性別分佈圓餅圖 - 增強精緻版"""
224
  # 過濾數據
225
  filtered_df = df.copy()
226
  if venues and '全部' not in venues:
 
229
  # 假設有一個月份欄位,如果沒有請調整
230
  filtered_df = filtered_df[filtered_df['月份'] == month]
231
 
232
+ # 年齡篩選
233
+ if age_range:
234
+ ages = self.calculate_age(filtered_df['2.出生年(民國__年)'])
235
+ age_mask = (ages >= age_range[0]) & (ages <= age_range[1])
236
+ filtered_df = filtered_df[age_mask]
237
+
238
+ # 取得性別資料
239
  gender_counts = filtered_df['1. 性別'].value_counts().reset_index()
240
  gender_counts.columns = ['性別', '人數']
241
 
 
244
  gender_counts['百分比'] = (gender_counts['人數'] / total * 100).round(1)
245
  gender_counts['標籤'] = gender_counts.apply(lambda x: f"{x['性別']}: {x['人數']}人 ({x['百分比']}%)", axis=1)
246
 
247
+ # 獲取篩選條件說明
248
+ filter_description = []
249
+ if venues and '全部' not in venues:
250
+ filter_description.append(f"場域: {', '.join(venues)}")
251
+ if month and month != '全部':
252
+ filter_description.append(f"月份: {month}")
253
+ if age_range and (age_range[0] != min(self.calculate_age(df['2.出生年(民國__年)'])) or
254
+ age_range[1] != max(self.calculate_age(df['2.出生年(民國__年)']))):
255
+ filter_description.append(f"年齡: {age_range[0]}-{age_range[1]}歲")
256
+
257
+ filter_text = "(" + ", ".join(filter_description) + ")" if filter_description else ""
258
 
259
+ # 設定顏色映射 - 男性藍色,女性紅色 - 使用更精緻的顏色
260
+ color_map = {'男性': '#1976D2', '女性': '#D32F2F'}
261
+
262
+ # 建立子圖佈局以添加更多自定義元素
263
  fig = px.pie(
264
  gender_counts,
265
  names='性別',
266
  values='人數',
267
+ title=f'👥 受訪者性別分布{filter_text}',
268
  color='性別',
269
  color_discrete_map=color_map,
270
  hover_data=['人數', '百分比'],
 
272
  custom_data=['標籤']
273
  )
274
 
275
+ # 更新圖表佈局
276
+ fig.update_layout(
277
+ font=dict(family="Arial", size=16),
278
+ title_font=dict(family="Arial Black", size=24),
279
+ title_x=0.5, # 標題置中
280
+ legend_title_text="性別",
281
+ legend=dict(
282
+ orientation="h",
283
+ yanchor="bottom",
284
+ y=-0.2,
285
+ xanchor="center",
286
+ x=0.5,
287
+ font=dict(size=16),
288
+ bordercolor="#E0E0E0",
289
+ borderwidth=2
290
+ ),
291
+ margin=dict(l=20, r=20, t=80, b=100),
292
+ paper_bgcolor='white',
293
+ annotations=[
294
+ dict(
295
+ text=f"總受訪人數: {total}人",
296
+ x=0.5, y=-0.3,
297
+ xref="paper",
298
+ yref="paper",
299
+ showarrow=False,
300
+ font=dict(size=16, color="#616161")
301
+ )
302
+ ]
303
+ )
304
+
305
+ # 添加男女比例標籤
306
+ male_count = gender_counts.loc[gender_counts['性別'] == '男性', '人數'].values[0] if '男性' in gender_counts['性別'].values else 0
307
+ female_count = gender_counts.loc[gender_counts['性別'] == '女性', '人數'].values[0] if '女性' in gender_counts['性別'].values else 0
308
+
309
+ # 計算男女比例
310
+ if male_count > 0 and female_count > 0:
311
+ ratio = round(male_count / female_count, 2)
312
+ ratio_text = f"男女比例 = {ratio}:1"
313
+ elif male_count > 0 and female_count == 0:
314
+ ratio_text = "僅有男性"
315
+ elif female_count > 0 and male_count == 0:
316
+ ratio_text = "僅有女性"
317
+ else:
318
+ ratio_text = "無性別數據"
319
+
320
+ fig.add_annotation(
321
+ text=ratio_text,
322
+ x=0.5, y=-0.15,
323
+ xref="paper",
324
+ yref="paper",
325
+ showarrow=False,
326
+ font=dict(size=16, color="#424242", family="Arial Bold")
327
+ )
328
+
329
+ # 更新懸停資訊
330
  fig.update_traces(
331
  textinfo='percent+label',
332
+ hovertemplate='%{customdata[0]}',
333
+ textfont_size=16,
334
+ marker=dict(line=dict(color='#FFFFFF', width=2)),
335
+ pull=[0.03, 0.03], # 稍���分離餅圖片段
336
+ rotation=45 # 旋轉角度
337
  )
338
 
339
  st.plotly_chart(fig, use_container_width=True)
340
+
341
+ # 在圓餅圖下方添加簡單分析
342
+ st.markdown("""
343
+ <div style="background-color:#F5F5F5; padding:15px; border-radius:10px; margin-top:10px; border-left:5px solid #1976D2;">
344
+ <h4 style="color:#1976D2;">📊 性別分佈簡易分析</h4>
345
+ """, unsafe_allow_html=True)
346
+
347
+ # 生成簡單分析文字
348
+ if total > 0:
349
+ majority_gender = '男性' if male_count > female_count else '女性' if female_count > male_count else '男女相等'
350
+ majority_pct = max(male_count, female_count) / total * 100 if male_count != female_count else 50
351
+
352
+ if male_count != female_count:
353
+ st.markdown(f"""
354
+ <p>本次調查中,<strong>{majority_gender}</strong>佔多數,約佔總體的<strong>{majority_pct:.1f}%</strong>。</p>
355
+ """, unsafe_allow_html=True)
356
+ else:
357
+ st.markdown("<p>本次調查中,男女比例相等,各佔50%。</p>", unsafe_allow_html=True)
358
+ else:
359
+ st.markdown("<p>目前沒有足夠的性別數據進行分析。</p>", unsafe_allow_html=True)
360
+
361
+ st.markdown("</div>", unsafe_allow_html=True)
362
 
363
  # 🎨 Streamlit UI
364
  def main():
365
+ st.set_page_config(
366
+ page_title="數位示範場域問卷調查分析",
367
+ layout="wide",
368
+ initial_sidebar_state="expanded"
369
+ )
370
 
371
+ # 自定義CSS樣式
372
+ st.markdown("""
373
+ <style>
374
+ .main-header {
375
+ font-size: 42px;
376
+ font-weight: bold;
377
+ color: #1E88E5;
378
+ text-align: center;
379
+ margin-bottom: 10px;
380
+ padding-bottom: 15px;
381
+ border-bottom: 2px solid #e0e0e0;
382
+ }
383
+ .sub-header {
384
+ font-size: 24px;
385
+ color: #424242;
386
+ text-align: center;
387
+ margin-bottom: 30px;
388
+ }
389
+ .unit-name {
390
+ font-size: 28px;
391
+ font-weight: bold;
392
+ color: #1565C0;
393
+ text-align: center;
394
+ padding: 10px;
395
+ background-color: #E3F2FD;
396
+ border-radius: 8px;
397
+ margin: 20px 0;
398
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
399
+ }
400
+ .card {
401
+ padding: 20px;
402
+ border-radius: 10px;
403
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
404
+ margin-bottom: 20px;
405
+ background-color: white;
406
+ }
407
+ </style>
408
+ """, unsafe_allow_html=True)
409
+
410
+ # 主標題與副標題
411
+ st.markdown('<div class="main-header">📊 數位示範場域問卷調查分析報告</div>', unsafe_allow_html=True)
412
+ st.markdown('<div class="sub-header">本報告提供全面的問卷調查分析與視覺化圖表,協助了解民眾滿意度與使用者特性</div>', unsafe_allow_html=True)
413
 
414
  # 讀取數據
415
  df = read_google_sheet(sheet_id, gid)
 
417
  if df is not None:
418
  analyzer = SurveyAnalyzer()
419
 
420
+ # 設置單位名稱(假設有「單位名稱」欄位,若無則顯示預設值)
421
+ unit_name = "數位示範場域滿意度調查中心"
422
+ if '單位名稱' in df.columns and not df['單位名稱'].isnull().all():
423
+ unit_names = df['單位名稱'].unique()
424
+ if len(unit_names) == 1:
425
+ unit_name = unit_names[0]
426
+
427
+ # 顯示單位名稱
428
+ st.markdown(f'<div class="unit-name">{unit_name}</div>', unsafe_allow_html=True)
429
+
430
+ # 新增場域和月份篩選器(使用更美觀的設計)
431
+ st.sidebar.markdown("### 🔍 **數據篩選**")
432
+ st.sidebar.markdown("---")
433
 
434
+ # 場域選擇
435
  if '場域名稱' in df.columns:
436
  venues = ['全部'] + sorted(df['場域名稱'].unique().tolist())
 
437
  else:
438
  # 如果沒有場域欄位,創建10個虛擬場域供選擇
439
+ venue_names = [
440
+ "臺北數位樂學園", "新北創新學院", "桃園智慧中心",
441
+ "臺中數位學苑", "臺南創客基地", "高雄創新園區",
442
+ "宜蘭數位中心", "花蓮創新基地", "臺東學習中心", "金門數位樂園"
443
+ ]
444
+ venues = ['全部'] + venue_names
445
 
446
+ selected_venues = st.sidebar.multiselect(
447
+ "📍 **選擇場域**",
448
+ venues,
449
+ default=['全部'],
450
+ help="可選擇多個場域進行數據分析比較"
451
+ )
452
+
453
+ # 月份選擇
454
  if '月份' in df.columns:
455
  months = ['全部'] + sorted(df['月份'].unique().tolist())
 
456
  else:
457
  # 如果沒有月份欄位,可以創建虛擬月份選項
458
+ current_year = datetime.now().year
459
+ months = ['全部'] + [f'{current_year}年{i+1}月' for i in range(12)]
460
+
461
+ selected_month = st.sidebar.selectbox(
462
+ "📅 **選擇月份**",
463
+ months,
464
+ help="選擇特定月份查看數據趨勢"
465
+ )
466
+
467
+ # 年齡區間篩選
468
+ st.sidebar.markdown("### 📊 **年齡區間篩選**")
469
+ ages = analyzer.calculate_age(df['2.出生年(民國__年)'])
470
+ min_age, max_age = int(ages.min()), int(ages.max())
471
+ age_range = st.sidebar.slider(
472
+ "選擇年齡範圍",
473
+ min_age,
474
+ max_age,
475
+ (min_age, max_age),
476
+ help="拖曳調整以篩選特定年齡區間的受訪者"
477
+ )
478
 
479
  # 📌 基本統計數據
480
  st.sidebar.header("📌 選擇數據分析")
481
  selected_analysis = st.sidebar.radio("選擇要查看的分析",
482
  ["📋 問卷統計報告", "📊 滿意度統計", "🟠 性別分佈"])
483
 
484
+ # 應用所有篩選條件
485
+ filtered_df = df.copy()
486
+ if selected_venues and '全部' not in selected_venues:
487
+ if '場域名稱' in filtered_df.columns:
488
+ filtered_df = filtered_df[filtered_df['場域名稱'].isin(selected_venues)]
489
+ if selected_month and selected_month != '全部':
490
+ if '月份' in filtered_df.columns:
491
+ filtered_df = filtered_df[filtered_df['月份'] == selected_month]
492
+
493
+ # 年齡篩選
494
+ if age_range:
495
+ ages = analyzer.calculate_age(filtered_df['2.出生年(民國__年)'])
496
+ age_mask = (ages >= age_range[0]) & (ages <= age_range[1])
497
+ filtered_df = filtered_df[age_mask]
498
+
499
+ # 顯示當前選擇的篩選器
500
+ filter_status = []
501
+ if selected_venues and '全部' not in selected_venues:
502
+ filter_status.append(f"📍 場域: {', '.join(selected_venues)}")
503
+ if selected_month and selected_month != '全部':
504
+ filter_status.append(f"📅 月份: {selected_month}")
505
+ if age_range and (age_range[0] != min(analyzer.calculate_age(df['2.出生年(民國__年)'])) or
506
+ age_range[1] != max(analyzer.calculate_age(df['2.出生年(民國__年)']))):
507
+ filter_status.append(f"👥 年齡: {age_range[0]}-{age_range[1]}歲")
508
+
509
+ if filter_status:
510
+ st.markdown("""
511
+ <div style="background-color:#E3F2FD; padding:10px; border-radius:8px; margin-bottom:20px; border-left:4px solid #1976D2;">
512
+ <h4 style="margin-bottom:10px; color:#1565C0;">🔍 當前篩選條件</h4>
513
+ """, unsafe_allow_html=True)
514
+
515
+ for status in filter_status:
516
+ st.markdown(f"<p style='margin:5px 0;'>{status}</p>", unsafe_allow_html=True)
517
+
518
+ # 顯示篩選後的樣本數
519
+ st.markdown(f"""
520
+ <p style='margin-top:10px; font-weight:bold;'>📊 篩選後樣本數: {len(filtered_df)}人</p>
521
+ </div>
522
+ """, unsafe_allow_html=True)
523
+
524
+ # 數據分析區塊
525
  if selected_analysis == "📋 問卷統計報告":
526
+ st.markdown('<h2 style="color:#1976D2;">📋 問卷統計報告</h2>', unsafe_allow_html=True)
527
+
528
+ # 生成目前篩選條件下的報告
529
+ report = analyzer.generate_report(filtered_df)
530
+
531
+ # 使用卡片樣式顯示統計信息
532
+ col1, col2 = st.columns(2)
533
+
534
+ with col1:
535
+ st.markdown('<div class="card">', unsafe_allow_html=True)
536
+ st.markdown('<h3 style="color:#1976D2; border-bottom:1px solid #e0e0e0; padding-bottom:10px;">📊 基本統計數據</h3>', unsafe_allow_html=True)
537
+
538
+ for key, value in report['基本統計'].items():
539
+ if isinstance(value, dict):
540
+ st.markdown(f"<p><strong>{key}:</strong></p>", unsafe_allow_html=True)
541
+ for k, v in value.items():
542
+ st.markdown(f"<p style='margin-left:20px;'>- {k}: {v}</p>", unsafe_allow_html=True)
543
+ else:
544
+ st.markdown(f"<p><strong>{key}:</strong> {value}</p>", unsafe_allow_html=True)
545
+
546
+ st.markdown('</div>', unsafe_allow_html=True)
547
+
548
+ with col2:
549
+ st.markdown('<div class="card">', unsafe_allow_html=True)
550
+ # Add the rest of the code here
551
 
552
  if __name__ == "__main__":
553
  main()