Просмотр исходного кода

feat(渠道效果分析): 增加二级品类矩阵到CSV和HTML

CSV新增:
- 渠道×二级品类明细.csv
- 渠道×二级品类矩阵.csv

HTML新增:
- 渠道×二级品类 回流率矩阵
- 渠道×二级品类 点击UV矩阵

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
yangxiaohui 2 месяцев назад
Родитель
Сommit
515810a2dc
2 измененных файлов с 92 добавлено и 6 удалено
  1. 7 3
      tasks/渠道效果分析/analyze.py
  2. 85 3
      tasks/渠道效果分析/visualize.py

+ 7 - 3
tasks/渠道效果分析/analyze.py

@@ -334,13 +334,17 @@ channel_category_csv = channel_category_csv.sort_values(['渠道', '点击UV'],
 channel_category_csv.to_csv(output_dir / f"{latest_file.stem}_渠道品类明细.csv", index=False, encoding='utf-8-sig')
 log(f"  渠道×品类明细: {latest_file.stem}_渠道品类明细.csv")
 
-# 4. 渠道×二级品类 Top
+# 4. 渠道×二级品类明细
 if 'merge二级品类' in df.columns:
     cat2_csv = channel_cat2[channel_cat2['点击uv'] >= 100].copy()
     cat2_csv.columns = ['渠道', '一级品类', '二级品类', '点击UV', '回流率']
     cat2_csv = cat2_csv.sort_values('回流率', ascending=False)
-    cat2_csv.to_csv(output_dir / f"{latest_file.stem}_渠道二级品类.csv", index=False, encoding='utf-8-sig')
-    log(f"  渠道×二级品类: {latest_file.stem}_渠道二级品类.csv")
+    cat2_csv.to_csv(output_dir / f"{latest_file.stem}_渠道二级品类明细.csv", index=False, encoding='utf-8-sig')
+    log(f"  渠道×二级品类明细: {latest_file.stem}_渠道二级品类明细.csv")
+
+    # 5. 渠道×二级品类矩阵(回流率)
+    pivot_cat2_ror.to_csv(output_dir / f"{latest_file.stem}_渠道二级品类矩阵.csv", encoding='utf-8-sig')
+    log(f"  渠道×二级品类矩阵: {latest_file.stem}_渠道二级品类矩阵.csv")
 
 # 5. 每日趋势
 daily_csv = daily_channel.copy()

+ 85 - 3
tasks/渠道效果分析/visualize.py

@@ -154,7 +154,63 @@ for cat in valid_categories:
             cells.append("<td>-</td>")
     recommend_rows.append("<tr>" + "".join(cells) + "</tr>")
 
-# 5. 趋势数据
+# 5. 二级品类热力图
+channel_cat2 = df.groupby(['channel', 'merge一级品类', 'merge二级品类']).apply(
+    lambda x: pd.Series({
+        '点击uv': x['点击uv'].sum(),
+        '回流率': (x['再分享回流率'] * x['点击uv']).sum() / x['点击uv'].sum() if x['点击uv'].sum() > 0 else 0
+    }), include_groups=False
+).reset_index()
+
+# 创建二级品类标签
+channel_cat2['cat2_label'] = channel_cat2.apply(
+    lambda r: f"{str(r['merge一级品类'])[:6]}/{str(r['merge二级品类'])[:8]}"
+    if pd.notna(r['merge二级品类']) else str(r['merge一级品类'])[:12], axis=1
+)
+
+# 二级品类汇总
+cat2_stats = channel_cat2.groupby('cat2_label').agg({'点击uv': 'sum'}).reset_index()
+cat2_stats = cat2_stats[cat2_stats['点击uv'] >= 1000].sort_values('点击uv', ascending=False)
+valid_cat2_labels = cat2_stats.head(20)['cat2_label'].tolist()
+
+pivot_cat2_ror = channel_cat2.pivot_table(index='cat2_label', columns='channel', values='回流率')
+pivot_cat2_uv = channel_cat2.pivot_table(index='cat2_label', columns='channel', values='点击uv', fill_value=0)
+
+# 二级品类回流率热力图
+cat2_ror_header = "<tr><th>二级品类</th>" + "".join([f"<th>{c[:10]}</th>" for c in heatmap_cols]) + "</tr>"
+cat2_ror_rows = []
+for cat2 in valid_cat2_labels:
+    if cat2 not in pivot_cat2_ror.index:
+        continue
+    cells = [f"<td>{cat2[:15]}</td>"]
+    for ch in heatmap_cols:
+        if ch in pivot_cat2_ror.columns and pd.notna(pivot_cat2_ror.loc[cat2, ch]):
+            val = pivot_cat2_ror.loc[cat2, ch] * 100
+            cls = get_cell_class(val)
+            cells.append(f'<td class="{cls}">{val:.1f}%</td>')
+        else:
+            cells.append("<td>-</td>")
+    cat2_ror_rows.append("<tr>" + "".join(cells) + "</tr>")
+
+# 二级品类UV热力图
+cat2_uv_rows = []
+for cat2 in valid_cat2_labels:
+    if cat2 not in pivot_cat2_uv.index:
+        continue
+    cells = [f"<td>{cat2[:15]}</td>"]
+    for ch in heatmap_cols:
+        if ch in pivot_cat2_uv.columns:
+            val = pivot_cat2_uv.loc[cat2, ch]
+            if val > 0:
+                cls = get_uv_class(val)
+                cells.append(f'<td class="{cls}">{int(val):,}</td>')
+            else:
+                cells.append("<td>-</td>")
+        else:
+            cells.append("<td>-</td>")
+    cat2_uv_rows.append("<tr>" + "".join(cells) + "</tr>")
+
+# 6. 趋势数据
 top_channels = channel_stats.head(6)['channel'].tolist()
 trend_data = {}
 for ch in top_channels:
@@ -293,12 +349,38 @@ html_content = f"""<!DOCTYPE html>
             </table>
         </div>
 
-        <h2>5. 每日回流率趋势</h2>
+        <h2>5. 渠道×二级品类 回流率矩阵</h2>
+        <div class="chart-container heatmap matrix-section">
+            <div class="legend">
+                <span class="high">高 &gt;30%</span>
+                <span class="medium">中 15-30%</span>
+                <span class="low">低 &lt;15%</span>
+            </div>
+            <table>
+                {cat2_ror_header}
+                {"".join(cat2_ror_rows)}
+            </table>
+        </div>
+
+        <h2>6. 渠道×二级品类 点击UV矩阵</h2>
+        <div class="chart-container heatmap matrix-section">
+            <div class="legend">
+                <span class="high">高 &gt;5万</span>
+                <span class="medium">中 1-5万</span>
+                <span class="low">低 &lt;1万</span>
+            </div>
+            <table>
+                {cat2_ror_header}
+                {"".join(cat2_uv_rows)}
+            </table>
+        </div>
+
+        <h2>7. 每日回流率趋势</h2>
         <div class="chart-container">
             <canvas id="trendChart"></canvas>
         </div>
 
-        <h2>6. UV vs 回流率 散点分布</h2>
+        <h2>8. UV vs 回流率 散点分布</h2>
         <div class="chart-container">
             <canvas id="scatterChart"></canvas>
         </div>