Python技术迷

非常牛批的可视化库Plotly

昨天晚上十一点多,我在公司楼下找共享单车,手机一震,产品经理给我发了个微信语音,大概意思就是: “东哥,那个销售看板啊,要是能鼠标一划就看到每个点的明细,还能放大、拖动、筛选地区,就更好了,你看方不方便…?”

我人都已经在地库了,你跟我谈交互可视化,对吧。 我当时脑子里第一个反应就是:要不用一下那个…那个啥…Plotly。

其实我以前画图一直是 matplotlib + seaborn,那种写完 plt.plot() 然后调半天坐标轴、标题、中文字体的老路子你们懂的,图是能出,但一旦领导说“这个能不能交互操作一下?” 你心里就“咯噔”一下:完了,又得上前端、又得弄 echarts 之类的了,数据还得再给一份。

后来我是真的被折腾烦了,才开始认真用 Plotly,结果发现:这玩意儿你要说“非常牛批”吧,有点夸张,但在我日常写 Python 的场景里,确实是:能少挨两次需求回炉的库。 你们要是看过我之前那个写数据库、MQ、还有 TCP 抓包排查 Bug 那几篇,大概就是同一个人骂骂咧咧的语气。

Plotly 到底图啥?就一句话

简单粗暴说一句:

Plotly = “写 Python 代码 → 直接得到一个能拖、能缩放、能悬浮显示详情、能导出 HTML 的交互图表”

跟 matplotlib 最大的区别就是,matplotlib 默认是“死图”,你保存个 PNG 发给别人就结束了; Plotly 默认是“活图”,本质上是帮你把数据变成一坨前端能认识的 JSON,再用它内置的 JS 库渲染成交互图。

对我们这种后端/数据方向的来说,就是: 我依然写 Python,但是效果看起来像“有人帮我写完了前端可视化”。

上个最小可用例子,感受一下味道:

import plotly.express as px
import pandas as pd

# 构造一点假数据,别纠结业务含义
df = pd.DataFrame({
"day": pd.date_range("2025-01-01", periods=7, freq="D"),
"pv": [120, 180, 160, 230, 260, 300, 280]
})

fig = px.line(
    df,
    x="day",
    y="pv",
    markers=True,
    title="一周页面访问量趋势"
)

# 直接在 notebook 或者浏览器里弹出交互图
fig.show()

# 如果想甩给产品一个 HTML 文件:
fig.write_html("pv_trend.html", include_plotlyjs="cdn")

你不用写一行 JS,出来的图就是:

  • 鼠标悬浮就有 tooltip
  • 可以框选一段时间放大
  • 图右上角自带导出 PNG、缩放重置、框选等一堆小按钮

这个时候你再回头看 matplotlib,那感觉就像是回到功能机时代。

我平时在几个场景里用 Plotly

我就不整什么“优点一二三”了,直接说我真正在用的几个场景,你对号入座就行。

1)数据分析 / 探索阶段:快速看数据

比如你在写一个推荐算法,或者简单做点 A/B 分析,其实最常干的一件事就是: “把某个指标按时间 / 按人群 / 按渠道先画出来看看”。

这时候我基本只用 plotly.express,它是 Plotly 的“快餐版 API”。

比如你有一份转化率数据,想看不同渠道的趋势:

import plotly.express as px
import pandas as pd

df = pd.DataFrame({
"date": pd.date_range("2025-01-01", periods=10, freq="D").repeat(3),
"channel": ["广告投放", "自然流量", "老用户召回"] * 10,
"ctr": [0.03, 0.05, 0.04, 0.035, 0.06, 0.045, 0.032, 0.055, 0.043] * 3 + [0.05]  # 随便凑
})[:30]

fig = px.line(
    df,
    x="date",
    y="ctr",
    color="channel",
    markers=True,
    title="不同渠道点击率趋势(示意)",
    labels={"date": "日期", "ctr": "点击率", "channel": "渠道"}
)

fig.show()

这个图你一出来,鼠标在某个点一划:

  • 这一天每个渠道是多少
  • 哪条线是哪个渠道

一眼就很清楚。 而且你可以在图例上点某个渠道,把其他线隐藏,这对于“会议上随手回答一个人突然问的问题”特别有用。

2)业务报表 / 看板:给领导看的那种

像销售看板、运营看板这种,一般会有:

  • 左边一个“总体趋势”
  • 右边一堆“按地区 / 产品 / 客户维度拆开的柱状图”

这类我就会上 graph_objects + 子图,把多个图塞到一张大画布里,看起来就像一个 mini BI。

直接上一个稍微完整点的例子,你可以拷贝过去直接改字段名用。

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

# 假装这是某个月的销售数据
df = pd.DataFrame({
"day": pd.date_range("2025-01-01", periods=10, freq="D"),
"amount": [10, 12, 9, 15, 18, 20, 22, 19, 25, 30],
"order_cnt": [100, 110, 90, 130, 140, 150, 155, 148, 170, 190],
"region": ["华东", "华南", "华北", "华东", "华南", "华北", "华东", "华南", "华北", "华东"]
})

# 1 行 2 列的布局:左边折线,右边柱状
fig = make_subplots(
    rows=1, cols=2,
    subplot_titles=("销售额 & 订单数趋势", "按地区汇总销售额"),
    specs=[[{"secondary_y": True}, {"secondary_y": False}]]
)

# 左侧:销售额折线(主轴)
fig.add_trace(
    go.Scatter(
        x=df["day"],
        y=df["amount"],
        name="销售额(万)",
        mode="lines+markers"
    ),
    row=1, col=1, secondary_y=False
)

# 左侧:订单数折线(副轴)
fig.add_trace(
    go.Scatter(
        x=df["day"],
        y=df["order_cnt"],
        name="订单数",
        mode="lines+markers"
    ),
    row=1, col=1, secondary_y=True
)

# 右侧:按地区汇总的柱状
region_sum = df.groupby("region", as_index=False)["amount"].sum()
fig.add_trace(
    go.Bar(
        x=region_sum["region"],
        y=region_sum["amount"],
        name="各地区销售额(万)"
    ),
    row=1, col=2
)

# 整体样式顺手改一下
fig.update_layout(
    title_text="某月销售看板(示意)",
    legend=dict(orientation="h", x=0.3, y=-0.1)
)
fig.update_xaxes(title_text="日期", row=1, col=1)
fig.update_yaxes(title_text="销售额(万)", row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text="订单数", row=1, col=1, secondary_y=True)
fig.update_xaxes(title_text="地区", row=1, col=2)
fig.update_yaxes(title_text="销售额(万)", row=1, col=2)

fig.show()

这玩意儿跑完以后,你基本上就有了一个能在周会里直接 share screen 的小看板:

  • 鼠标移到任意点上,两个指标一起显示
  • 柱状图那边想看哪个地区多,肉眼可见
  • 想导出截图,右上角点一下

关键是:这一切都是 Python 里完成的,不用你再切去写前端页面。

3)给非技术同学一个“可玩”的页面

这个是我后来才发现的一个小爽点。

以前我给运营同学看数据,要么发 Excel,要么发截图。 她们要是说“能不能看一下只看华东地区、最近 7 天的?” 我就得重新跑一遍 SQL,再重新画个图,再截图,再发一次。

现在我一般是:直接 fig.write_html("xxx.html"),然后把这个 HTML 扔到内部文件服务器或者直接微信发过去(小文件的话)。

HTML 里面自带所有交互:

  • 对方可以在页面上缩放、拖动
  • 底下有一个滑条可以只看一段时间(时间序列图会带 range slider)
  • 鼠标选一大片区域直接放大

对方玩一圈,发现“啊,确实就这样”,就不会拉着你改来改去。 你也少改几次图,少几次“帮忙导出一份”的支线任务。

一些容易忽略但很实用的小细节

我自己踩的一些小点,随手说几条,想到哪说到哪。

  1. 中文和字体问题有时候在本地浏览器看中文没问题,放到另一个机器上就变成小方块,那多半是字体没装全。 简单粗暴一点的做法是:在 update_layout 里指定一个常见字体,比如 "Microsoft YaHei",大多数 Windows 机器都有。

    fig.update_layout(
        font=dict(
            family="Microsoft YaHei",
            size=12
        )
    )
  2. 数值显示格式默认 tooltip 可能会给你很多小数位,看着很难受,可以用 hovertemplate 自己控制格式,比如保留两位:

    import plotly.express as px

    fig = px.line(df, x="day", y="pv")
    fig.update_traces(hovertemplate="日期=%{x}<br>PV=%{y:.2f}")
    fig.show()

  3. 性能这块Plotly 说到底是前端在渲染,所以数据量太大的时候(比如十几万点)浏览器会有点吃不消。 这种情况下,要么先在后端做降采样,要么用它家带的 scattergl(基于 WebGL 的)。 但坦白讲,很多业务场景根本用不到那么多点,你会先被 PM 吐槽“看不清”,而不是被性能打死。

和 Dash 捆一块儿,会更香一点

很多人一听 Plotly,就会顺带问一句:那 Dash 呢?

一句话:

Plotly 是画一个一个图;Dash 是用 Python 拼一个完整的“可视化页面 / 小应用”。

比如:

  • 左边几个下拉框选条件
  • 中间两三个图联动
  • 右边一堆关键指标卡片

Dash 可以完全用 Python 来写页面结构和交互逻辑,Plotly 那些 fig 对象直接拿进去用。

这个展开讲就长了,今天就不铺开了,你可以先把 Plotly 用熟,等有一天你发现自己开始频繁地给人发 HTML 图的时候,大概率就该上 Dash 了。

顺手再给一个最“朴素”的例子:柱状图 + 颜色映射

有时候你就想做一个“看异常值”的小图,比如把某天销售额特别低/特别高的颜色标出来,这种 Plotly 写起来也挺顺手。

import plotly.express as px
import pandas as pd

df = pd.DataFrame({
"day": pd.date_range("2025-02-01", periods=15, freq="D"),
"amount": [10, 11, 9, 8, 20, 22, 18, 17, 16, 30, 28, 26, 25, 24, 23]
})

# 简单规则:小于 10 认为异常,给个标签
df["flag"] = df["amount"].apply(lambda x: "异常偏低"if x < 10else"正常")

fig = px.bar(
    df,
    x="day",
    y="amount",
    color="flag",
    title="15 天销售额(标出异常偏低)",
    labels={"day": "日期", "amount": "销售额(万)", "flag": "状态"}
)

fig.show()

你开个周会,把这个图往 PPT 一贴,别人问“那几天为啥这么低?” 你只需要淡定地说一句:“那几天系统在升级(或者运营没投放),我已经标出来了,后面会补。” 图说话的感觉,会比你念一堆数字强太多。


行,差不多就扯到这。

Plotly 这东西,你光看别人吹没什么感觉,真要有感觉,还得自己随便找个小数据集,照着上面这些例子敲几行,把图弹出来晃两下。

等哪天晚上又有人喊你“能不能搞个交互图看看趋势”,你脑子里就会条件反射先想起: “要不我写个 Plotly 算了,快点。”

我这边先去把刚才那个销售看板的需求再收个尾,不然产品又要来催了,你要是哪块用 Plotly 卡住了,回头再聊。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB