Rooobert commited on
Commit
bd532bf
·
verified ·
1 Parent(s): bdf7d63

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -33
app.py CHANGED
@@ -1,8 +1,10 @@
1
  import streamlit as st
2
  import pandas as pd
3
  import plotly.express as px
 
 
4
  from dataclasses import dataclass, field
5
- from typing import Dict, Tuple, Any
6
 
7
  # 📥 讀取 Google 試算表函數
8
  def read_google_sheet(sheet_id, sheet_number=0):
@@ -41,46 +43,148 @@ class SurveyAnalyzer:
41
  '5.在示範場域可以獲得需要的協助',
42
  '6.對於示範場域的服務感到滿意'
43
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  def generate_report(self, df: pd.DataFrame) -> Dict[str, Any]:
46
  """📝 生成問卷調查報告"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  return {
48
  '基本統計': {
49
  '總受訪人數': len(df),
50
- '性別分布': df['1. 性別'].value_counts().to_dict(),
51
- '教育程度分布': df['3.教育程度'].value_counts().to_dict(),
52
- '平均年齡': f"{pd.to_numeric(df['2.出生年(民國__年)'], errors='coerce').mean():.1f}歲"
53
  },
54
  '滿意度統計': {
55
- '整體平均滿意度': f"{df['6.對於示範場域的服務感到滿意'].mean():.2f}",
56
- '最高分項目': df[self.satisfaction_columns].mean().idxmax(),
57
- '最低分項目': df[self.satisfaction_columns].mean().idxmin()
58
  }
59
  }
60
 
61
- def plot_satisfaction_correlation(self, df: pd.DataFrame):
62
- """🔥 滿意度相關性熱力圖"""
63
- correlation_matrix = df[self.satisfaction_columns].corr()
64
- fig = px.imshow(correlation_matrix, text_auto=True, color_continuous_scale='viridis',
65
- title='🔥 滿意度項目相關性熱力圖')
66
-
67
- # ✅ 放大圖表
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  fig.update_layout(
69
- font=dict(size=20),
70
- title_font=dict(size=26, family="Arial Black"),
71
- width=1000,
72
- height=800,
73
- coloraxis_colorbar=dict(title="相關性"),
74
  )
75
-
 
 
 
 
 
 
76
  st.plotly_chart(fig, use_container_width=True)
77
 
78
- def plot_gender_distribution(self, df: pd.DataFrame):
79
- """🟠 性別分佈圓餅圖"""
80
- gender_counts = df['1. 性別'].value_counts().reset_index()
 
 
 
 
 
 
 
 
81
  gender_counts.columns = ['性別', '人數']
82
- fig = px.pie(gender_counts, names='性別', values='人數', title='🟠 受訪者性別分布',
83
- color_discrete_sequence=px.colors.sequential.Sunset)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  st.plotly_chart(fig, use_container_width=True)
85
 
86
  # 🎨 Streamlit UI
@@ -96,26 +200,52 @@ def main():
96
  if df is not None:
97
  analyzer = SurveyAnalyzer()
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  # 📌 基本統計數據
100
  st.sidebar.header("📌 選擇數據分析")
101
  selected_analysis = st.sidebar.radio("選擇要查看的分析",
102
- ["📋 問卷統計報告", "🔥 滿意度相關性熱力圖", "🟠 性別分佈"])
103
 
104
  if selected_analysis == "📋 問卷統計報告":
105
  st.header("📋 問卷統計報告")
106
  report = analyzer.generate_report(df)
107
  for category, stats in report.items():
108
- with st.expander(f"🔍 {category}"):
109
  for key, value in stats.items():
110
- st.write(f"**{key}**: {value}")
 
 
 
 
 
111
 
112
- elif selected_analysis == "🔥 滿意���相關性熱力圖":
113
- st.header("🔥 滿意度相關性熱力圖")
114
- analyzer.plot_satisfaction_correlation(df)
115
 
116
  elif selected_analysis == "🟠 性別分佈":
117
  st.header("🟠 性別分佈")
118
- analyzer.plot_gender_distribution(df)
119
 
120
  if __name__ == "__main__":
121
- main()
 
1
  import streamlit as st
2
  import pandas as pd
3
  import plotly.express as px
4
+ import numpy as np
5
+ from datetime import datetime
6
  from dataclasses import dataclass, field
7
+ from typing import Dict, List, Tuple, Any
8
 
9
  # 📥 讀取 Google 試算表函數
10
  def read_google_sheet(sheet_id, sheet_number=0):
 
43
  '5.在示範場域可以獲得需要的協助',
44
  '6.對於示範場域的服務感到滿意'
45
  ]
46
+ self.satisfaction_short_names = [
47
+ '多元課程與活動',
48
+ '生活應用有幫助',
49
+ '服務人員親切',
50
+ '空間設備友善',
51
+ '獲得需要協助',
52
+ '整體服務滿意'
53
+ ]
54
+
55
+ def calculate_age(self, birth_year_column):
56
+ """🔢 計算年齡(從民國年到實際年齡)"""
57
+ # 獲取當前年份(西元年)
58
+ current_year = datetime.now().year
59
+
60
+ # 將 NaN 或無效值處理為 NaN
61
+ birth_years = pd.to_numeric(birth_year_column, errors='coerce')
62
+
63
+ # 民國年份轉西元年份 (民國年+1911=西元年)
64
+ western_years = birth_years + 1911
65
+
66
+ # 計算年齡
67
+ ages = current_year - western_years
68
+
69
+ return ages
70
 
71
  def generate_report(self, df: pd.DataFrame) -> Dict[str, Any]:
72
  """📝 生成問卷調查報告"""
73
+ # 計算年齡
74
+ ages = self.calculate_age(df['2.出生年(民國__年)'])
75
+
76
+ # 取得教育程度分布(帶計數單位)
77
+ education_counts = df['3.教育程度'].value_counts().to_dict()
78
+ education_with_counts = {k: f"{v}人" for k, v in education_counts.items()}
79
+
80
+ # 性別分布(帶計數單位)
81
+ gender_counts = df['1. 性別'].value_counts().to_dict()
82
+ gender_with_counts = {k: f"{v}人" for k, v in gender_counts.items()}
83
+
84
+ # 計算每個滿意度項目的平均分數和標準差
85
+ satisfaction_stats = {}
86
+ for i, col in enumerate(self.satisfaction_columns):
87
+ mean_score = df[col].mean()
88
+ std_dev = df[col].std()
89
+ satisfaction_stats[self.satisfaction_short_names[i]] = {
90
+ '平均分數': f"{mean_score:.2f}",
91
+ '標準差': f"{std_dev:.2f}"
92
+ }
93
+
94
  return {
95
  '基本統計': {
96
  '總受訪人數': len(df),
97
+ '性別分布': gender_with_counts,
98
+ '教育程度分布': education_with_counts,
99
+ '平均年齡': f"{ages.mean():.1f}歲"
100
  },
101
  '滿意度統計': {
102
+ '整體平均滿意度': f"{df[self.satisfaction_columns].mean().mean():.2f}",
103
+ '各項滿意度': satisfaction_stats
 
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({
115
+ '滿意度項目': self.satisfaction_short_names,
116
+ '平均分數': satisfaction_means,
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:
154
+ filtered_df = filtered_df[filtered_df['場域名稱'].isin(venues)]
155
+ if month and month != '全部':
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
+
162
+ # 計算百分比
163
+ total = gender_counts['人數'].sum()
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=['人數', '百分比'],
178
+ labels={'人數': '人數', '百分比': '百分比'},
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
 
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()