資料科學第六週-期中了!用Streamlit展示文字資訊與圖表

前言-會使用到什麼讓網站上線
在這週,我們要將前面所學到的DataFrame與資料視覺化圖表的上線,可參考動態展示網站:
https://taoyuan-data.streamlit.app/

若只是在本地(Local)端呈現,會使用到的技術包含:
- Streamlit - 建置網站
- Pandas and Plotly - 資訊分析與視覺化工具
如果要上雲端,讓人能夠連上,要學會的包含(我會寫在下一篇):
- Venv - Python內建虛擬環境設定
- Git and Github - 儲存和協作軟體開發專案的線上平台
- Streamlit Cloud - 雲端部署網站的平台服務
Streamlit簡介
Streamlit 是一個開源的 Python 框架,專為快速建立美觀且互動性強的資料展示而設計,只要寫幾行程式碼,就可以將 Python 腳本轉變成互動式網頁應用。Let’s try it!
Streamlit安裝與匯入
pip install streamlit創建一檔案這取名為app.py ,將套件匯入
import streamlit as stStreamlit 文字類型
在使用文字時與平常記筆記的Notion類似,使用Markdown語法可以設定標題

如圖,標題可以使用
st.title('桃園市相關數據展示')使用title 等同於使用write 加上markdown語法#如下:
st.write('# 桃園市相關數據展示')所以不想記太多函式的話,可以全部用write 也OK
次標題可以用##
st.write('## 中壢高商乙丙級通過率')Streamlit 上線
這樣,執行後就可以在本地端測試了:

會看到streamlit run 你的檔案位置 複製並輸入在命令提示字元就可以看到結果。

接者每當你改動程式碼後存檔,只要重新整理頁面,就會跟改動了
更方便的是,你可以點選右上角的… > Settings

再點選Run on save ,網頁就會在你存檔時,不用重整網頁,自動更新囉,非常快速方便吧!

Streamlit 欄位排版
三欄式切版
可以在一行中指定要切成幾欄,例如我要切成三欄
col1, col2,col3 = st.columns(3)每欄中有多行指令要設定的話,可以配上with ,還進行欄位的設定
官網範例

metric
實作中使用metric 這個函式,當欄位是要強調「某個值或訊息」的時候,很適合使用,其中參數包含:
label:指標的名稱,用於描述數值的內容。
value:主顯示數值。
delta(可選):和之前數值的變化,用於顯示增加或減少的趨勢(通常是數字或百分比)。
delta_color(可選):控制變化值的顏色。"normal":預設,增加顯示綠色,減少顯示紅色。
"inverse":反轉顏色,增加顯示紅色,減少顯示綠色。
help(可選):一段說明文字,當使用者將滑鼠懸停在指標名稱上時會顯示。
實作:
col1, col2,col3, col4 = st.columns(4)
col1.metric('112年度電乙本校參檢人數', 84, delta=-20, delta_color="inverse", help=None)
col2.metric('112年度電乙全國參檢人數', 6874, delta=7651-6874, delta_color="inverse", help=None)
col3.metric('112年度電乙本校通過率', f'{48/84*100:.2f}%', delta='-13.1%', delta_color="inverse", help=None)
col4.metric('112年度電乙全國通過率', f'{3370/6874*100:.2f}%', delta=f'{((3370/6874)-(3654/7651))*100:.2f}%', delta_color="inverse", help=None)
和台灣不同,預設「漲是綠色、跌是紅色」,所以在delta_color 要使用inverse
不同比例切版
除了可以在一行中指定要切成幾欄,也可以利用list 的方式設定比例,如官網範例:

Plotly製圖I - 繪折線圖
實作前幾節的資料,用plotly來呈現,幾個plotly的用法可以注意一下:
- 載入Plotly中可以互動的元件
import plotly.graph_objects as go- 建立空的圖表
fig = go.Figure()- 將線型加入,將我要加入的地區用
list呈現add_trace():在fig中加入軌跡(線圖)
Scatter():散布圖/折線圖,根據後面的mode決定
name:這個參數呈現在圖例(legend)當中
for area in ['臺灣地區-元', '新北市-元', '臺北市-元', '桃園市-元']:
fig.add_trace(x=df['年'],y=df[area],mode='lines+markers',name=area)- 圖的額外設定,其中
hovermode可以設定x,y,closet,互動時會依據方式來呈現資料
fig.update_layout(
#title='各地區收入變化',
xaxis_title='年份',
yaxis_title='收入 (元)',
hovermode='x',
width=700 # 設定圖表寬度
)- 在streamlit呈現出來,
use_container_width=True能讓圖形根據網頁大小而做變化,更符合RWD建議加入
st.plotly_chart(fig,use_container_width=True)此區段程式碼:
import plotly.graph_objects as go
data = {
'年': [1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022],
'臺灣地區-元': [1070268, 1089575, 1091478, 1064136, 1064153, 1064825, 1074665, 1082168, 1099739, 1108674, 1099994, 1074180, 1071938, 1104265, 1122379, 1140271, 1157926, 1167284, 1194572, 1231112, 1249031, 1274196, 1293719, 1314023, 1340848],
'新北市-元': [1087242, 1102563, 1095192, 1098408, 1100003, 1093053, 1109639, 1135398, 1125605, 1099472, 1159279, 1110774, 1071131, 1116342, 1101389, 1129598, 1146991, 1171978, 1223867, 1265798, 1292753, 1319841, 1352548, 1381603, 1421385],
'臺北市-元': [1442675, 1472979, 1530735, 1505506, 1514440, 1501916, 1488180, 1514069, 1526228, 1550134, 1538257, 1515793, 1564298, 1537890, 1570778, 1545415, 1575819, 1581899, 1568945, 1648122, 1649348, 1723021, 1716591, 1732126, 1752411],
'桃園市-元': [1163181, 1174654, 1209363, 1143050, 1192731, 1163092, 1203215, 1192419, 1179444, 1210511, 1182721, 1146080, 1122238, 1183732, 1238698, 1257146, 1327963, 1307158, 1317790, 1337361, 1378732, 1392199, 1424027, 1448909, 1449549],
'臺中市-元': [1102701, 1137007, 1038480, 1055944, 1015972, 1023452, 990633, 988496, 1048174, 1105239, 1026631, 977731, 987259, 1100346, 1067060, 1126875, 1152523, 1169183, 1140325, 1245350, 1279865, 1298497, 1289700, 1304508, 1338826],
'臺南市-元': [911488, 896601, 933736, 872189, 898270, 936057, 943051, 881412, 950332, 972699, 976116, 924950, 886924, 948383, 927231, 996434, 991990, 1007093, 1063495, 1079199, 1086077, 1079174, 1086475, 1120580, 1143699],
'高雄市-元': [1029317, 1051762, 1031569, 1003108, 974592, 972280, 1011408, 1046729, 1030212, 1081799, 1068765, 1052162, 1052260, 1043941, 1085971, 1107383, 1112287, 1145895, 1166824, 1186204, 1219246, 1224668, 1224100, 1231562, 1263068],
'宜蘭縣-元': [976550, 1022506, 972996, 877199, 846963, 943028, 874576, 994967, 931588, 909097, 1006018, 904809, 971775, 877016, 1075706, 979013, 1071335, 1160320, 1085846, 1098807, 1069997, 1093475, 1138365, 1136419, 1165558],
'新竹縣-元': [1113042, 1167454, 1232173, 1106603, 1127123, 1133617, 1196515, 1304360, 1164274, 1098234, 1289463, 1281933, 1300116, 1372358, 1367712, 1346768, 1389453, 1283995, 1365150, 1616327, 1519478, 1539555, 1619782, 1689337, 1702134],
'苗栗縣-元': [943047, 945975, 935836, 944066, 921405, 949888, 895950, 931906, 1030851, 1000447, 919930, 926267, 964594, 1014144, 1012306, 1020185, 1100084, 1008241, 1166196, 1029485, 1045881, 1073028, 1161999, 1214424, 1273250],
'彰化縣-元': [951047, 891310, 897000, 845708, 943182, 962720, 945506, 906328, 961572, 922001, 902838, 890929, 887707, 920937, 953701, 936595, 940572, 926717, 994353, 970491, 997162, 1026792, 996066, 1086809, 1108221],
'南投縣-元': [875980, 1005079, 874344, 836262, 830936, 819394, 879780, 897891, 964953, 860836, 851992, 978331, 932725, 881743, 986881, 896557, 919551, 878760, 916199, 894368, 967369, 940893, 997605, 985686, 1048879],
'雲林縣-元': [782106, 889682, 810634, 873803, 817942, 809617, 849594, 769997, 906358, 794205, 767431, 744181, 748256, 817778, 824211, 838094, 865131, 876670, 896101, 868663, 860237, 933883, 988062, 1024191, 1016913],
'嘉義縣-元': [707349, 766905, 857615, 783819, 730084, 738385, 778574, 797895, 810323, 750236, 764933, 741766, 775017, 804668, 880625, 858253, 789406, 890742, 896217, 901022, 940780, 850597, 906554, 916631, 891498],
'屏東縣-元': [919710, 879362, 913848, 838027, 927042, 919906, 847872, 894772, 899657, 953471, 842372, 877498, 850116, 866795, 889560, 861063, 832681, 869818, 911258, 958999, 985681, 964547, 1064508, 1046302, 1083933],
'臺東縣-元': [702299, 725065, 780967, 760989, 774329, 737947, 755711, 695369, 753191, 860701, 699334, 805395, 674899, 733168, 796622, 799026, 820549, 746981, 797395, 889108, 884510, 874386, 891340, 911136, 916928],
'花蓮縣-元': [887914, 883537, 933625, 933484, 845923, 880361, 858345, 795376, 862688, 878356, 843646, 808632, 904472, 920602, 927400, 966607, 860613, 924706, 948501, 910090, 937898, 956973, 1032499, 1026488, 1010041],
'澎湖縣-元': [634053, 723916, 831230, 732306, 760406, 739670, 874714, 778659, 853872, 789752, 902952, 851835, 986954, 828441, 887077, 922916, 932694, 792696, 1069545, 857821, 959761, 1037554, 1091011, 796484, 919357],
'基隆市-元': [999088, 1058154, 1052113, 964170, 893884, 881716, 1048399, 1052699, 948817, 999562, 976639, 1026166, 1040931, 1018118, 955197, 1002341, 957360, 1072433, 1074314, 1096361, 1070748, 1140481, 1106963, 1111556, 1099526],
'新竹市-元': [1207558, 1268541, 1395702, 1419946, 1320717, 1344434, 1479816, 1361016, 1478303, 1554456, 1462204, 1426854, 1448209, 1479675, 1439066, 1535411, 1576797, 1427572, 1537317, 1572296, 1426379, 1602826, 1618903, 1602415, 1722889],
'嘉義市-元': [1059622, 958462, 1005451, 975877, 1015957, 927635, 832402, 984171, 966863, 899387, 898229, 836551, 842337, 925444, 1095203, 1241161, 1157962, 1106004, 1154411, 1090947, 1152499, 1208298, 1225219, 1217617, 1282798]
}
df = pd.DataFrame(data) #各地區年收資料
col_rowB_1, col_rowB_2 = st.columns((7,3))
with col_rowB_1:
st.write('### 桃園區與臺灣地區收入變化')
# 使用 plotly 建立折線圖
fig = go.Figure()
# 添加每個選取的地區到圖表中
for area in ['臺灣地區-元', '新北市-元', '臺北市-元', '桃園市-元']:
fig.add_trace(go.Scatter(x=df['年'], y=df[area], mode='lines+markers', name=area))
# 設定圖表標題和軸標籤
fig.update_layout(
#title='各地區收入變化',
xaxis_title='年份',
yaxis_title='收入 (元)',
hovermode='x',
width=700 # 設定圖表寬度
)
# 在 Streamlit 中顯示圖表
st.plotly_chart(fig,use_container_width=True)
with col_rowB_2:
st.write('### 原始資料')
st.write(df)
側邊欄設定
側邊欄是Streamlit一個有趣的設計,可以讓使用者自行篩選資料,讓圖表視覺化的呈現更為彈性,以上面的地區收入變化為例:
側邊欄的標題
分為主標題st.sidebar.header 和次標題st.sidebar.subheader 非常直觀
st.sidebar.subheader('收入-選擇地區')多選條件
可以選定不同地區做即時的比較,所以使用multiselect ,其中
option:代表所有可選擇的標籤
default:代表網頁一開始預設有的標籤
selected_areas = st.sidebar.multiselect(
'選擇地區',
options=df.columns[1:], # 不包含 '年' 欄位
default=['臺灣地區-元', '新北市-元', '臺北市-元', '桃園市-元'] # 預設選擇
)側邊欄就被叫出來拉!

因為要讓圖可以跟著調整,所以對原本圖的呈現進行修改:
要加入軌跡add_trace() 的線,改為由selected_areas 這個變數所控制,就這麼簡單!
for area in selected_areas:
fig.add_trace(go.Scatter(x=df['年'], y=df[area], mode='lines+markers', name=area))修改後就可以及時控制圖囉

側邊欄風格調整
另外因為Streamlit預設主視覺顏色很鮮豔,一樣可以透過Setting來做修改,點擊Setting後選擇「Edit active theme」即可變換主題顏色

繪製原餅圖
這裡使用之前廟宇的資料,先透過sidebar進行設定,這樣在算「主祀神祇的比例」的時候,可以篩選,要計入的神明。
這邊要注意的是option_list ,因為原本df_count_ratio.index 得到的是一個Series,要轉成文字串列才能被選擇。
st.sidebar.subheader('比例-選擇主祀神祇')
options_list = df_count_ratio.index.astype(str).tolist()
selected_labels = st.sidebar.multiselect(
'選擇主祀神祇',
options=options_list,
default=options_list # 預設顯示全部
)再來就差不多了,label 代表圓餅圖要出現的標籤,values 代表各個值,跟前面畫圖是一樣的
hole 則是用來設計中間的空心,改稱「甜甜圈圖」,可有可無。
# 使用 Plotly 繪製互動式圓餅圖
fig = go.Figure(
go.Pie(
labels=filtered_data.index,
values=filtered_data,
hole=0.3, # 设置为甜甜圈图表
textinfo='percent+label' # 显示百分比和标签
)
)
# 在 Streamlit 中顯示圖表
st.plotly_chart(fig)此段完整程式碼:
st.write('## 桃園區廟宇登記數量分析')
col_rowC_1, col_rowC_2 = st.columns((5,5))
with col_rowC_1:
st.write('### 桃園市各廟宇主祀神祇比例')
# 從 API 讀取 JSON 數據
data = pd.read_json('http://data.tycg.gov.tw/api/v1/rest/datastore/b2247404-3d92-4829-9855-0cd5e71b92b3?format=json&limit=500')
df = pd.DataFrame(data['result']['records'])
# 計算各主祀神祇的數量和比例
df_count = df['主祀神祇'].value_counts(normalize=True)
df_count_ratio = df_count / df_count.sum()
# 計算 "其他" 的比例
df_count_ratio['其他'] = df_count_ratio[df_count_ratio < 0.03].sum()
# 保留比例大於等於 3% 的項目,並包含 "其他"
df_count_ratio = df_count_ratio[df_count_ratio >= 0.03]
# 在 Streamlit sidebar 中創建多選篩選
# 轉換為純字串列表
st.sidebar.subheader('比例-選擇主祀神祇')
options_list = df_count_ratio.index.astype(str).tolist()
selected_labels = st.sidebar.multiselect(
'選擇主祀神祇',
options=options_list,
default=options_list # 預設顯示全部
)
# 根據選擇篩選數據
filtered_data = df_count_ratio[selected_labels]
# 使用 Plotly 繪製互動式圓餅圖
fig = go.Figure(
go.Pie(
labels=filtered_data.index,
values=filtered_data,
hole=0.3, # 设置为甜甜圈图表
textinfo='percent+label' # 显示百分比和标签
)
)
# 在 Streamlit 中顯示圖表
st.plotly_chart(fig)
繪製長條圖 和 滑桿側邊欄
樞紐分析表Pivot_table
在前面我們使用pivot_table 來做神明與教別的交叉數量分析,將pivot_table 顯示出來更方便了解:
# 建立交叉表,顯示不同主祀神祇所對應的教別數量
pivot_table = pd.pivot_table(
df,
index='主祀神祇',
columns='教別',
values='寺廟名稱',
aggfunc='count',
fill_value=0
).reset_index()
因為等等要建置的滑桿最小值和最大值和「總廟數」有關,於是又加了一個欄位,將所有列,第一欄以外(iloc[:,1:])進行橫向加總(axis=1)
# 添加 "總寺廟數量" 列
pivot_table['總寺廟數量'] = pivot_table.iloc[:, 1:].sum(axis=1)滑桿Slider
設定滑桿,找到最大值和最小值即可
min_count, max_count = st.sidebar.slider(
'選擇寺廟數量篩選範圍',
min_value=int(pivot_table['總寺廟數量'].min()),
max_value=int(pivot_table['總寺廟數量'].max()),
value=(int(pivot_table['總寺廟數量'].min()), int(pivot_table['總寺廟數量'].max()))
)
長條圖繪製前,講一下長表格melt
因為要透過「教別」來進行分組的長條圖,可以想見,x軸是依教別分好組的神祇,y軸是寺廟數量。
因此我想讓表格如下圖,每個神祇對應到四種教別的寺廟數量

但原本pivot_table中,教別在欄,這時候就適合用melt將欄融化成列!
melt 的三個參數:
id_vars- 代表主要不轉換的鍵
var_name- 代表要融化的欄
value_name- 代表表中還要保留的值
# 根據數量範圍篩選神祇
filtered_table = pivot_table[(pivot_table['總寺廟數量'] >= min_count) & (pivot_table['總寺廟數量'] <= max_count)]
# 去除 "總寺廟數量" 列,轉換為長格式
filtered_table_long = filtered_table.drop(columns='總寺廟數量').melt(id_vars='主祀神祇', var_name='教別', value_name='寺廟數量')
st.write(filtered_table_long)
# 去除寺廟數量為 0 的項目
filtered_table_long = filtered_table_long[filtered_table_long['寺廟數量'] > 0]
繪製長條圖
轉換為長表格後,就可以用Plotly來進行Bar Chart的繪製,其中barmode 代表長條圖的顯示模式,也可以試著調整看看其他效果。
barmode='group':並排顯示條形,用於對比不同類別。
barmode='stack':堆疊顯示條形,用於展示類別總和及其貢獻。
barmode='overlay':覆蓋顯示條形,容易遮擋信息,不常用。
barmode='relative':相對堆疊顯示,展示各類別在x軸標籤下的比例。
# 使用 Plotly 繪製長條圖
fig = px.bar(
filtered_table_long,
x='主祀神祇',
y='寺廟數量',
color='教別',
barmode='group',
title='',
labels={'寺廟數量': '寺廟數量', '主祀神祇': '主祀神祇', '教別': '教別'}
)
# 在 Streamlit 中顯示長條圖
st.plotly_chart(fig)以上為將之前資料放上Streamlit的過程教學,在實作時應該很夠大家消化一下了。
我把地圖folium 的功能放在下一篇分開講!順便記錄一下,十月的颱風,放了兩天!
最後放上完整程式碼連結:
https://github.com/alu1006/dashboard_streamlit
版權聲明
文章標題:資料科學第六週-期中了!用Streamlit展示文字資訊與圖表
文章作者:阿盧老師
文章連結:https://codinglu.tw/blog/streamlit-dashboard
授權條款:本文採用 CC BY-NC 4.0 授權。轉載請標明出處。