查看全课大纲(第 43 / 45 节)
- 1.课程简介
- 2.环境搭建
- 3.预备知识
- 4.pyecharts初体验
- 5.数据可视化概述
- 6.数据可视化的目标和作用
- 7.数据可视化工具对比分析
- 8.数据可视化基本流程
- 9.数据可视化设计原则
- 10.pyecharts简介
- 11.官方图表样例
- 12.常用图表的基本结构
- 13.数据格式
- 14.图表的分类
- 15.柱状图与条形图
- 16.其他对比图表
- 17.常用配置项(一)
- 18.常用配置项(二)
- 19.折线图 面积图
- 20.K线图
- 21.其他时间图表
- 22.常用配置项
- 23.散点图 气泡图
- 24.热力图
- 25.其他相关性图表
- 26.饼图
- 27.矩形树图
- 28.其他成分比例图表
- 29.箱型图与直方图
- 30.3D柱状图与折线图
- 31.其他统计图表
- 32.地图 Map
- 33.地理坐标系Geo
- 34.3D 地图
- 35.网络关系图
- 36.主题河流图
- 37.词云图
- 38.组合图表组件
- 39.仪表盘相关图表
- 40.项目介绍
- 41.数据准备
- 42.子图制作(一)
- 43.子图制作(二)
- 44.数据大屏制作
- 45.项目总结
子图制作(二)
商品、行为与客户价值:三类子图分析
小象实战讲义 · ECharts 数据可视化
上一节完成了用户流量趋势。本节继续从商品和行为两个角度回答问题:哪些品类和单品贡献了更多点击,用户在哪个环节流失,以及如何用 R、F 两个维度识别值得重点运营的客户。三组图分别对应三个业务问题,而不是为了把所有图表放进同一张页面。
💡 核心导读
- 商品维度:按品类和单品聚合点击次数,用 Top10 饼图识别头部集中度。
- 行为维度:把点击、收藏/加购、支付转换成百分比漏斗,定位最大流失环节。
- 客户价值:用最近购买时间和购买频次的中位数二值化,构造四类 RFM 客户。
- 图表解释边界:区分“点击热度”“行为转化”和“客户价值”,避免用一张图回答三个不同问题。
一、商品维度:Top10 品类与 Top10 单品
项目 notebook 先按 item_category 或 item_id 统计点击记录,再按次数降序取前 10 条。类别 ID 和单品 ID 都是脱敏标识;如果业务方提供名称映射,才能在图上显示中文名称。
下面用确定性小样本复现同一逻辑。十个品类与十个单品的点击量依次为 8、7、6、5、4、3、2、2、1、1,便于核对排序是否正确。
import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import Page, Pie
from pyecharts.globals import CurrentConfig
CurrentConfig.ONLINE_HOST = "assets/"
product_specs = [
(2101, 10, 8),
(2102, 20, 7),
(2103, 30, 6),
(2104, 40, 5),
(2105, 50, 4),
(2106, 60, 3),
(2107, 70, 2),
(2108, 80, 2),
(2109, 90, 1),
(2110, 100, 1),
]
product_rows = []
for item_id, category_id, click_count in product_specs:
for offset in range(click_count):
product_rows.append(
{
"user_id": 3000 + offset,
"item_id": item_id,
"item_category": category_id,
}
)
click_df = pd.DataFrame(product_rows)
top_categories = (
click_df.groupby("item_category")["user_id"]
.count()
.sort_values(ascending=False)
.head(10)
)
top_items = (
click_df.groupby("item_id")["user_id"]
.count()
.sort_values(ascending=False)
.head(10)
)
category_pairs = [(str(category), int(count)) for category, count in top_categories.items()]
item_pairs = [(str(item), int(count)) for item, count in top_items.items()]
category_pie = (
Pie()
.add("类别", category_pairs, radius=["32%", "66%"])
.set_global_opts(
title_opts=opts.TitleOpts(title="Top10 商品类别"),
legend_opts=opts.LegendOpts(is_show=False),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{d}%"))
)
item_pie = (
Pie()
.add("单品", item_pairs, radius=["32%", "66%"])
.set_global_opts(
title_opts=opts.TitleOpts(title="Top10 单品"),
legend_opts=opts.LegendOpts(is_show=False),
)
.set_series_opts(label_opts=opts.LabelOpts(formatter="{d}%"))
)
Page(layout=Page.SimplePageLayout).add(category_pie, item_pie).render("product_top10.html")
print(f"商品点击样本: {len(click_df)} 条")
print("Top3 类别:", list(category_pairs[:3]))
print("Top3 单品:", list(item_pairs[:3]))商品点击样本: 39 条
Top3 类别: [('10', 8), ('20', 7), ('30', 6)]
Top3 单品: [('2101', 8), ('2102', 7), ('2103', 6)]怎么读这两张饼图:类别图回答“资源应优先投向哪类商品”,单品图回答“哪些具体商品是头部流量入口”。两张图都只展示头部集合,百分比的分母是 Top10 总量,不是全站点击总量,因此不能把图中占比直接解释成全站市场份额。
二、行为维度:点击到支付的漏斗
原始项目按 behavior_desc 统计点击、收藏、加入购物车和支付的记录数。收藏与加购没有严格先后关系,所以漏斗把它们合并为“收藏/加购”阶段,再计算相对点击量的百分比。
下面的样本沿用原课结论口径:点击 10000 条、收藏 322 条、加购 446 条、支付 148 条。
from pyecharts import options as opts
from pyecharts.charts import Funnel
from pyecharts.globals import CurrentConfig
CurrentConfig.ONLINE_HOST = "assets/"
pv = 10000
favor = 322
cart = 446
buy = 148
print(f"点击: {pv}")
print(f"点击→收藏: {favor / pv:.2%}")
print(f"点击→加入购物车: {cart / pv:.2%}")
print(f"点击→支付: {buy / pv:.2%}")
print(f"收藏/加购→支付: {buy / (favor + cart):.2%}")
interest_count = favor + cart
funnel_rates = [
round(pv / pv * 100, 2),
round(interest_count / pv * 100, 2),
round(buy / pv * 100, 2),
]
behavior_funnel = (
Funnel()
.add(
series_name="用户行为",
data_pair=[
("点击", funnel_rates[0]),
("收藏/加入购物车", funnel_rates[1]),
("支付", funnel_rates[2]),
],
tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{b}: {c}%"),
label_opts=opts.LabelOpts(is_show=True, position="outside"),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="用户行为漏斗分析"),
legend_opts=opts.LegendOpts(is_show=False),
)
)
behavior_funnel.render("behavior_funnel.html")点击: 10000
点击→收藏: 3.22%
点击→加入购物车: 4.46%
点击→支付: 1.48%
收藏/加购→支付: 19.27%
结论不是“收藏和加购不重要”。从点击到兴趣行为只有 7.68%,这是最大的流失段;但一旦用户收藏或加购,后续支付转化达到 19.27%,显著高于直接点击到支付的 1.48%。因此运营重点应放在引导兴趣行为,并缩短收藏/加购到支付的路径。
三、客户价值:用 R、F 中位数构造四类客户
项目数据没有消费金额字段,因此只使用 R(Recency)和 F(Frequency):
- R:距离最近一次支付的天数,越小越活跃;按中位数分组,小于等于中位数的记为 1。
- F:支付次数,越大越忠诚;大于中位数的记为 1。
- RF 组合:
11重要价值、10重要发展、01重要保持、00重要挽留。
下面构造 8 个用户,并让四类客户各有 2 人,方便验证分组逻辑。
import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.globals import CurrentConfig
CurrentConfig.ONLINE_HOST = "assets/"
snapshot_date = pd.Timestamp("2018-12-18")
user_specs = [
(1, 1, 3),
(2, 2, 3),
(3, 3, 1),
(4, 4, 1),
(5, 10, 3),
(6, 11, 3),
(7, 12, 1),
(8, 13, 1),
]
order_rows = []
for user_id, days_ago, frequency in user_specs:
purchase_date = snapshot_date - pd.Timedelta(days=days_ago)
for order_offset in range(frequency):
order_rows.append(
{
"user_id": user_id,
"item_id": 5000 + order_offset,
"date": purchase_date,
}
)
orders = pd.DataFrame(order_rows)
last_purchase = orders.groupby("user_id")["date"].max()
recency = (snapshot_date - last_purchase).dt.days.rename("recency_days")
frequency = orders.groupby("user_id").size().rename("frequency")
rfm = pd.concat([recency, frequency], axis=1)
median_recency = rfm["recency_days"].median()
median_frequency = rfm["frequency"].median()
rfm["R"] = (rfm["recency_days"] <= median_recency).astype(int)
rfm["F"] = (rfm["frequency"] > median_frequency).astype(int)
rfm["RF"] = rfm["R"].astype(str) + rfm["F"].astype(str)
rfm["user_tag"] = rfm["RF"].map(
{
"11": "重要价值客户",
"10": "重要发展客户",
"01": "重要保持客户",
"00": "重要挽留客户",
}
)
tag_order = ["重要价值客户", "重要发展客户", "重要保持客户", "重要挽留客户"]
user_tag_count = rfm["user_tag"].value_counts().reindex(tag_order, fill_value=0)
user_tag_pie = (
Pie()
.add(
series_name="客户分层",
data_pair=[(tag, int(count)) for tag, count in user_tag_count.items()],
radius=["32%", "68%"],
label_opts=opts.LabelOpts(is_show=True, position="outside"),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="RFM 客户价值分层"),
legend_opts=opts.LegendOpts(is_show=False),
)
.set_series_opts(
tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{b}: {d}%")
)
)
user_tag_pie.render("rfm_segments.html")
print(f"RFM 中位数: R={median_recency:.2f}, F={median_frequency:.2f}")
print("用户价值分层:")
print(user_tag_count.to_string())RFM 中位数: R=7.00, F=2.00
用户价值分层:
user_tag
重要价值客户 2
重要发展客户 2
重要保持客户 2
重要挽留客户 2四类客户的运营动作不同:
- 重要价值客户:近期活跃且购买频繁,重点是保持体验、会员权益和稳定复购。
- 重要发展客户:近期活跃但频次不高,重点是提高购买次数,例如首复购提醒和品类优惠。
- 重要保持客户:历史频次高但近期沉默,重点是唤醒,例如消息推送和召回权益。
- 重要挽留客户:近期与频次都偏低,需要持续曝光、优惠触达,并进一步识别流失原因。
四、把三组图放回同一个业务故事
商品 Top10 解释“用户把点击集中在哪些货上”,漏斗解释“兴趣行为在哪一步衰减”,RFM 解释“哪些客户值得优先运营”。三者可以并列展示,但不能用一个总百分比互相替代。
在真实项目中,先确认行为统计窗口、Top10 分母和 RFM 快照日期,再把结果交给大屏。否则同一张图换一天数据,百分比和客户分层都可能变化。
📝 动手练一练
修改 Top10:如果业务只看点击量不少于 5 的商品,应该在哪一步过滤?
点击查看参考答案
先按
item_id聚合得到点击计数,再过滤count >= 5,最后重新排序并取前 10。不能先截取前 10 再过滤,否则可能漏掉原本排在第 11 位但达到阈值的商品。解释漏斗:收藏和加购为什么可以合并成一个阶段?
点击查看参考答案
在本项目中,收藏和加购都表示用户对商品产生了兴趣,二者没有严格先后顺序,也不要求同时发生。合并后得到“至少发生一种兴趣行为”的阶段,用于观察点击到支付的中间转化。若要研究两种行为各自的效率,应另画两张分渠道漏斗。
修改 RFM 阈值:把 R 的“小于等于中位数记 1”改成“小于中位数记 1”,会带来什么影响?
点击查看参考答案
等于中位数的用户会从高价值组移到低价值组,四类客户的人数分布可能改变。阈值本身就是业务规则,必须在报告中写明;比较不同周期的分层结果时,也要保证阈值定义一致。
本章小结
本节从商品、行为、客户价值三个问题出发:Top10 饼图观察头部集中度,行为漏斗定位流失环节,R/F 二值 RFM 划分客户。每组图都必须先固定分母、统计窗口和阈值,再解释业务含义。
📋 行动清单
- 分别计算 Top10 品类和 Top10 单品,并说明百分比分母。
- 计算点击到收藏、加购、支付,以及收藏/加购到支付的转化率。
- 按 R、F 中位数构造
11/10/01/00四组客户,并写出各组的一条运营动作。
—— 小象教研组
