我们用 UserAnalyticsData 这个“乐高工厂”一次性生成 4 组核心指标:

维度积木块一句话卖点
设备分布Mobile 45% / Desktop 30% / Tablet 20%一眼看清“移动端优先”是不是伪命题
小时/周活跃度7×24 热力图工作日 9-18 点才是黄金 9 小时
全年日历365 天日活周末 vs 工作日,谁才是真高活
转化漏斗Homepage → Success 6 级流失购物车到支付不到 83%,钱袋子破洞

代码只有 60 行,却像 4 条高速公路,把数据直接开到图表门口。

analytics_data = UserAnalyticsData(seed=42)

环形图:掏空中心,留白暗示“其他”永远只是配角。

热力图:sns.heatmap 直接标数字,色块深浅=流量高低,0 思考成本。

年历图:用 calmap.yearplot 把 365 天压成一张“二维码”,周末红成一片。

漏斗图:不用第三方库,纯手工 matplotlib.patches.Rectangle

详细案例:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import calmap
import matplotlib.patches as patches
from datetime import datetime, timedelta

class UserAnalyticsData:
    def __init__(self, seed=42):
        np.random.seed(seed)
        self.generate_all_data()
    
    def generate_device_data(self):
        """生成设备使用分布数据"""
        self.devices = ['Mobile', 'Desktop', 'Tablet', 'Other']
        self.device_values = [45, 30, 20, 5]
        
    def generate_hourly_activity(self):
        """生成每小时活跃度数据"""
        self.hours = range(24)
        self.days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        # 模拟工作时间段(9-18点)活跃度较高的情况
        base_activity = np.random.randint(10, 30, size=(7, 24))
        work_hours_boost = np.zeros((7, 24))
        work_hours_boost[:, 9:18] = np.random.randint(30, 70, size=(7, 9))
        self.activity_data = (base_activity + work_hours_boost).astype(int)
        
    def generate_yearly_activity(self):
        """生成全年活跃度数据"""
        self.dates = pd.date_range('2024-01-01', '2024-12-31')
        base_activity = np.random.randint(50, 100, len(self.dates))
        # 模拟周末活跃度较高的情况
        weekend_mask = self.dates.weekday.isin([5, 6])
        weekend_boost = np.where(weekend_mask, np.random.randint(50, 100, len(self.dates)), 0)
        self.daily_activity = pd.Series(base_activity + weekend_boost, index=self.dates)
        
    def generate_user_flow(self):
        """生成用户流转数据"""
        self.stages = ['Homepage', 'Category', 'Product', 'Cart', 'Payment', 'Success']
        # 模拟真实的转化漏斗
        initial_users = 100
        conversion_rates = [0.8, 0.5, 0.75, 0.83]  # 各阶段转化率
        self.flow_values = [initial_users]
        current_users = initial_users
        for rate in conversion_rates:
            current_users = int(current_users * rate)
            self.flow_values.append(current_users)
            
    def generate_all_data(self):
        """生成所有需要的数据"""
        self.generate_device_data()
        self.generate_hourly_activity()
        self.generate_yearly_activity()
        self.generate_user_flow()

class UserAnalyticsVisualizer:
    def __init__(self, data):
        self.data = data
        
    def setup_plot_style(self):
        """设置绘图样式"""
        plt.figure(figsize=(20, 15))
        plt.rcParams['figure.facecolor'] = 'white'
        plt.rcParams['axes.facecolor'] = 'white'
        plt.rcParams['grid.color'] = 'gray'
        plt.rcParams['grid.linestyle'] = '--'
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
        plt.rcParams['axes.unicode_minus'] = False  # 处理负号显示
        
    def plot_device_distribution(self, ax):
        """绘制设备分布环形图"""
        plt.sca(ax)
        plt.pie(self.data.device_values, labels=self.data.devices, 
                autopct='%1.1f%%', pctdistance=0.85,
                wedgeprops=dict(width=0.5))
        plt.title('设备分布环形图')
        
    def plot_activity_heatmap(self, ax):
        """绘制活跃度热力图"""
        plt.sca(ax)
        sns.heatmap(self.data.activity_data, 
                    xticklabels=self.data.hours,
                    yticklabels=self.data.days,
                    cmap='YlOrRd', 
                    annot=True, 
                    fmt='d')  # 使用'd'因为数据已经转换为整数
        plt.title('活跃度热力图')
        
    def plot_yearly_calendar(self, ax):
        """绘制年度日历图"""
        plt.sca(ax)
        calmap.yearplot(self.data.daily_activity, 
                       year=2024, 
                       cmap='YlOrRd',
                       fillcolor='lightgrey', 
                       daylabels='MTWTFSS')
        plt.title('年度日历图 (2024)')
        
    def draw_flow(self, ax, x, y, width, height, color):
        """绘制流动效果辅助函数"""
        rect = patches.Rectangle((x, y), width, height, 
                               facecolor=color, alpha=0.6)
        ax.add_patch(rect)
        
    def plot_user_flow(self, ax):
        """绘制用户流转图"""
        plt.sca(ax)
        colors = plt.cm.Oranges(np.linspace(0.3, 0.9, len(self.data.stages)))
        y_positions = np.linspace(0.2, 0.8, len(self.data.stages))
        
        for i in range(len(self.data.flow_values)):
            self.draw_flow(ax, i*0.15, y_positions[i], 0.1, 
                         self.data.flow_values[i]/200, colors[i])
            plt.text(i*0.15-0.02, y_positions[i], 
                    self.data.stages[i], rotation=45)
            if i < len(self.data.flow_values):
                plt.text(i*0.15+0.05, y_positions[i]+self.data.flow_values[i]/400, 
                        str(self.data.flow_values[i]))
        
        ax.set_xlim(-0.1, 1)
        ax.set_ylim(0, 1)
        ax.axis('off')
        plt.title('用户流转图')
        
    def create_visualization(self):
        """创建所有可视化图表"""
        self.setup_plot_style()
        
        # 创建子图
        ax1 = plt.subplot(221)
        ax2 = plt.subplot(222)
        ax3 = plt.subplot(223)
        ax4 = plt.subplot(224)
        
        # 绘制各个图表
        self.plot_device_distribution(ax1)
        self.plot_activity_heatmap(ax2)
        self.plot_yearly_calendar(ax3)
        self.plot_user_flow(ax4)
        
        plt.tight_layout()
        plt.show()
        
    def generate_report(self):
        """生成数据分析报告"""
        print("\n数据分析报告:")
        print("1. 设备使用分布:")
        print(f"   - 移动端用户占比最高,达到{self.data.device_values[0]}%")
        print(f"   - 桌面端次之,占{self.data.device_values[1]}%")
        
        print("\n2. 用户活跃时间分析:")
        print("   - 工作日活跃度高于周末")
        print("   - 高峰时段集中在9:00-18:00")
        
        print("\n3. 全年活跃度分析:")
        print(f"   - 平均日活跃用户数: {self.data.daily_activity.mean():.0f}")
        print(f"   - 最高日活跃用户数: {self.data.daily_activity.max()}")
        
        print("\n4. 用户行为流转:")
        conversion_rate = (self.data.flow_values[-1] / self.data.flow_values[0]) * 100
        cart_to_payment = (self.data.flow_values[-2] / self.data.flow_values[-3]) * 100
        print(f"   - 从首页到成功支付的转化率约为{conversion_rate:.1f}%")
        print(f"   - 购物车到支付环节的转化率为{cart_to_payment:.1f}%")

# 使用示例
if __name__ == "__main__":
    # 生成数据
    analytics_data = UserAnalyticsData(seed=42)
    
    # 创建可视化器并展示结果
    visualizer = UserAnalyticsVisualizer(analytics_data)
    visualizer.create_visualization()
    visualizer.generate_report()