github star gitee star atomgit star PyPI Downloads AI 编程 AI 交流群

大家好,我是正在实战各种AI项目的程序员晚枫。

欢迎来到第五个也是最后一个实战项目!前面我们学会了分析数据、预测销量、识别用户,但分析报告做得再好,不如一个实时仪表盘来得直观

今天我们要用Python搭建一个交互式数据仪表盘,让老板打开浏览器就能看到关键指标,还能自己筛选、下钻。这就是传说中的管理驾驶舱


项目背景

需求场景

你是公司的数据分析师,领导说:

  • "每天发Excel报表太麻烦了"
  • "我想随时看手机就知道业绩"
  • "能不能点一下就看到详细数据?"

目标产出

一个Web版的数据仪表盘,包含:

  • 核心KPI指标卡
  • 趋势图表(支持时间筛选)
  • 多维度分析(地区/品类/渠道)
  • 交互式过滤和联动

技术选型

工具用途
DashWeb应用框架
Plotly交互式图表
Pandas数据处理
BootstrapUI样式

安装依赖:

1
pip install dash plotly pandas

准备数据

1
2
3
4
5
6
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
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

# 生成完整的业务数据
np.random.seed(42)

def generate_dashboard_data(n_records=5000):
"""生成模拟业务数据"""
end_date = datetime(2024, 12, 31)
start_date = end_date - timedelta(days=365)

dates = pd.date_range(start=start_date, end=end_date, periods=n_records)

data = {
'订单日期': dates,
'订单ID': [f'ORD{str(i).zfill(8)}' for i in range(n_records)],
'地区': np.random.choice(['华北', '华东', '华南', '西南', '东北'], n_records),
'产品类别': np.random.choice(['电子产品', '服装', '食品', '家居', '美妆'], n_records),
'销售渠道': np.random.choice(['线上', '线下', '分销'], n_records, p=[0.5, 0.3, 0.2]),
'客户类型': np.random.choice(['新客户', '老客户'], n_records, p=[0.3, 0.7]),
'订单金额': np.random.lognormal(5, 1, n_records).round(2),
'数量': np.random.randint(1, 10, n_records),
'利润': np.random.lognormal(4, 0.8, n_records).round(2)
}

df = pd.DataFrame(data)
df['月份'] = df['订单日期'].dt.to_period('M').astype(str)
df['季度'] = df['订单日期'].dt.quarter.apply(lambda x: f'Q{x}')
df['星期'] = df['订单日期'].dt.day_name()

return df

# 生成并保存数据
df = generate_dashboard_data()
df.to_csv('dashboard_data.csv', index=False, encoding='utf-8-sig')
print(f"生成了 {len(df)} 条记录")
print(f"总金额: ¥{df['订单金额'].sum():,.0f}")
print(f"总利润: ¥{df['利润'].sum():,.0f}")

仪表盘架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import dash
from dash import dcc, html, Input, Output, callback
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# 初始化Dash应用
app = dash.Dash(__name__)

# 加载数据
df = pd.read_csv('dashboard_data.csv', encoding='utf-8-sig')
df['订单日期'] = pd.to_datetime(df['订单日期'])

# 获取筛选选项
regions = ['全部'] + sorted(df['地区'].unique().tolist())
categories = ['全部'] + sorted(df['产品类别'].unique().tolist())
channels = ['全部'] + sorted(df['销售渠道'].unique().tolist())
months = sorted(df['月份'].unique().tolist())

布局设计

1
2
3
4
5
6
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 定义页面布局
app.layout = html.Div([
# 标题栏
html.Div([
html.H1('📊 业务数据管理驾驶舱',
style={'textAlign': 'center', 'color': '#2c3e50', 'padding': '20px'}),
html.P('实时监控销售业绩与运营指标',
style={'textAlign': 'center', 'color': '#7f8c8d', 'marginTop': '-10px'})
], style={'backgroundColor': '#ecf0f1', 'marginBottom': '20px'}),

# 筛选面板
html.Div([
html.Div([
html.Label('地区:', style={'fontWeight': 'bold'}),
dcc.Dropdown(
id='region-filter',
options=[{'label': r, 'value': r} for r in regions],
value='全部',
clearable=False
)
], style={'width': '23%', 'display': 'inline-block', 'marginRight': '2%'}),

html.Div([
html.Label('产品类别:', style={'fontWeight': 'bold'}),
dcc.Dropdown(
id='category-filter',
options=[{'label': c, 'value': c} for c in categories],
value='全部',
clearable=False
)
], style={'width': '23%', 'display': 'inline-block', 'marginRight': '2%'}),

html.Div([
html.Label('销售渠道:', style={'fontWeight': 'bold'}),
dcc.Dropdown(
id='channel-filter',
options=[{'label': c, 'value': c} for c in channels],
value='全部',
clearable=False
)
], style={'width': '23%', 'display': 'inline-block', 'marginRight': '2%'}),

html.Div([
html.Label('时间范围:', style={'fontWeight': 'bold'}),
dcc.DatePickerRange(
id='date-range',
start_date=df['订单日期'].min(),
end_date=df['订单日期'].max(),
display_format='YYYY-MM-DD'
)
], style={'width': '25%', 'display': 'inline-block'})
], style={'padding': '20px', 'backgroundColor': 'white',
'borderRadius': '10px', 'margin': '0 20px 20px 20px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'}),

# KPI指标卡
html.Div(id='kpi-cards', style={
'display': 'flex', 'justifyContent': 'space-around',
'margin': '0 20px 20px 20px'
}),

# 图表区域
html.Div([
# 第一行:趋势图
html.Div([
dcc.Graph(id='trend-chart', style={'height': '400px'})
], style={'width': '100%', 'marginBottom': '20px'}),

# 第二行:双列布局
html.Div([
html.Div([
dcc.Graph(id='category-chart', style={'height': '350px'})
], style={'width': '48%', 'display': 'inline-block',
'marginRight': '4%'}),

html.Div([
dcc.Graph(id='region-chart', style={'height': '350px'})
], style={'width': '48%', 'display': 'inline-block'})
]),

# 第三行:渠道分析和TOP产品
html.Div([
html.Div([
dcc.Graph(id='channel-chart', style={'height': '350px'})
], style={'width': '48%', 'display': 'inline-block',
'marginRight': '4%', 'marginTop': '20px'}),

html.Div([
dcc.Graph(id='customer-chart', style={'height': '350px'})
], style={'width': '48%', 'display': 'inline-block',
'marginTop': '20px'})
])
], style={'padding': '0 20px'}),

# 数据表格
html.Div([
html.H3('📋 详细数据', style={'marginTop': '30px'}),
html.Div(id='data-table')
], style={'padding': '20px', 'margin': '20px',
'backgroundColor': 'white', 'borderRadius': '10px',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'}),

# 自动刷新
dcc.Interval(id='interval-component', interval=300*1000, n_intervals=0) # 5分钟刷新
])

回调函数

1
2
3
4
5
6
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@callback(
[Output('kpi-cards', 'children'),
Output('trend-chart', 'figure'),
Output('category-chart', 'figure'),
Output('region-chart', 'figure'),
Output('channel-chart', 'figure'),
Output('customer-chart', 'figure'),
Output('data-table', 'children')],
[Input('region-filter', 'value'),
Input('category-filter', 'value'),
Input('channel-filter', 'value'),
Input('date-range', 'start_date'),
Input('date-range', 'end_date')]
)
def update_dashboard(region, category, channel, start_date, end_date):
"""根据筛选条件更新整个仪表盘"""

# 数据筛选
filtered_df = df.copy()
filtered_df = filtered_df[
(filtered_df['订单日期'] >= start_date) &
(filtered_df['订单日期'] <= end_date)
]

if region != '全部':
filtered_df = filtered_df[filtered_df['地区'] == region]
if category != '全部':
filtered_df = filtered_df[filtered_df['产品类别'] == category]
if channel != '全部':
filtered_df = filtered_df[filtered_df['销售渠道'] == channel]

# 计算KPI
total_sales = filtered_df['订单金额'].sum()
total_profit = filtered_df['利润'].sum()
total_orders = len(filtered_df)
avg_order_value = filtered_df['订单金额'].mean()
profit_margin = (total_profit / total_sales * 100) if total_sales > 0 else 0

# KPI卡片
kpi_cards = html.Div([
create_kpi_card('💰 总销售额', f'¥{total_sales:,.0f}', '#3498db'),
create_kpi_card('📈 总利润', f'¥{total_profit:,.0f}', '#2ecc71'),
create_kpi_card('📦 订单数', f'{total_orders:,}', '#e74c3c'),
create_kpi_card('💵 客单价', f'¥{avg_order_value:.0f}', '#f39c12'),
create_kpi_card('📊 利润率', f'{profit_margin:.1f}%', '#9b59b6')
], style={'display': 'flex', 'justifyContent': 'space-around', 'width': '100%'})

# 趋势图
trend_fig = create_trend_chart(filtered_df)

# 品类分析图
category_fig = create_category_chart(filtered_df)

# 地区分析图
region_fig = create_region_chart(filtered_df)

# 渠道分析图
channel_fig = create_channel_chart(filtered_df)

# 客户分析图
customer_fig = create_customer_chart(filtered_df)

# 数据表格
table = create_data_table(filtered_df)

return kpi_cards, trend_fig, category_fig, region_fig, channel_fig, customer_fig, table

def create_kpi_card(title, value, color):
"""创建KPI卡片"""
return html.Div([
html.H4(title, style={'color': '#7f8c8d', 'margin': '0', 'fontSize': '14px'}),
html.H2(value, style={'color': color, 'margin': '10px 0', 'fontSize': '28px'})
], style={
'backgroundColor': 'white', 'padding': '20px', 'borderRadius': '10px',
'textAlign': 'center', 'width': '18%',
'boxShadow': '0 2px 4px rgba(0,0,0,0.1)'
})

图表组件

1
2
3
4
5
6
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def create_trend_chart(df):
"""销售趋势图"""
daily_sales = df.groupby('订单日期').agg({
'订单金额': 'sum',
'利润': 'sum'
}).reset_index()

fig = make_subplots(specs=[[{"secondary_y": True}]])

fig.add_trace(
go.Scatter(x=daily_sales['订单日期'], y=daily_sales['订单金额'],
mode='lines', name='销售额', line=dict(color='#3498db', width=2)),
secondary_y=False
)

fig.add_trace(
go.Scatter(x=daily_sales['订单日期'], y=daily_sales['利润'],
mode='lines', name='利润', line=dict(color='#2ecc71', width=2)),
secondary_y=True
)

fig.update_layout(
title='销售与利润趋势',
xaxis_title='日期',
hovermode='x unified',
template='plotly_white',
height=400
)

fig.update_yaxes(title_text="销售额", secondary_y=False)
fig.update_yaxes(title_text="利润", secondary_y=True)

return fig

def create_category_chart(df):
"""品类分析图"""
category_stats = df.groupby('产品类别').agg({
'订单金额': 'sum',
'利润': 'sum'
}).reset_index().sort_values('订单金额', ascending=True)

fig = go.Figure()

fig.add_trace(go.Bar(
y=category_stats['产品类别'],
x=category_stats['订单金额'],
name='销售额',
orientation='h',
marker_color='#3498db'
))

fig.add_trace(go.Bar(
y=category_stats['产品类别'],
x=category_stats['利润'],
name='利润',
orientation='h',
marker_color='#2ecc71'
))

fig.update_layout(
title='各品类销售与利润',
barmode='group',
template='plotly_white',
height=350,
showlegend=True
)

return fig

def create_region_chart(df):
"""地区分析图"""
region_stats = df.groupby('地区').agg({
'订单金额': 'sum',
'订单ID': 'count'
}).rename(columns={'订单ID': '订单数'}).reset_index()

fig = px.pie(region_stats, values='订单金额', names='地区',
title='各地区销售额占比',
color_discrete_sequence=px.colors.sequential.Blues)

fig.update_traces(textposition='inside', textinfo='percent+label')
fig.update_layout(template='plotly_white', height=350)

return fig

def create_channel_chart(df):
"""渠道分析图"""
channel_stats = df.groupby('销售渠道').agg({
'订单金额': ['sum', 'mean'],
'订单ID': 'count'
}).reset_index()
channel_stats.columns = ['销售渠道', '总销售额', '平均订单金额', '订单数']

fig = px.bar(channel_stats, x='销售渠道', y=['总销售额', '订单数'],
title='各渠道销售表现',
barmode='group',
color_discrete_sequence=['#3498db', '#e74c3c'])

fig.update_layout(template='plotly_white', height=350)

return fig

def create_customer_chart(df):
"""客户分析图"""
customer_stats = df.groupby('客户类型').agg({
'订单金额': 'sum',
'利润': 'sum',
'订单ID': 'count'
}).reset_index()

fig = make_subplots(rows=1, cols=2, specs=[[{"type": "pie"}, {"type": "bar"}]],
subplot_titles=('客户类型占比', '客户价值对比'))

fig.add_trace(
go.Pie(labels=customer_stats['客户类型'],
values=customer_stats['订单金额'],
name="销售额"),
row=1, col=1
)

fig.add_trace(
go.Bar(x=customer_stats['客户类型'],
y=customer_stats['利润'],
name="利润",
marker_color=['#2ecc71', '#f39c12']),
row=1, col=2
)

fig.update_layout(template='plotly_white', height=350, showlegend=False)

return fig

def create_data_table(df):
"""创建数据摘要表格"""
summary = df.groupby('产品类别').agg({
'订单金额': ['sum', 'mean', 'count'],
'利润': 'sum'
}).round(2)

summary.columns = ['总销售额', '平均订单金额', '订单数', '总利润']
summary = summary.reset_index()
summary['利润率'] = (summary['总利润'] / summary['总销售额'] * 100).round(1)

return html.Table([
html.Thead(html.Tr([html.Th(col) for col in summary.columns])),
html.Tbody([html.Tr([html.Td(summary.iloc[i][col]) for col in summary.columns])
for i in range(len(summary))])
], style={'width': '100%', 'borderCollapse': 'collapse'})

运行应用

1
2
3
4
if __name__ == '__main__':
print("启动仪表盘服务器...")
print("请在浏览器访问: http://127.0.0.1:8050")
app.run_server(debug=True, host='0.0.0.0', port=8050)

部署到服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
# 生产环境部署配置(app.py)
import dash
from dash import dcc, html
# ... 其他导入

# 关闭调试模式
app = dash.Dash(__name__)
server = app.server # 用于Gunicorn

# ... 布局和回调代码 ...

if __name__ == '__main__':
app.run_server(debug=False, host='0.0.0.0', port=8050)

使用Gunicorn启动:

1
gunicorn -w 4 -b 0.0.0.0:8050 app:server

项目总结

学到的技能

  • ✅ Dash框架开发Web应用
  • ✅ Plotly交互式可视化
  • ✅ 响应式布局设计
  • ✅ 回调函数与状态管理
  • ✅ 数据筛选与联动

仪表盘设计原则

  1. 一页原则:关键信息一眼看完
  2. 层次清晰:KPI → 趋势 → 明细
  3. 交互友好:筛选器要明显易用
  4. 配色专业:商务风格,避免花哨
  5. 响应及时:数据量大时考虑缓存

扩展方向

  • 接入真实数据库(MySQL/PostgreSQL)
  • 添加用户登录和权限控制
  • 实现定时数据更新
  • 移动端适配优化
  • 导出PDF报告功能

课程完结

恭喜!你已经完成了Python数据分析实战课程的全部内容!

回顾所学

  1. ✅ Python数据分析基础(NumPy/Pandas)
  2. ✅ 数据清洗与变换技巧
  3. ✅ 数据可视化(Matplotlib/Seaborn/pyecharts)
  4. ✅ 描述性统计与假设检验
  5. ✅ 项目1:销售报表自动化
  6. ✅ 项目2:RFM用户分层
  7. ✅ 项目3:库存预测分析
  8. ✅ 项目4:竞品价格监控
  9. ✅ 项目5:数据仪表盘搭建

下一步建议

  • 多做项目,积累实战经验
  • 学习机器学习,进阶预测分析
  • 考取数据分析师认证
  • 关注行业动态,持续学习

💬 加入学习交流群

扫码加入Python学习交流群,和数千名同学一起进步:

👉 点击加入交流群

群里不定期分享:

  • 数据分析实战案例
  • Python学习资料
  • 求职面试经验
  • 行业最新动态

推荐:AI Python数据分析实战营

🎁 限时福利:送《利用Python进行数据分析》实体书

👉 点击了解详情


进阶:Streamlit搭建Web仪表盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# app.py
import streamlit as st
import pandas as pd
import plotly.express as px

st.set_page_config(page_title='数据分析仪表盘', layout='wide')

# 侧边栏
st.sidebar.title('筛选条件')
date_range = st.sidebar.date_input('日期范围')
category = st.sidebar.multiselect('品类', options=['A', 'B', 'C'])

# 主区域
st.title('📊 销售分析仪表盘')

col1, col2, col3 = st.columns(3)
col1.metric('总销售额', '¥1,234,567', '+12.3%')
col2.metric('订单数', '8,765', '+5.2%')
col3.metric('客单价', '¥141', '+6.7%')

# 图表
fig = px.line(df, x='date', y='sales', color='category')
st.plotly_chart(fig, use_container_width=True)

# 运行:streamlit run app.py

自动刷新仪表盘

1
2
3
4
5
6
7
8
# 定时刷新数据
import time

placeholder = st.empty()
while True:
df = pd.read_csv('latest_data.csv')
placeholder.dataframe(df)
time.sleep(300) # 每5分钟刷新

避坑指南

❌ 坑1:仪表盘加载慢

1
2
3
4
5
6
7
# 使用缓存加速
@st.cache_data(ttl=300) # 缓存5分钟
def load_data():
return pd.read_csv('large_dataset.csv')

# 只加载必要的列
df = pd.read_csv('data.csv', usecols=['date', 'category', 'amount'])

Gradio搭建仪表盘(比Streamlit更简单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# pip install gradio
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt

def show_analysis(category, date_range):
df = pd.read_csv('sales_data.csv')

# 筛选
filtered = df[df['category'] == category]

# 生成图表
fig, ax = plt.subplots(figsize=(10, 5))
filtered.groupby('date')['sales'].sum().plot(ax=ax)
ax.set_title(f'{category}销售趋势')

return fig, f"总销售额: ¥{filtered['sales'].sum():,.0f}"

demo = gr.Interface(
fn=show_analysis,
inputs=[
gr.Dropdown(['电子产品', '服装', '食品', '家居'], label='品类'),
gr.Textbox(label='日期范围')
],
outputs=[gr.Plot(), gr.Text()],
title='销售分析仪表盘'
)

demo.launch() # 浏览器自动打开

Dashboard设计原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. 信息层次清晰
├── 一级指标(KPI卡片)→ 最显眼
├── 二级指标(趋势图)→ 中等位置
└── 三级指标(明细表)→ 可展开查看

2. 配色一致
├── 主色:品牌色
├── 辅色:2-3种搭配色
└── 警告色:红/黄/绿

3. 交互友好
├── 筛选器放在顶部或侧边
├── 图表支持悬停查看数据
└── 数据表格支持排序和搜索

4. 响应式设计
├── 支持不同屏幕尺寸
└── 移动端可查看关键指标

定时刷新仪表盘数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import streamlit as st
import time
from datetime import datetime

# 自动刷新
st.sidebar.markdown(f"最后更新: {datetime.now()}")

if st.sidebar.button('刷新数据'):
st.rerun()

# 或者使用st.cache_data设置过期时间
@st.cache_data(ttl=300) # 5分钟过期
def load_fresh_data():
return pd.read_csv('latest_data.csv')

仪表盘设计模板

1
2
3
4
5
6
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
51
52
53
54
55
56
57
58
59
60
61
# Streamlit仪表盘完整模板
import streamlit as st
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

st.set_page_config(page_title='数据分析仪表盘', layout='wide', page_icon='📊')

# ========== 侧边栏 ==========
st.sidebar.title('⚙️ 筛选条件')
date_range = st.sidebar.date_input('📅 日期范围', [])
category = st.sidebar.multiselect('📦 品类', ['电子产品', '服装', '食品', '家居'])
region = st.sidebar.multiselect('🌍 地区', ['华东', '华南', '华北', '西南'])

# ========== 主区域 ==========
st.title('📊 数据分析仪表盘')
st.markdown('---')

# KPI卡片
col1, col2, col3, col4 = st.columns(4)
col1.metric('💰 总销售额', '¥1,234,567', '+12.3%', delta_color='normal')
col2.metric('📦 订单数', '8,765', '+5.2%')
col3.metric('💎 客单价', '¥141', '+6.7%')
col4.metric('👥 活跃用户', '3,456', '-2.1%', delta_color='inverse')

st.markdown('---')

# 图表区域
col_left, col_right = st.columns(2)

with col_left:
st.subheader('📈 销售趋势')
fig_trend = px.line(df, x='date', y='sales', color='category')
st.plotly_chart(fig_trend, use_container_width=True)

with col_right:
st.subheader('🏆 品类排行')
fig_bar = px.bar(df.groupby('category')['sales'].sum().reset_index(),
x='category', y='sales', color='category')
st.plotly_chart(fig_bar, use_container_width=True)

# 第二行图表
col_left2, col_right2 = st.columns(2)

with col_left2:
st.subheader('🗺️ 地区分布')
fig_pie = px.pie(df, values='sales', names='region')
st.plotly_chart(fig_pie, use_container_width=True)

with col_right2:
st.subheader('📊 相关性分析')
fig_scatter = px.scatter(df, x='price', y='sales', color='category',
size='quantity', hover_data=['product_name'])
st.plotly_chart(fig_scatter, use_container_width=True)

# 数据表
st.markdown('---')
st.subheader('📋 明细数据')
st.dataframe(df, use_container_width=True)

# 运行命令:streamlit run dashboard.py

仪表盘部署方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 方案1:本地运行
streamlit run dashboard.py

# 方案2:Streamlit Cloud(免费)
# 1. 把代码推到GitHub
# 2. 访问 share.streamlit.io
# 3. 连接GitHub仓库,一键部署

# 方案3:内网部署
# 1. 服务器安装streamlit
# 2. nohup streamlit run dashboard.py --server.port 8501 &
# 3. 团队通过 http://server:8501 访问

# 方案4:Docker部署
# Dockerfile
# FROM python:3.10
# COPY . /app
# RUN pip install -r requirements.txt
# EXPOSE 8501
# CMD ["streamlit", "run", "dashboard.py"]

Streamlit仪表盘完整代码

1
2
3
4
5
6
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 保存为 dashboard.py,运行 streamlit run dashboard.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import date

# ========== 页面配置 ==========
st.set_page_config(
page_title='数据分析仪表盘',
page_icon='📊',
layout='wide'
)

# ========== 加载数据 ==========
@st.cache_data
def load_data():
np.random.seed(42)
n = 10000
df = pd.DataFrame({
'date': pd.date_range('2025-01-01', periods=n, freq='1H'),
'product': np.random.choice(['手机', '电脑', '平板', '耳机', '手表'], n),
'channel': np.random.choice(['线上', '线下'], n, p=[0.7, 0.3]),
'city': np.random.choice(['北京', '上海', '广州', '深圳', '成都'], n),
'amount': np.random.exponential(1000, n).round(2),
'quantity': np.random.randint(1, 5, n)
})
return df

df = load_data()

# ========== 侧边栏 ==========
st.sidebar.title('⚙️ 筛选')
cities = st.sidebar.multiselect('城市', df['city'].unique(), df['city'].unique())
products = st.sidebar.multiselect('商品', df['product'].unique(), df['product'].unique())
channels = st.sidebar.multiselect('渠道', df['channel'].unique(), df['channel'].unique())

# 筛选数据
filtered = df[df['city'].isin(cities) & df['product'].isin(products) & df['channel'].isin(channels)]

# ========== 主区域 ==========
st.title('📊 数据分析仪表盘')
st.caption(f'数据更新时间: {date.today()}')

# KPI卡片
col1, col2, col3, col4 = st.columns(4)
col1.metric('💰 总销售额', f"¥{filtered['amount'].sum():,.0f}")
col2.metric('📦 订单数', f"{len(filtered):,}")
col3.metric('💎 客单价', f"¥{filtered['amount'].mean():,.0f}")
col4.metric('📈 日均销售', f"¥{filtered.groupby(filtered['date'].dt.date)['amount'].sum().mean():,.0f}")

st.divider()

# 图表区域
col_left, col_right = st.columns(2)

with col_left:
st.subheader('📈 销售趋势')
daily = filtered.set_index('date').resample('D')['amount'].sum().reset_index()
fig = px.line(daily, x='date', y='amount')
st.plotly_chart(fig, use_container_width=True)

with col_right:
st.subheader('🏆 品类排行')
cat_sales = filtered.groupby('product')['amount'].sum().reset_index()
fig = px.bar(cat_sales, x='product', y='amount', color='product')
st.plotly_chart(fig, use_container_width=True)

# 第二行
col_left2, col_right2 = st.columns(2)

with col_left2:
st.subheader('🗺️ 渠道占比')
ch_sales = filtered.groupby('channel')['amount'].sum().reset_index()
fig = px.pie(ch_sales, values='amount', names='channel')
st.plotly_chart(fig, use_container_width=True)

with col_right2:
st.subheader('📊 价格分布')
fig = px.histogram(filtered, x='amount', nbins=50, color='channel')
st.plotly_chart(fig, use_container_width=True)

# 数据表
st.divider()
st.subheader('📋 明细数据')
st.dataframe(filtered.head(100), use_container_width=True)

仪表盘设计checklist

  • 页面标题和图标
  • KPI卡片(4-6个关键指标)
  • 趋势图(折线/面积图)
  • 对比图(柱状/饼图)
  • 分布图(直方图/箱线图)
  • 筛选器(日期/品类/地区)
  • 明细数据表
  • 响应式布局

仪表盘设计模式对比

方案优点缺点适用场景
Streamlit代码简单,Python原生不够灵活快速原型
Dash(Plotly)灵活,专业学习曲线陡专业仪表盘
Gradio最简单功能有限演示展示
Superset无需代码需要部署企业级
Tableau强大的可视化收费商业分析

Dash仪表盘模板

1
2
3
4
5
6
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
51
52
53
54
55
# pip install dash plotly
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import numpy as np

app = Dash(__name__)

# 模拟数据
np.random.seed(42)
df = pd.DataFrame({
'date': pd.date_range('2025-01-01', periods=365),
'sales': np.cumsum(np.random.randn(365)) + 500,
'category': np.random.choice(['A', 'B', 'C', 'D'], 365)
})

app.layout = html.Div([
html.H1('数据分析仪表盘', style={'textAlign': 'center'}),

# KPI卡片
html.Div([
html.Div([html.H3('总销售额'), html.H2(f'¥{df["sales"].sum():,.0f}')],
style={'width': '23%', 'display': 'inline-block', 'textAlign': 'center', 'background': '#f0f0f0', 'padding': '20px', 'margin': '1%'}),
html.Div([html.H3('日均销售'), html.H2(f'¥{df["sales"].mean():,.0f}')],
style={'width': '23%', 'display': 'inline-block', 'textAlign': 'center', 'background': '#f0f0f0', 'padding': '20px', 'margin': '1%'}),
html.Div([html.H3('最高单日'), html.H2(f'¥{df["sales"].max():,.0f}')],
style={'width': '23%', 'display': 'inline-block', 'textAlign': 'center', 'background': '#f0f0f0', 'padding': '20px', 'margin': '1%'}),
html.Div([html.H3('数据天数'), html.H2(f'{len(df)}天')],
style={'width': '23%', 'display': 'inline-block', 'textAlign': 'center', 'background': '#f0f0f0', 'padding': '20px', 'margin': '1%'}),
]),

# 图表
dcc.Graph(id='trend-chart'),
dcc.Graph(id='category-chart'),

# 筛选器
dcc.Dropdown(id='category-filter', options=[{'label': c, 'value': c} for c in df['category'].unique()],
value=df['category'].unique(), multi=True)
])

@app.callback(
[Output('trend-chart', 'figure'), Output('category-chart', 'figure')],
Input('category-filter', 'value')
)
def update_charts(selected_categories):
filtered = df[df['category'].isin(selected_categories)]

fig1 = px.line(filtered, x='date', y='sales', title='销售趋势')
fig2 = px.bar(filtered.groupby('category')['sales'].sum().reset_index(),
x='category', y='sales', title='品类对比')

return fig1, fig2

if __name__ == '__main__':
app.run_server(debug=True, port=8050)

数据刷新与部署方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 方案1:定时刷新数据
import schedule
import time

def refresh_data():
global df
df = pd.read_csv('latest_data.csv')
print(f'数据已刷新: {len(df)}条')

schedule.every(5).minutes.do(refresh_data)

# 方案2:API方式提供数据
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/data')
def get_data():
return jsonify(df.to_dict(orient='records'))

# 方案3:Docker部署
# Dockerfile:
# FROM python:3.10
# WORKDIR /app
# COPY requirements.txt .
# RUN pip install -r requirements.txt
# COPY . .
# EXPOSE 8050
# CMD ["python", "dashboard.py"]

课程导航

上一篇: 项目4-竞品价格监控与分析

系列完结


PS:从数据小白到能搭仪表盘,你已经走了很远。记住,数据分析的核心不是工具,而是用数据解决问题的思维。继续加油!



📚 推荐教材

主教材《Excel+Python 飞速搞定数据分析与处理(图灵出品)》

💬 联系我

平台账号/链接
微信扫码加好友
微博@程序员晚枫
知乎@程序员晚枫
抖音@程序员晚枫
小红书@程序员晚枫
B 站Python 自动化办公社区

主营业务:AI 编程培训、企业内训、技术咨询

🎓 AI 编程实战课程

想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!