Plotly 交互图表:可探索的 Web 可视化

FreeGuideOnline 最新 2026-06-16

什么是 Plotly 交互图表

Plotly 是一个开源的交互式图表库,支持 Python、R 和 Julia 等语言。通过 Plotly 创建的图表天生具备缩放、平移、悬停提示、图例过滤等交互能力,并可直接嵌入网页、Jupyter Notebook 或构建仪表板。与 Matplotlib 生成的静态图片不同,用户可以在浏览器中自由探索数据,获取更深层次的信息。

Plotly 的核心优势在于:

  • 无需 JavaScript 即可生成 Web 交互图表
  • 图表类型丰富,从基础统计图到 3D 曲面、地理地图、网络图
  • 与 Dash 无缝集成,可直接转为生产级 Web 应用
  • 响应式设计,自动适配屏幕尺寸

环境准备与安装

安装 Plotly

推荐使用 Python 的包管理工具进行安装,Plotly 分为两个主要模块:

  • plotly.express:高层 API,用极少代码生成美观图表,适合快速探索。
  • plotly.graph_objects:底层 API,提供更细粒度的控制,适合高度定制化场景。
pip install plotly

若需要在 Jupyter 环境中渲染,建议一并安装 nbformatipywidgets

pip install "notebook>=5.3" "ipywidgets>=7.5"

在 Jupyter Notebook 中启用

安装后直接在 Notebook 中导入即可,Plotly 会自动检测环境并渲染图表。如果出现渲染问题,可手动设置渲染器:

import plotly.io as pio
pio.renderers.default = 'notebook'

对于 VS Code 等其他编辑器,通常使用 plotly.io.show 在浏览器中打开交互图表。

第一张交互图表:从 express 开始

plotly.express (别名 px) 是快速出图的绝佳选择。下面用内置的 gapminder 数据集创建一张动态气泡图。

import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.scatter(
    df, x="gdpPercap", y="lifeExp",
    size="pop", color="continent",
    hover_name="country", log_x=True,
    size_max=60, title="2007年各国GDP与人均寿命"
)
fig.show()

图表交互体验

  • 鼠标悬停点可查看国家、人口、GDP等详细信息。
  • 拖拽框选放大局部区域,双击重置。
  • 点击图例中的大洲名称可隐藏/显示对应数据点。
  • 右上角工具栏支持保存为 PNG、SVG 或全屏查看。

核心交互功能详解

Plotly 的交互并非事后添加的装饰,而是融入图表基因。以下功能开箱即用:

悬停标签与自定义信息

默认悬停显示所有映射字段,可通过 hover_data 控制显示内容,或使用 update_traces 自定义模板。

fig = px.scatter(
    df, x="gdpPercap", y="lifeExp", color="continent",
    hover_data={"pop": True, "iso_alpha": True}
)
# 自定义悬停模板
fig.update_traces(
    hovertemplate="<b>%{hovertext}</b><br>GDP: %{x:.2f}<br>人口: %{marker.size}<extra></extra>"
)

<extra></extra> 用于移除默认的 trace 名称。

缩放与平移

图表默认支持 拖拽 平移和 滚轮 缩放。可以在 config 参数中配置缩放模式:

fig.show(config={'scrollZoom': True})  # 开启滚轮缩放

图例交互与筛选

图例支持单击隐藏/显示 trace,双击隔离显示某个 trace。该行为可以通过 legend 属性控制:

fig.update_layout(legend_itemclick="toggleothers")  # 单击隔离显示

选择与联动

对于包含多个子图的场景,通过 hovermode='x unified' 可实现跨子图的统一悬停参考线。进阶用法中,Plotly 可通过 Dash 实现图表间联动筛选,后文会介绍。

定制图表外观:布局与样式

更新布局:标题、轴标签、背景

使用 update_layout 可以全面修改图表外观。

fig.update_layout(
    title={
        'text': "GDP与人均寿命关系",
        'xanchor': 'center',
        'x': 0.5
    },
    xaxis_title="人均GDP(对数尺度)",
    yaxis_title="预期寿命",
    plot_bgcolor='rgba(240,240,240,1)',
    font=dict(family="Arial", size=14)
)

颜色与主题

Plotly 内置多种配色模板,可通过 template 一键切换:

fig = px.scatter(df, x="gdpPercap", y="lifeExp", template="plotly_dark")

常用模板:"ggplot2", "seaborn", "plotly_white", "plotly_dark", "presentation"。也可自定义离散色板或连续色阶。

fig.update_layout(colorway=['#636EFA', '#EF553B', '#00CC96'])

注释与形状

使用 add_annotationadd_shape 在图表上添加说明或标记区域:

fig.add_annotation(
    x=4.5, y=80, text="高收入高寿命",
    showarrow=True, arrowhead=1, ax=20, ay=-40
)
fig.add_shape(
    type="rect", x0=5, x1=5.5, y0=50, y1=85,
    fillcolor="LightSalmon", opacity=0.3, line_width=0
)

不止于散点图:常用交互图表类型

折线图与时间序列

df_line = px.data.gapminder().query("country=='China'")
fig = px.line(df_line, x='year', y='lifeExp', markers=True, title='中国预期寿命变化')
fig.update_xaxes(rangeslider_visible=True)  # 添加范围滑块,便于缩放时间窗口

范围滑块是时间序列的强力工具,用户可拖拽滑块自由调整查看范围。

柱状图与聚合

fig = px.bar(df, x='continent', y='pop', color='continent',
             title='各大洲总人口', labels={'pop':'总人口'})

悬停可显示每根柱子的精确数值,canvas 模式渲染保证大数据量下的性能。

热力图与矩阵探索

import plotly.express as px
z = [[.1, .3, .5, .7],
     [.2, .4, .6, .8],
     [.3, .5, .7, .9]]
fig = px.imshow(z, text_auto=True, aspect="auto", title='矩阵热力图')
fig.update_xaxes(side="top")  # 将 x 轴移至顶部

悬停可显示行列索引与精确值,支持色阶自定义。

3D 散点图

fig = px.scatter_3d(df, x='gdpPercap', y='lifeExp', z='pop',
                     color='continent', size='pop', hover_name='country')
fig.update_layout(scene=dict(xaxis_title='GDP', yaxis_title='寿命', zaxis_title='人口'))

3D 图表支持旋转、缩放,悬停依然可用。

地图可视化

Plotly 内置地理数据和投影,可与 GeoPandas 数据直接结合:

fig = px.choropleth(df, locations='country', locationmode='country names',
                    color='lifeExp', hover_name='country',
                    color_continuous_scale=px.colors.sequential.Plasma,
                    title='世界预期寿命分布')
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

支持缩放平移,单击国家可查看详细信息。

使用 graph_objects 实现精细控制

express 无法满足定制需求时,可通过 graph_objects 拼装图表。每个 trace 都是一个独立的数据层。

import plotly.graph_objects as go

fig = go.Figure()
fig.add_trace(go.Scatter(
    x=df_line['year'], y=df_line['lifeExp'],
    mode='lines+markers', name='预期寿命',
    line=dict(color='firebrick', width=3)
))
fig.add_trace(go.Bar(
    x=df_line['year'], y=df_line['gdpPercap']/100,
    name='人均GDP (百美元)', yaxis='y2',
    marker_color='lightslategrey'
))
fig.update_layout(
    yaxis=dict(title='预期寿命'),
    yaxis2=dict(title='人均GDP', overlaying='y', side='right'),
    hovermode='x unified'
)
fig.show()

这种方式可以自由混用多种图表类型,并精确定义每个 trace 的样式、轴绑定等。

子图与组合展示

通过 make_subplots 可创建多面板布局,实现复杂的数据故事。

from plotly.subplots import make_subplots

fig = make_subplots(
    rows=2, cols=2,
    subplot_titles=("散点图", "柱状图", "折线图", "热力图"),
    specs=[[{"type": "scatter"}, {"type": "xy"}],
           [{"type": "scatter"}, {"type": "heatmap"}]]
)
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=[7,2,9]), row=1, col=2)
fig.add_trace(go.Scatter(x=[1,2,3], y=[2,4,8], mode='lines'), row=2, col=1)
fig.add_trace(go.Heatmap(z=[[1,2],[3,4]]), row=2, col=2)
fig.update_layout(height=700, showlegend=False)
fig.show()

跨子图悬停链接可通过 hovermode='x unified'spike 线条实现联动效果。

导出与分享交互图表

导出为独立 HTML

交互图表可保存为独立的 HTML 文件,包含所有数据和 JavaScript 资源,任何人用浏览器即可打开探索。

fig.write_html("interactive_chart.html")

可选参数 include_plotlyjs='cdn' 可减小文件体积,使用在线 CDN 加载 Plotly.js。

静态图片导出

需要安装 kaleido 包:

pip install -U kaleido

然后导出:

fig.write_image("chart.png", scale=2)

支持 PNG, SVG, PDF 等格式,高 DPI 输出。

在网页中嵌入

将导出的 HTML 以 iframe 或直接复制 <div> 块嵌入到个人博客、网站中,交互性完全保留。

从单图到仪表板:Plotly Dash 简介

当需要多图表联动、下拉菜单筛选等更复杂的交互时,可以将 Plotly 图表与 Dash 结合。Dash 是用于构建分析型 Web 应用的 Python 框架,完全用 Python 编写,无需 JavaScript。

一个极简 Dash 应用:

from dash import Dash, dcc, html, Input, Output
import plotly.express as px

app = Dash(__name__)

app.layout = html.Div([
    dcc.Dropdown(['China', 'India', 'USA'], 'China', id='country-dropdown'),
    dcc.Graph(id='life-exp-graph')
])

@app.callback(
    Output('life-exp-graph', 'figure'),
    Input('country-dropdown', 'value')
)
def update_figure(selected_country):
    df = px.data.gapminder().query("country == @selected_country")
    fig = px.line(df, x='year', y='lifeExp')
    return fig

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

运行后访问 http://127.0.0.1:8050,即可体验下拉框驱动图表更新的完整交互。

性能优化建议

  • WebGL 渲染:对于超过数千个散点的图表,设置 render_mode='webgl' 可大幅提升性能。
  • 数据降采样:使用 scatterglscattergl 的 trace,或先对大数据聚合再绘图。
  • 避免太多 trace:大量独立 trace 会导致渲染缓慢,可合并到一个 trace 中。
  • 使用 plotly.express 的分面:facet 比手动创建多个 trace 更高效。

学习路径与参考资源

  • 官方文档:Plotly 拥有极其详尽的示例库,每个图表类型都有可交互的示例。
  • Cheat Sheetplotly.express 常用函数速查,适合放在手边。
  • 社区论坛:Plotly Community Forum 中积累了大量定制化问题解答。
  • Dash 官方教程:接续学习,将图表变为完整应用。

Plotly 将数据可视化从静态报告提升为可探索的叙事工具。从快速出图的 express 到深度定制的 graph_objects,再到部署为 Dash 应用,整个生态为不同层次的用户提供了完整的交互图表解决方案。