日式极简服饰复购率分析程序,对比简约无Logo服饰与印花潮款长期留存数据。

📅 2026/6/26 18:55:30
日式极简服饰复购率分析程序,对比简约无Logo服饰与印花潮款长期留存数据。
构建一个基于生存分析与用户分群的复购率评估系统。这个程序的核心逻辑是利用Kaplan-Meier 生存分析来对比两种截然不同的时尚产品哲学——极简无 Logo与印花潮款——在用户生命周期中的留存表现。实际应用场景描述在《时尚产业与品牌创新》课程中我们探讨了两种极端的品牌策略* 策略 A日式极简无 Logo* 哲学侘寂Wabi-sabi、留白、去品牌化。* 代表Muji、Uniqlo U、The Row。* 痛点没有显眼的 Logo用户为什么要回来买是靠衣橱基础件替换驱动还是靠美学信仰驱动* 策略 B印花潮款Graphic/Pattern* 哲学视觉冲击、社交货币、话题驱动。* 代表Palace、Ggalleria、国潮印花。* 痛点社交媒体的快时尚逻辑。用户是不是用完即弃复购是不是完全依赖下一波新图案品牌面临的灵魂拷问我该坚持做高毛利的印花爆款赌下一波趋势还是转型做低毛利但高复购的基础极简款赌用户终身价值 LTV引入痛点1. 数据维度单一大多数品牌只看整体复购率Overall Retention Rate这不严谨。极简款可能第 1 个月卖得差但第 12 个月用户还在买高 LTV潮款可能首发爆了但 3 个月后没人看了低 LTV。2. 幸存者偏差只看买了两次以上的人的复购率忽略了买一次就消失的人导致高估了产品吸引力。3. 缺乏对比框架没有把时间作为一个核心变量纳入分析。你需要知道的是用户流失的速度而不是一个静态的百分比。核心逻辑讲解我们将使用 Kaplan-Meier 生存分析通常用于医疗临床试验看病人存活率这里我们看用户存活率。1. 定义死亡在时尚语境下死亡 用户最后一次购买后超过了平均复购周期仍未再次购买。2. 定义生存用户虽然在观察期内没有再次购买但还在复购窗口期内比如平均 60 天买一次30 天时他还没买但他还没死。3. 对比曲线* 极简组曲线可能下降缓慢用户买基础款是习惯流失慢。* 潮款组曲线可能前期骤降用户追新不追旧一旦热度过气留存崩盘。4. 统计检验Log-Rank Test用一个 P 值来科学回答这两类产品的复购差异是偶然的还是本质上的代码模块化1.data_generator.py (数据构建模块)import numpy as npimport pandas as pdclass UserBehaviorGenerator:模拟用户购买行为数据生成器基于两种产品哲学的差异构建合成数据集def __init__(self, seed42):np.random.seed(seed)def generate_cohort(self, n_users5000, product_typeminimalist):生成用户队列数据:param n_users: 用户数量:param product_type: minimalist(极简) or graphic(潮款):return: DataFrameusers []for uid in range(n_users):if product_type minimalist:# --- 极简无 Logo 特征 ---# 1. 购买频率较高 (复购周期短)# 2. 客单价中等偏上 (基础款溢价)# 3. 生命周期长 (不追潮流看中质感)purchase_cycle max(7, int(np.random.normal(45, 15))) # 平均45天买一次ltv_months max(6, int(np.random.normal(18, 6))) # 平均活18个月avg_spend np.random.normal(350, 80) # 客单价 350 左右churn_rate 0.03 # 月流失率低else:# --- 印花潮款特征 ---# 1. 购买频率前期高但衰减极快# 2. 客单价低 (冲动消费)# 3. 生命周期短 (下一波热度来了就跑了)purchase_cycle max(5, int(np.random.normal(25, 10))) # 首发冲动强ltv_months max(2, int(np.random.exponential(6))) # 指数衰减平均寿命短avg_spend np.random.normal(180, 50) # 客单价低churn_rate 0.15 # 月流失率高# 生成购买记录current_month 0total_spent 0purchase_count 0is_churned Falsewhile current_month ltv_months:# 模拟流失if np.random.random() churn_rate and purchase_count 0:is_churned Truebreak# 模拟购买current_month purchase_cycleif current_month ltv_months:break# 花费波动actual_spend max(50, avg_spend * np.random.normal(1.0, 0.2))total_spent actual_spendpurchase_count 1users.append({user_id: f{product_type[:3]}_{uid},product_type: product_type,purchase_count: purchase_count,total_spent: round(total_spent, 2),ltv_months: ltv_months,is_churned: is_churned,avg_spend_per_purchase: round(total_spent / max(1, purchase_count), 2)})return pd.DataFrame(users)2.retention_analyzer.py (核心分析引擎)import numpy as npimport pandas as pdfrom scipy import statsclass RetentionAnalyzer:复购率与留存分析引擎实现 Kaplan-Meier 生存分析与统计检验def __init__(self, df_minimalist, df_graphic):self.df_m df_minimalistself.df_g df_graphicself.combined pd.concat([df_minimalist, df_graphic], ignore_indexTrue)def kaplan_meier_estimate(self, group_labelminimalist, max_periods24):计算 Kaplan-Meier 生存曲线数据返回每个时间点的存活概率if group_label minimalist:df self.df_melse:df self.df_g# 按生命周期月份统计# 在每个月份 t计算活过 t 个月的概率survival_data []for t in range(1, max_periods 1):# 在 t 时刻仍存活的用户数# 逻辑用户的 ltv_months t 意味着他们在 t 月时还没流失at_risk len(df[df[ltv_months] t])# 在 t 时刻死亡的用户数# 逻辑刚好在 t 月流失简化处理ltv 刚好等于 tevents len(df[df[ltv_months] t])# 删失数据还在观察期内但未发生事件censored len(df[(df[ltv_months] t) (df[is_churned] False)])survival_data.append({period: t,at_risk: at_risk,events: events,censored: censored,survival_prob: None # 待计算})# 计算生存概率s_t 1.0for i, row in enumerate(survival_data):if row[at_risk] 0:s_t * (1 - row[events] / row[at_risk])row[survival_prob] round(s_t, 4)return pd.DataFrame(survival_data)def compare_retention(self, periods12):对比两种产品在前 N 个月的留存率输出详细的对比表格# 极简组m_retention []for p in range(1, periods 1):alive len(self.df_m[self.df_m[ltv_months] p])total len(self.df_m)m_retention.append(round(alive / total * 100, 2))# 潮款组g_retention []for p in range(1, periods 1):alive len(self.df_g[self.df_g[ltv_months] p])total len(self.df_g)g_retention.append(round(alive / total * 100, 2))comparison pd.DataFrame({月份: range(1, periods 1),极简无Logo留存率(%): m_retention,印花潮款留存率(%): g_retention,})comparison[差值(百分点)] (comparison[极简无Logo留存率(%)] -comparison[印花潮款留存率(%)]).round(2)return comparisondef log_rank_test(self):Log-Rank 检验检验两组生存曲线的差异是否具有统计显著性H0: 两组生存曲线无差异# 简化版的 Log-Rank 检验# 合并两组数据的时间线all_times []for _, row in self.df_m.iterrows():all_times.append((row[ltv_months], 1, minimalist))for _, row in self.df_g.iterrows():all_times.append((row[ltv_months], 1, graphic))df_all pd.DataFrame(all_times, columns[time, event, group])df_all df_all.sort_values(time).reset_index(dropTrue)# 计算期望事件数 (简化计算)m_total len(self.df_m)g_total len(self.df_g)n_total m_total g_total# 在每个时间点计算观察值 vs 期望值# 这里用简化的 Mantel-Haenszel 统计量chi_square 0details []for t in sorted(df_all[time].unique()):at_risk_m len(df_all[(df_all[time] t) (df_all[group] minimalist)])at_risk_g len(df_all[(df_all[time] t) (df_all[group] graphic)])events_m len(df_all[(df_all[time] t) (df_all[group] minimalist) (df_all[event] 1)])events_g len(df_all[(df_all[time] t) (df_all[group] graphic) (df_all[event] 1)])total_at_risk at_risk_m at_risk_gtotal_events events_m events_gif total_at_risk 0 and total_events 0:expected_m total_events * (at_risk_m / total_at_risk)expected_g total_events * (at_risk_g / total_at_risk)if expected_m 0:chi_square ((events_m - expected_m) ** 2) / expected_mif expected_g 0:chi_square ((events_g - expected_g) ** 2) / expected_gdetails.append({time: t,at_risk_m: at_risk_m,at_risk_g: at_risk_g,events_m: events_m,events_g: events_g,expected_m: round(expected_m, 1),expected_g: round(expected_g, 1),})# 自由度 1 的卡方分布p_value 1 - stats.chi2.cdf(chi_square, df1)return {chi_square: round(chi_square, 4),p_value: round(p_value, 6),is_significant: p_value 0.05,details: pd.DataFrame(details)}def ltv_analysis(self):计算并对比两组的用户终身价值LTVm_avg_ltv self.df_m[total_spent].mean()g_avg_ltv self.df_g[total_spent].mean()m_avg_months self.df_m[ltv_months].mean()g_avg_months self.df_g[ltv_months].mean()m_avg_spend self.df_m[avg_spend_per_purchase].mean()g_avg_spend self.df_g[avg_spend_per_purchase].mean()m_avg_purchases self.df_m[purchase_count].mean()g_avg_purchases self.df_g[purchase_count].mean()return {minimalist: {avg_ltv: round(m_avg_ltv, 2),avg_lifetime_months: round(m_avg_months, 1),avg_spend_per_purchase: round(m_avg_spend, 2),avg_purchase_count: round(m_avg_purchases, 1),},graphic: {avg_ltv: round(g_avg_ltv, 2),avg_lifetime_months: round(g_avg_months, 1),avg_spend_per_purchase: round(g_avg_spend, 2),avg_purchase_count: round(g_avg_purchases, 1),}}3.visualizer.py (可视化仪表盘)import matplotlib.pyplot as pltimport numpy as npclass RetentionDashboard:复购率分析可视化仪表盘COLORS {minimalist: #2C3E50, # 极简黑灰graphic: #E74C3C, # 潮款红}classmethoddef plot_survival_curves(cls, analyzer, filenamesurvival_curves.png):绘制 Kaplan-Meier 生存曲线对比fig, ax plt.subplots(figsize(12, 7))# 极简组km_m analyzer.kaplan_meier_estimate(minimalist)ax.plot(km_m[period], km_m[survival_prob] * 100,colorcls.COLORS[minimalist], linewidth2.5, label日式极简无 Logo,markero, markersize4)# 潮款组km_g analyzer.kaplan_meier_estimate(graphic)ax.plot(km_g[period], km_g[survival_prob] * 100,colorcls.COLORS[graphic], linewidth2.5, label印花潮款,markers, markersize4)# 标注关键时间点ax.axhline(y50, colorgray, linestyle:, alpha0.5, label50% 生存线)# 找到中位数生存时间m_median km_m[km_m[survival_prob] 0.5].iloc[0][period] if len(km_m[km_m[survival_prob] 0.5]) 0 else 24g_median km_g[km_g[survival_prob] 0.5].iloc[0][period] if len(km_g[km_g[survival_prob] 0.5]) 0 else 24ax.annotate(f极简中位数: {m_median}月, xy(m_median, 50),xytext(m_median 1, 60), fontsize9, colorcls.COLORS[minimalist],fontweightbold, arrowpropsdict(arrowstyle-))ax.annotate(f潮款中位数: {g_median}月, xy(g_median, 50),xytext(g_median - 3, 40), fontsize9, colorcls.COLORS[graphic],fontweightbold, arrowpropsdict(arrowstyle-))ax.set_xlabel(时间月, fontsize12)ax.set_ylabel(用户存活率%, fontsize12)ax.set_title(用户生命周期对比日式极简 vs 印花潮款, fontsize14, fontweightbold)ax.legend(fontsize10, locupper right)ax.grid(True, alpha0.3)ax.set_xlim(0, 24)ax.set_ylim(0, 105)plt.tight_layout()plt.savefig(filename, dpi120)plt.show()print(f[图表] 生存曲线已保存: {filename})classmethoddef plot_retention_comparison(cls, comparison_df, filenameretention_comparison.png):绘制留存率对比柱状图fig, ax plt.subplots(figsize(12, 6))x comparison_df[月份]width 0.35ax.bar(x - width/2, comparison_df[极简无Logo留存率(%)], width,label日式极简无 Logo, colorcls.COLORS[minimalist], alpha0.8)ax.bar(x width/2, comparison_df[印花潮款留存率(%)], width,label印花潮款, colorcls.COLORS[graphic], alpha0.8)ax.set_xlabel(时间月, fontsize12)ax.set_ylabel(留存率%, fontsize12)ax.set_title(月度留存率对比, fontsize14, fontweightbold)ax.legend(fontsize10)ax.grid(axisy, alpha0.3)ax.set_xlim(0.5, 13)plt.tight_layout()plt.savefig(filename, dpi120)plt.show()print(f[图表] 留存对比已保存: {filename})classmethoddef plot_ltv_comparison(cls, ltv_data, filenameltv_comparison.png):绘制 LTV 对比雷达图fig, ax plt.subplots(figsize(8, 8), subplot_kwdict(polarTrue))categories [平均 LTV, 生命周期(月), 单次消费(元), 购买频次]N len(categories)angles [n / float(N) * 2 * np.pi for n in range(N)]angles angles[:1]# 极简组m_vals [ltv_data[minimalist][avg_ltv] / 1000, # 归一化ltv_data[minimalist][avg_lifetime_months] / 24,ltv_data[minimalist][avg_spend_per_purchase] / 500,ltv_data[minimalist][avg_purchase_count] / 10]m_vals m_vals[:1]ax.plot(angles, m_vals, o-, linewidth2, colorcls.COLORS[minimalist],label日式极简无 Logo)ax.fill(angles, m_vals, alpha0.15, colorcls.COLORS[minimalist])# 潮款组g_vals [ltv_data[graphic][avg_ltv] / 1000,ltv_data[graphic][avg_lifetime_months] / 24,ltv_data[graphic][avg_spend_per_purchase] / 500,ltv_data[graphic][avg_purchase_count] / 10]g_vals g_vals[:1]ax.plot(angles, g_vals, s-, linewidth2, colorcls.COLORS[graphic],label印花潮款)ax.fill(angles, g_vals, alpha0.15, colorcls.COLORS[graphic])ax.set_xticks(angles[:-1])ax.set_xticklabels(categories, fontsize10)ax.set_title(用户价值画像对比, fontsize14, fontweightbold, pad20)ax.legend(fontsize9, locupper right, bbox_to_anchor(1.25, 1.1))plt.tight_layout()plt.savefig(filename, dpi120)plt.show()print(f[图表] LTV 对比已保存: {filename})4.main.py (主程序入口)from data_generator import UserBehaviorGeneratorfrom retention_analyzer import RetentionAnalyzerfrom visualizer import RetentionDashboarddef run_analysis():端到端复购率分析流程print( * 70)print( 日式极简 vs 印花潮款复购率与用户留存对比分析)print( * 70)# Step 1: 生成模拟用户数据print(\n[Step 1/5] 生成用户行为数据...)generator UserBehaviorGenerator(seed42)df_minimalist generator.generate_cohort(n_users5000, product_typeminimalist)df_graphic generator.generate_cohort(n_users5000, product_typegraphic)print(f 极简组用户数: {len(df_minimalist):,})print(f 潮款组用户数: {len(df_graphic):,})# Step 2: 初始化分析器print(\n[Step 2/5] 初始化留存分析引擎...)analyzer RetentionAnalyzer(df_minimalist, df_graphic)# Step 3: 留存率对比print(\n[Step 3/5] 计算月度留存率对比...)comparison analyzer.compare_retention(periods12)print(\n ── 月度留存率对比 ───────────────────────)print(f {月份:4} {极简无Logo:10} {印花潮款:10} {差值:8})print(f {-*44})for _, row in comparison.iterrows():print(f {row[月份]:4.0f} {row[极简无Logo留存率(%)]:9.1f}% f{row[印花潮款留存率(%)]:9.1f}% {row[差值(百分点)]:7.1f})# Step 4: LTV 分析print(\n[Step 4/5] 计算用户终身价值LTV...)ltv_data analyzer.ltv_analysis()print(\n ── LTV 对比 ──────────────────────────────)print(f {指标:20} {极简无Logo:12} {印花潮款:12})print(f {-*48})m ltv_data[minimalist]g ltv_data[graphic]print(f {平均 LTV元:20} {m[avg_ltv]:12,.0f} {g[avg_ltv]:12,.0f})print(f {生命周期月:20} {m[avg_lifetime_months]:12.1f} {g[avg_lifetime_months]:12.1f})print(f {单次消费元:20} {m[avg_spend_per_purchase]:12,.0f} {g[avg_spend_per_purchase]:12,.0f})print(f {购买频次:20} {m[avg_purchase_count]:12.1f} {g[avg_purchase_count]:12.1f})# Step 5: 统计检验print(\n[Step 5/5] 执行 Log-Rank 统计检验...)test_result analyzer.log_rank_test()print(f\n ── 统计检验结果 ─────────────────────────)print(f Chi-Square 值: {test_result[chi_square]:.4f})print(f P 值: {test_result[p_value]:.6f})if test_result[is_significant]:print(f 结论: ✅ 两组差异具有统计显著性P 0.05)print(f → 极简与潮款的留存差异不是偶然是产品哲学的本质不同)else:print(f 结论: ⚠️ 两组差异不显著P 0.05)print(f → 需要更多数据或样本量来验证差异)# Step 6: 可视化print(\n [可视化] 正在渲染图表...)dashboard RetentionDashboard()dashboard.plot_survival_curves(analyzer)dashboard.plot_retention_comparison(comparison)dashboard.plot_ltv_comparison(ltv_data)print(\n * 70)print( 分析完成所有图表已保存。)print( * 70)if __name__ __main__:run_analysis()README.md# Japanese Minimalist vs Graphic Trend — 复购率对比分析器## 项目简介基于 Kaplan-Meier 生存分析的复购率评估系统。对比日式极简无 Logo与印花潮款的长期用户留存差异。## ⚙️ 运行环境- Python 3.8- 依赖库: numpy, pandas, matplotlib, scipy## 快速开始1. 安装依赖: pip install numpy pandas matplotlib scipy2. 运行主程序: python main.py## 输出说明程序将输出1. **终端报告**: 月度留存率对比表、LTV 分析、Log-Rank 检验结果。2. **survival_curves.png**: Kaplan-Meier 生存曲线对比。3. **retention_comparison.png**: 月度留存率柱状图。4. **ltv_comparison.png**: 用户价值画像雷达图。## ️ 参数调整在 main.py 的 run_analysis() 中修改- n_users: 模拟用户数默认 5000/组- seed: 随机种子保证可复现性在 data_generator.py 中调整用户行为参数- purchase_cycle: 平均购买周期天- ltv_months: 用户生命周期月- avg_spend: 平均客单价- churn_rate: 月流失率## 目录结构retention_analyzer/├── data_generator.py # 用户行为数据生成器├── retention_analyzer.py # 核心分析引擎├── visualizer.py # 可视化仪表盘├── main.py # 主程序入口└── README.md核心知识点卡片┌──────────────────────────────────────────────────────┐│ 知识点卡片时尚产业用户留存与 LTV 分析 │├──────────────────────────────────────────────────────┤│ ││ 1. Kaplan-Meier 生存分析 ││ ────────────────────────────────────────────────── ││ • 起源医学临床试验病人存活率分析 ││ • 转译用户的存活 持续复购 ││ • 核心公式S(t) Π(1 - d_i/n_i) ││ • 优势能处理删失数据用户还在观察期内 ││ ││ 2. 复购率 vs 留存率 ││ ────────────────────────────────────────────────── ││ • 复购率买过 2 次以上 / 总用户数静态 ││ • 留存率经过 t 时间后仍活跃的用户比例动态 ││ • 关键洞察极简款的 12 月留存 潮款 3 月留存 ││ ││ 3. Log-Rank 检验 ││ ────────────────────────────────────────────────── ││ • 零假设 H0两组生存曲线完全相同 ││ • 备择假设 H1两组生存曲线有差异 ││ • P 0.05 → 差异显著不是偶然 ││ ││利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛