非常牛批的可视化库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 pddf = 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) 鼠标选一大片区域直接放大
对方玩一圈,发现“啊,确实就这样”,就不会拉着你改来改去。 你也少改几次图,少几次“帮忙导出一份”的支线任务。
一些容易忽略但很实用的小细节
我自己踩的一些小点,随手说几条,想到哪说到哪。
中文和字体问题有时候在本地浏览器看中文没问题,放到另一个机器上就变成小方块,那多半是字体没装全。 简单粗暴一点的做法是:在
update_layout里指定一个常见字体,比如"Microsoft YaHei",大多数 Windows 机器都有。fig.update_layout(
font=dict(
family="Microsoft YaHei",
size=12
)
)数值显示格式默认 tooltip 可能会给你很多小数位,看着很难受,可以用
hovertemplate自己控制格式,比如保留两位:import plotly.express as pxfig = px.line(df, x="day", y="pv")
fig.update_traces(hovertemplate="日期=%{x}<br>PV=%{y:.2f}")
fig.show()性能这块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 pddf = 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