Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +356 -32
src/streamlit_app.py
CHANGED
|
@@ -1,40 +1,364 @@
|
|
| 1 |
-
import altair as alt
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
""
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
"
|
| 28 |
-
"y": y,
|
| 29 |
-
"idx": indices,
|
| 30 |
-
"rand": np.random.randn(num_points),
|
| 31 |
-
})
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import requests
|
| 4 |
+
import plotly.express as px
|
| 5 |
+
import plotly.graph_objects as go
|
| 6 |
+
from plotly.subplots import make_subplots
|
| 7 |
+
import numpy as np
|
| 8 |
+
import tempfile
|
| 9 |
+
import os
|
| 10 |
|
| 11 |
+
# 設置頁面配置
|
| 12 |
+
st.set_page_config(
|
| 13 |
+
page_title="碳排放數據可視化分析",
|
| 14 |
+
page_icon="🌱",
|
| 15 |
+
layout="wide",
|
| 16 |
+
initial_sidebar_state="expanded"
|
| 17 |
+
)
|
| 18 |
|
| 19 |
+
# 標題和介紹
|
| 20 |
+
st.title("🌱 碳排放數據可視化分析")
|
| 21 |
+
st.markdown("---")
|
| 22 |
+
st.write("此應用程式分析台灣公司的碳排放數據,包括範疇一和範疇二的排放量。")
|
| 23 |
|
| 24 |
+
# 側邊欄設置
|
| 25 |
+
st.sidebar.header("⚙️ 設置選項")
|
| 26 |
|
| 27 |
+
# 數據載入功能
|
| 28 |
+
@st.cache_data
|
| 29 |
+
def load_data():
|
| 30 |
+
"""載入並處理碳排放數據"""
|
| 31 |
+
try:
|
| 32 |
+
# 顯示載入狀態
|
| 33 |
+
with st.spinner("正在載入數據..."):
|
| 34 |
+
url = "https://mopsfin.twse.com.tw/opendata/t187ap46_O_1.csv"
|
| 35 |
+
response = requests.get(url)
|
| 36 |
+
|
| 37 |
+
# 使用臨時文件
|
| 38 |
+
with tempfile.NamedTemporaryFile(mode='wb', suffix='.csv', delete=False) as tmp_file:
|
| 39 |
+
tmp_file.write(response.content)
|
| 40 |
+
tmp_file_path = tmp_file.name
|
| 41 |
+
|
| 42 |
+
# 讀取CSV文件
|
| 43 |
+
df = pd.read_csv(tmp_file_path, encoding="utf-8-sig")
|
| 44 |
+
|
| 45 |
+
# 清理臨時文件
|
| 46 |
+
os.unlink(tmp_file_path)
|
| 47 |
+
|
| 48 |
+
# 數據清理
|
| 49 |
+
original_shape = df.shape
|
| 50 |
+
df = df.dropna()
|
| 51 |
+
|
| 52 |
+
# 尋找正確的欄位名稱
|
| 53 |
+
company_cols = [col for col in df.columns if "公司" in col or "代號" in col or "股票" in col]
|
| 54 |
+
emission_cols = [col for col in df.columns if "排放" in col]
|
| 55 |
+
|
| 56 |
+
# 自動識別欄位
|
| 57 |
+
company_col = "公司代號"
|
| 58 |
+
scope1_col = "範疇一排放量(公噸CO2e)"
|
| 59 |
+
scope2_col = "範疇二排放量(公噸CO2e)"
|
| 60 |
+
|
| 61 |
+
if company_col not in df.columns and company_cols:
|
| 62 |
+
company_col = company_cols[0]
|
| 63 |
+
|
| 64 |
+
if scope1_col not in df.columns:
|
| 65 |
+
scope1_candidates = [col for col in emission_cols if "範疇一" in col or "Scope1" in col]
|
| 66 |
+
if scope1_candidates:
|
| 67 |
+
scope1_col = scope1_candidates[0]
|
| 68 |
+
|
| 69 |
+
if scope2_col not in df.columns:
|
| 70 |
+
scope2_candidates = [col for col in emission_cols if "範疇二" in col or "Scope2" in col]
|
| 71 |
+
if scope2_candidates:
|
| 72 |
+
scope2_col = scope2_candidates[0]
|
| 73 |
+
|
| 74 |
+
# 轉換數值格式
|
| 75 |
+
if scope1_col in df.columns:
|
| 76 |
+
df[scope1_col] = pd.to_numeric(df[scope1_col], errors='coerce')
|
| 77 |
+
if scope2_col in df.columns:
|
| 78 |
+
df[scope2_col] = pd.to_numeric(df[scope2_col], errors='coerce')
|
| 79 |
+
|
| 80 |
+
# 移除轉換後的空值
|
| 81 |
+
available_cols = [col for col in [scope1_col, scope2_col, company_col] if col in df.columns]
|
| 82 |
+
df = df.dropna(subset=available_cols)
|
| 83 |
+
|
| 84 |
+
return df, original_shape, company_col, scope1_col, scope2_col, company_cols, emission_cols
|
| 85 |
+
|
| 86 |
+
except Exception as e:
|
| 87 |
+
st.error(f"載入數據時發生錯誤: {str(e)}")
|
| 88 |
+
return None, None, None, None, None, None, None
|
| 89 |
|
| 90 |
+
# 載入數據
|
| 91 |
+
data_result = load_data()
|
| 92 |
+
if data_result[0] is not None:
|
| 93 |
+
df, original_shape, company_col, scope1_col, scope2_col, company_cols, emission_cols = data_result
|
| 94 |
+
|
| 95 |
+
# 顯示數據基本信息
|
| 96 |
+
col1, col2, col3 = st.columns(3)
|
| 97 |
+
with col1:
|
| 98 |
+
st.metric("原始數據筆數", original_shape[0])
|
| 99 |
+
with col2:
|
| 100 |
+
st.metric("處理後數據筆數", df.shape[0])
|
| 101 |
+
with col3:
|
| 102 |
+
st.metric("總欄位數", df.shape[1])
|
| 103 |
+
|
| 104 |
+
# 側邊欄控制項
|
| 105 |
+
st.sidebar.subheader("📊 圖表選項")
|
| 106 |
+
|
| 107 |
+
# 圖表類型選擇
|
| 108 |
+
chart_types = st.sidebar.multiselect(
|
| 109 |
+
"選擇要顯示的圖表:",
|
| 110 |
+
["旭日圖", "雙層圓餅圖", "散點圖", "綜合旭日圖"],
|
| 111 |
+
default=["旭日圖", "雙層圓餅圖"]
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# 公司數量選擇
|
| 115 |
+
max_companies = min(30, len(df))
|
| 116 |
+
num_companies = st.sidebar.slider(
|
| 117 |
+
"顯示公司數量:",
|
| 118 |
+
min_value=5,
|
| 119 |
+
max_value=max_companies,
|
| 120 |
+
value=min(15, max_companies),
|
| 121 |
+
step=5
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
# 顯示數據統計
|
| 125 |
+
if st.sidebar.checkbox("顯示數據統計", value=True):
|
| 126 |
+
st.subheader("📈 數據統計摘要")
|
| 127 |
+
|
| 128 |
+
if all(col in df.columns for col in [scope1_col, scope2_col]):
|
| 129 |
+
col1, col2 = st.columns(2)
|
| 130 |
+
|
| 131 |
+
with col1:
|
| 132 |
+
st.write("**範疇一排放量統計:**")
|
| 133 |
+
scope1_stats = df[scope1_col].describe()
|
| 134 |
+
st.write(f"- 平均值: {scope1_stats['mean']:.2f} 公噸CO2e")
|
| 135 |
+
st.write(f"- 中位數: {scope1_stats['50%']:.2f} 公噸CO2e")
|
| 136 |
+
st.write(f"- 最大值: {scope1_stats['max']:.2f} 公噸CO2e")
|
| 137 |
+
st.write(f"- 最小值: {scope1_stats['min']:.2f} 公噸CO2e")
|
| 138 |
+
|
| 139 |
+
with col2:
|
| 140 |
+
st.write("**範疇二排放量統計:**")
|
| 141 |
+
scope2_stats = df[scope2_col].describe()
|
| 142 |
+
st.write(f"- 平均值: {scope2_stats['mean']:.2f} 公噸CO2e")
|
| 143 |
+
st.write(f"- 中位數: {scope2_stats['50%']:.2f} 公噸CO2e")
|
| 144 |
+
st.write(f"- 最大值: {scope2_stats['max']:.2f} 公噸CO2e")
|
| 145 |
+
st.write(f"- 最小值: {scope2_stats['min']:.2f} 公噸CO2e")
|
| 146 |
+
|
| 147 |
+
# 圖表生成函數
|
| 148 |
+
def create_sunburst_chart(df, num_companies):
|
| 149 |
+
"""創建旭日圖"""
|
| 150 |
+
if all(col in df.columns for col in [company_col, scope1_col, scope2_col]):
|
| 151 |
+
df_top = df.nlargest(num_companies, scope1_col)
|
| 152 |
+
sunburst_data = []
|
| 153 |
+
|
| 154 |
+
for _, row in df_top.iterrows():
|
| 155 |
+
company = str(row[company_col])
|
| 156 |
+
scope1 = row[scope1_col]
|
| 157 |
+
scope2 = row[scope2_col]
|
| 158 |
+
|
| 159 |
+
sunburst_data.extend([
|
| 160 |
+
dict(ids=f"公司-{company}", labels=f"公司 {company}", parents="", values=scope1 + scope2),
|
| 161 |
+
dict(ids=f"範疇一-{company}", labels=f"範疇一: {scope1:.0f}", parents=f"公司-{company}", values=scope1),
|
| 162 |
+
dict(ids=f"範疇二-{company}", labels=f"範疇二: {scope2:.0f}", parents=f"公司-{company}", values=scope2)
|
| 163 |
+
])
|
| 164 |
+
|
| 165 |
+
fig_sunburst = go.Figure(go.Sunburst(
|
| 166 |
+
ids=[d['ids'] for d in sunburst_data],
|
| 167 |
+
labels=[d['labels'] for d in sunburst_data],
|
| 168 |
+
parents=[d['parents'] for d in sunburst_data],
|
| 169 |
+
values=[d['values'] for d in sunburst_data],
|
| 170 |
+
branchvalues="total",
|
| 171 |
+
hovertemplate='<b>%{label}</b><br>排放量: %{value:.0f} 公噸CO2e<extra></extra>',
|
| 172 |
+
maxdepth=3
|
| 173 |
+
))
|
| 174 |
+
|
| 175 |
+
fig_sunburst.update_layout(
|
| 176 |
+
title=f"碳排放量旭日圖 (前{num_companies}家公司)",
|
| 177 |
+
font_size=12,
|
| 178 |
+
height=600
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
return fig_sunburst
|
| 182 |
+
return None
|
| 183 |
+
|
| 184 |
+
def create_nested_pie_chart(df, num_companies):
|
| 185 |
+
"""創建雙層圓餅圖"""
|
| 186 |
+
if all(col in df.columns for col in [company_col, scope1_col, scope2_col]):
|
| 187 |
+
df_top = df.nlargest(num_companies, scope1_col)
|
| 188 |
+
|
| 189 |
+
fig = make_subplots(
|
| 190 |
+
rows=1, cols=2,
|
| 191 |
+
specs=[[{"type": "pie"}, {"type": "pie"}]],
|
| 192 |
+
subplot_titles=("範疇一排放量", "範疇二排放量")
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
fig.add_trace(go.Pie(
|
| 196 |
+
labels=df_top[company_col],
|
| 197 |
+
values=df_top[scope1_col],
|
| 198 |
+
name="範疇一",
|
| 199 |
+
hovertemplate='<b>%{label}</b><br>範疇一排放量: %{value:.0f} 公噸CO2e<br>佔比: %{percent}<extra></extra>',
|
| 200 |
+
textinfo='label+percent',
|
| 201 |
+
textposition='auto'
|
| 202 |
+
), row=1, col=1)
|
| 203 |
+
|
| 204 |
+
fig.add_trace(go.Pie(
|
| 205 |
+
labels=df_top[company_col],
|
| 206 |
+
values=df_top[scope2_col],
|
| 207 |
+
name="範疇二",
|
| 208 |
+
hovertemplate='<b>%{label}</b><br>範疇二排放量: %{value:.0f} 公噸CO2e<br>佔比: %{percent}<extra></extra>',
|
| 209 |
+
textinfo='label+percent',
|
| 210 |
+
textposition='auto'
|
| 211 |
+
), row=1, col=2)
|
| 212 |
+
|
| 213 |
+
fig.update_layout(
|
| 214 |
+
title_text=f"碳排放量圓餅圖比較 (前{num_companies}家公司)",
|
| 215 |
+
showlegend=True,
|
| 216 |
+
height=600
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
return fig
|
| 220 |
+
return None
|
| 221 |
+
|
| 222 |
+
def create_scatter_plot(df):
|
| 223 |
+
"""創建散點圖"""
|
| 224 |
+
if all(col in df.columns for col in [company_col, scope1_col, scope2_col]):
|
| 225 |
+
fig_scatter = px.scatter(
|
| 226 |
+
df,
|
| 227 |
+
x=scope1_col,
|
| 228 |
+
y=scope2_col,
|
| 229 |
+
hover_data=[company_col],
|
| 230 |
+
title="範疇一 vs 範疇二排放量散點圖",
|
| 231 |
+
labels={
|
| 232 |
+
scope1_col: "範疇一排放量 (公噸CO2e)",
|
| 233 |
+
scope2_col: "範疇二排放量 (公噸CO2e)"
|
| 234 |
+
},
|
| 235 |
+
hover_name=company_col
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
fig_scatter.update_layout(height=600)
|
| 239 |
+
return fig_scatter
|
| 240 |
+
return None
|
| 241 |
+
|
| 242 |
+
def create_comprehensive_sunburst(df, num_companies):
|
| 243 |
+
"""創建綜合旭日圖"""
|
| 244 |
+
if all(col in df.columns for col in [company_col, scope1_col, scope2_col]):
|
| 245 |
+
df_copy = df.copy()
|
| 246 |
+
df_copy['total_emission'] = df_copy[scope1_col] + df_copy[scope2_col]
|
| 247 |
+
df_copy['emission_level'] = pd.cut(df_copy['total_emission'],
|
| 248 |
+
bins=[0, 1000, 5000, 20000, float('inf')],
|
| 249 |
+
labels=['低排放(<1K)', '中排放(1K-5K)', '高排放(5K-20K)', '超高排放(>20K)'])
|
| 250 |
+
|
| 251 |
+
sunburst_data = []
|
| 252 |
+
companies_per_level = max(1, num_companies // 4)
|
| 253 |
+
|
| 254 |
+
for level in df_copy['emission_level'].unique():
|
| 255 |
+
if pd.isna(level):
|
| 256 |
+
continue
|
| 257 |
+
level_companies = df_copy[df_copy['emission_level'] == level].nlargest(companies_per_level, 'total_emission')
|
| 258 |
+
|
| 259 |
+
for _, row in level_companies.iterrows():
|
| 260 |
+
company = str(row[company_col])
|
| 261 |
+
scope1 = row[scope1_col]
|
| 262 |
+
scope2 = row[scope2_col]
|
| 263 |
+
total = scope1 + scope2
|
| 264 |
+
|
| 265 |
+
sunburst_data.extend([
|
| 266 |
+
dict(ids=str(level), labels=str(level), parents="", values=total),
|
| 267 |
+
dict(ids=f"{level}-{company}", labels=f"{company}", parents=str(level), values=total),
|
| 268 |
+
dict(ids=f"{level}-{company}-範疇一", labels=f"範疇一({scope1:.0f})",
|
| 269 |
+
parents=f"{level}-{company}", values=scope1),
|
| 270 |
+
dict(ids=f"{level}-{company}-範疇二", labels=f"範疇二({scope2:.0f})",
|
| 271 |
+
parents=f"{level}-{company}", values=scope2)
|
| 272 |
+
])
|
| 273 |
+
|
| 274 |
+
fig_comprehensive = go.Figure(go.Sunburst(
|
| 275 |
+
ids=[d['ids'] for d in sunburst_data],
|
| 276 |
+
labels=[d['labels'] for d in sunburst_data],
|
| 277 |
+
parents=[d['parents'] for d in sunburst_data],
|
| 278 |
+
values=[d['values'] for d in sunburst_data],
|
| 279 |
+
branchvalues="total",
|
| 280 |
+
hovertemplate='<b>%{label}</b><br>排放量: %{value:.0f} 公噸CO2e<extra></extra>',
|
| 281 |
+
maxdepth=4
|
| 282 |
+
))
|
| 283 |
+
|
| 284 |
+
fig_comprehensive.update_layout(
|
| 285 |
+
title="分級碳排放量旭日圖",
|
| 286 |
+
font_size=10,
|
| 287 |
+
height=700
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
return fig_comprehensive
|
| 291 |
+
return None
|
| 292 |
+
|
| 293 |
+
# 顯示選中的圖表
|
| 294 |
+
st.subheader("📊 互動式圖表")
|
| 295 |
+
|
| 296 |
+
if "旭日圖" in chart_types:
|
| 297 |
+
st.write("### 🌞 旭日圖")
|
| 298 |
+
fig1 = create_sunburst_chart(df, num_companies)
|
| 299 |
+
if fig1:
|
| 300 |
+
st.plotly_chart(fig1, use_container_width=True)
|
| 301 |
+
else:
|
| 302 |
+
st.error("無法創建旭日圖,缺少必要欄位")
|
| 303 |
+
|
| 304 |
+
if "雙層圓餅圖" in chart_types:
|
| 305 |
+
st.write("### 🥧 雙層圓餅圖")
|
| 306 |
+
fig2 = create_nested_pie_chart(df, num_companies)
|
| 307 |
+
if fig2:
|
| 308 |
+
st.plotly_chart(fig2, use_container_width=True)
|
| 309 |
+
else:
|
| 310 |
+
st.error("無法創建圓餅圖,缺少必要欄位")
|
| 311 |
+
|
| 312 |
+
if "散點圖" in chart_types:
|
| 313 |
+
st.write("### 📈 散點圖")
|
| 314 |
+
fig3 = create_scatter_plot(df)
|
| 315 |
+
if fig3:
|
| 316 |
+
st.plotly_chart(fig3, use_container_width=True)
|
| 317 |
+
else:
|
| 318 |
+
st.error("無法創建散點圖,缺少必要欄位")
|
| 319 |
+
|
| 320 |
+
if "綜合旭日圖" in chart_types:
|
| 321 |
+
st.write("### 🌟 綜合旭日圖")
|
| 322 |
+
fig4 = create_comprehensive_sunburst(df, num_companies)
|
| 323 |
+
if fig4:
|
| 324 |
+
st.plotly_chart(fig4, use_container_width=True)
|
| 325 |
+
else:
|
| 326 |
+
st.error("無法創建綜合旭日圖,缺少必要欄位")
|
| 327 |
+
|
| 328 |
+
# 顯示原始數據
|
| 329 |
+
if st.sidebar.checkbox("顯示原始數據"):
|
| 330 |
+
st.subheader("📋 原始數據預覽")
|
| 331 |
+
st.dataframe(df.head(100), use_container_width=True)
|
| 332 |
+
|
| 333 |
+
# 數據下載功能
|
| 334 |
+
if st.sidebar.button("下載處理後數據"):
|
| 335 |
+
csv = df.to_csv(index=False, encoding='utf-8-sig')
|
| 336 |
+
st.sidebar.download_button(
|
| 337 |
+
label="💾 下載 CSV 文件",
|
| 338 |
+
data=csv,
|
| 339 |
+
file_name="carbon_emission_data.csv",
|
| 340 |
+
mime="text/csv"
|
| 341 |
+
)
|
| 342 |
+
|
| 343 |
+
# 偵錯信息
|
| 344 |
+
if st.sidebar.checkbox("顯示偵錯信息"):
|
| 345 |
+
st.subheader("🔧 偵錯信息")
|
| 346 |
+
st.write("**識別的欄位:**")
|
| 347 |
+
st.write(f"- 公司欄位: {company_col}")
|
| 348 |
+
st.write(f"- 範疇一欄位: {scope1_col}")
|
| 349 |
+
st.write(f"- 範疇二欄位: {scope2_col}")
|
| 350 |
+
st.write("**所有可用欄位:**")
|
| 351 |
+
st.write(df.columns.tolist())
|
| 352 |
|
| 353 |
+
else:
|
| 354 |
+
st.error("無法載入數據,請檢查網路連接或數據源。")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
+
# 頁面底部信息
|
| 357 |
+
st.markdown("---")
|
| 358 |
+
st.markdown(
|
| 359 |
+
"""
|
| 360 |
+
**數據來源:** 台灣證券交易所公開資訊觀測站
|
| 361 |
+
**更新時間:** 根據數據源自動更新
|
| 362 |
+
**製作:** Streamlit 碳排放數據分析應用
|
| 363 |
+
"""
|
| 364 |
+
)
|