import streamlit as st import pandas as pd import plotly.express as px import plotly.graph_objs as go import numpy as np from datetime import datetime from dataclasses import dataclass, field from typing import Dict, List, Tuple, Any # 📥 讀取 Google 試算表函數 def read_google_sheet(sheet_id, sheet_number=0): """📥 從 Google Sheets 讀取數據""" url = f'https://docs.google.com/spreadsheets/d/{sheet_id}/export?format=csv&gid={sheet_number}' try: df = pd.read_csv(url) return df except Exception as e: st.error(f"❌ 讀取失敗:{str(e)}") return None class SurveyAnalyzer: """📊 問卷分析類""" def __init__(self): # 滿意度欄位名稱 self.satisfaction_columns = [ '1.示範場域提供多元的數位課程與活動', '2.示範場域的數位課程與活動對我的生活應用有幫助', '3.示範場域的服務人員親切有禮貌', '4.示範場域的服務空間與數位設備友善方便', '5.在示範場域可以獲得需要的協助', '6.對於示範場域的服務感到滿意' ] # 對應的簡短名稱 self.satisfaction_short_names = [ '多元課程與活動', '生活應用幫助', '服務人員親切', '空間設備友善', '獲得需要協助', '整體服務滿意' ] def plot_satisfaction_scores(self, df: pd.DataFrame): """📊 示範場域滿意度平均分數圖表""" # 確保所有滿意度欄位都存在 existing_columns = [col for col in self.satisfaction_columns if col in df.columns] # 計算平均分數和標準差 satisfaction_means = [df[col].mean() for col in existing_columns] satisfaction_stds = [df[col].std() for col in existing_columns] # 創建數據框 satisfaction_df = pd.DataFrame({ '滿意度項目': [self.satisfaction_short_names[self.satisfaction_columns.index(col)] for col in existing_columns], '平均分數': satisfaction_means, '標準差': satisfaction_stds }) # 排序結果(由高到低) satisfaction_df = satisfaction_df.sort_values(by='平均分數', ascending=False) # 建立顏色漸變映射 color_scale = [ [0, '#90CAF9'], # 淺藍色 [0.5, '#2196F3'], # 中藍色 [1, '#1565C0'] # 深藍色 ] # 繪製條形圖 fig = px.bar( satisfaction_df, x='滿意度項目', y='平均分數', error_y='標準差', title='📊 示範場域各項滿意度分析', color='平均分數', color_continuous_scale=color_scale, text='平均分數', hover_data={ '滿意度項目': True, '平均分數': ':.2f', '標準差': ':.2f' } ) # 調整圖表佈局 fig.update_layout( font=dict(family="Arial", size=16), title_font=dict(family="Arial Black", size=24), title_x=0.5, # 標題置中 xaxis_title="滿意度項目", yaxis_title="平均分數", yaxis_range=[0, 5], # 評分範圍從0開始,視覺上更明顯 plot_bgcolor='rgba(240,240,240,0.8)', # 淺灰色背景 paper_bgcolor='white', xaxis_tickangle=-25, # 斜角標籤,避免重疊 margin=dict(l=40, r=40, t=80, b=60), legend_title_text="平均分數", shapes=[ # 添加參考線 - 4分線 dict( type='line', yref='y', y0=4, y1=4, xref='paper', x0=0, x1=1, line=dict(color='rgba(220,20,60,0.5)', width=2, dash='dash') ) ], annotations=[ # 參考線標籤 dict( x=0.02, y=4.1, xref='paper', yref='y', text='優良標準 (4分)', showarrow=False, font=dict(size=14, color='rgba(220,20,60,0.8)') ) ] ) # 調整文字格式 fig.update_traces( texttemplate='%{y:.2f}', textposition='outside', marker_line_color='rgb(8,48,107)', marker_line_width=1.5, opacity=0.85 ) # 計算整體平均滿意度(只計算存在的欄位) overall_satisfaction = df[existing_columns].mean().mean() # 返回圖表和整體滿意度 return fig, overall_satisfaction, len(df) def analyze_demographic_data(self, df: pd.DataFrame): """分析性別和教育程度""" # 性別分佈 if '性別' in df.columns: gender_counts = df['性別'].value_counts() gender_pie = go.Figure(data=[go.Pie( labels=gender_counts.index, values=gender_counts.values, hole=.3, title='性別分佈' )]) gender_pie.update_layout(title='📊 性別分佈') else: gender_pie = None st.warning("資料中缺少性別欄位") # 教育程度分佈 if '教育程度' in df.columns: education_counts = df['教育程度'].value_counts() education_bar = go.Figure(data=[go.Bar( x=education_counts.index, y=education_counts.values, text=education_counts.values, textposition='auto' )]) education_bar.update_layout( title='📊 教育程度分佈', xaxis_title='教育程度', yaxis_title='人數' ) else: education_bar = None st.warning("資料中缺少教育程度欄位") return gender_pie, education_bar def main(): st.set_page_config(page_title="示範場域滿意度調查", layout="wide") # 讀取 Google Sheet 數據 sheet_id = "1Wc15DZWq48MxL7nXAsROJ6sRvH5njSa1ea0aaOGUOVk" gid = "1168424766" df = read_google_sheet(sheet_id, gid) if df is not None: # 創建分析器 analyzer = SurveyAnalyzer() # 顯示標題 st.title("📊 示範場域滿意度調查分析") # 提示缺少的滿意度欄位 missing_columns = [col for col in analyzer.satisfaction_columns if col not in df.columns] if missing_columns: st.warning(f"⚠️ 缺少以下滿意度欄位: {missing_columns}") # 繪製滿意度圖表 satisfaction_fig, overall_satisfaction, num_respondents = analyzer.plot_satisfaction_scores(df) # 顯示滿意度圖表 st.plotly_chart(satisfaction_fig, use_container_width=True) # 顯示整體滿意度 st.markdown(f""" ### 📈 整體滿意度分析 - **受訪人數**: {num_respondents} 人 - **整體平均滿意度**: {overall_satisfaction:.2f} 分 #### 🔍 滿意度解讀 - 0-1分: 非常不滿意 - 1-2分: 不滿意 - 2-3分: 普通 - 3-4分: 滿意 - 4-5分: 非常滿意 根據調查結果,整體滿意度為 {overall_satisfaction:.2f} 分, """, unsafe_allow_html=True) # 根據整體滿意度提供文字解讀 if overall_satisfaction < 2: st.warning("⚠️ 整體滿意度較低,建議深入檢討服務品質") elif overall_satisfaction < 3: st.info("ℹ️ 整體滿意度處於普通水平,可以進一步改善服務") elif overall_satisfaction < 4: st.success("✅ 整體滿意度良好,但仍有提升空間") else: st.balloons() st.success("🎉 整體滿意度非常高,表現優異!") # 人口統計分析 st.header("👥 人口統計分析") # 創建兩列顯示 col1, col2 = st.columns(2) # 性別分佈 with col1: gender_pie, _ = analyzer.analyze_demographic_data(df) if gender_pie: st.plotly_chart(gender_pie, use_container_width=True) # 教育程度分佈 with col2: _, education_bar = analyzer.analyze_demographic_data(df) if education_bar: st.plotly_chart(education_bar, use_container_width=True) if __name__ == "__main__": main()