Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,109 +1,60 @@
|
|
1 |
import streamlit as st
|
2 |
-
import pandas as pd
|
3 |
import requests
|
|
|
4 |
import plotly.express as px
|
5 |
-
import matplotlib
|
6 |
-
import
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
# 🛠️ 確保選擇的欄位存在
|
50 |
-
existing_columns = set(df.columns)
|
51 |
-
valid_selected_columns = [col for col in selected_columns if col in existing_columns]
|
52 |
-
|
53 |
-
if not valid_selected_columns:
|
54 |
-
st.error(f"❌ 選擇的欄位無法在數據中找到,請檢查欄位名稱!\n\n📋 可用欄位: {list(existing_columns)}")
|
55 |
-
return
|
56 |
-
|
57 |
-
df = df.dropna(subset=["公司名稱"] + valid_selected_columns) # **移除 NaN 確保長度相同**
|
58 |
-
df[valid_selected_columns] = df[valid_selected_columns].apply(pd.to_numeric, errors='coerce')
|
59 |
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
fig = px.line(df, x="公司名稱", y=valid_selected_columns, title=f"{df_name} - 折線圖", color_discrete_sequence=theme)
|
66 |
-
elif chart_type == "長條圖":
|
67 |
-
fig = px.bar(df, x="公司名稱", y=valid_selected_columns, title=f"{df_name} - 長條圖", color_discrete_sequence=theme)
|
68 |
-
elif chart_type == "餅圖":
|
69 |
-
total_emissions = df.groupby("公司名稱")[valid_selected_columns].sum().reset_index()
|
70 |
-
total_emissions = total_emissions.melt(id_vars=["公司名稱"], value_vars=valid_selected_columns, var_name="排放類型", value_name="總排放量")
|
71 |
-
fig = px.pie(total_emissions, values='總排放量', names='公司名稱', title=f"{df_name} - 餅圖", color_discrete_sequence=theme, hole=0.3)
|
72 |
-
fig.update_traces(textposition='inside', textinfo='percent+label')
|
73 |
-
|
74 |
-
if fig:
|
75 |
-
fig = beautify_chart(fig)
|
76 |
-
st.plotly_chart(fig, use_container_width=True)
|
77 |
|
78 |
-
#
|
79 |
-
st.subheader("
|
80 |
-
|
81 |
-
|
82 |
-
# **下載數據**
|
83 |
-
urls = [
|
84 |
-
"https://mopsfin.twse.com.tw/opendata/t187ap46_L_1.csv",
|
85 |
-
"https://mopsfin.twse.com.tw/opendata/t187ap46_O_2.csv",
|
86 |
-
"https://mopsfin.twse.com.tw/opendata/t187ap46_L_6.csv"
|
87 |
-
]
|
88 |
-
|
89 |
-
dfs = [download_and_load_csv(url) for url in urls]
|
90 |
-
combined_df = pd.concat(dfs, ignore_index=True).fillna(0) # 合併數據並填充 NaN 值
|
91 |
-
|
92 |
-
# **Streamlit UI**
|
93 |
-
st.title("📊 台灣企業 ESG 數據分析")
|
94 |
-
|
95 |
-
st.subheader("📂 數據預覽")
|
96 |
-
st.dataframe(combined_df)
|
97 |
-
|
98 |
-
# **選擇數據欄位**
|
99 |
-
emission_columns = [col for col in ["範疇一排放量(噸CO2e)", "範疇二排放量(噸CO2e)", "範疇三排放量(噸CO2e)"] if col in combined_df.columns]
|
100 |
-
selected_columns = st.multiselect("選擇要顯示的排放類別", emission_columns, default=emission_columns[:1] if emission_columns else [])
|
101 |
-
|
102 |
-
# **選擇圖表類型**
|
103 |
-
chart_type = st.selectbox("選擇圖表類型", ["折線圖", "長條圖", "餅圖"])
|
104 |
-
|
105 |
-
# **生成圖表**
|
106 |
-
if selected_columns:
|
107 |
-
generate_plots(combined_df, "綜合數據", selected_columns, chart_type)
|
108 |
else:
|
109 |
-
st.
|
|
|
1 |
import streamlit as st
|
|
|
2 |
import requests
|
3 |
+
import pandas as pd
|
4 |
import plotly.express as px
|
5 |
+
import matplotlib as mpl
|
6 |
+
import matplotlib.font_manager as fm
|
7 |
+
import os
|
8 |
+
from io import StringIO
|
9 |
+
|
10 |
+
def install_custom_font():
|
11 |
+
font_path = "TaipeiSansTCBeta-Regular.ttf"
|
12 |
+
if not os.path.exists(font_path):
|
13 |
+
font_url = "https://drive.google.com/uc?id=1eGAsTN1HBpJAkeVM57_C7ccp7hbgSz3_&export=download"
|
14 |
+
font_response = requests.get(font_url)
|
15 |
+
with open(font_path, "wb") as font_file:
|
16 |
+
font_file.write(font_response.content)
|
17 |
+
fm.fontManager.addfont(font_path)
|
18 |
+
mpl.rc('font', family='Taipei Sans TC Beta')
|
19 |
+
|
20 |
+
install_custom_font()
|
21 |
+
|
22 |
+
st.title("📊 台股財報數據可視化")
|
23 |
+
|
24 |
+
# 直接讀取固定 URL 的 CSV 檔案
|
25 |
+
url = "https://mopsfin.twse.com.tw/opendata/t187ap46_L_1.csv"
|
26 |
+
response = requests.get(url)
|
27 |
+
response.encoding = "utf-8"
|
28 |
+
|
29 |
+
df = pd.read_csv(StringIO(response.text))
|
30 |
+
df_cleaned = df.fillna(0) # 填補缺失值
|
31 |
+
|
32 |
+
st.subheader("📌 數據預覽")
|
33 |
+
st.write(df_cleaned.head())
|
34 |
+
|
35 |
+
# 讓用戶篩選數據
|
36 |
+
st.subheader("🔍 數據篩選")
|
37 |
+
filter_col = st.selectbox("選擇要篩選的欄位", df_cleaned.columns)
|
38 |
+
unique_values = df_cleaned[filter_col].unique()
|
39 |
+
selected_value = st.selectbox("選擇篩選值", unique_values)
|
40 |
+
filtered_df = df_cleaned[df_cleaned[filter_col] == selected_value]
|
41 |
+
|
42 |
+
st.write(filtered_df.head())
|
43 |
+
|
44 |
+
# 讓用戶選擇數值欄位
|
45 |
+
numeric_cols = filtered_df.select_dtypes(include=['number']).columns
|
46 |
+
if len(numeric_cols) > 0:
|
47 |
+
col_choice = st.selectbox("選擇要視覺化的數值欄位", numeric_cols)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
+
if st.button("生成圖表"):
|
50 |
+
# 長條圖
|
51 |
+
st.subheader("📊 長條圖")
|
52 |
+
fig_bar = px.bar(filtered_df.head(10), x=filtered_df.index[:10], y=col_choice, title=f"{col_choice} 長條圖")
|
53 |
+
st.plotly_chart(fig_bar)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
# 圓餅圖
|
56 |
+
st.subheader("🥧 圓餅圖")
|
57 |
+
fig_pie = px.pie(filtered_df.head(10), names=filtered_df.index[:10], values=col_choice, title=f"{col_choice} 圓餅圖")
|
58 |
+
st.plotly_chart(fig_pie)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
else:
|
60 |
+
st.warning("⚠ 沒有數值型欄位可供選擇")
|