此技能引导 AI 以标准数据科学工作流完成数据分析任务:从数据理解 → EDA → 模型选择(带决策判断)→ 建模验证 → 可视化解释。
读数据 → 展示概览 → [检查点1:确认分析目标] → EDA → [检查点2:确认模型方向] → 建模 → 可视化 → 结论
当用户提出以下请求时,必须触发此技能:
先处理常见文件问题再往下走:
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(' ', '_') |
展示数据概览后,必须询问用户:
> 我已看到你的数据有 [N 行 × M 列],列包括:[列名列表]。
> 请告诉我:
> 1. 你最关心的是哪个变量?(目标变量)
> 2. 你的分析目的是?(预测 / 找规律 / 对比差异 / 其他)
>
> 如果不确定,我可以帮你探索所有变量关系后再决定。
只有用户回答后,才进入步骤1(EDA)。
关键原则:先看图,再建模
EDA 完成后,展示关键发现并询问用户:
> 通过 EDA,我发现:
> - [最强相关的变量对]
> - [数据分布特征]
> - [潜在问题,如共线性]
>
> 建议使用 [推荐模型],因为 [理由]。
> 你是否同意这个方向?
等用户确认后,才进入步骤2(建模)。
AI 判断数据特征的方法:
① 看目标变量类型:
- 连续数值(销售额、温度)→ 回归问题
- 离散类别(是/否、A/B/C)→ 分类问题
- 时间戳有序序列 → 时序预测问题
② 看自变量与目标的关系(通过散点图判断):
- 近似直线 → 线性关系
- 弯曲/抛物线 → 非线性关系
- 无规律、变量多 → 用随机森林探索重要性
③ 检查样本量:
- < 100 行:线性/多项式回归
- 100-1000 行:随机森林、梯度提升
- > 1000 行:任何模型均可
模型选择表:
| 问题类型 | 数据特征 | 推荐模型 | 可视化方式 |
|---|---|---|---|
| --------- | --------- | --------- | ----------- |
| 回归 | 两变量线性(r绝对值>0.6) | 线性回归 | 散点图+回归线 |
| 回归 | 两变量非线性 | 多项式回归(先2阶再3阶) | 曲线拟合图 |
| 回归 | 多变量、关系未知 | 随机森林回归 | 特征重要性图 |
| 分类 | 二分类、线性可分 | 逻辑回归 | 决策边界图 |
| 分类 | 多分类或非线性 | 决策树/随机森林 | 混淆矩阵 |
| 时序 | 有明确时间列 | 趋势分解+线性拟合 | 时序图+预测区间 |
| 探索 | 变量间复杂关系 | 相关性分析+PCA | 热力图、主成分图 |
# 根据样本量决定是否划分
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)
模型评估(必须输出):
图表设计原则:
fig, ax = plt.subplots() 面向对象 API代码占位符替换规则(执行时必须替换):
| 占位符 | 替换为 |
|---|---|
| ------- | ------- |
| '变量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()
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 搜索扩展技能:
共 1 个版本