Rooobert commited on
Commit
933be95
·
verified ·
1 Parent(s): 278bb6a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -208
app.py CHANGED
@@ -1,114 +1,4 @@
1
- def main():
2
- # 設置頁面配置
3
- st.set_page_config(
4
- page_title="114年度樂齡學習數位示範體驗場域 服務滿意度調查分析報告",
5
- page_icon="📊",
6
- layout="wide"
7
- )
8
-
9
- # 添加標題和子標題
10
- st.markdown("""
11
- # 114年度樂齡學習數位示範體驗場域 服務滿意度調查分析報告
12
- ## 全面理解樂齡學習者數位服務體驗
13
-
14
- 本報告提供全面的問卷調查分析與視覺化圖表,深入剖析樂齡學習者參與數位示範場域服務的滿意情形。
15
- 透過詳細的統計分析和互動式圖表,我們旨在呈現樂齡學習者的服務體驗和需求洞察。
16
-
17
- ### 報告製作單位
18
- **國立中正大學高齡教育研究中心專案管理團隊**
19
- """)
20
-
21
- # 分隔線
22
- st.markdown("---")
23
-
24
- # 上傳 CSV 檔案
25
- uploaded_file = st.file_uploader("上傳 CSV 檔案", type=['csv'])
26
-
27
- # 預設數據按鈕
28
- use_default_data = st.button('使用預設範例數據')
29
-
30
- # 數據載入和處理
31
- df = None
32
- analyzer = None
33
-
34
- if uploaded_file is not None:
35
- # 讀取上傳的 CSV 檔案
36
- try:
37
- df = pd.read_csv(uploaded_file, encoding='utf-8')
38
- st.success("CSV 檔案上傳成功!")
39
- except Exception as e:
40
- st.error(f"無法讀取檔案:{e}")
41
- return
42
-
43
- elif use_default_data:
44
- # 使用預設數據
45
- df = read_google_sheet(sheet_id, gid)
46
-
47
- if df is None:
48
- st.error("無法讀取預設數據,請上傳 CSV 檔案")
49
- return
50
-
51
- # 如果有數據,則進行分析
52
- if df is not None:
53
- analyzer = SurveyAnalyzer()
54
-
55
- # 新增場域和月份篩選器
56
- st.sidebar.header("🔍 數據篩選")
57
-
58
- # 假設數據有「場域名稱」欄位,如果名稱不同請調整
59
- if '場域名稱' in df.columns:
60
- venues = ['全部'] + sorted(df['場域名稱'].unique().tolist())
61
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
62
- else:
63
- # 如果沒有場域欄位,創建10個虛擬場域供選擇
64
- venues = ['全部'] + [f'場域{i+1}' for i in range(10)]
65
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
66
-
67
- # 假設數據有「月份」欄位,如果沒有請調整
68
- if '月份' in df.columns:
69
- months = ['全部'] + sorted(df['月份'].unique().tolist())
70
- selected_month = st.sidebar.selectbox("選擇月份", months)
71
- else:
72
- # 如果沒有月份欄位,可以創建虛擬月份選項
73
- months = ['全部'] + [f'{i+1}月' for i in range(12)]
74
- selected_month = st.sidebar.selectbox("選擇月份", months)
75
-
76
- # 📌 基本統計數據
77
- st.sidebar.header("📌 選擇數據分析")
78
- selected_analysis = st.sidebar.radio("選擇要查看的分析",
79
- ["📋 問卷統計報告", "📊 滿意度統計", "🟠 性別分佈"])
80
-
81
- if selected_analysis == "📋 問卷統計報告":
82
- st.header("📋 問卷統計報告")
83
- report = analyzer.generate_report(df)
84
- for category, stats in report.items():
85
- with st.expander(f"🔍 {category}", expanded=True):
86
- for key, value in stats.items():
87
- if key == '各項滿意度':
88
- st.write(f"**{key}:**")
89
- for item, item_stats in value.items():
90
- st.write(f" - **{item}**: {', '.join([f'{k}: {v}' for k, v in item_stats.items()])}")
91
- else:
92
- st.write(f"**{key}**: {value}")
93
-
94
- elif selected_analysis == "📊 滿意度統計":
95
- st.header("📊 滿意度統計")
96
- analyzer.plot_satisfaction_scores(df)
97
-
98
- elif selected_analysis == "🟠 性別分佈":
99
- st.header("🟠 性別分佈")
100
- analyzer.plot_gender_distribution(df, selected_venues, selected_month)
101
-
102
- # 報告說明
103
- st.markdown("---")
104
- st.markdown("""
105
- ### 報告說明
106
- - **數據來源**:114年度樂齡學習數位示範體驗場域調查問卷
107
- - **分析目的**:評估樂齡學習者對數位示範場域服務的滿意度
108
- - **報告解讀**:本報告提供服務滿意度的客觀量化指標,旨在協助改進服務品質
109
- """)
110
- else:
111
- st.info("請上傳 CSV 檔案或使用預設範例數據")import streamlit as st
112
  import pandas as pd
113
  import plotly.express as px
114
  import numpy as np
@@ -143,25 +33,50 @@ class SurveyMappings:
143
  class SurveyAnalyzer:
144
  """📊 問卷分析類"""
145
 
146
- def __init__(self):
147
  self.mappings = SurveyMappings()
148
- self.satisfaction_columns = [
149
- '1. 示範場域提供多元的數位課程與活動',
150
- '2.示範場域的數位課程與活動對我的生活應用有幫助',
151
- '3: 示範場域的服務人員親切有禮貌',
152
- '4.示範場域的服務空間與數位設備友善方便',
153
- '5.在示範場域可以獲得需要的協助',
154
- '6.對於示範場域的服務感到滿意'
155
- ]
156
- self.satisfaction_short_names = [
157
- '多元課程與活動',
158
- '生活應用有幫助',
159
- '服務人員親切',
160
- '空間設備友善',
161
- '獲得需要協助',
162
- '整體服務滿意'
 
 
 
 
 
 
 
 
 
163
  ]
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  def calculate_age(self, birth_year_column):
166
  """🔢 計算年齡(從民國年到實際年齡)"""
167
  # 獲取當前年份(西元年)
@@ -181,14 +96,48 @@ class SurveyAnalyzer:
181
  def generate_report(self, df: pd.DataFrame) -> Dict[str, Any]:
182
  """📝 生成問卷調查報告"""
183
  # 計算年齡
184
- ages = self.calculate_age(df['2.出生年(民國__年)'])
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  # 取得教育程度分布(帶計數單位)
187
- education_counts = df['3.教育程度'].value_counts().to_dict()
 
 
 
 
 
 
 
 
 
 
 
188
  education_with_counts = {k: f"{v}人" for k, v in education_counts.items()}
189
 
190
  # 性別分布(帶計數單位)
191
- gender_counts = df['1. 性別'].value_counts().to_dict()
 
 
 
 
 
 
 
 
 
 
 
192
  gender_with_counts = {k: f"{v}人" for k, v in gender_counts.items()}
193
 
194
  # 計算每個滿意度���目的平均分數和標準差
@@ -260,13 +209,38 @@ class SurveyAnalyzer:
260
  """🟠 性別分佈圓餅圖(使用藍色和紅色)"""
261
  # 過濾數據
262
  filtered_df = df.copy()
 
 
 
 
 
 
 
 
 
263
  if venues and '全部' not in venues:
264
- filtered_df = filtered_df[filtered_df['場域名稱'].isin(venues)]
 
 
 
 
 
 
 
 
 
265
  if month and month != '全部':
266
- # 假設有一個月份欄位,如果沒有請調整
267
- filtered_df = filtered_df[filtered_df['月份'] == month]
268
-
269
- gender_counts = filtered_df['1. 性別'].value_counts().reset_index()
 
 
 
 
 
 
 
270
  gender_counts.columns = ['性別', '人數']
271
 
272
  # 計算百分比
@@ -324,85 +298,35 @@ def main():
324
  # 上傳 CSV 檔案
325
  uploaded_file = st.file_uploader("上傳 CSV 檔案", type=['csv'])
326
 
 
 
 
 
 
327
  if uploaded_file is not None:
328
- # 讀取上傳的 CSV 檔案
329
  try:
330
  df = pd.read_csv(uploaded_file, encoding='utf-8')
 
 
 
 
 
 
 
 
 
331
 
332
- # 儲存到 session state,以便在其他函數中使用
333
- st.session_state['uploaded_file'] = uploaded_file
334
-
335
- # 創建分析器
336
- analyzer = SurveyAnalyzer()
337
 
338
- elif st.button('使用預設範例數據'):
339
- # 如果沒有上傳檔案,提供預設數據讀取
340
- df = read_google_sheet(sheet_id, gid)
341
-
342
- if df is None:
343
- st.error("無法讀取預設數據,請上傳 CSV 檔案")
344
- return
345
-
346
- analyzer = SurveyAnalyzer()
347
- else:
348
- st.info("請上傳 CSV 檔案或使用預設範例數據")
349
- return
350
 
351
  # 新增場域和月份篩選器
352
  st.sidebar.header("🔍 數據篩選")
353
 
354
- # 假設數據有「場域名稱」欄位,如果名稱不同請調整
355
- if '場域名稱' in df.columns:
356
- venues = ['全部'] + sorted(df['場域名稱'].unique().tolist())
357
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
358
- else:
359
- # 如果沒有場域欄位,創建10個虛擬場域供選擇
360
- venues = ['全部'] + [f'場域{i+1}' for i in range(10)]
361
- selected_venues = st.sidebar.multiselect("選擇場域", venues, default=['全部'])
362
-
363
- # 假設數據有「月份」欄位,如果沒有請調整
364
- if '月份' in df.columns:
365
- months = ['全部'] + sorted(df['月份'].unique().tolist())
366
- selected_month = st.sidebar.selectbox("選擇月份", months)
367
- else:
368
- # 如果沒有月份欄位,可以創建虛擬月份選項
369
- months = ['全部'] + [f'{i+1}月' for i in range(12)]
370
- selected_month = st.sidebar.selectbox("選擇月份", months)
371
-
372
- # 📌 基本統計數據
373
- st.sidebar.header("📌 選擇數據分析")
374
- selected_analysis = st.sidebar.radio("選擇要查看的分析",
375
- ["📋 問卷統計報告", "📊 滿意度統計", "🟠 性別分佈"])
376
-
377
- if selected_analysis == "📋 問卷統計報告":
378
- st.header("📋 問卷統計報告")
379
- report = analyzer.generate_report(df)
380
- for category, stats in report.items():
381
- with st.expander(f"🔍 {category}", expanded=True):
382
- for key, value in stats.items():
383
- if key == '各項滿意度':
384
- st.write(f"**{key}:**")
385
- for item, item_stats in value.items():
386
- st.write(f" - **{item}**: {', '.join([f'{k}: {v}' for k, v in item_stats.items()])}")
387
- else:
388
- st.write(f"**{key}**: {value}")
389
-
390
- elif selected_analysis == "📊 滿意度統計":
391
- st.header("📊 滿意度統計")
392
- analyzer.plot_satisfaction_scores(df)
393
-
394
- elif selected_analysis == "🟠 性別分佈":
395
- st.header("🟠 性別分佈")
396
- analyzer.plot_gender_distribution(df, selected_venues, selected_month)
397
-
398
- # 報告說明
399
- st.markdown("---")
400
- st.markdown("""
401
- ### 報告說明
402
- - **數據來源**:114年度樂齡學習數位示範體驗場域調查問卷
403
- - **分析目的**:評估樂齡學習者對數位示範場域服務的滿意度
404
- - **報告解讀**:本報告提供服務滿意度的客觀量化指標,旨在協助改進服務品質
405
- """)
406
-
407
- if __name__ == "__main__":
408
- main()
 
1
+ import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import pandas as pd
3
  import plotly.express as px
4
  import numpy as np
 
33
  class SurveyAnalyzer:
34
  """📊 問卷分析類"""
35
 
36
+ def __init__(self, df):
37
  self.mappings = SurveyMappings()
38
+
39
+ # Method to find the closest matching column
40
+ def find_matching_column(possible_columns):
41
+ for col in possible_columns:
42
+ # Try different variations of potential column names
43
+ variations = [
44
+ col,
45
+ col.replace(':', ''),
46
+ col.replace('.', ''),
47
+ col.strip()
48
+ ]
49
+ for var in variations:
50
+ if var in df.columns:
51
+ return var
52
+ return None
53
+
54
+ # Predefined column templates
55
+ column_templates = [
56
+ ('多元課程與活動', ['示範場域提供多元的數位課程與活動', '1: 示範場域提供多元的數位課程與活動']),
57
+ ('生活應用有幫助', ['示範場域的數位課程與活動對我的生活應用有幫助', '2.示範場域的數位課程與活動對我的生活應用有幫助']),
58
+ ('服務人員親切', ['示範場域的服務人員親切有禮貌', '3: 示範場域的服務人員親切有禮貌']),
59
+ ('空間設備友善', ['示範場域的服務空間與數位設備友善方便', '4.示範場域的服務空間與數位設備友善方便']),
60
+ ('獲得需要協助', ['在示範場域可以獲得需要的協助', '5.在示範場域可以獲得需要的協助']),
61
+ ('整體服務滿意', ['對於示範場域的服務感到滿意', '6.對於示範場域的服務感到滿意'])
62
  ]
63
 
64
+ # Find matching columns
65
+ self.satisfaction_short_names = []
66
+ self.satisfaction_columns = []
67
+
68
+ for short_name, column_options in column_templates:
69
+ matched_col = None
70
+ for col_option in column_options:
71
+ matched_col = find_matching_column([col_option])
72
+ if matched_col:
73
+ self.satisfaction_columns.append(matched_col)
74
+ self.satisfaction_short_names.append(short_name)
75
+ break
76
+
77
+ if not matched_col:
78
+ st.warning(f"Could not find column for {short_name}")
79
+
80
  def calculate_age(self, birth_year_column):
81
  """🔢 計算年齡(從民國年到實際年齡)"""
82
  # 獲取當前年份(西元年)
 
96
  def generate_report(self, df: pd.DataFrame) -> Dict[str, Any]:
97
  """📝 生成問卷調查報告"""
98
  # 計算年齡
99
+ age_column = '2.出生年(民國__年)'
100
+ # 找到最接近的年齡欄位
101
+ possible_age_columns = [
102
+ '2.出生年(民國__年)',
103
+ '出生年',
104
+ '出生年(民國__年)'
105
+ ]
106
+ for col in possible_age_columns:
107
+ if col in df.columns:
108
+ age_column = col
109
+ break
110
+
111
+ ages = self.calculate_age(df[age_column])
112
 
113
  # 取得教育程度分布(帶計數單位)
114
+ education_column = '3.教育程度'
115
+ # 找到最接近的教育程度欄位
116
+ possible_education_columns = [
117
+ '3.教育程度',
118
+ '教育程度'
119
+ ]
120
+ for col in possible_education_columns:
121
+ if col in df.columns:
122
+ education_column = col
123
+ break
124
+
125
+ education_counts = df[education_column].value_counts().to_dict()
126
  education_with_counts = {k: f"{v}人" for k, v in education_counts.items()}
127
 
128
  # 性別分布(帶計數單位)
129
+ gender_column = '1. 性別'
130
+ # 找到最接近的性別欄位
131
+ possible_gender_columns = [
132
+ '1. 性別',
133
+ '性別'
134
+ ]
135
+ for col in possible_gender_columns:
136
+ if col in df.columns:
137
+ gender_column = col
138
+ break
139
+
140
+ gender_counts = df[gender_column].value_counts().to_dict()
141
  gender_with_counts = {k: f"{v}人" for k, v in gender_counts.items()}
142
 
143
  # 計算每個滿意度���目的平均分數和標準差
 
209
  """🟠 性別分佈圓餅圖(使用藍色和紅色)"""
210
  # 過濾數據
211
  filtered_df = df.copy()
212
+
213
+ # 場域篩選
214
+ venue_column = '場域名稱'
215
+ possible_venue_columns = ['場域名稱', 'venue']
216
+ for col in possible_venue_columns:
217
+ if col in filtered_df.columns:
218
+ venue_column = col
219
+ break
220
+
221
  if venues and '全部' not in venues:
222
+ filtered_df = filtered_df[filtered_df[venue_column].isin(venues)]
223
+
224
+ # 月份篩選
225
+ month_column = '月份'
226
+ possible_month_columns = ['月份', 'month']
227
+ for col in possible_month_columns:
228
+ if col in filtered_df.columns:
229
+ month_column = col
230
+ break
231
+
232
  if month and month != '全部':
233
+ filtered_df = filtered_df[filtered_df[month_column] == month]
234
+
235
+ # 性別欄位
236
+ gender_column = '1. 性別'
237
+ possible_gender_columns = ['1. 性別', '性別']
238
+ for col in possible_gender_columns:
239
+ if col in filtered_df.columns:
240
+ gender_column = col
241
+ break
242
+
243
+ gender_counts = filtered_df[gender_column].value_counts().reset_index()
244
  gender_counts.columns = ['性別', '人數']
245
 
246
  # 計算百分比
 
298
  # 上傳 CSV 檔案
299
  uploaded_file = st.file_uploader("上傳 CSV 檔案", type=['csv'])
300
 
301
+ # 初始化數據和分析器
302
+ df = None
303
+ analyzer = None
304
+
305
+ # 檢查是否有上傳檔案
306
  if uploaded_file is not None:
 
307
  try:
308
  df = pd.read_csv(uploaded_file, encoding='utf-8')
309
+ st.success("CSV 檔案上傳成功!")
310
+ except Exception as e:
311
+ st.error(f"無法讀取檔案:{e}")
312
+ return
313
+
314
+ # 如果沒有上傳檔案,提供使用預設數據的選項
315
+ if df is None:
316
+ if st.button('使用預設範例數據'):
317
+ df = read_google_sheet(sheet_id, gid)
318
 
319
+ if df is None:
320
+ st.error("無法讀取預設數據,請上傳 CSV 檔案")
321
+ return
 
 
322
 
323
+ # 如果有數據,則進行分析
324
+ if df is not None:
325
+ analyzer = SurveyAnalyzer(df)
 
 
 
 
 
 
 
 
 
326
 
327
  # 新增場域和月份篩選器
328
  st.sidebar.header("🔍 數據篩選")
329
 
330
+ # 場域篩選
331
+ venue_column = '場域名稱'
332
+ possible_venue_columns = ['場域名稱', 'venue']