前言

作为Python的新一代数据可视化库,Plotly相比Matplotlib有两大优势:

  1. 简洁易用:图表对象像嵌套dict,通过修改属性改变形态,学习成本远低于Matplotlib
  2. 动态交互:图表可交互——点击查看数据、拖拽放大、隐藏数据列,也可导出为静态图

两种接口

接口 简称 特点 适用场景
plotly.graph_objects go 面向对象,精细控制 定制化图表
plotly.express px 函数式,快速绘图 探索性分析

核心思想:Figure = data(数据) + layout(布局)

# Figure就像一个嵌套的Python dict
fig = go.Figure(
    data=[go.Scatter(x=[1,2,3], y=[4,5,6])],
    layout=go.Layout(title="My Chart")
)

Part 1:plotly.graph_objects 绘图原理

Figure结构

import plotly.graph_objects as go

# 创建Figure
fig = go.Figure()

# 添加trace(数据层)
fig.add_trace(go.Scatter(
    x=[1, 2, 3, 4],
    y=[10, 11, 12, 13],
    mode='lines+markers',
    name='数据系列1'
))

# 修改layout(布局层)
fig.update_layout(
    title='简单折线图',
    xaxis_title='X轴',
    yaxis_title='Y轴',
    template='plotly_white'
)

fig.show()

常用Trace类型

类型 用途 示例
go.Scatter 折线图/散点图 趋势分析
go.Bar 柱形图 分类对比
go.Histogram 直方图 分布分析
go.Heatmap 热力图 相关性矩阵
go.Pie 饼图 占比分析
go.Box 箱线图 离群值检测
go.Surface 3D曲面 三维数据

多子图

from plotly.subplots import make_subplots

# 创建2x2子图布局
fig = make_subplots(
    rows=2, cols=2,
    subplot_titles=('图1', '图2', '图3', '图4')
)

# 添加trace到指定位置
fig.add_trace(go.Scatter(x=[1,2,3], y=[4,5,6]), row=1, col=1)
fig.add_trace(go.Bar(x=['A','B','C'], y=[10,20,30]), row=1, col=2)
fig.add_trace(go.Histogram(x=np.random.randn(1000)), row=2, col=1)
fig.add_trace(go.Pie(labels=['A','B','C'], values=[30,40,30]), row=2, col=2)

fig.update_layout(height=600, showlegend=False)
fig.show()

Part 2:plotly.express 快速绘图

基本用法

import plotly.express as px
import pandas as pd

# 准备数据
df = pd.DataFrame({
    '城市': ['北京', '上海', '广州', '深圳'],
    'GDP': [36000, 38000, 25000, 27000],
    '人口': [2154, 2487, 1868, 1756]
})

# 一行代码绘图
fig = px.bar(df, x='城市', y='GDP', title='城市GDP对比')
fig.show()

内置数据集

# Plotly内置数据集
df = px.data.gapminder()  # Gapminder世界发展数据
df = px.data.iris()       # 鸢尾花数据集
df = px.data.tips()       # 餐厅小费数据
df = px.data.stocks()     # 股票数据

Part 3:5种图表实战对比

1. 柱形图

# go方式
fig = go.Figure()
fig.add_trace(go.Bar(
    x=['产品A', '产品B', '产品C'],
    y=[100, 150, 120],
    name='销量',
    marker_color='indianred'
))
fig.update_layout(title='产品销量对比')

# px方式(更简洁)
fig = px.bar(df, x='产品', y='销量', color='类别',
             barmode='group', title='产品销量对比')

2. 折线图

# 股票数据示例
df = px.data.stocks()

# go方式
fig = go.Figure()
for col in ['GOOG', 'AAPL', 'AMZN']:
    fig.add_trace(go.Scatter(
        x=df['date'], y=df[col],
        mode='lines',
        name=col
    ))
fig.update_layout(title='科技股走势')

# px方式
fig = px.line(df, x='date', y=['GOOG', 'AAPL', 'AMZN'],
              title='科技股走势')

3. 散点图

# 鸢尾花数据集
df = px.data.iris()

# go方式
fig = go.Figure()
for species in df['species'].unique():
    subset = df[df['species'] == species]
    fig.add_trace(go.Scatter(
        x=subset['sepal_width'],
        y=subset['sepal_length'],
        mode='markers',
        name=species
    ))

# px方式(自动分组+颜色映射)
fig = px.scatter(df, x='sepal_width', y='sepal_length',
                 color='species', size='petal_length',
                 title='鸢尾花数据分布')

4. 热力图

import numpy as np

# 相关性矩阵
corr = df[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']].corr()

# go方式
fig = go.Figure(data=go.Heatmap(
    z=corr.values,
    x=corr.columns,
    y=corr.columns,
    colorscale='RdBu_r',
    zmin=-1, zmax=1
))
fig.update_layout(title='特征相关性热力图')

# px方式
fig = px.imshow(corr, text_auto='.2f',
                color_continuous_scale='RdBu_r',
                title='特征相关性热力图')

5. 直方图

# 生成正态分布数据
data = np.random.randn(1000)

# go方式
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=data,
    nbinsx=30,
    name='正态分布',
    opacity=0.75
))
fig.update_layout(title='数据分布直方图')

# px方式
fig = px.histogram(x=data, nbins=30,
                   title='数据分布直方图',
                   labels={'x': '', 'y': '频次'})

Part 4:样式与主题

内置主题

# 可用主题
themes = [
    'plotly',          # 默认主题
    'plotly_white',    # 白色背景
    'plotly_dark',     # 暗色主题
    'ggplot2',         # 仿ggplot2
    'seaborn',         # 仿seaborn
    'simple_white',    # 简洁白色
    'presentation',    # 演示用
]

fig.update_layout(template='plotly_dark')

自定义样式

fig.update_layout(
    # 标题
    title=dict(
        text='图表标题',
        font=dict(size=20, color='black'),
        x=0.5  # 居中
    ),
    # 坐标轴
    xaxis=dict(
        title='X轴标题',
        showgrid=True,
        gridwidth=1,
        gridcolor='lightgray'
    ),
    yaxis=dict(title='Y轴标题'),
    # 图例
    legend=dict(
        orientation='h',  # 水平排列
        yanchor='bottom',
        y=1.02,
        xanchor='right',
        x=1
    ),
    # 尺寸
    width=800,
    height=500,
    # 边距
    margin=dict(l=50, r=50, t=80, b=50)
)

Part 5:导出与嵌入

导出静态图

# 需要安装kaleido: pip install kaleido
fig.write_image('chart.png')      # PNG
fig.write_image('chart.svg')      # SVG
fig.write_image('chart.pdf')      # PDF
fig.write_html('chart.html')      # 交互式HTML

嵌入Web应用

# Streamlit
import streamlit as st
st.plotly_chart(fig, use_container_width=True)

# Dash
from dash import Dash, dcc
app = Dash()
app.layout = dcc.Graph(figure=fig)

# Jupyter Notebook
fig.show()  # 直接显示

实战案例:股票K线图

import plotly.graph_objects as go
from plotly.subplots import make_subplots

# 创建带成交量的K线图
fig = make_subplots(
    rows=2, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.03,
    row_heights=[0.7, 0.3]
)

# K线
fig.add_trace(go.Candlestick(
    x=df['date'],
    open=df['open'],
    high=df['high'],
    low=df['low'],
    close=df['close'],
    name='K线'
), row=1, col=1)

# 成交量
fig.add_trace(go.Bar(
    x=df['date'],
    y=df['volume'],
    name='成交量',
    marker_color='rgba(0,0,255,0.3)'
), row=2, col=1)

fig.update_layout(
    title='股票K线图',
    xaxis_rangeslider_visible=False,
    template='plotly_white',
    height=600
)

fig.show()

go vs px 选择指南

场景 推荐 原因
探索性数据分析 px 一行代码,快速迭代
定制化报告 go 精细控制每个元素
多子图布局 go + make_subplots 灵活布局
动画/滑块 px 内置动画支持
3D图表 go 更多3D类型
发布级图表 go 完全控制样式

总结

Plotly的两种接口互补:

  • px:快速原型,适合数据探索阶段
  • go:精细控制,适合最终呈现阶段

两者可以混用——用px快速验证,再用go精调样式。

# px创建 → go修改
fig = px.scatter(df, x='x', y='y', color='group')
fig.update_traces(marker=dict(size=10, opacity=0.7))
fig.update_layout(template='plotly_dark')

Notebook文件:F:\202507\plotly深入浅出.ipynb,5060+行完整教程,包含所有图表的代码和输出。

Stay tuned! 🚀