使用Python 和 Streamlit 构建的多维度游戏玩家数据分析
这是一个基于 Python 和 Streamlit 构建的多维度游戏玩家数据分析应用。项目旨在为游戏运营团队提供一个全面、交互式的数据分析工具,通过12个核心维度深度剖析玩家行为,帮助制定科学的运营策略,提升玩家留存和付费转化。
·

游戏玩家数据分析与可视化
概述
这是一个基于 Python 和 Streamlit 构建的多维度游戏玩家数据分析应用。项目旨在为游戏运营团队提供一个全面、交互式的数据分析工具,通过12个核心维度深度剖析玩家行为,帮助制定科学的运营策略,提升玩家留存和付费转化。
目标
- 数据洞察:从海量玩家数据中提取有价值的信息
- 用户分群:精细化识别不同类型玩家特征
- 预测分析:提前识别潜在流失风险和高价值用户
- 决策支持:基于数据提供可执行的运营建议
架构
技术栈全景
┌─────────────────────────────────────────────┐
│ 前端界面层 (UI Layer) │
│ ┌─────────────────────────────────────┐ │
│ │ Streamlit + Custom CSS + Plotly │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────────┤
│ 业务逻辑层 (Logic Layer) │
│ ┌─────────────────────────────────────┐ │
│ │ Pandas + NumPy + Scikit-learn │ │
│ │ + 数据分析算法 + 预测模型 │ │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────────┤
│ 数据处理层 (Data Layer) │
│ ┌─────────────────────────────────────┐ │
│ │ CSV文件处理 + 数据预处理 │
│ │ + 特征工程 + 缓存机制 │
│ └─────────────────────────────────────┘ │
├─────────────────────────────────────────────┤
│ 基础设施层 (Infrastructure) │
│ ┌─────────────────────────────────────┐ │
│ │ Python 3.10+ + 依赖管理系统 │
│ │ + 开发环境配置 │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
详细技术栈配置
| 技术类别 | 具体组件 | 版本 | 主要用途 |
|---|---|---|---|
| 前端框架 | Streamlit | 1.28.0 | 构建交互式Web界面 |
| 数据处理 | Pandas | 2.1.4 | 数据加载、清洗、转换 |
| 数值计算 | NumPy | 1.24.3 | 数值运算、数组处理 |
| 可视化 | Plotly | 5.17.0 | 交互式图表、3D可视化 |
| 机器学习 | Scikit-learn | 1.3.2 | 聚类分析、预测模型 |
| 可选依赖 | Seaborn | 0.13.0 | 统计可视化(降级可用) |
| 系统支持 | Python | 3.10+ | 运行环境 |
结构设计
1. 原始数据字段 (12个维度)
| 字段名称 | 数据类型 | 说明 | 示例 |
|---|---|---|---|
| 用户ID | Integer | 玩家唯一标识 | 100001 |
| 玩家性别 | String | 性别信息 | 男/女 |
| 玩家年龄 | Integer | 实际年龄 | 27 |
| 所在区域 | String | 地理位置 | 华东/华南/华北 |
| 游戏段位 | String | 竞技水平 | 荣耀青铜/最强王者 |
| 日均时长 | Float | 平均每日游戏时长(小时) | 3.9 |
| 累计消费 | Float | 历史总消费金额(元) | 22.41 |
| 擅长位置 | String | 游戏角色偏好 | 坦克/刺客/辅助 |
| 综合胜率 | Float | 胜率(0-1) | 0.483 |
| KDA指数 | Float | 击杀/死亡/助攻综合评分 | 5.4 |
| 登录频率 | String | 活跃度指标 | 每天/每周3-5次 |
| 行为模式 | String | 玩家类型标识 | 竞技型/社交型 |
2. 衍生字段设计(通过特征工程生成)
特征工程架构
# 数据预处理函数 - 特征工程核心
def preprocess_data(df):
# 1. 离散化处理
df['消费分段'] = pd.cut(df['累计消费'], bins=[0, 20, 100, float('inf')],
labels=['低消费', '中消费', '高消费'])
df['年龄分段'] = pd.cut(df['玩家年龄'], bins=[0, 18, 25, 30, 100],
labels=['青少年', '青年', '成年', '中老年'])
# 2. 编码转换
rank_order = ['荣耀青铜', '秩序白银', '尊贵铂金', '永恒钻石', '至尊星耀', '最强王者']
df['段位等级'] = df['游戏段位'].map({r: i+1 for i, r in enumerate(rank_order)})
# 3. 复合特征
df['活跃度分数'] = (df['登录频率等级'] * 0.6 +
df['日均时长'] / df['日均时长'].max() * 0.4)
# 4. 分群标签
df['活跃度分段'] = pd.qcut(df['活跃度分数'], q=[0, 0.33, 0.66, 1],
labels=['流失用户', '普通用户', '核心用户'])
return df
衍生字段详情
| 衍生字段 | 生成方法 | 业务含义 | 数据类型 |
|---|---|---|---|
| 消费分段 | pd.cut(累计消费) | 玩家付费能力等级 | Categorical |
| 年龄分段 | pd.cut(玩家年龄) | 年龄段分布 | Categorical |
| 段位等级 | 有序编码映射 | 竞技水平量化 | Integer |
| 登录频率等级 | 频率编码 | 活跃度量化 | Integer |
| 活跃度分数 | 加权计算(0.6登录等级+0.4时长标准化) | 综合活跃度评分 | Float |
| 活跃度分段 | pd.qcut(活跃度分数) | 活跃度分群标签 | Categorical |
| 技能分数 | 0.4段位等级+0.3胜率100+0.3KDA标准化 | 综合技能评分 | Float |
| 技能分段 | pd.qcut(技能分数) | 技能水平分群 | Categorical |
| 流失风险 | 规则模型预测 | 流失可能性评分 | Float |
| 流失风险等级 | pd.cut(流失风险) | 流失风险等级 | Categorical |
| 付费潜力 | 规则模型预测 | 未来付费可能性 | Categorical |
| 预计晋升时间 | 规则模型预测 | 段位提升时间估计 | Categorical |
| 用户价值分 | 0.5消费+0.3活跃度+0.2*技能 | 综合价值评分 | Float |
功能详解
模块一:用户画像分析
1.1 人口统计画像
- 性别分布:饼图展示男女比例
- 年龄分段:条形图展示各年龄段人数
- 区域分布:柱状图展示各地区用户数量
1.2 游戏能力画像
- 段位分布:条形图展示各段位用户数
- 位置偏好:饼图展示不同位置玩家比例
- KDA分布:直方图展示技能水平
- 胜率分布:直方图展示胜负情况
1.3 行为习惯画像
- 登录频率:条形图展示活跃频率
- 行为模式:饼图展示玩家类型
- 日均时长:直方图展示游戏时长习惯
1.4 消费能力画像
- 消费分段:饼图展示高/中/低消费比例
- 累计消费:直方图展示消费金额分布
模块二:用户细分分析
2.1 价值分群
- 高消费用户:累计消费>100元
- 中消费用户:20元<累计消费≤100元
- 低消费用户:累计消费≤20元
2.2 活跃度分群
- 核心用户:活跃度分数前33%
- 普通用户:活跃度分数中间34%
- 流失用户:活跃度分数后33%
2.3 技能分群
- 高手:技能分数前33%
- 进阶玩家:技能分数中间34%
- 新手:技能分数后33%
2.4 偏好分群
- 基于擅长位置分析消费分布
- 识别不同位置偏好的用户特征
2.5 行为分群
- 按行为模式分析段位和消费分布
- 识别竞技型、社交型、休闲型玩家特征
模块三:🔗 关联与相关性分析
3.1 消费驱动因素分析
- 时长vs消费:散点图分析游戏时长与消费关系
- 段位vs消费:箱线图分析不同段位消费分布
- 胜率vs消费:散点图分析胜率与消费关联
3.2 活跃度影响因素
- 年龄vs登录频率:热力图分析年龄与活跃度关系
- 区域vs登录频率:热力图分析区域与活跃度关联
3.3 技能成长路径
- 时长vsKDA:散点图分析游戏时长与技能关系
- 段位vsKDA:箱线图分析不同段位技能水平
3.4 区域差异分析
- 区域消费对比:柱状图展示各地区平均消费
- 区域活跃度:柱状图展示各地区游戏时长
- 区域段位分布:热力图分析各地区段位特征
模块四:预测性分析
4.1 用户流失预测模型
def predict_churn_risk(row):
"""基于规则的流失风险预测"""
login_score = {'每月几次': 3, '每周1-2次': 2, '每周3-5次': 1, '每天': 0}
behavior_score = {'娱乐型': 1, '休闲型': 2, '社交型': 3, '收藏型': 2, '竞技型': 4}
risk = (login_score.get(row['登录频率'], 2) * 0.4 +
(1 if row['日均时长'] < 2 else 0) * 0.3 +
behavior_score.get(row['行为模式'], 2) * 0.3)
return min(risk, 3)
4.2 付费潜力预测
- 高潜力:累计消费>100元
- 中潜力:20元<累计消费≤100元
- 低潜力:累计消费≤20元
4.3 段位晋升时间预测
- 1-2周:KDA>4且胜率>0.55
- 2-4周:KDA>3且胜率>0.5
- 4-8周:其他情况
模块五:运营决策支持
5.1 高价值用户识别
- TOP10用户:基于用户价值分排序
- 价值分布:识别高价值用户特征
- 专属福利策略:针对高价值用户制定个性化方案
5.2 流失预警干预
- 高风险用户识别:流失风险等级=高风险
- 召回策略:针对不同类型用户的召回方案
- 个性化推送:基于行为模式的定向内容
5.3 匹配优化建议
- 段位平衡算法:优化对战匹配机制
- 技能权重调整:综合胜率、KDA、时长多维度
- 新手保护机制:根据游戏时长调整匹配范围
5.4 内容推荐策略
- 位置偏好推荐:基于擅长位置的个性化内容
- 行为模式推荐:针对不同类型玩家的差异化推荐
- 消费能力推荐:基于消费分段的商品推荐
5.5 定价策略分析
- 区域差异定价:基于各地区平均消费水平
- 分层定价模型:基础版/进阶版/豪华版策略
- 促销活动优化:根据不同用户特征制定促销方案
模块六:可视化分析
6.1 用户分布分析
- 区域热力图:展示各地区用户分布密度
- 年龄/段位金字塔:金字塔图展示用户结构
6.2 趋势分析
- 登录频率趋势:折线图展示活跃度变化
- 日均时长分布:柱状图展示游戏时长趋势
6.3 对比分析
- 性别消费对比:柱状图对比男女消费差异
- 区域活跃度对比:柱状图对比各地区游戏时长
6.4 关联分析
- 多维度散点图:时长vs消费、胜率vsKDA
- 3D散点图:时长、消费、胜率三维关联分析
6.5 分群分析
- 雷达图分群对比:各行为模式用户画像对比
- 标准化对比图:多维度标准化后的对比分析
🔧 关键技术实现
1. 数据预处理优化
1.1 特征工程策略
class FeatureEngineer:
def __init__(self):
self.rank_mapping = None
self.login_mapping = None
def fit(self, df):
"""学习特征转换规则"""
# 段位有序编码
rank_order = ['荣耀青铜', '秩序白银', '尊贵铂金', '永恒钻石', '至尊星耀', '最强王者']
self.rank_mapping = {r: i+1 for i, r in enumerate(rank_order)}
# 登录频率有序编码
login_order = ['每月几次', '每周1-2次', '每周3-5次', '每天']
self.login_mapping = {l: i+1 for i, l in enumerate(login_order)}
def transform(self, df):
"""应用特征转换"""
df_transformed = df.copy()
# 离散化特征
df_transformed['消费分段'] = pd.cut(df['累计消费'],
bins=[0, 20, 100, float('inf')],
labels=['低消费', '中消费', '高消费'])
# 有序编码
df_transformed['段位等级'] = df['游戏段位'].map(self.rank_mapping)
df_transformed['登录频率等级'] = df['登录频率'].map(self.login_mapping)
# 复合特征
df_transformed = self._create_composite_features(df_transformed)
return df_transformed
1.2 缓存机制优化
@st.cache_data(ttl=3600, max_entries=5)
def load_data(file):
"""带缓存的数据加载"""
df = pd.read_csv(file)
# 数据类型转换
numeric_columns = ['日均时长', '累计消费', '综合胜率', 'KDA指数', '玩家年龄']
for col in numeric_columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
return df
2. 可视化架构
2.1 响应式图表系统
class ResponsiveChartSystem:
def __init__(self):
self.theme = 'plotly_white'
def create_scatter_matrix(self, df, dimensions, color_col=None):
"""创建响应式散点矩阵"""
fig = px.scatter_matrix(df,
dimensions=dimensions,
color=color_col,
title="多维关联分析矩阵",
opacity=0.7,
template=self.theme)
# 自适应调整
fig.update_traces(diagonal_visible=True)
fig.update_layout(height=800)
return fig
def create_animated_timeline(self, df, time_col, value_cols):
"""创建动画时间线"""
fig = px.line(df, x=time_col, y=value_cols,
title="时间趋势分析",
template=self.theme)
fig.update_layout(
hovermode='x unified',
xaxis=dict(title="时间轴"),
yaxis=dict(title="指标值")
)
return fig
2.2 交互式组件
class InteractiveComponents:
def __init__(self):
self.filters = {}
def create_dynamic_filter(self, df, column_name):
"""创建动态过滤器"""
unique_values = df[column_name].unique()
filter_widget = st.multiselect(
f"筛选 {column_name}",
options=unique_values,
default=unique_values[:5] if len(unique_values) > 5 else unique_values
)
self.filters[column_name] = filter_widget
return filter_widget
def apply_filters(self, df):
"""应用过滤器"""
filtered_df = df.copy()
for col, values in self.filters.items():
if values:
filtered_df = filtered_df[filtered_df[col].isin(values)]
return filtered_df
3. 机器学习应用
3.1 K-means聚类分析
class PlayerClustering:
def __init__(self, n_clusters=4):
self.n_clusters = n_clusters
self.scaler = StandardScaler()
self.kmeans = KMeans(n_clusters=n_clusters, random_state=42)
self.cluster_labels = None
def fit_transform(self, df):
"""聚类分析"""
# 特征选择
features = ['段位等级', '日均时长', '累计消费', '综合胜率', 'KDA指数']
X = df[features].fillna(0)
# 标准化
X_scaled = self.scaler.fit_transform(X)
# K-means聚类
self.cluster_labels = self.kmeans.fit_predict(X_scaled)
# 分析聚类特征
self._analyze_clusters(df, self.cluster_labels)
return self.cluster_labels
3.2 预测模型架构
class PredictiveModels:
def __init__(self):
self.churn_model = RandomForestClassifier()
self.value_model = RandomForestRegressor()
def train_churn_model(self, X_train, y_train):
"""训练流失预测模型"""
X_train_encoded = self._encode_features(X_train)
self.churn_model.fit(X_train_encoded, y_train)
# 模型评估
scores = cross_val_score(self.churn_model, X_train_encoded, y_train, cv=5)
return scores.mean()
def predict_user_value(self, user_features):
"""预测用户价值"""
return self.value_model.predict([user_features])[0]
UI/UX 设计架构
1. 视觉设计系统
1.1 色彩体系
/* 主色调 - 游戏科技感 */
:root {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--success-gradient: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
--warning-gradient: linear-gradient(135deg, #f39c12 0%, #e74c3c 100%);
--info-gradient: linear-gradient(135deg, #3498db 0%, #2c3e50 100%);
}
1.2 组件设计规范
class UIConstants:
# 字体系统
FONT_PRIMARY = "'Segoe UI', 'Microsoft YaHei', sans-serif"
HEADER_FONT_SIZE = "3rem"
SECTION_FONT_SIZE = "1.8rem"
# 间距系统
SPACING_UNIT = "15px"
SECTION_MARGIN = "25px"
CARD_PADDING = "20px"
# 圆角系统
BORDER_RADIUS_SM = "10px"
BORDER_RADIUS_MD = "15px"
BORDER_RADIUS_LG = "20px"
2. 布局系统
2.1 响应式布局
class LayoutManager:
def __init__(self):
self.breakpoints = {
'xs': 576,
'sm': 768,
'md': 992,
'lg': 1200
}
def create_responsive_columns(self, num_cols):
"""创建响应式列布局"""
col_widths = [f"{100/num_cols}%" for _ in range(num_cols)]
return st.columns(col_widths)
def adaptive_grid(self, items, cols_per_row=3):
"""自适应网格布局"""
rows = (len(items) + cols_per_row - 1) // cols_per_row
for row in range(rows):
cols = st.columns(cols_per_row)
start_idx = row * cols_per_row
end_idx = min(start_idx + cols_per_row, len(items))
for i, item in enumerate(items[start_idx:end_idx]):
with cols[i]:
# 渲染项目
pass
3. 交互设计
3.1 用户流设计
用户流程:
1. 打开应用 → 2. 上传数据 → 3. 查看总览指标 → 4. 选择分析模块
↓
5. 交互式分析 → 6. 查看可视化 → 7. 获取建议 → 8. 导出结果
3.2 反馈机制
class FeedbackSystem:
def show_loading_state(self):
"""显示加载状态"""
with st.spinner("正在处理数据..."):
# 执行耗时操作
pass
def show_progress_bar(self, current, total, label="进度"):
"""显示进度条"""
progress = current / total
st.progress(progress, text=f"{label}: {current}/{total}")
def show_notification(self, message, level="info"):
"""显示通知"""
if level == "success":
st.success(message)
elif level == "warning":
st.warning(message)
elif level == "error":
st.error(message)
else:
st.info(message)
部署
1. 环境配置
1.1 依赖管理
# requirements.txt 详细说明
streamlit==1.28.0 # 核心Web框架
pandas==2.1.4 # 数据处理
numpy==1.24.3 # 数值计算
plotly==5.17.0 # 交互式可视化
scikit-learn==1.3.2 # 机器学习
seaborn==0.13.0 # 统计可视化(可选)
# 环境设置脚本
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
pip install -r requirements.txt
1.2 配置管理
# config.py
import os
from pathlib import Path
class AppConfig:
# 路径配置
BASE_DIR = Path(__file__).parent
DATA_DIR = BASE_DIR / "data"
# 应用配置
APP_TITLE = "游戏玩家数据分析平台"
APP_ICON = "🎮"
LAYOUT = "wide"
# 数据处理配置
CACHE_TTL = 3600 # 缓存时间(秒)
MAX_ROWS = 10000 # 最大处理行数
@classmethod
def setup_directories(cls):
"""创建必要的目录"""
os.makedirs(cls.DATA_DIR, exist_ok=True)
2. 性能优化
2.1 数据缓存策略
class CacheManager:
def __init__(self, ttl=3600):
self.cache = {}
self.ttl = ttl
def get_or_compute(self, key, compute_func):
"""缓存或计算数据"""
if key in self.cache:
cached_item = self.cache[key]
if time.time() - cached_item['timestamp'] < self.ttl:
return cached_item['data']
# 计算并缓存
data = compute_func()
self.cache[key] = {
'data': data,
'timestamp': time.time()
}
return data
2.2 内存优化
class MemoryOptimizer:
@staticmethod
def optimize_dataframe(df):
"""优化DataFrame内存使用"""
df_optimized = df.copy()
# 数值类型优化
for col in df.select_dtypes(include=['int64']).columns:
df_optimized[col] = pd.to_numeric(df[col], downcast='integer')
# 字符串类型优化
for col in df.select_dtypes(include=['object']).columns:
df_optimized[col] = df[col].astype('category')
return df_optimized
3. 监控与日志
3.1 日志系统
import logging
class LoggingSystem:
def __init__(self, log_level=logging.INFO):
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def log_performance(self, operation, duration):
"""记录性能日志"""
self.logger.info(f"{operation} 完成,耗时: {duration:.2f}秒")
def log_data_stats(self, df):
"""记录数据统计日志"""
self.logger.info(f"数据形状: {df.shape}")
self.logger.info(f"列: {list(df.columns)}")
3.2 监控指标
class PerformanceMonitor:
def __init__(self):
self.metrics = {
'data_loading_time': [],
'preprocessing_time': [],
'analysis_time': []
}
def track_performance(self, metric_name, duration):
"""跟踪性能指标"""
if metric_name in self.metrics:
self.metrics[metric_name].append(duration)
# 记录统计信息
avg_time = np.mean(self.metrics[metric_name])
self._log_metric(metric_name, duration, avg_time)
def get_performance_report(self):
"""生成性能报告"""
report = {}
for metric, values in self.metrics.items():
if values:
report[metric] = {
'count': len(values),
'avg': np.mean(values),
'min': np.min(values),
'max': np.max(values)
}
return report
代码
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.cluster import KMeans
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')
# 可选导入seaborn
try:
import seaborn as sns
seaborn_available = True
except ImportError:
seaborn_available = False
# 提供一个替代的sns模块占位符
class SnsStub:
def __getattr__(self, name):
def stub_function(*args, **kwargs):
# 静默返回None或空值
return None
return stub_function
sns = SnsStub()
# 页面配置
st.set_page_config(page_title="游戏玩家数据分析平台", page_icon="🎮", layout="wide")
# 自定义CSS
st.markdown("""
<style>
.main-header {
font-size: 3rem;
font-weight: 800;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-align: center;
padding: 30px 0;
margin-bottom: 20px;
}
.metric-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 15px;
color: white;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.metric-card:hover {
transform: translateY(-5px);
}
.stMetric [data-baseweb="metric-label"] {
color: white;
font-weight: 600;
font-size: 0.9rem;
opacity: 0.9;
}
.stMetric [data-baseweb="metric-value"] {
color: white;
font-size: 2.2rem;
font-weight: 700;
}
.section-header {
font-size: 1.8rem;
font-weight: 700;
color: #2c3e50;
margin: 25px 0 15px 0;
padding-bottom: 10px;
border-bottom: 3px solid #3498db;
}
.info-card {
background: linear-gradient(135deg, #3498db 0%, #2c3e50 100%);
padding: 25px;
border-radius: 15px;
color: white;
margin: 15px 0;
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.2);
}
.warning-card {
background: linear-gradient(135deg, #f39c12 0%, #e74c3c 100%);
padding: 25px;
border-radius: 15px;
color: white;
margin: 15px 0;
box-shadow: 0 4px 15px rgba(243, 156, 18, 0.2);
}
.success-card {
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
padding: 25px;
border-radius: 15px;
color: white;
margin: 15px 0;
box-shadow: 0 4px 15px rgba(46, 204, 113, 0.2);
}
.sidebar .sidebar-content {
background: linear-gradient(180deg, #2c3e50 0%, #1a252f 100%);
}
.stSelectbox, .stRadio, .stExpander {
margin-bottom: 15px;
}
.stExpander > div {
border-radius: 10px !important;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.stTab {
margin-top: 20px;
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 10px;
}
.feature-title {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 10px;
}
.feature-description {
font-size: 0.95rem;
color: #666;
line-height: 1.5;
}
</style>
""", unsafe_allow_html=True)
# 数据加载与处理
@st.cache_data
def load_data(file):
df = pd.read_csv(file)
df['日均时长'] = pd.to_numeric(df['日均时长'], errors='coerce')
df['累计消费'] = pd.to_numeric(df['累计消费'], errors='coerce')
df['综合胜率'] = pd.to_numeric(df['综合胜率'], errors='coerce')
df['KDA指数'] = pd.to_numeric(df['KDA指数'], errors='coerce')
df['玩家年龄'] = pd.to_numeric(df['玩家年龄'], errors='coerce')
return df
@st.cache_data
def preprocess_data(df):
df_processed = df.copy()
# 段位编码
rank_order = ['荣耀青铜', '秩序白银', '尊贵铂金', '永恒钻石', '至尊星耀', '最强王者']
df_processed['段位等级'] = df_processed['游戏段位'].map({r: i+1 for i, r in enumerate(rank_order)})
# 登录频率编码
login_order = ['每月几次', '每周1-2次', '每周3-5次', '每天']
df_processed['登录频率等级'] = df_processed['登录频率'].map({l: i+1 for i, l in enumerate(login_order)})
# 年龄分段
df_processed['年龄分段'] = pd.cut(df_processed['玩家年龄'],
bins=[0, 18, 25, 30, 100],
labels=['青少年', '青年', '成年', '中老年'])
# 消费分段 - 在预处理阶段创建,确保所有函数都能访问
df_processed['消费分段'] = pd.cut(df_processed['累计消费'],
bins=[0, 20, 100, float('inf')],
labels=['低消费', '中消费', '高消费'])
# 时长分段
df_processed['时长分段'] = pd.cut(df_processed['日均时长'],
bins=[0, 2, 4, 6, float('inf')],
labels=['0-2小时', '2-4小时', '4-6小时', '6+小时'])
# 活跃度分数 - 在预处理阶段计算
df_processed['活跃度分数'] = (
df_processed['登录频率等级'] * 0.6 +
(df_processed['日均时长'] / df_processed['日均时长'].max()) * 0.4
)
# 活跃度分段
df_processed['活跃度分段'] = pd.qcut(df_processed['活跃度分数'],
q=[0, 0.33, 0.66, 1],
labels=['流失用户', '普通用户', '核心用户'])
# 技能分数
df_processed['技能分数'] = (
df_processed['段位等级'] * 0.4 +
df_processed['综合胜率'] * 100 * 0.3 +
(df_processed['KDA指数'] / df_processed['KDA指数'].max()) * 30
)
# 技能分段
df_processed['技能分段'] = pd.qcut(df_processed['技能分数'],
q=[0, 0.33, 0.66, 1],
labels=['新手', '进阶', '高手'])
return df_processed
def calculate_metrics(df):
metrics = {
'总玩家数': len(df),
'平均年龄': round(df['玩家年龄'].mean(), 1),
'平均日均时长': round(df['日均时长'].mean(), 1),
'总累计消费': round(df['累计消费'].sum(), 2),
'平均累计消费': round(df['累计消费'].mean(), 2),
'平均胜率': round(df['综合胜率'].mean() * 100, 1),
'平均KDA': round(df['KDA指数'].mean(), 1),
}
return metrics
def predict_churn_risk(row):
"""预测流失风险评分"""
login_score = {'每月几次': 3, '每周1-2次': 2, '每周3-5次': 1, '每天': 0}
behavior_score = {'娱乐型': 1, '休闲型': 2, '社交型': 3, '收藏型': 2, '竞技型': 4}
risk = (login_score.get(row['登录频率'], 2) * 0.4 +
(1 if row['日均时长'] < 2 else 0) * 0.3 +
behavior_score.get(row['行为模式'], 2) * 0.3)
return min(risk, 3)
def predict_pay_potential(row):
"""预测付费潜力"""
if row['累计消费'] > 100:
return '高'
elif row['累计消费'] > 20:
return '中'
else:
return '低'
def predict_promotion_time(row):
"""预测段位晋升时间"""
if row['游戏段位'] == '最强王者':
return '已达巅峰'
elif row['KDA指数'] > 4 and row['综合胜率'] > 0.55:
return '1-2周'
elif row['KDA指数'] > 3 and row['综合胜率'] > 0.5:
return '2-4周'
else:
return '4-8周'
# 用户画像分析
def user_profile_analysis(df, df_processed):
st.markdown("## 📊 用户画像分析")
# 1. 人口统计画像
with st.expander("👥 人口统计画像", expanded=True):
col1, col2, col3 = st.columns(3)
with col1:
gender_dist = df['玩家性别'].value_counts()
fig_gender = px.pie(gender_dist, values=gender_dist.values, names=gender_dist.index,
title='玩家性别分布', hole=0.5,
color_discrete_sequence=px.colors.qualitative.Set3)
st.plotly_chart(fig_gender, use_container_width=True)
with col2:
age_dist = df_processed['年龄分段'].value_counts().sort_index()
fig_age = px.bar(age_dist, x=age_dist.index, y=age_dist.values,
title='年龄分段分布',
labels={'x': '年龄段', 'y': '人数'},
color=age_dist.values,
color_continuous_scale='Blues')
st.plotly_chart(fig_age, use_container_width=True)
with col3:
region_dist = df['所在区域'].value_counts()
fig_region = px.bar(region_dist, x=region_dist.index, y=region_dist.values,
title='区域分布',
labels={'x': '区域', 'y': '人数'},
color=region_dist.values,
color_continuous_scale='Viridis')
st.plotly_chart(fig_region, use_container_width=True)
# 2. 游戏能力画像
with st.expander("⚔️ 游戏能力画像", expanded=True):
col1, col2 = st.columns(2)
with col1:
rank_dist = df['游戏段位'].value_counts().sort_index()
fig_rank = px.bar(rank_dist, x=rank_dist.index, y=rank_dist.values,
title='段位分布',
labels={'x': '段位', 'y': '人数'},
color=rank_dist.values,
color_continuous_scale='RdYlGn_r')
fig_rank.update_xaxes(tickangle=45)
st.plotly_chart(fig_rank, use_container_width=True)
with col2:
position_dist = df['擅长位置'].value_counts()
fig_position = px.pie(position_dist, values=position_dist.values, names=position_dist.index,
title='擅长位置分布', hole=0.4,
color_discrete_sequence=px.colors.qualitative.Pastel)
st.plotly_chart(fig_position, use_container_width=True)
# KDA和胜率分布
col1, col2 = st.columns(2)
with col1:
fig_kda = px.histogram(df, x='KDA指数', nbins=50,
title='KDA指数分布',
color_discrete_sequence=['#3498db'])
st.plotly_chart(fig_kda, use_container_width=True)
with col2:
fig_winrate = px.histogram(df, x='综合胜率', nbins=50,
title='综合胜率分布',
color_discrete_sequence=['#2ecc71'])
st.plotly_chart(fig_winrate, use_container_width=True)
# 3. 行为习惯画像
with st.expander("🎯 行为习惯画像", expanded=True):
col1, col2, col3 = st.columns(3)
with col1:
login_dist = df['登录频率'].value_counts()
login_order = ['每天', '每周3-5次', '每周1-2次', '每月几次']
login_dist = login_dist.reindex(login_order)
fig_login = px.bar(login_dist, x=login_dist.index, y=login_dist.values,
title='登录频率分布',
labels={'x': '登录频率', 'y': '人数'},
color=login_dist.values,
color_continuous_scale='YlOrRd')
st.plotly_chart(fig_login, use_container_width=True)
with col2:
behavior_dist = df['行为模式'].value_counts()
fig_behavior = px.pie(behavior_dist, values=behavior_dist.values, names=behavior_dist.index,
title='行为模式分布', hole=0.5,
color_discrete_sequence=px.colors.qualitative.Bold)
st.plotly_chart(fig_behavior, use_container_width=True)
with col3:
fig_duration = px.histogram(df, x='日均时长', nbins=50,
title='日均时长分布',
color_discrete_sequence=['#9b59b6'])
st.plotly_chart(fig_duration, use_container_width=True)
# 4. 消费能力画像
with st.expander("💰 消费能力画像", expanded=True):
col1, col2 = st.columns(2)
# 直接使用预处理阶段创建的消费分段
spend_dist = df_processed['消费分段'].value_counts()
with col1:
fig_spend = px.pie(spend_dist, values=spend_dist.values, names=spend_dist.index,
title='消费能力分布', hole=0.5,
color_discrete_sequence=['#95a5a6', '#f39c12', '#e74c3c'])
st.plotly_chart(fig_spend, use_container_width=True)
with col2:
fig_spend_hist = px.histogram(df, x='累计消费', nbins=50,
title='累计消费金额分布',
color_discrete_sequence=['#16a085'])
st.plotly_chart(fig_spend_hist, use_container_width=True)
# 用户细分分析
def segmentation_analysis(df, df_processed):
st.markdown("## 🎯 用户细分/分群分析")
# 1. 按价值分群
with st.expander("💎 按价值分群", expanded=True):
col1, col2 = st.columns(2)
with col1:
spend_segment = df_processed['消费分段'].value_counts()
fig_spend_seg = px.bar(spend_segment, x=spend_segment.index, y=spend_segment.values,
title='价值分群(按累计消费)',
labels={'x': '消费级别', 'y': '人数'},
color=spend_segment.index,
color_discrete_map={'低消费': '#95a5a6', '中消费': '#f39c12', '高消费': '#e74c3c'})
st.plotly_chart(fig_spend_seg, use_container_width=True)
with col2:
# 高价值用户特征分析
high_value = df_processed[df_processed['消费分段'] == '高消费']
if len(high_value) > 0:
st.markdown("### 高价值用户特征")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("人数", len(high_value))
with col2:
st.metric("平均胜率", f"{high_value['综合胜率'].mean()*100:.1f}%")
with col3:
st.metric("平均KDA", f"{high_value['KDA指数'].mean():.1f}")
# 2. 按活跃度分群
with st.expander("🔥 按活跃度分群", expanded=True):
col1, col2 = st.columns(2)
with col1:
active_dist = df_processed['活跃度分段'].value_counts()
fig_active = px.bar(active_dist, x=active_dist.index, y=active_dist.values,
title='活跃度分群',
labels={'x': '活跃度级别', 'y': '人数'},
color=active_dist.values,
color_continuous_scale='RdYlGn')
st.plotly_chart(fig_active, use_container_width=True)
with col2:
# 活跃度与消费关系
fig_active_spend = px.box(df_processed, x='活跃度分段', y='累计消费',
title='不同活跃度用户的消费分布',
color='活跃度分段')
st.plotly_chart(fig_active_spend, use_container_width=True)
# 3. 按技能分群
with st.expander("🎮 按技能分群", expanded=True):
col1, col2 = st.columns(2)
with col1:
skill_dist = df_processed['技能分段'].value_counts()
fig_skill = px.pie(skill_dist, values=skill_dist.values, names=skill_dist.index,
title='技能水平分群', hole=0.5,
color_discrete_sequence=['#3498db', '#f39c12', '#e74c3c'])
st.plotly_chart(fig_skill, use_container_width=True)
with col2:
# 技能与活跃度关系
fig_skill_active = px.scatter(df_processed, x='技能分数', y='活跃度分数',
color='技能分段',
title='技能分数 vs 活跃度分数',
labels={'技能分数': '技能分数', '活跃度分数': '活跃度分数'},
hover_data=['游戏段位', '综合胜率'])
st.plotly_chart(fig_skill_active, use_container_width=True)
# 4. 按偏好分群
with st.expander("⭐ 按偏好分群", expanded=True):
# 确保df_processed中有消费分段字段
if '消费分段' in df_processed.columns:
# 创建一个包含擅长位置和消费分段的DataFrame用于分组
temp_df = df_processed[['擅长位置', '消费分段']].copy()
position_analysis = temp_df.groupby(['擅长位置', '消费分段']).size().unstack(fill_value=0)
fig_position_spend = px.bar(position_analysis.reset_index().melt(id_vars=['擅长位置'],
var_name='消费级别',
value_name='人数'),
x='擅长位置', y='人数', color='消费级别',
title='不同位置偏好的消费分布',
barmode='stack',
color_discrete_map={'低消费': '#95a5a6', '中消费': '#f39c12', '高消费': '#e74c3c'})
st.plotly_chart(fig_position_spend, use_container_width=True)
else:
st.warning("消费分段字段不存在,无法进行偏好分群分析")
# 5. 按行为分群
with st.expander("🎭 按行为分群", expanded=True):
col1, col2 = st.columns(2)
with col1:
behavior_rank = df.groupby(['行为模式', '游戏段位']).size().unstack(fill_value=0)
fig_behavior_rank = px.bar(behavior_rank.reset_index().melt(id_vars=['行为模式'],
var_name='段位',
value_name='人数'),
x='行为模式', y='人数', color='段位',
title='不同行为模式的段位分布',
barmode='stack')
st.plotly_chart(fig_behavior_rank, use_container_width=True)
with col2:
# 确保df_processed中有消费分段字段
if '消费分段' in df_processed.columns:
# 创建一个包含行为模式和消费分段的DataFrame用于分组
temp_df = df_processed[['行为模式', '消费分段']].copy()
behavior_spend = temp_df.groupby(['行为模式', '消费分段']).size().unstack(fill_value=0)
fig_behavior_spend = px.bar(behavior_spend.reset_index().melt(id_vars=['行为模式'],
var_name='消费级别',
value_name='人数'),
x='行为模式', y='人数', color='消费级别',
title='不同行为模式的消费分布',
barmode='stack',
color_discrete_map={'低消费': '#95a5a6', '中消费': '#f39c12', '高消费': '#e74c3c'})
st.plotly_chart(fig_behavior_spend, use_container_width=True)
else:
st.warning("消费分段字段不存在,无法进行行为模式的消费分布分析")
# 关联与相关性分析
def correlation_analysis(df, df_processed):
st.markdown("## 🔗 关联与相关性分析")
# 1. 消费驱动因素
with st.expander("💰 消费驱动因素分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
# 创建一个包含所需字段的DataFrame用于散点图
scatter_df = df_processed[['日均时长', '累计消费']].copy()
if '消费分段' in df_processed.columns:
scatter_df['消费分段'] = df_processed['消费分段']
fig_spend_duration = px.scatter(scatter_df, x='日均时长', y='累计消费',
title='日均时长 vs 累计消费',
color='消费分段',
labels={'日均时长': '日均时长(小时)', '累计消费': '累计消费(元)'})
else:
fig_spend_duration = px.scatter(scatter_df, x='日均时长', y='累计消费',
title='日均时长 vs 累计消费',
labels={'日均时长': '日均时长(小时)', '累计消费': '累计消费(元)'})
st.plotly_chart(fig_spend_duration, use_container_width=True)
with col2:
fig_spend_rank = px.box(df, x='游戏段位', y='累计消费',
title='不同段位的消费分布',
labels={'游戏段位': '段位', '累计消费': '累计消费(元)'})
fig_spend_rank.update_xaxes(tickangle=45)
st.plotly_chart(fig_spend_rank, use_container_width=True)
with col1:
fig_spend_winrate = px.scatter(df, x='综合胜率', y='累计消费',
title='综合胜率 vs 累计消费',
labels={'综合胜率': '胜率', '累计消费': '累计消费(元)'})
st.plotly_chart(fig_spend_winrate, use_container_width=True)
# 2. 活跃度影响因素
with st.expander("📊 活跃度影响因素分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
login_age = df.groupby(['玩家年龄', '登录频率']).size().unstack(fill_value=0)
login_age.columns.name = '登录频率'
fig_login_age = px.imshow(login_age.values,
x=login_age.columns,
y=login_age.index,
title='年龄 vs 登录频率热力图',
labels=dict(x="登录频率", y="年龄", color="人数"),
aspect="auto")
st.plotly_chart(fig_login_age, use_container_width=True)
with col2:
login_region = df.groupby(['所在区域', '登录频率']).size().unstack(fill_value=0)
login_region.columns.name = '登录频率'
fig_login_region = px.imshow(login_region.values,
x=login_region.columns,
y=login_region.index,
title='区域 vs 登录频率热力图',
labels=dict(x="登录频率", y="区域", color="人数"))
st.plotly_chart(fig_login_region, use_container_width=True)
# 3. 技能成长路径
with st.expander("📈 技能成长路径分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
fig_kda_duration = px.scatter(df, x='日均时长', y='KDA指数',
title='日均时长 vs KDA指数',
color='游戏段位',
labels={'日均时长': '日均时长(小时)', 'KDA指数': 'KDA指数'})
st.plotly_chart(fig_kda_duration, use_container_width=True)
with col2:
fig_kda_rank = px.box(df, x='游戏段位', y='KDA指数',
title='不同段位的KDA分布',
labels={'游戏段位': '段位', 'KDA指数': 'KDA指数'})
fig_kda_rank.update_xaxes(tickangle=45)
st.plotly_chart(fig_kda_rank, use_container_width=True)
# 4. 区域差异分析
with st.expander("🗺️ 区域差异分析", expanded=True):
col1, col2, col3 = st.columns(3)
with col1:
region_spend = df.groupby('所在区域')['累计消费'].mean().sort_values(ascending=False)
fig_region_spend = px.bar(x=region_spend.index, y=region_spend.values,
title='各区域平均消费',
labels={'x': '区域', 'y': '平均消费(元)'},
color=region_spend.values,
color_continuous_scale='Plasma')
st.plotly_chart(fig_region_spend, use_container_width=True)
with col2:
region_active = df_processed.groupby('所在区域')['日均时长'].mean().sort_values(ascending=False)
fig_region_active = px.bar(x=region_active.index, y=region_active.values,
title='各区域平均日均时长',
labels={'x': '区域', 'y': '平均时长(小时)'},
color=region_active.values,
color_continuous_scale='Blues')
st.plotly_chart(fig_region_active, use_container_width=True)
with col3:
# 段位分布
region_rank = pd.crosstab(df['所在区域'], df['游戏段位'])
fig_region_rank = px.imshow(region_rank.values,
x=region_rank.columns,
y=region_rank.index,
title='区域 vs 段位分布热力图',
labels=dict(x="段位", y="区域", color="人数"))
st.plotly_chart(fig_region_rank, use_container_width=True)
# 预测性分析
def predictive_analysis(df, df_processed):
st.markdown("## 🔮 预测性分析")
# 1. 用户流失预测
with st.expander("⚠️ 用户流失预测", expanded=True):
df_processed['流失风险'] = df_processed.apply(predict_churn_risk, axis=1)
df_processed['流失风险等级'] = pd.cut(df_processed['流失风险'],
bins=[0, 1, 2, 3],
labels=['低风险', '中风险', '高风险'])
col1, col2 = st.columns(2)
with col1:
churn_dist = df_processed['流失风险等级'].value_counts()
fig_churn = px.pie(churn_dist, values=churn_dist.values, names=churn_dist.index,
title='流失风险分布', hole=0.5,
color_discrete_sequence=['#2ecc71', '#f39c12', '#e74c3c'])
st.plotly_chart(fig_churn, use_container_width=True)
with col2:
# 确保使用df_processed中包含消费分段字段的数据
if '消费分段' in df_processed.columns:
# 创建一个包含登录频率和消费分段的DataFrame用于分组
temp_df = df_processed[['登录频率', '消费分段']].copy()
churn_login = temp_df.groupby(['登录频率', '消费分段']).size().reset_index(name='人数')
fig_churn_login = px.bar(churn_login, x='登录频率', y='人数', color='消费分段',
title='登录频率与消费关系',
barmode='stack')
st.plotly_chart(fig_churn_login, use_container_width=True)
else:
# 如果消费分段不存在,使用游戏段位
temp_df = df_processed[['登录频率', '游戏段位']].copy()
churn_login = temp_df.groupby(['登录频率', '游戏段位']).size().reset_index(name='人数')
fig_churn_login = px.bar(churn_login, x='登录频率', y='人数', color='游戏段位',
title='登录频率与段位关系',
barmode='stack')
st.plotly_chart(fig_churn_login, use_container_width=True)
# 流失用户详细分析
st.markdown("### 高流失风险用户特征")
high_churn = df_processed[df_processed['流失风险等级'] == '高风险']
if len(high_churn) > 0:
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("高风险人数", len(high_churn))
with col2:
st.metric("平均日均时长", f"{high_churn['日均时长'].mean():.1f}h")
with col3:
st.metric("平均累计消费", f"¥{high_churn['累计消费'].mean():.1f}")
with col4:
st.metric("平均胜率", f"{high_churn['综合胜率'].mean()*100:.1f}%")
# 2. 付费潜力预测
with st.expander("💎 付费潜力预测", expanded=True):
df_processed['付费潜力'] = df_processed.apply(predict_pay_potential, axis=1)
col1, col2 = st.columns(2)
with col1:
potential_dist = df_processed['付费潜力'].value_counts()
fig_potential = px.pie(potential_dist, values=potential_dist.values, names=potential_dist.index,
title='付费潜力分布', hole=0.5,
color_discrete_sequence=['#3498db', '#f39c12', '#e74c3c'])
st.plotly_chart(fig_potential, use_container_width=True)
with col2:
potential_rank = df_processed.groupby(['付费潜力', '游戏段位']).size().reset_index(name='人数')
fig_potential_rank = px.bar(potential_rank, x='游戏段位', y='人数', color='付费潜力',
title='不同段位的付费潜力分布',
barmode='stack')
fig_potential_rank.update_xaxes(tickangle=45)
st.plotly_chart(fig_potential_rank, use_container_width=True)
# 3. 段位晋升预测
with st.expander("🏆 段位晋升预测", expanded=True):
df_processed['预计晋升时间'] = df_processed.apply(predict_promotion_time, axis=1)
col1, col2 = st.columns(2)
with col1:
promotion_dist = df_processed['预计晋升时间'].value_counts()
fig_promotion = px.bar(promotion_dist, x=promotion_dist.index, y=promotion_dist.values,
title='段位晋升时间预测分布',
labels={'x': '预计晋升时间', 'y': '人数'},
color=promotion_dist.values,
color_continuous_scale='RdYlGn_r')
st.plotly_chart(fig_promotion, use_container_width=True)
with col2:
promotion_kda = df_processed[df_processed['预计晋升时间'] != '已达巅峰'].groupby(
['预计晋升时间', '游戏段位']).size().reset_index(name='人数')
if len(promotion_kda) > 0:
fig_promotion_rank = px.bar(promotion_kda, x='游戏段位', y='人数', color='预计晋升时间',
title='不同段位的晋升时间预测',
barmode='stack')
fig_promotion_rank.update_xaxes(tickangle=45)
st.plotly_chart(fig_promotion_rank, use_container_width=True)
# 运营决策支持
def decision_support(df, df_processed):
st.markdown("## 📋 运营决策支持")
# 1. 高价值用户识别
with st.expander("💎 高价值用户识别", expanded=True):
df_processed['用户价值分'] = (
(df_processed['累计消费'] / df_processed['累计消费'].max()) * 100 * 0.5 +
df_processed['活跃度分数'] / df_processed['活跃度分数'].max() * 50 * 0.3 +
df_processed['技能分数'] / df_processed['技能分数'].max() * 20 * 0.2
)
top_users = df_processed.nlargest(10, '用户价值分')
st.markdown("### TOP 10 高价值用户")
user_display = top_users[['用户ID', '游戏段位', '累计消费', '日均时长', '综合胜率', 'KDA指数', '登录频率']].copy()
user_display['综合胜率'] = (user_display['综合胜率'] * 100).round(1).astype(str) + '%'
st.dataframe(user_display, use_container_width=True)
st.markdown("### 📌 运营建议")
st.info("""
**针对高价值用户的专属福利策略:**
- 发放专属限量皮肤/道具
- 提供优先客服通道
- 邀请参与内测和赛事
- 定期推送个性化内容推荐
- 设置VIP专属活动日
""")
# 2. 流失预警干预
with st.expander("⚠️ 流失预警干预", expanded=True):
# 如果流失风险等级字段不存在,先创建它
if '流失风险等级' not in df_processed.columns:
df_processed['流失风险'] = df_processed.apply(predict_churn_risk, axis=1)
df_processed['流失风险等级'] = pd.cut(df_processed['流失风险'],
bins=[0, 1, 2, 3],
labels=['低风险', '中风险', '高风险'])
high_churn = df_processed[df_processed['流失风险等级'] == '高风险'].nlargest(10, '累计消费')
if len(high_churn) > 0:
st.markdown("### 需要重点召回的高价值流失风险用户")
churn_display = high_churn[['用户ID', '游戏段位', '累计消费', '日均时长', '登录频率', '行为模式']].copy()
st.dataframe(churn_display, use_container_width=True)
else:
st.info("未发现高风险流失用户")
st.markdown("### 📌 召回策略")
st.warning("""
**对登录频率下降用户的召回活动:**
- 发送回归礼包和专属福利
- 推送好友互动提醒
- 邀请参与回归专属活动
- 根据行为模式推送个性化内容
- 设置连续登录奖励
""")
# 3. 匹配优化建议
with st.expander("⚔️ 匹配优化建议", expanded=True):
# 段位KDA统计
rank_stats = df.groupby('游戏段位').agg({
'综合胜率': 'mean',
'KDA指数': 'mean',
'日均时长': 'mean'
}).round(3)
st.markdown("### 各段位平均数据")
st.dataframe(rank_stats, use_container_width=True)
st.markdown("### 📌 匹配建议")
st.success("""
**根据段位、胜率、KDA优化对战匹配算法:**
- 扩大匹配考虑维度:综合胜率 + KDA指数 + 日均时长
- 设置动态匹配范围:活跃时间短的玩家匹配更接近水平的对手
- 引入段位保护机制:连续败北时适当降低匹配难度
- 组队匹配优化:综合计算队伍平均实力
- 避免新手对老手:根据注册时间和游戏时长增加匹配权重
""")
# 4. 内容推荐
with st.expander("🎯 内容推荐", expanded=True):
col1, col2 = st.columns(2)
with col1:
st.markdown("#### 按位置偏好推荐")
position_rec = df.groupby('擅长位置').agg({
'累计消费': 'mean',
'日均时长': 'mean',
'综合胜率': 'mean'
}).round(2)
st.dataframe(position_rec, use_container_width=True)
with col2:
st.markdown("#### 按行为模式推荐")
behavior_rec = df.groupby('行为模式').agg({
'累计消费': 'mean',
'日均时长': 'mean',
'玩家年龄': 'mean'
}).round(2)
st.dataframe(behavior_rec, use_container_width=True)
st.markdown("### 📌 推荐策略")
st.info("""
**根据擅长位置和行为模式推荐游戏内容:**
**竞技型玩家:**
- 推荐排位赛活动和赛事
- 新英雄/技能攻略
- 高段位对局回放
**社交型玩家:**
- 推荐组队开黑活动
- 好友系统功能
- 公会/战队内容
**休闲型玩家:**
- 推荐娱乐模式活动
- 皮肤和外观内容
- 成就任务系统
**收藏型玩家:**
- 推荐限定皮肤和道具
- 收集成就系统
- 稀有道具活动
""")
# 5. 定价策略
with st.expander("💰 定价策略", expanded=True):
region_pricing = df.groupby('所在区域').agg({
'累计消费': ['mean', 'median', 'std']
}).round(2)
region_pricing.columns = ['平均消费', '中位数消费', '消费标准差']
fig_region_pricing = px.bar(region_pricing, x=region_pricing.index, y='平均消费',
title='各区域平均消费对比',
labels={'x': '区域', '平均消费': '平均消费(元)'},
color='平均消费',
color_continuous_scale='Viridis')
st.plotly_chart(fig_region_pricing, use_container_width=True)
st.dataframe(region_pricing, use_container_width=True)
st.markdown("### 📌 定价建议")
st.info("""
**基于区域消费差异制定差异化定价:**
- **高消费区域**:可推出更多高价值礼包,提供会员专属服务
- **中等消费区域**:平衡定价策略,推出性价比优惠组合
- **低消费区域**:增加小额多次消费选项,降低单次消费门槛
- **区域性促销**:针对不同区域节日和特点推出活动
- **分层定价**:同一产品提供基础版、进阶版、豪华版
""")
# 可视化分析
def visualization_analysis(df, df_processed):
st.markdown("## 📊 可视化分析")
# 1. 用户分布
with st.expander("🗺️ 用户分布分析", expanded=True):
tab1, tab2 = st.tabs(["区域热力图", "年龄/段位金字塔"])
with tab1:
# 区域分布热力
region_age = pd.crosstab(df['所在区域'], df_processed['年龄分段'])
fig_region_heat = px.imshow(region_age.values,
x=region_age.columns,
y=region_age.index,
title='区域 × 年龄分布热力图',
labels=dict(x="年龄段", y="区域", color="人数"),
color_continuous_scale='YlOrRd',
aspect="auto")
st.plotly_chart(fig_region_heat, use_container_width=True)
with tab2:
# 年龄段位金字塔
rank_order = ['荣耀青铜', '秩序白银', '尊贵铂金', '永恒钻石', '至尊星耀', '最强王者']
age_order = ['青少年', '青年', '成年', '中老年']
pyramid_data = []
for age in age_order:
for rank in rank_order:
count = len(df_processed[(df_processed['年龄分段'] == age) & (df_processed['游戏段位'] == rank)])
pyramid_data.append({'年龄段': age, '段位': rank, '人数': count})
pyramid_df = pd.DataFrame(pyramid_data)
fig_pyramid = px.bar(pyramid_df, x='人数', y='段位',
color='年龄段',
orientation='h',
title='年龄×段位分布金字塔',
labels={'人数': '人数', '段位': '段位'},
barmode='stack',
color_discrete_map={'青少年': '#3498db', '青年': '#2ecc71',
'成年': '#f39c12', '中老年': '#e74c3c'})
st.plotly_chart(fig_pyramid, use_container_width=True)
# 2. 趋势分析
with st.expander("📈 趋势分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
login_order = ['每月几次', '每周1-2次', '每周3-5次', '每天']
login_freq = df['登录频率'].value_counts().reindex(login_order)
fig_login_trend = px.line(x=login_freq.index, y=login_freq.values,
title='登录频率趋势',
labels={'x': '登录频率', 'y': '人数'},
markers=True)
fig_login_trend.update_traces(line_color='#3498db', marker_size=10)
st.plotly_chart(fig_login_trend, use_container_width=True)
with col2:
# 日均时长分段
df_processed['时长分段'] = pd.cut(df_processed['日均时长'],
bins=[0, 2, 4, 6, float('inf')],
labels=['0-2小时', '2-4小时', '4-6小时', '6+小时'])
duration_dist = df_processed['时长分段'].value_counts()
fig_duration_trend = px.bar(x=duration_dist.index, y=duration_dist.values,
title='日均时长分布',
labels={'x': '时长分段', 'y': '人数'},
color=duration_dist.values,
color_continuous_scale='Blues')
st.plotly_chart(fig_duration_trend, use_container_width=True)
# 3. 对比分析
with st.expander("📊 对比分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
gender_spend = df.groupby('玩家性别')['累计消费'].mean().reset_index()
fig_gender_spend = px.bar(gender_spend, x='玩家性别', y='累计消费',
title='性别消费对比',
labels={'玩家性别': '性别', '累计消费': '平均消费(元)'},
color='累计消费',
color_continuous_scale='RdYlGn')
st.plotly_chart(fig_gender_spend, use_container_width=True)
with col2:
region_active = df_processed.groupby('所在区域')['日均时长'].mean().sort_values(ascending=False).reset_index()
fig_region_active = px.bar(region_active, x='所在区域', y='日均时长',
title='区域活跃度对比',
labels={'所在区域': '区域', '日均时长': '平均日均时长(小时)'},
color='日均时长',
color_continuous_scale='YlOrRd')
st.plotly_chart(fig_region_active, use_container_width=True)
# 性别段位对比
col1, col2 = st.columns(2)
with col1:
gender_rank = pd.crosstab(df['玩家性别'], df['游戏段位'])
fig_gender_rank = px.imshow(gender_rank.values,
x=gender_rank.columns,
y=gender_rank.index,
title='性别 × 段位分布热力图',
labels=dict(x="段位", y="性别", color="人数"),
color_continuous_scale='Viridis',
aspect="auto")
st.plotly_chart(fig_gender_rank, use_container_width=True)
with col2:
gender_kda = df.groupby('玩家性别')[['KDA指数', '综合胜率']].mean()
gender_kda['综合胜率'] = gender_kda['综合胜率'] * 100
fig_gender_kda = px.bar(gender_kda.reset_index().melt(id_vars=['玩家性别']),
x='玩家性别', y='value', color='variable',
title='性别 × 技能指标对比',
labels={'玩家性别': '性别', 'value': '数值', 'variable': '指标'},
barmode='group')
st.plotly_chart(fig_gender_kda, use_container_width=True)
# 4. 关联分析(散点图)
with st.expander("🔗 关联分析", expanded=True):
col1, col2 = st.columns(2)
with col1:
fig_scatter1 = px.scatter(df, x='日均时长', y='累计消费',
title='日均时长 vs 累计消费',
labels={'日均时长': '日均时长(小时)', '累计消费': '累计消费(元)'},
color='游戏段位',
opacity=0.6)
st.plotly_chart(fig_scatter1, use_container_width=True)
with col2:
fig_scatter2 = px.scatter(df, x='综合胜率', y='KDA指数',
title='综合胜率 vs KDA指数',
labels={'综合胜率': '胜率', 'KDA指数': 'KDA指数'},
color='游戏段位',
opacity=0.6)
st.plotly_chart(fig_scatter2, use_container_width=True)
# 3D散点图
fig_3d = px.scatter_3d(df, x='日均时长', y='累计消费', z='综合胜率',
color='游戏段位',
title='多维度关联分析(3D)',
labels={'日均时长': '日均时长', '累计消费': '累计消费', '综合胜率': '胜率'},
opacity=0.7)
st.plotly_chart(fig_3d, use_container_width=True)
# 5. 分群分析(雷达图)
with st.expander("🎯 分群分析 - 用户画像雷达图", expanded=True):
col1, col2 = st.columns(2)
with col1:
# 按行为模式分群
behavior_radar = df.groupby('行为模式').agg({
'玩家年龄': 'mean',
'日均时长': 'mean',
'累计消费': 'mean',
'综合胜率': 'mean',
'KDA指数': 'mean'
}).reset_index()
behavior_radar['综合胜率'] = behavior_radar['综合胜率'] * 100
fig_radar1 = px.line_polar(behavior_radar, r='综合胜率', theta='行为模式',
line_close=True, title='各行为模式胜率对比')
st.plotly_chart(fig_radar1, use_container_width=True)
with col2:
# 按段位分群
rank_radar = df.groupby('游戏段位').agg({
'日均时长': 'mean',
'累计消费': 'mean',
'综合胜率': 'mean',
'KDA指数': 'mean'
}).reset_index()
rank_radar['综合胜率'] = rank_radar['综合胜率'] * 100
# 标准化数据用于雷达图
radar_data = rank_radar.melt(id_vars=['游戏段位'], var_name='指标', value_name='数值')
for col in ['日均时长', '累计消费', '综合胜率', 'KDA指数']:
max_val = radar_data[radar_data['指标'] == col]['数值'].max()
radar_data.loc[radar_data['指标'] == col, '数值'] = radar_data.loc[
radar_data['指标'] == col, '数值'] / max_val * 100
fig_radar2 = px.line_polar(radar_data[radar_data['游戏段位'] == '最强王者'],
r='数值', theta='指标',
line_close=True, title='最强王者玩家画像')
st.plotly_chart(fig_radar2, use_container_width=True)
# 多行为模式对比雷达图
st.markdown("### 各行为模式综合画像对比")
fig_radar_compare = go.Figure()
categories = ['年龄', '日均时长', '累计消费', '胜率', 'KDA']
for behavior in ['竞技型', '社交型', '休闲型', '娱乐型', '收藏型']:
if behavior in behavior_radar['行为模式'].values:
data = behavior_radar[behavior_radar['行为模式'] == behavior].iloc[0]
values = [
(data['玩家年龄'] / behavior_radar['玩家年龄'].max()) * 100,
(data['日均时长'] / behavior_radar['日均时长'].max()) * 100,
(data['累计消费'] / behavior_radar['累计消费'].max()) * 100,
data['综合胜率'],
(data['KDA指数'] / behavior_radar['KDA指数'].max()) * 100
]
fig_radar_compare.add_trace(go.Scatterpolar(
r=values,
theta=categories,
fill='toself',
name=behavior
))
fig_radar_compare.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 100])),
showlegend=True,
title="各行为模式综合画像对比(标准化后)"
)
st.plotly_chart(fig_radar_compare, use_container_width=True)
# 主函数
def main():
# 标题
st.markdown('<div class="main-header">🎮 游戏玩家数据分析平台</div>', unsafe_allow_html=True)
# 侧边栏
with st.sidebar:
st.header("📂 数据上传")
# 文件上传
uploaded_file = st.file_uploader("上传CSV文件", type=['csv'])
if uploaded_file:
st.success(f"✅ 已加载: {uploaded_file.name}")
else:
st.info("💡 请上传包含玩家数据的CSV文件")
st.markdown("""
**数据格式要求:**
- 用户ID
- 玩家性别
- 玩家年龄
- 所在区域
- 游戏段位
- 日均时长
- 累计消费
- 擅长位置
- 综合胜率
- KDA指数
- 登录频率
- 行为模式
""")
# 默认使用示例数据
if uploaded_file is None:
try:
import os
default_file = r"d:\daku\游戏玩家数据分析\player_behavior_data.csv"
if os.path.exists(default_file):
uploaded_file = open(default_file, 'rb', encoding=None)
st.info(f"使用默认数据: {os.path.basename(default_file)}")
except:
pass
# 数据加载
if uploaded_file:
df = load_data(uploaded_file)
df_processed = preprocess_data(df)
# 显示关键指标
metrics = calculate_metrics(df)
st.markdown("---")
col1, col2, col3, col4, col5, col6, col7 = st.columns(7)
with col1:
st.metric("👥 总玩家数", metrics['总玩家数'])
with col2:
st.metric("🎂 平均年龄", f"{metrics['平均年龄']}岁")
with col3:
st.metric("⏱️ 平均日均时长", f"{metrics['平均日均时长']}h")
with col4:
st.metric("💰 总累计消费", f"¥{metrics['总累计消费']:,.0f}")
with col5:
st.metric("💵 平均消费", f"¥{metrics['平均累计消费']:.1f}")
with col6:
st.metric("🏆 平均胜率", f"{metrics['平均胜率']}%")
with col7:
st.metric("⚔️ 平均KDA", metrics['平均KDA'])
st.markdown("---")
# 分析模块选择
analysis_type = st.radio(
"📊 选择分析模块",
["📈 用户画像分析", "🎯 用户细分/分群分析", "🔗 关联与相关性分析",
"🔮 预测性分析", "📋 运营决策支持", "📊 可视化分析"],
horizontal=True,
label_visibility="collapsed"
)
# 执行对应分析
if analysis_type == "📈 用户画像分析":
user_profile_analysis(df, df_processed)
elif analysis_type == "🎯 用户细分/分群分析":
segmentation_analysis(df, df_processed)
elif analysis_type == "🔗 关联与相关性分析":
correlation_analysis(df, df_processed)
elif analysis_type == "🔮 预测性分析":
predictive_analysis(df, df_processed)
elif analysis_type == "📋 运营决策支持":
decision_support(df, df_processed)
elif analysis_type == "📊 可视化分析":
visualization_analysis(df, df_processed)
else:
# 欢迎页面
st.markdown("""
<div style="text-align: center; padding: 40px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; color: white; margin-bottom: 40px;">
<h1 style="font-size: 3.5rem; margin-bottom: 20px;">🎮 游戏玩家数据分析平台</h1>
<p style="font-size: 1.5rem; margin-bottom: 30px;">基于Python与Streamlit构建的多维度数据分析应用</p>
<div style="background: rgba(255,255,255,0.2); padding: 20px; border-radius: 15px; display: inline-block;">
<p style="font-size: 1.2rem; margin: 0;">📁 请在左侧上传CSV文件开始分析</p>
</div>
</div>
""", unsafe_allow_html=True)
# 平台特色介绍
st.markdown('<div class="section-header">🌟 平台特色</div>', unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns(4)
with col1:
st.markdown("""
<div style="text-align: center; padding: 25px; background: #f8f9fa; border-radius: 15px; height: 280px;">
<div class="feature-icon">📊</div>
<div class="feature-title">多维度分析</div>
<div class="feature-description">
基于12个玩家维度的全面分析,涵盖人口统计、游戏能力、行为习惯和消费能力
</div>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown("""
<div style="text-align: center; padding: 25px; background: #f8f9fa; border-radius: 15px; height: 280px;">
<div class="feature-icon">🎯</div>
<div class="feature-title">智能分群</div>
<div class="feature-description">
支持5种用户细分方式:价值、活跃度、技能、偏好、行为模式分群
</div>
</div>
""", unsafe_allow_html=True)
with col3:
st.markdown("""
<div style="text-align: center; padding: 25px; background: #f8f9fa; border-radius: 15px; height: 280px;">
<div class="feature-icon">🔮</div>
<div class="feature-title">预测分析</div>
<div class="feature-description">
提供流失预测、付费潜力预测、段位晋升预测等智能分析功能
</div>
</div>
""", unsafe_allow_html=True)
with col4:
st.markdown("""
<div style="text-align: center; padding: 25px; background: #f8f9fa; border-radius: 15px; height: 280px;">
<div class="feature-icon">💡</div>
<div class="feature-title">决策支持</div>
<div class="feature-description">
基于数据分析结果,提供可执行的运营策略和决策建议
</div>
</div>
""", unsafe_allow_html=True)
# 功能介绍卡片
st.markdown('<div class="section-header">📋 功能模块</div>', unsafe_allow_html=True)
# 第一行功能
col1, col2, col3 = st.columns(3)
with col1:
with st.container():
st.markdown('<div class="info-card">', unsafe_allow_html=True)
st.markdown("### 📈 用户画像分析")
st.markdown("- 👥 **人口统计画像**: 性别、年龄、区域分布")
st.markdown("- ⚔️ **游戏能力画像**: 段位、胜率、KDA、位置")
st.markdown("- 🎯 **行为习惯画像**: 时长、登录频率、行为模式")
st.markdown("- 💰 **消费能力画像**: 消费分布、消费能力评估")
st.markdown('</div>', unsafe_allow_html=True)
with col2:
with st.container():
st.markdown('<div class="info-card">', unsafe_allow_html=True)
st.markdown("### 🎯 用户细分分析")
st.markdown("- 💎 **按价值分群**: 高/中/低消费用户")
st.markdown("- 🔥 **按活跃度分群**: 核心/普通/流失用户")
st.markdown("- 🎮 **按技能分群**: 新手/进阶/高手")
st.markdown("- ⭐ **按偏好分群**: 不同位置偏好")
st.markdown("- 🎭 **按行为分群**: 社交/竞技/休闲型")
st.markdown('</div>', unsafe_allow_html=True)
with col3:
with st.container():
st.markdown('<div class="info-card">', unsafe_allow_html=True)
st.markdown("### 🔗 关联性分析")
st.markdown("- 💰 **消费驱动因素**: 时长、段位、胜率与消费关系")
st.markdown("- 📊 **活跃度影响因素**: 年龄、区域对活跃度影响")
st.markdown("- 📈 **技能成长路径**: 时长、段位与KDA关联")
st.markdown("- 🗺️ **区域差异分析**: 各区域消费、段位、活跃度对比")
st.markdown('</div>', unsafe_allow_html=True)
# 第二行功能
col4, col5, col6 = st.columns(3)
with col4:
with st.container():
st.markdown('<div class="warning-card">', unsafe_allow_html=True)
st.markdown("### 🔮 预测性分析")
st.markdown("- ⚠️ **用户流失预测**: 基于登录频率、时长、行为模式")
st.markdown("- 💎 **付费潜力预测**: 基于段位、活跃度、行为模式")
st.markdown("- 🏆 **段位晋升预测**: 基于KDA、胜率、游戏时长")
st.markdown('</div>', unsafe_allow_html=True)
with col5:
with st.container():
st.markdown('<div class="success-card">', unsafe_allow_html=True)
st.markdown("### 📋 运营决策支持")
st.markdown("- 💎 **高价值用户识别**: TOP10用户及运营策略")
st.markdown("- ⚠️ **流失预警干预**: 需要召回的用户及策略")
st.markdown("- ⚔️ **匹配优化建议**: 优化对战匹配算法")
st.markdown("- 🎯 **内容推荐**: 基于位置偏好推荐")
st.markdown("- 💰 **定价策略**: 区域差异化定价")
st.markdown('</div>', unsafe_allow_html=True)
with col6:
with st.container():
st.markdown('<div class="info-card">', unsafe_allow_html=True)
st.markdown("### 📊 可视化分析")
st.markdown("- 🗺️ **用户分布**: 区域热力图、年龄/段位金字塔")
st.markdown("- 📈 **趋势分析**: 登录频率、日均时长趋势")
st.markdown("- 📊 **对比分析**: 性别消费、区域活跃度对比")
st.markdown("- 🔗 **关联分析**: 时长vs消费、胜率vs段位散点图")
st.markdown("- 🎯 **分群分析**: 用户画像雷达图")
st.markdown('</div>', unsafe_allow_html=True)
# 数据格式要求
st.markdown('<div class="section-header">📁 数据格式要求</div>', unsafe_allow_html=True)
data_format = """
<div style="background: #f8f9fa; padding: 20px; border-radius: 15px; border-left: 5px solid #3498db;">
<h4>CSV文件需包含以下12个维度:</h4>
<table style="width: 100%; border-collapse: collapse; margin-top: 15px;">
<tr style="background: #3498db; color: white;">
<th style="padding: 12px; text-align: left;">字段</th>
<th style="padding: 12px; text-align: left;">说明</th>
<th style="padding: 12px; text-align: left;">示例</th>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">用户ID</td>
<td style="padding: 10px;">玩家唯一标识</td>
<td style="padding: 10px;">100001</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">玩家性别</td>
<td style="padding: 10px;">性别</td>
<td style="padding: 10px;">男/女</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">玩家年龄</td>
<td style="padding: 10px;">年龄</td>
<td style="padding: 10px;">27</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">所在区域</td>
<td style="padding: 10px;">区域</td>
<td style="padding: 10px;">华东/华南/华北</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">游戏段位</td>
<td style="padding: 10px;">段位</td>
<td style="padding: 10px;">荣耀青铜/尊贵铂金/最强王者</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">日均时长</td>
<td style="padding: 10px;">平均每日游戏时长(小时)</td>
<td style="padding: 10px;">3.9</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">累计消费</td>
<td style="padding: 10px;">累计消费金额(元)</td>
<td style="padding: 10px;">22.41</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">擅长位置</td>
<td style="padding: 10px;">擅长的游戏位置</td>
<td style="padding: 10px;">坦克/刺客/辅助/射手</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">综合胜率</td>
<td style="padding: 10px;">综合胜率(0-1之间)</td>
<td style="padding: 10px;">0.483</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">KDA指数</td>
<td style="padding: 10px;">KDA指数</td>
<td style="padding: 10px;">5.4</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 10px;">登录频率</td>
<td style="padding: 10px;">登录频率</td>
<td style="padding: 10px;">每天/每周3-5次/每月几次</td>
</tr>
<tr>
<td style="padding: 10px;">行为模式</td>
<td style="padding: 10px;">行为模式类型</td>
<td style="padding: 10px;">竞技型/社交型/休闲型</td>
</tr>
</table>
</div>
"""
st.markdown(data_format, unsafe_allow_html=True)
if __name__ == "__main__":
main()
更多推荐
所有评论(0)