Dash 全面入門教學:從 0 到打造互動式資料分析應用
Dash 是由 Plotly 所開發的 Python Web 應用框架,專為資料分析與視覺化設計。你可以用 Dash 快速打造具有豐富互動功能的網頁應用,而不需要學習前端技術如 JavaScript、HTML 或 CSS。
📦 安裝 Dash
在開始之前,請確認你已安裝 Python 3.7+。
pip install dash也建議安裝
pandas
和 plotly
:pip install pandas plotly
🏗 Dash 架構概觀
Dash 應用的基本組成有三大部分:
- Layout:定義使用者介面(UI),使用 Dash HTML 與 Core Components。
- Callback:設定互動邏輯(例如按鈕點擊、下拉選單變更)。
- Server 啟動點:透過 Flask 啟動本地或雲端應用。
📋 基礎範例:互動式圖表
import dash
from dash import html, dcc, Input, Output
import plotly.express as px
import pandas as pd
# 建立簡單資料
df = px.data.gapminder()
# 初始化應用
app = dash.Dash(__name__)
# Layout 介面
app.layout = html.Div([
html.H1("🌍 世界人口與壽命分析"),
dcc.Dropdown(
id='country-dropdown',
options=[{'label': c, 'value': c} for c in df['country'].unique()],
value='Taiwan'
),
dcc.Graph(id='life-exp-vs-year')
])
# 設定 callback
@app.callback(
Output('life-exp-vs-year', 'figure'),
Input('country-dropdown', 'value')
)
def update_graph(selected_country):
filtered_df = df[df['country'] == selected_country]
fig = px.line(filtered_df, x='year', y='lifeExp',
title=f'{selected_country} 壽命變化趨勢',
markers=True)
return fig
# 啟動 server
if __name__ == '__main__':
app.run(debug=True)
點擊網頁的部分,即可開啟


🔧 元件說明與進階技巧
🔹 Layout 元件介紹
Dash 提供兩大元件類別:
dash.html
: HTML 元素,例如html.Div
,html.H1
,html.Button
dash.dcc
: Dash Core Components,例如dcc.Graph
,dcc.Input
,dcc.Dropdown
html.Div([
html.Label("輸入名字:"),
dcc.Input(id='input-name', type='text'),
html.Button("送出", id='submit-btn')
])
🔄 Callback 設定與資料綁定
Dash 使用 回呼函數(callback)來建立互動。
基本結構
@app.callback(
Output('輸出元件ID', '屬性'),
Input('輸入元件ID', '屬性')
)
def 處理函數(輸入參數):
return 回傳值
支援多輸入輸出
@app.callback(
[Output('out1', 'children'), Output('out2', 'style')],
[Input('in1', 'value'), Input('in2', 'n_clicks')]
)
def multi_output(v1, v2):
...
return val1, style2
📊 整合 Pandas + Plotly
Dash 通常搭配 pandas
處理資料,搭配 plotly.express
建立互動圖表。
import pandas as pd
import plotly.express as px
df = pd.read_csv('sales.csv') # 假設你有一個銷售資料表
fig = px.bar(df, x='產品', y='銷售額', color='地區')
將此圖整合進 Dash:
dcc.Graph(figure=fig)
🧪 加入更多互動:多圖表 + 輸入框
html.Div([
dcc.Input(id='input-year', type='number', value=2007),
dcc.Graph(id='graph-gdp'),
dcc.Graph(id='graph-life')
])
@app.callback(
[Output('graph-gdp', 'figure'),
Output('graph-life', 'figure')],
Input('input-year', 'value')
)
def update_graphs(year):
dff = df[df['year'] == year]
fig1 = px.bar(dff, x='country', y='gdpPercap', title='GDP per Capita')
fig2 = px.bar(dff, x='country', y='lifeExp', title='Life Expectancy')
return fig1, fig2
📁 檔案上傳功能
dcc.Upload(
id='upload-data',
children=html.Button('上傳 CSV'),
multiple=False
)
@app.callback(Output('output-data', 'children'),
Input('upload-data', 'contents'))
def handle_upload(contents):
if contents:
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
return html.Div([dcc.Graph(figure=px.line(df))]
程式範例整合進去
import dash
from dash import html, dcc, Input, Output, State
import plotly.express as px
import pandas as pd
import base64
import io
# 原始資料
df = px.data.gapminder()
# 初始化 Dash 應用
app = dash.Dash(__name__)
# Layout 設定
app.layout = html.Div([
html.H1("🌍 世界人口與壽命分析"),
dcc.Dropdown(
id='country-dropdown',
options=[{'label': c, 'value': c} for c in df['country'].unique()],
value='Taiwan',
style={'width': '50%'}
),
html.Br(),
dcc.Upload(
id='upload-data',
children=html.Button('📤 上傳 CSV 檔案', style={'fontSize': '16px'}),
multiple=False
),
html.Div(id='upload-message', style={'margin': '10px', 'color': 'green'}),
dcc.Graph(id='life-exp-vs-year')
])
# 使用者上傳 CSV 資料後更新圖表
@app.callback(
Output('life-exp-vs-year', 'figure'),
Output('upload-message', 'children'),
Input('upload-data', 'contents'),
State('upload-data', 'filename'),
State('country-dropdown', 'value')
)
def handle_upload(contents, filename, selected_country):
if contents:
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
# 嘗試讀取 CSV 資料
df_uploaded = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
fig = px.line(df_uploaded, x=df_uploaded.columns[0], y=df_uploaded.columns[1],
title=f'自訂 CSV: {df_uploaded.columns[1]} 隨 {df_uploaded.columns[0]} 的變化')
return fig, f"✅ 已成功載入 {filename}"
except Exception as e:
return dash.no_update, f"❌ 上傳失敗:{str(e)}"
# 如果沒上傳,就顯示預設圖(根據選取國家)
filtered_df = df[df['country'] == selected_country]
fig = px.line(filtered_df, x='year', y='lifeExp',
title=f'{selected_country} 壽命變化趨勢',
markers=True)
return fig, ""
# 預設圖表:當選取國家時(且沒有上傳新檔)更新圖表
@app.callback(
Output('country-dropdown', 'value'),
Input('country-dropdown', 'value')
)
def preserve_dropdown(value):
return value # 確保 Dropdown 維持最新選取值(避免被 callback 覆蓋)
# 啟動應用
if __name__ == '__main__':
app.run(debug=True)

📦 專案結構建議
dash_app/
│
├── app.py # 主應用程式
├── assets/ # 放 CSS, 圖片等前端資源
│ └── style.css
├── data/ # 放資料檔案
├── components/ # 拆分的 UI 或 callback 模組(可選)