数据建模与可视化
此技能引导 AI 以标准数据科学工作流完成数据分析任务:从数据理解 → EDA → 模型选择(带决策判断)→ 建模验证 → 可视化解释。
TL;DR(快速执行路径)
读数据 → 展示概览 → [检查点1:确认分析目标] → EDA → [检查点2:确认模型方向] → 建模 → 可视化 → 结论
适用场景
当用户提出以下请求时,必须触发此技能:
- 分析这个CSV/Excel文件
- 帮我建立一个数学模型
- 画图展示这两个变量的关系
- 预测未来的趋势
- 找出影响X的关键因素
- 这个数据有什么规律
- 做个相关性分析
- 用 matplotlib/seaborn 做可视化
- 数据挖掘、数据分析报告
工作流程
步骤0:读取文件(边界处理优先)
先处理常见文件问题再往下走:
try:
df = pd.read_csv(file_path, encoding='utf-8')
except UnicodeDecodeError:
df = pd.read_csv(file_path, encoding='gbk')
# Excel 文件
df = pd.read_excel(file_path, sheet_name=0)
print(f"数据形状: {df.shape}")
print(f"列名: {df.columns.tolist()}")
print(f"数据类型:\n{df.dtypes}")
print(f"缺失值:\n{df.isnull().sum()}")
print(f"数值型列统计:\n{df.describe()}")
数据质量边界处理:
| 问题 | 处理方式 |
|------|---------|
| 行数 < 30 | 警告用户:样本量过少,仅做描述性分析,不建模 |
| 行数 < 10 | 仅描述统计+可视化,明确告知无法建模 |
| 缺失值 > 30% | 告知用户并询问处理策略(删除/填充/保留) |
| 全部是分类型变量 | 跳过回归,重点做频率分析、交叉表、卡方检验 |
| 列名有空格/特殊字符 | 执行 df.columns = df.columns.str.strip().str.replace(' ', '_') |
检查点1:确认分析目标(EDA前必须暂停)
展示数据概览后,必须询问用户:
我已看到你的数据有 [N 行 × M 列],列包括:[列名列表]。 请告诉我:
- 你最关心的是哪个变量?(目标变量)
- 你的分析目的是?(预测 / 找规律 / 对比差异 / 其他)
如果不确定,我可以帮你探索所有变量关系后再决定。
只有用户回答后,才进入步骤1(EDA)。
步骤1:探索性数据分析(EDA)
关键原则:先看图,再建模
-
单变量分析:
- 数值型:直方图/KDE,观察分布(正态/偏态/多峰)
- 分类型:柱状图,统计各类别占比
-
双变量关系分析(按用户目标变量优先):
- 数值 vs 数值:散点图 + Pearson 相关系数(|r| > 0.7 为强相关)
- 数值 vs 分类:箱线图(对比各组均值和分布)
- 分类 vs 分类:交叉表 + 卡方检验
-
多变量关系:
- 计算相关性矩阵(仅数值型列)
- 绘制热力图(识别强相关/共线性)
检查点2:确认模型方向(建模前必须暂停)
EDA 完成后,展示关键发现并询问用户:
通过 EDA,我发现:
- [最强相关的变量对]
- [数据分布特征]
- [潜在问题,如共线性]
建议使用 [推荐模型],因为 [理由]。 你是否同意这个方向?
等用户确认后,才进入步骤2(建模)。
步骤2:选择合适的数学模型
AI 判断数据特征的方法:
① 看目标变量类型:
- 连续数值(销售额、温度)→ 回归问题
- 离散类别(是/否、A/B/C)→ 分类问题
- 时间戳有序序列 → 时序预测问题
② 看自变量与目标的关系(通过散点图判断):
- 近似直线 → 线性关系
- 弯曲/抛物线 → 非线性关系
- 无规律、变量多 → 用随机森林探索重要性
③ 检查样本量:
- < 100 行:线性/多项式回归
- 100-1000 行:随机森林、梯度提升
- > 1000 行:任何模型均可
模型选择表:
| 问题类型 | 数据特征 | 推荐模型 | 可视化方式 | |---------|---------|---------|-----------| | 回归 | 两变量线性(r绝对值>0.6) | 线性回归 | 散点图+回归线 | | 回归 | 两变量非线性 | 多项式回归(先2阶再3阶) | 曲线拟合图 | | 回归 | 多变量、关系未知 | 随机森林回归 | 特征重要性图 | | 分类 | 二分类、线性可分 | 逻辑回归 | 决策边界图 | | 分类 | 多分类或非线性 | 决策树/随机森林 | 混淆矩阵 | | 时序 | 有明确时间列 | 趋势分解+线性拟合 | 时序图+预测区间 | | 探索 | 变量间复杂关系 | 相关性分析+PCA | 热力图、主成分图 |
步骤3:建立模型并验证
# 根据样本量决定是否划分
if len(df) >= 50:
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
else:
print("样本量较少,使用全部数据训练,结果仅供参考")
model.fit(X, y)
y_pred = model.predict(X)
模型评估(必须输出):
- 回归:R²、RMSE、MAE
- 分类:准确率、混淆矩阵、F1分数
- R² < 0.3 时,主动告知"模型拟合较差",建议检查遗漏变量/尝试非线性模型
步骤4:可视化与解释
图表设计原则:
- 使用
fig, ax = plt.subplots()面向对象 API - 最小 figsize=(10, 6),多图面板 figsize=(14, 10)
- 每图必须有 xlabel, ylabel, title(标题说明核心发现)
- 色盲友好:推荐 "Set2" 或 "colorblind" 调色板,避免红绿配对
代码占位符替换规则(执行时必须替换):
| 占位符 | 替换为 | |-------|-------| | '变量1' | 实际列名,如 '广告费' | | '数值列' | 实际数值型列名 | | '分类列' | 实际分类型列名 | | '时间列' | 实际时间/日期列名 |
常用代码模板
基础设置
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_squared_error
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style="whitegrid", font_scale=1.1)
plt.rcParams['figure.dpi'] = 100
散点图 + 回归线(含相关系数)
fig, ax = plt.subplots(figsize=(10, 6))
sns.regplot(x='变量1', y='变量2', data=df,
scatter_kws={'alpha': 0.5, 's': 50},
line_kws={'color': 'red', 'linewidth': 2})
r, p = stats.pearsonr(df['变量1'].dropna(), df['变量2'].dropna())
ax.set_xlabel('变量1含义', fontsize=12)
ax.set_ylabel('变量2含义', fontsize=12)
ax.set_title(f'变量1 vs 变量2(r={r:.2f}, p={p:.3f})', fontsize=14)
plt.tight_layout()
plt.savefig('scatter_regression.png', dpi=300, bbox_inches='tight')
相关性热力图
corr = df.select_dtypes(include=[np.number]).corr()
fig, ax = plt.subplots(figsize=(max(8, len(corr)*1.2), max(6, len(corr))))
mask = np.triu(np.ones_like(corr, dtype=bool))
sns.heatmap(corr, mask=mask, annot=True, fmt='.2f',
cmap='RdBu_r', center=0, square=True,
linewidths=0.5, cbar_kws={'shrink': 0.8})
ax.set_title('特征相关性矩阵(下三角)', fontsize=14)
plt.tight_layout()
多图组合面板(EDA总览)
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes[0, 0].hist(df['数值列'], bins=30, edgecolor='black', alpha=0.7, color='steelblue')
axes[0, 0].set_title('数值列分布')
axes[0, 0].set_xlabel('数值列名称')
axes[0, 0].set_ylabel('频次')
sns.boxplot(x='分类列', y='数值列', data=df, ax=axes[0, 1], palette='Set2')
axes[0, 1].set_title('按类别分组的数值分布')
axes[1, 0].plot(df['时间列'], df['数值列'], marker='o', markersize=3, linewidth=1)
axes[1, 0].set_title('时间趋势')
axes[1, 0].tick_params(axis='x', rotation=45)
axes[1, 1].scatter(df['变量1'], df['变量2'], alpha=0.5, s=30)
axes[1, 1].set_title('变量1 vs 变量2')
plt.tight_layout()
模型结果展示
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].scatter(y_test, y_pred, alpha=0.6, s=40)
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', linewidth=2)
rmse = mean_squared_error(y_test, y_pred, squared=False)
axes[0].set_xlabel('实际值')
axes[0].set_ylabel('预测值')
axes[0].set_title(f'预测效果 (R²={r2:.3f}, RMSE={rmse:.2f})')
feature_importance = pd.DataFrame({
'feature': X.columns,
'importance': abs(model.coef_)
}).sort_values('importance', ascending=True)
axes[1].barh(feature_importance['feature'], feature_importance['importance'], color='steelblue')
axes[1].set_xlabel('|系数值|(重要性)')
axes[1].set_title('特征重要性排序')
plt.tight_layout()
非线性拟合(多项式回归)
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
for degree in [2, 3]:
model_poly = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model_poly.fit(X_train, y_train)
score = model_poly.score(X_test, y_test)
print(f"{degree}阶多项式 R²={score:.3f}")
x_range = np.linspace(X.min(), X.max(), 300).reshape(-1, 1)
y_range = model_poly.predict(x_range)
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(X, y, alpha=0.5, label='原始数据')
ax.plot(x_range, y_range, 'r-', linewidth=2, label=f'{degree}阶多项式拟合')
ax.legend()
ax.set_title(f'非线性拟合 (R²={score:.3f})')
plt.tight_layout()
常见错误与避免方法
| 常见错误 | 正确做法 |
|---------|---------|
| 默认图表尺寸太小 | 始终设置 figsize=(10, 6) 或更大 |
| 颜色过于杂乱 | 使用 "Set2" 或 "colorblind" 调色板 |
| 缺少坐标轴标签 | 每图必须有 xlabel, ylabel, title |
| 不处理缺失值 | 先检查 df.isnull().sum(),再处理 |
| 不做训练测试集划分 | 样本 >= 50 时必须划分,报告测试集性能 |
| 不解释图表 | 必须用一句话说明图表核心发现 |
| 相关性等于因果 | 明确说明"相关不等于因果" |
| 占位符未替换 | 代码中的'变量1'、'数值列'必须替换为实际列名 |
| 忽略样本量限制 | 样本 < 30 时降级为描述性分析 |
输出要求
完成分析后,必须提供:
- 数据概览:行列数、缺失值情况、各列数据类型
- 关键发现:用通俗语言说明最重要的数据规律
- 模型结果:模型类型、关键参数、性能指标及其含义
- 可视化图表:至少关键关系图 + 模型结果图
- 业务解释:将统计结论翻译成业务建议("也就是说……")
- 局限性说明:相关性不等于因果性、样本量限制等
何时寻求外部技能
遇到以下情况,用 find-skills 搜索扩展技能:
- 高级统计检验(ANOVA、ARIMA 等)
- 地理信息图、网络关系图等特殊可视化
- XGBoost、神经网络等高级模型
- 交互式图表(Plotly、Bokeh)
- 大规模数据处理(Dask、Spark)
微信扫一扫