基于Pandas与Matplotlib的用户RFM消费轨迹图绘制及代码效果偏差排查
RFM消费轨迹可视化问题排查与正确实现
核心问题定位
- 多记录首个用户展示异常:未对同一uid下的消费记录按时间升序排序,分组时未将同用户的所有消费点绑定到固定纵轴行位,导致跨用户的点错位,把其他用户的记录渲染到了首个用户对应的纵轴位置。
- 圆点尺寸匹配失准:直接将原始M值(通常为几十到数百的客单价)传入matplotlib散点的
s参数(该参数单位为点面积,无自动缩放逻辑),未做归一化映射,导致尺寸要么过小不可见,要么过大溢出画布。 - 位置映射逻辑错配:未将R值换算为“距离Now虚线的水平相对间隔”,直接将R值作为绝对横坐标赋值,导致点的整体位置偏移,无法通过与Now线的距离表征最近消费的时间间隔。
正确实现方案
数据预处理步骤
- 排序校验:对原始DataFrame按
uid、消费时间戳做升序排序,保证同一用户的消费序列按时间先后排布,相邻点的水平间隔自然对应两次消费的时间差,匹配Frequency维度的映射要求。 - 坐标绑定:将“Now”垂直虚线固定在画布的x=0位置,每一笔消费的横坐标设定为
0 - 对应R值的相对刻度——R值越大(距当前时间越久),横坐标越靠左,最右侧的点与Now线的间隔严格等于对应用户的R值大小。 - 尺寸归一化:将M值的取值区间线性映射到散点尺寸的合理范围(如15~200的面积区间),映射公式为
scatter_size = 15 + (M - M.min())/(M.max() - M.min()) * 185,保证尺寸与M值成正比且不会溢出。 - 纵轴分配:为每个唯一uid分配固定纵轴坐标,所有该用户的消费点统一渲染在对应y值的水平线上,从根源上避免跨用户的点错位。
核心可运行代码片段
import matplotlib.pyplot as plt import pandas as pd # 初始化画布 fig, ax = plt.subplots(figsize=(10, 6)) # 固定Now参考线的横坐标 NOW_X = 0 # 绘制Now垂直虚线 ax.axvline(x=NOW_X, color="#888888", linestyle="--") ax.text(NOW_X + 0.2, ax.get_ylim()[1], "Now", va="top", color="#888888") # 为每个用户分配固定纵轴位置 unique_uids = df["uid"].unique() y_axis_pos = {uid: idx for idx, uid in enumerate(reversed(unique_uids))} # M值归一化到散点尺寸合理区间 m_min, m_max = df["M"].min(), df["M"].max() df["plot_size"] = 15 + (df["M"] - m_min) / (m_max - m_min) * 185 # 逐用户绘制消费点序列 for uid, user_group in df.groupby("uid"): # 按时间升序排序保证序列顺序正确 user_group_sorted = user_group.sort_values("消费时间") # 横坐标匹配R值:距Now越近R越小,x越靠近0 x_list = NOW_X - user_group_sorted["R"].values # 所有点绑定到用户对应的固定y坐标 y_list = [y_axis_pos[uid]] * len(user_group_sorted) ax.scatter( x=x_list, y=y_list, s=user_group_sorted["plot_size"], alpha=0.7, edgecolor="white" ) # 坐标轴配置 ax.set_yticks(list(y_axis_pos.values())) ax.set_yticklabels([f"用户{uid}" for uid in y_axis_pos.keys()]) ax.set_xlabel("消费时间轴(距当前间隔)") ax.spines[["top", "right"]].set_visible(False) plt.tight_layout() plt.show()
效果校验标准
- 同一用户行的最右侧点与Now虚线的水平距离,完全对应该用户的R值大小
- 同一行相邻两个点的水平间隔,对应两次消费的时间差,匹配Frequency映射
- 所有点的面积随M值增大成正比放大,无尺寸溢出或不可见问题
内容的提问来源于stack exchange,提问作者Shlomi Schwartz
相关产品推荐
相关产品推荐

