🔍

数据分析技术训练营

从零基础到实战 · 6大模块 · 视频+教程+代码+练习

15
课程章节
45+
代码示例
75+
练习题
45周
建议周期
Python 模拟器已就绪 — 支持 numpy / pandas / 基础语法
🧹
Chapter 01

数据清洗与预处理

入门2周
📖图文教程

1. 认识脏数据

在真实世界中,数据几乎从来不是完美的。打开一个 CSV 文件,你可能会发现缺失值(某些格子是空的)、重复行异常值(比如年龄=200)、格式错误。使用 Pandas 的 df.info()df.describe() 可以快速了解数据全貌。

Python
import pandas as pd
data = {'姓名': ['张三', '李四', '王五', '张三', '赵六'],
    '年龄': [25, 30, None, 25, 200],
    '工资': [5000, 8000, 6000, 5000, 7000]}
df = pd.DataFrame(data)
print("数据形状:", df.shape)
print(df.isnull().sum())
print(df.describe())

2. 处理缺失值

缺失值处理有三种策略:删除填充(均值/中位数/众数)、插值

Python
# 删除缺失行
df_clean = df.dropna()
# 中位数填充(对异常值更鲁棒)
df_filled = df.fillna({'年龄': df['年龄'].median()})
print("填充后:", df_filled)

3. 重复值与异常值

使用 df.duplicated() 检测重复行。使用 IQR 方法检测异常值。

Python
import numpy as np
print("重复行:", df.duplicated().sum())
Q1, Q3 = df['年龄'].quantile([0.25, 0.75])
IQR = Q3 - Q1
outliers = df[(df['年龄'] < Q1-1.5*IQR) | (df['年龄'] > Q3+1.5*IQR)]
print("异常值:\n", outliers)
💡详细解释
❓ 为什么数据清洗如此重要?

数据科学界名言:"Garbage In, Garbage Out"。数据科学家约花费 60-80% 的时间在数据清洗上。

🛠 怎么选择缺失值处理方法?

删除:缺失 < 5% 时最简单。均值:适合正态分布。中位数:有异常值时更鲁棒。众数:适合分类数据。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. 以下哪个方法最适合处理少量缺失值(< 5%)?
答案:B。缺失比例很小时直接删除最简单有效。
Q2. IQR 方法中异常值判定范围是?
答案:C。IQR 方法用 Q1-1.5*IQR 和 Q3+1.5*IQR 作为异常值边界。
Q3. 有异常值时填充缺失值应优先选择?
答案:B。中位数不受极端值影响,更能代表数据中心趋势。
✍️实操题
1-1 补全代码:使用中位数填充缺失值
df['Age'] = df['Age'].fillna(df['Age'].())
df['Age'] = df['Age'].fillna(df['Age'].median())
1-2 补全代码:IQR 异常值检测
upper = Q3 + * (Q3 - Q1)
upper = Q3 + 1.5 * (Q3 - Q1) outliers = df[df['Fare'] > upper]
🏆案例实训
📜 任务:电商用户数据清洗报告

某电商平台提供了 100 条用户注册数据,包含姓名、年龄、邮箱、注册日期、消费金额等字段,数据中存在缺失值、重复值、格式错误等问题。

要求:

  • 生成模拟原始数据(包含 100 条记录,至少含 5 个字段)
  • 在数据中人为引入缺失值、重复记录和异常值
  • 检测并统计缺失值的数量和比例
  • 删除重复记录(保留第一条)
  • 对异常值进行处理(如年龄超出合理范围、金额为负数等)
  • 输出清洗前后的数据对比报告
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
⚙️
Chapter 02

特征工程实战

进阶2周
📖图文教程

1. 标准化与归一化

标准化将数据转为均值0、标准差1。归一化缩放到 [0, 1]。特征量纲差异大时必须先做标准化。

Python
import numpy as np
data = np.array([[25,5000],[30,8000],[35,12000],[40,15000]])
# 标准化
data_std = (data - data.mean(axis=0)) / data.std(axis=0)
print("标准化后均值:", data_std.mean(axis=0).round(4))
# 归一化
data_norm = (data - data.min(axis=0)) / (data.max(axis=0) - data.min(axis=0))
print("归一化范围:", data_norm.min(axis=0), "~", data_norm.max(axis=0))

2. 编码技术

标签编码将分类值映射为整数。独热编码为每个类别创建二进制列。

Python
import pandas as pd
colors = pd.Series(['红','绿','蓝','红','绿'])
print("标签编码:", colors.map({v:i for i,v in enumerate(colors.unique())}).tolist())
print("独热编码:\n", pd.get_dummies(colors, prefix='颜色'))

3. 特征创建

从现有特征创造新特征:数学组合、日期拆分、文本提取。

Python
import pandas as pd
df = pd.DataFrame({'建造年份':[2000,1995,2010],'总面积':[120,85,150],'价格':[300,220,450]})
df['房屋年龄'] = 2024 - df['建造年份']
df['每平米价格'] = (df['价格'] / df['总面积']).round(2)
print(df)
💡详细解释
❓ 为什么特征工程比模型选择更重要?

"特征决定上限,模型只是逼近上限"。好的特征能让简单模型超越复杂模型。

🛠 标准化 vs 归一化?

标准化适合 SVM、逻辑回归。归一化适合 KNN、图像处理。有异常值时优先用标准化。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. 独热编码的主要缺点是?
答案:B。100个取值的特征独热编码会新增100列,导致维度灾难。
Q2. 标准化后均值和标准差分别是?
答案:C。标准化公式 (x-mean)/std 变换后均值为0,标准差为1。
Q3. 哪种方法不适合有序分类变量("低"、"中"、"高")?
答案:B。独热编码会丢失有序变量间的顺序关系。
✍️实操题
2-1 补全代码:标准化分母
data_std = (data - data.mean(axis=0)) / data.(axis=0)
data_std = (data - data.mean(axis=0)) / data.std(axis=0)
2-2 补全代码:标签编码
df['Sex'] = df['Sex'].map({'male': , 'female': })
df['Sex'] = df['Sex'].map({'male': 1, 'female': 0})
🏆案例实训
📜 任务:房价预测特征工程

某房产网站收集了房屋数据,请对原始特征进行特征工程处理,构造有用特征并分析特征重要性。

要求:

  • 生成模拟房产数据(200 条,含面积、卧室数、楼层、房龄、距离地铁站距离等字段)
  • 构造组合特征(如均价 = 总价 / 面积)
  • 对分类特征进行编码转换
  • 对数值特征进行标准化/归一化处理
  • 计算各特征与房价的相关性,排序展示
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
📊
Chapter 03

数据建模与分析

进阶3周
📖图文教程

1. 线性回归

y = wx + b 拟合数据,最小化均方误差(MSE)。

Python
import numpy as np
X = np.array([1,2,3,4,5], dtype=float)
y = np.array([2.1,3.9,6.2,7.8,10.1])
w = ((X-X.mean())*(y-y.mean())).sum() / ((X-X.mean())**2).sum()
b = y.mean() - w*X.mean()
y_pred = w*X + b
r2 = 1 - ((y-y_pred)**2).sum()/((y-y.mean())**2).sum()
print(f"y = {w:.2f}x + {b:.2f}, R² = {r2:.4f}")

2. 逻辑回归(分类)

通过 Sigmoid 函数映射到 [0,1] 概率值,用于二分类。

Python
import numpy as np
def sigmoid(z): return 1/(1+np.exp(-z))
hours = np.array([1,2,3,4,5,6,7,8], dtype=float)
passed = np.array([0,0,0,1,0,1,1,1])
prob = sigmoid(0.8*hours - 3.0)
pred = (prob >= 0.5).astype(int)
print(f"准确率: {(pred==passed).mean():.2%}")

3. 模型评估

回归看 R²/MAE/RMSE,分类看精确率/召回率/F1。

Python
import numpy as np
yt = np.array([1,0,1,1,0,1,0,0,1,1])
yp = np.array([1,0,1,0,0,1,1,0,1,1])
tp=((yp==1)&(yt==1)).sum(); fp=((yp==1)&(yt==0)).sum()
fn=((yp==0)&(yt==1)).sum(); tn=((yp==0)&(yt==0)).sum()
prec=tp/(tp+fp) if tp+fp else 0; rec=tp/(tp+fn) if tp+fn else 0
f1=2*prec*rec/(prec+rec) if prec+rec else 0
print(f"准确率:{(tp+tn)/len(yt):.2%} 精确率:{prec:.2%} 召回率:{rec:.2%} F1:{f1:.2%}")
💡详细解释
❓ 为什么需要交叉验证?

K 折交叉验证将数据分成 K 份轮流测试,取平均值,结果更稳定可靠。

🛠 过拟合 vs 欠拟合?

过拟合:训练好但测试差(死记硬背)。欠拟合:训练和测试都差(太简单)。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. R² 分数的取值范围是?
答案:B。R² 可以为负值(模型比均值还差时),越接近1越好。
Q2. 医疗诊断场景中哪个指标最重要?
答案:C。漏诊代价远高于误诊,召回率(不漏掉病人)最重要。
Q3. 哪种方法能有效防止过拟合?
答案:B。增加数据量是最直接有效的防过拟合方法。
✍️实操题
3-1 补全代码:线性回归指数
w = ((X-X.mean())*(y-y.mean())).sum() / ((X-X.mean())**).sum()
w = ((X - X.mean()) * (y - y.mean())).sum() / ((X - X.mean()) ** 2).sum() b = y.mean() - w * X.mean() y_pred = w * X + b
3-2 补全代码:精确率计算
precision = tp / (tp + ) if (tp + ) else 0
precision = tp / (tp + fp) if (tp + fp) else 0 recall = tp / (tp + fn) if (tp + fn) else 0 f1 = 2 * precision * recall / (precision + recall) if (precision + recall) else 0
🏆案例实训
📜 任务:销售数据分析模型构建

某连锁超市收集了各个分店的销售数据,请构建数据分析模型,分析不同因素对销售额的影响。

要求:

  • 生成模拟销售数据(100 家门店,含客流量、客单价、折扣率、员工数、门店面积等)
  • 计算各门店的销售额 = 客流量 * 客单价
  • 分析不同因素与销售额的关系(散点图 + 相关系数)
  • 构建多元线性回归模型(从零实现)
  • 输出模型系数和 R² 评估指标
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🗃️
Chapter 04

SQL 高级查询技巧

入门2周
📖图文教程

1. SQL 基础查询

SELECT 选择列,FROM 指定表,WHERE 过滤条件。

SQL
SELECT name, salary FROM employees
WHERE salary > 5000 AND department = '技术部'
ORDER BY salary DESC LIMIT 10;

2. 聚合与分组

GROUP BY 分组,HAVING 过滤组。

SQL
SELECT department, COUNT(*) AS 人数, AVG(salary) AS 平均工资
FROM employees GROUP BY department
HAVING AVG(salary) > 5000;

3. 窗口函数

不折叠行,为每行计算额外值。

SQL
SELECT name, salary,
  ROW_NUMBER() OVER(PARTITION BY department ORDER BY salary DESC) AS 排名
FROM employees;

SELECT month, revenue,
  LAG(revenue, 1) OVER(ORDER BY month) AS 上月
FROM sales;
💡详细解释
❓ WHERE vs HAVING?

WHERE 分组前过滤行。HAVING 分组后过滤组。执行顺序:FROM->WHERE->GROUP BY->HAVING->SELECT。

🛠 ROW_NUMBER vs RANK?

ROW_NUMBER:连续 1,2,3,4。RANK:并列跳号 1,2,2,4。DENSE_RANK:并列不跳 1,2,2,3。

💻动手练习
💻 Python 模拟 SQL
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. SQL 正确执行顺序是?
答案:B。逻辑执行顺序:FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY->LIMIT。
Q2. RANK() 并列第2名后下一个排名是?
答案:C。RANK() 遇到并列会跳号,两个第2名后是第4名。
Q3. 窗口函数和 GROUP BY 的主要区别?
答案:B。GROUP BY 折叠行,窗口函数保留每行并附加统计信息。
✍️实操题
4-1 补全代码:聚合方法
r = emp.groupby('department').('salary').mean()
r = emp.groupby('department').agg({'salary': 'mean'}) # 或更详细的命名聚合: r = emp.groupby('department').agg(平均工资=('salary', 'mean'), 人数=('id', 'count'))
4-2 补全代码:移位方法
df['上月'] = df['revenue'].(1)
df['上月'] = df['revenue'].shift(1) df['增长'] = df['revenue'] - df['上月']
🏆案例实训
📜 任务:电商销售 SQL 分析场景模拟

某电商平台需要分析用户订单数据,请使用 Python 模拟 SQL 操作完成多表关联查询与分析。

要求:

  • 创建用户表、订单表、商品表、类目表(各 50-200 条记录)
  • 用字典/列表模拟 SQL 表连接操作
  • 完成分组统计(按类目统计销售额、按用户统计订单数)
  • 找出销售额 TOP 5 的商品
  • 计算各月的销售趋势
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🐍
Chapter 05

Python 数据分析库

入门3周
📖图文教程

1. NumPy 基础

ndarray 数组,向量化运算比循环快 10-100 倍。

Python
import numpy as np
arr = np.array([1,2,3,4,5])
print("向量化:", arr * 2 + 10)
print("均值:", arr.mean(), "标准差:", arr.std())
a = np.array([[1,2,3]]); b = np.array([[10],[20]])
print("广播:\n", a + b)

2. Pandas 数据操作

核心操作:筛选、分组、聚合、合并

Python
import pandas as pd
df = pd.DataFrame({'部门':['技术','市场','技术'],'工资':[8000,6000,12000]})
print(df[df['工资'] > 7000])
print(df.groupby('部门').agg(平均工资=('工资','mean')))

3. 文本可视化

Python
months = ['1月','2月','3月','4月','5月','6月']
sales = [100,120,115,140,135,160]
max_sales = max(sales)
for m, s in zip(months, sales):
    bar = '█' * int(s/max_sales*30)
    print(f"  {m} | {bar} {s}万")
💡详细解释
❓ 为什么 NumPy 比 Python 循环快?

NumPy 底层用 C 语言实现,利用连续内存和 SIMD 指令集,向量化运算避免了 Python 循环开销。

🛠 loc vs iloc?

df.loc[] 基于标签索引。df.iloc[] 基于位置索引。记忆:loc=label,iloc=integer location。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. Series 和 DataFrame 的区别?
答案:B。Series 是一维数组,DataFrame 是二维表格。
Q2. 基于标签的索引方法是?
答案:B。loc 基于标签,iloc 基于位置。
Q3. NumPy 广播机制规则是?
答案:B。广播从最右维度比较,兼容条件是相等或其中一个是1。
✍️实操题
5-1 补全代码:向量乘法
arr = np.array([1,2,3,4,5]); doubled = arr 2
arr = np.array([1, 2, 3, 4, 5]) doubled = arr * 2 print(doubled) # [2, 4, 6, 8, 10]
5-2 补全代码:loc 索引器
result = df.[df['工资'] > 10000, '姓名']
result = df.loc[df['工资'] > 10000, '姓名']
🏆案例实训
📜 任务:用户行为数据分析

某 APP 记录了用户的每日行为数据,请使用 pandas 风格的列表操作进行数据分析。

要求:

  • 生成 30 天 50 个用户的每日行为数据(登录次数、停留时长、操作次数等)
  • 计算每个用户的日均指标
  • 找出高活跃用户(登录 > 15 天)和低活跃用户(登录 < 5 天)
  • 比较两组用户的行为差异
  • 给出用户分层运营建议
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
⚒️
Chapter 06

数据挖掘算法应用

高级3周
📖图文教程

1. K-Means 聚类

把数据分成 K 组,使组内相似、组间不同。步骤:选中心->分配->更新->重复。

Python
import numpy as np
def kmeans(X, k, max_iters=100):
    np.random.seed(42)
    centers = X[np.random.choice(len(X), k, replace=False)].copy()
    for _ in range(max_iters):
        dists = np.array([[np.sqrt(((x-c)**2).sum()) for c in centers] for x in X])
        labels = dists.argmin(axis=1)
        new_c = np.array([X[labels==i].mean(axis=0) for i in range(k)])
        if np.allclose(centers, new_c): break
        centers = new_c
    return labels, centers

np.random.seed(42)
X = np.vstack([np.random.normal([2,2],0.5,(30,2)),
               np.random.normal([8,8],0.5,(30,2)),
               np.random.normal([2,8],0.5,(30,2))])
labels, centers = kmeans(X, 3)
for i in range(3):
    print(f"簇{i}: {(labels==i).sum()}个点, 中心=({centers[i][0]:.1f}, {centers[i][1]:.1f})")

2. PCA 降维

把高维数据压缩到低维,保留主要信息。

Python
import numpy as np
np.random.seed(42)
X = np.random.randn(100, 5)
# 手动 PCA
X_centered = X - X.mean(axis=0)
cov = np.cov(X_centered, rowvar=False)
eigenvalues, eigenvectors = np.linalg.eigh(cov)
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]
variance_ratio = eigenvalues / eigenvalues.sum()
print("各主成分方差解释率:", variance_ratio.round(4))
print("前2个累计:", variance_ratio[:2].sum().round(4))

3. 时间序列分析

移动平均、指数平滑,用于趋势预测。

Python
import numpy as np
np.random.seed(42)
sales = np.cumsum(np.random.randn(12) * 10) + 100
months = [f'{i+1}月' for i in range(12)]
# 3个月移动平均
ma3 = np.convolve(sales, np.ones(3)/3, mode='valid')
print("月份 | 实际 | MA3")
for i in range(len(ma3)):
    print(f"  {months[i+1]} | {sales[i+1]:.1f} | {ma3[i]:.1f}")
💡详细解释
❓ K-Means 的 K 值怎么选?

常用肘部法则:计算不同 K 值的簇内误差平方和(SSE),画图找"拐点"。拐点对应的 K 就是最优值。

🛠 PCA 降维会丢失信息吗?

会。方差解释率告诉我们保留了多少信息。通常保留累计解释率 > 85% 的主成分即可。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. K-Means 算法需要预先指定什么?
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(x, y, color='cyan', linewidth=2)
ax.set_title('正弦曲线', fontsize=14)
ax.set_xlabel('X 轴')
ax.set_ylabel('Y 轴')
ax.grid(True, alpha=0.3)
plt.show()

2. 散点图 (scatter)

plt.scatter(x, y) 显示两个变量的关系。参数 c 控制颜色,s 控制大小,alpha 控制透明度。

Python
np.random.seed(42)
x = np.random.randn(50)
y = np.random.randn(50)
colors = np.random.rand(50)
sizes = np.random.randint(20, 200, 50)

plt.figure(figsize=(6, 5))
plt.scatter(x, y, c=colors, s=sizes, alpha=0.7, cmap='viridis')
plt.colorbar(label='颜色映射')
plt.title('散点图示例')
plt.xlabel('特征 X')
plt.ylabel('特征 Y')
plt.grid(True, alpha=0.2)
plt.show()

3. 柱状图与直方图

柱状图 (plt.bar) 展示分类对比。直方图 (plt.hist) 展示数据分布。

Python
# 柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]
plt.bar(categories, values, color=['cyan', 'purple', 'blue', 'pink', 'green'])
plt.title('分类柱状图')
plt.ylabel('值')
plt.show()

# 直方图
data = np.random.randn(1000)
plt.hist(data, bins=30, alpha=0.7, color='cyan', edgecolor='white')
plt.title('数据分布直方图')
plt.xlabel('值')
plt.ylabel('频数')
plt.show()

4. 图表自定义技巧

通过 plt.style.use() 切换主题,plt.tight_layout() 自动调整布局,plt.savefig() 保存图片。

Python
# 多子图布局
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
x = np.linspace(0, 2*np.pi, 100)

axes[0, 0].plot(x, np.sin(x), color='cyan')
axes[0, 0].set_title('sin(x)')

axes[0, 1].plot(x, np.cos(x), color='purple')
axes[0, 1].set_title('cos(x)')

axes[1, 0].plot(x, np.sin(x)*np.cos(x), color='orange')
axes[1, 0].set_title('sin(x)*cos(x)')

axes[1, 1].plot(x, np.sin(x)**2, color='pink')
axes[1, 1].set_title('sin²(x)')

plt.tight_layout()
plt.show()
💡详细解释
❓ Figure 和 Axes 的区别?

Figure 是整张画布,Axes 是画布上的子图区域。一个 Figure 可以包含多个 Axes,就像一张纸上画多幅图。使用 plt.subplots() 可以同时创建两者。

🛠 什么时候用哪种图表?

折线图:趋势变化。散点图:两变量关系。柱状图:分类对比。直方图:数据分布。箱线图:发现异常值。选对图表类型比美化更重要。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作(文本模拟图表输出)
练习题
Q1. Matplotlib 中创建子图的最常用函数是?
答案:B。plt.subplots() 同时返回 Figure 和 Axes 对象,是最推荐的子图创建方式。
Q2. 直方图(hist)和柱状图(bar)的主要区别是?
答案:B。直方图针对连续数值数据分箱统计分布,柱状图针对分类数据比较数值大小。
Q3. 散点图中 alpha 参数的作用是什么?
答案:B。alpha 控制透明度,值在 0-1 之间,点重叠时降低透明度能更好展示密度分布。
✍️实操题
7-1 补全代码:创建子图
fig, axes = plt.(2, 2, figsize=(10, 8))
fig, axes = plt.subplots(2, 2, figsize=(10, 8)) # axes 是 2x2 的数组,通过 axes[行, 列] 访问每个子图
7-2 补全代码:设置图表标题
ax.('正弦曲线', fontsize=14)
ax.set_title('正弦曲线', fontsize=14) ax.set_xlabel('X 轴') ax.set_ylabel('Y 轴') # 使用 Axes 对象的方法来定制图表
🏆案例实训
📜 任务:销售数据可视化看板

某零售公司要求制作一份销售数据可视化分析报告,用文本图表展示各维度的销售表现。

要求:

  • 生成月度销售数据(12 个月,含销售额、利润、成本等指标)
  • 用文本柱状图展示月度销售额趋势
  • 按产品类别对比销售占比
  • 展示各区域销售排名
  • 输出分析结论与建议
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🏆案例实训
📜 任务:电商用户聚类分析

某电商平台希望通过用户消费数据对用户进行聚类分析,以便制定差异化的营销策略。

要求:

  • 生成 200 名用户的消费数据(年消费额、购买频次、客单价、会员时长等)
  • 对数据进行标准化处理
  • 使用 K-Means 算法从零实现聚类(K=3 或 4)
  • 分析每个聚类群体的特征差异
  • 为每个群体设计针对性的营销策略
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🌸
Chapter 08

高级数据可视化与 Seaborn

进阶2周
📖图文教程

1. Seaborn 主题与样式

Seaborn 基于 Matplotlib,提供更美观的默认主题。使用 sns.set_theme() 设置样式,sns.set_palette() 设置配色方案。

Python
import seaborn as sns
import matplotlib.pyplot as plt

sns.set_theme(style='darkgrid')
sns.set_palette('husl')

# 可用主题: darkgrid, whitegrid, dark, white, ticks
# 常用调色板: deep, muted, bright, pastel, dark, colorblind

2. 分布图与箱线图

直方图+核密度sns.histplot()kde=True 参数。箱线图sns.boxplot() 展示四分位数和异常值。小提琴图sns.violinplot() 结合箱线图与核密度。

Python
import numpy as np

np.random.seed(42)
data = np.random.randn(200)

# 分布图
sns.histplot(data, bins=25, kde=True, color='steelblue')
plt.title('数据分布(含核密度曲线)')
plt.show()

# 箱线图
sns.boxplot(data=data, orient='h', width=0.3)
plt.title('箱线图')
plt.show()

3. Pairplot 与热力图

sns.pairplot() 绘制特征两两关系的矩阵图,对角线是分布。sns.heatmap() 用颜色展示相关系数矩阵。

Python
import pandas as pd

df = pd.DataFrame({
    '年龄': np.random.randint(20, 60, 100),
    '工资': np.random.randint(3000, 25000, 100),
    '经验年数': np.random.randint(1, 30, 100),
    '绩效分': np.random.uniform(60, 100, 100)
})

# 相关系数热力图
corr = df.corr()
plt.figure(figsize=(8, 6))
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f',
            linewidths=0.5, square=True)
plt.title('特征相关系数热力图')
plt.show()

4. 分类数据可视化

sns.countplot() 统计分类频数,sns.barplot() 展示分组统计值,sns.catplot() 是分类图的统一接口。

Python
# 创建分类数据
tips = pd.DataFrame({
    '性别': np.random.choice(['男', '女'], 100),
    '星期': np.random.choice(['周一','周二','周三','周四','周五'], 100),
    '消费额': np.random.randint(50, 500, 100),
    '小费': np.random.randint(5, 80, 100)
})

# 分类计数
sns.countplot(data=tips, x='星期', hue='性别')
plt.title('各星期顾客性别分布')
plt.show()

# 分组柱状图
sns.barplot(data=tips, x='性别', y='消费额', hue='星期')
plt.title('性别与星期的消费额对比')
plt.show()
💡详细解释
❓ Seaborn vs Matplotlib?

Seaborn 是对 Matplotlib 的高级封装,优点是代码简洁、默认好看、自带数据集和统计功能。Matplotlib 更底层,自由度更高。实践中建议 Seaborn 做探索性分析,Matplotlib 做定制化图表。

🛠 热力图怎么看?

颜色越深表示相关性越强。红色正相关(同增同减),蓝色负相关(此消彼长)。数字越接近 1 或 -1 相关性越强,接近 0 表示几乎无关。对角线永远是 1(自己和自己的关系)。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作(文本模拟图表输出)
练习题
Q1. Seaborn 中用于绘制相关系数矩阵的函数是?
答案:A。sns.heatmap() 用于绘制热力图。pairplot() 是画两两关系矩阵图,但显示的是散点图而非相关系数。
Q2. 小提琴图(violinplot)相比箱线图的优势是?
答案:A。小提琴图同时展示了箱线图的统计量和核密度估计的形状,能更直观地看到数据的分布形态。
Q3. seaborn 中 histplot 的 kde=True 参数的作用是?
答案:B。kde=True 会在直方图上叠加一条平滑的核密度估计曲线,帮助观察数据的整体分布形态。
✍️实操题
8-1 补全代码:设置 Seaborn 主题
sns.(style='darkgrid')
sns.set_theme(style='darkgrid', palette='husl') # 也可以用旧版 API: sns.set_style('darkgrid')
8-2 补全代码:热力图的标注参数是
sns.heatmap(corr, =True, cmap='coolwarm')
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5, square=True) # annot=True 在格子中显示数值,fmt 控制数字格式,cmap 指定颜色映射
🏆案例实训
📜 任务:产品质量抽样检验报告

某工厂需要对新生产的一批产品进行质量检验,请用统计方法分析样本数据并给出检验结论。

要求:

  • 生成产品样本数据(200 个产品,含重量、尺寸、硬度等指标)
  • 计算样本的均值、标准差、中位数等统计量
  • 绘制分布频次文本图(直方图)
  • 与标准规格进行对比(假设检验)
  • 给出产品合格率分析报告
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🔍
Chapter 09

探索性数据分析 (EDA) 实战

进阶2周
📖图文教程

1. 数据概览:info() 与 describe()

df.info() 查看列类型和非空计数。df.describe() 统计数值列分布。这是任何 EDA 的第一步。

Python
import pandas as pd
import numpy as np

df = pd.read_csv('data.csv')
print(df.info())
print(df.describe().round(2))
print("缺失值:\n", df.isnull().sum())

2. 单变量分析

使用 value_counts() 分析分类变量,hist 分析数值变量分布,skew()kurtosis() 检查偏度和峰度。

Python
# 分类变量
print(df['类别'].value_counts())
print(df['类别'].value_counts(normalize=True).round(3))  # 占比

# 数值变量
print(f"偏度: {df['价格'].skew():.3f}")  # 0=对称, >0右偏, <0左偏
print(f"峰度: {df['价格'].kurtosis():.3f}")  # 峰度 >0 尖峰, <0 平峰

3. 双变量与多变量分析

使用 df.corr() 计算相关系数矩阵,pd.crosstab() 做交叉表。

Python
# 相关系数
corr = df.select_dtypes(include=[np.number]).corr()
print(corr['目标列'].sort_values(ascending=False))

# 交叉表
ct = pd.crosstab(df['地区'], df['产品类型'], margins=True, normalize='index')
print(ct.round(3))

4. 缺失值与异常值模式

EDA 还要关注缺失值的分布模式和异常值的成因,可以使用 df.isnull().sum() 和 IQR 方法系统检测。

Python
# 缺失值热力图(列维度)
missing_pct = (df.isnull().sum() / len(df)).sort_values(ascending=False)
print("缺失率>10%的列:")
print(missing_pct[missing_pct > 0.1])

# IQR 异常值检测
def find_outliers(df, col):
    Q1, Q3 = df[col].quantile([0.25, 0.75])
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    outliers = df[(df[col] < lower) | (df[col] > upper)]
    return len(outliers), lower, upper
💡详细解释
❓ EDA 的核心目标是什么?

四个字:"了解数据"。包括:数据长什么样?有没有缺失和异常?特征间有什么关系?哪个特征对目标影响最大?EDA 不是一次性的,而是一个循环迭代的过程。

🛠 偏度(skew)怎么看?

偏度=0:对称分布。偏度>0:右偏(长尾在右边,均值>中位数)。偏度<0:左偏(长尾在左边,均值<中位数)。|偏度|>1 表明严重偏斜,可能需要做变换(如 log 变换)。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. df.describe() 默认统计哪些列?
答案:B。df.describe() 默认只统计数值列(int64, float64),对分类列需要加 include='object' 参数。
Q2. 偏度(skew) > 0 表示数据分布?
答案:C。skew>0 右偏(正偏),均值>中位数>众数,长尾在右边。比如收入分布通常是右偏的。
Q3. EDA 过程中发现价格与面积的相关系数为 0.85,说明?
答案:B。相关系数 0.85 接近 1,表示面积和价格有很强的正相关关系,面积越大价格越高。
✍️实操题
9-1 补全代码:获取缺失值数量
missing = df.().sum()
missing = df.isnull().sum() # 返回每列的缺失值数量 # 加百分比: missing_pct = (df.isnull().sum() / len(df)) * 100
9-2 补全代码:分类变量频数统计
df['district'].()
df['district'].value_counts() # 统计每个区域的样本数量 # 加 normalize=True 可以看占比:df['district'].value_counts(normalize=True)
🏆案例实训
📜 任务:客户满意度调查报告

某电商平台收集了不同客户群体的满意度调查数据,请进行探索性分析并得出结论。

要求:

  • 生成模拟调查数据(200 名客户,含年龄段、会员等级、满意度评分等字段)
  • 按年龄段分组统计平均满意度,用文本柱状图展示
  • 比较不同会员等级(普通/银卡/金卡/钻石)的满意度差异
  • 计算各特征与满意度的相关系数,找出最重要的影响因素
  • 给出 3 条业务改进建议
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
📈
Chapter 10

统计分析与假设检验

进阶2周
📖图文教程

1. 描述性统计

描述性统计分两类:集中趋势(均值、中位数、众数)和离散程度(方差、标准差、IQR)。均值受异常值影响大,中位数更鲁棒。

Python
import numpy as np

data = np.array([12, 15, 14, 18, 20, 25, 16, 13, 22, 200])
print(f"均值: {data.mean():.2f}")
print(f"中位数: {np.median(data):.2f}")
print(f"标准差: {data.std():.2f}")
print(f"变异系数(CV): {data.std()/data.mean():.3f}")

2. 正态分布与中心极限定理

中心极限定理:不管原始分布是什么,样本均值的分布会趋近正态分布。这使我们能用正态分布做统计推断。

Python
# 验证中心极限定理
np.random.seed(42)
population = np.random.exponential(scale=3, size=100000)  # 指数分布(严重偏斜)

sample_means = []
for _ in range(1000):
    sample = np.random.choice(population, size=30)
    sample_means.append(sample.mean())

sample_means = np.array(sample_means)
print(f"样本均值分布: 均值={sample_means.mean():.3f}, 标准差={sample_means.std():.3f}")
print("原始总体均值:", population.mean())

3. t 检验与 p 值

t 检验用于比较两组均值是否有显著差异。p 值:在原假设成立时,观察到当前结果的概率。通常 p < 0.05 为统计显著。

Python
from scipy import stats

# 两组样本
group_a = np.array([85, 92, 78, 88, 95, 83, 90, 87])
group_b = np.array([72, 68, 75, 80, 71, 65, 78, 74])

t_stat, p_value = stats.ttest_ind(group_a, group_b)
print(f"t 统计量: {t_stat:.4f}")
print(f"p 值: {p_value:.6f}")
print(f"A组均值: {group_a.mean():.2f}, B组均值: {group_b.mean():.2f}")
if p_value < 0.05:
    print("结论: 两组有显著差异 (p<0.05)")
else:
    print("结论: 无统计学显著差异")

4. A/B 测试概念

A/B 测试本质就是假设检验:零假设 H₀:A=B;备择假设 H₁:A≠B(或 A>B)。收集数据 -> 计算 p 值 -> 做决策。

Python
# A/B 测试模拟
np.random.seed(42)
n_A, n_B = 500, 500
conv_A = np.random.binomial(1, 0.12, n_A)  # 转化率 12%
conv_B = np.random.binomial(1, 0.15, n_B)  # 转化率 15%

conv_rate_A = conv_A.mean()
conv_rate_B = conv_B.mean()
lift = (conv_rate_B - conv_rate_A) / conv_rate_A

print(f"A 组转化率: {conv_rate_A:.2%}")
print(f"B 组转化率: {conv_rate_B:.2%}")
print(f"相对提升: {lift:.2%}")
print(f"样本量: 每组 {n_A} 人")
💡详细解释
❓ p 值可以理解为"A/B 有效的概率"吗?

不能。p 值的正确理解是:假设两组实际上没有差异,观察到当前这么大的差异(甚至更大)的概率。p=0.03 的意思是:如果实际上两组没差异,只有 3% 的概率看到当前结果。误用 p 值是数据分析中最常见的错误之一。

🛠 什么时候用 t 检验 vs z 检验?

t 检验:样本量小(n<30)或总体标准差未知。z 检验:样本量大且总体标准差已知。实际数据分析中 t 检验更常用,因为总体标准差几乎总是未知的。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作(手动实现 t 检验)
练习题
Q1. p < 0.05 的含义是什么?
答案:B。p 值是在原假设成立的条件下,观察到当前结果(或更极端结果)的概率。p值越小,越有理由拒绝原假设。
Q2. 中心极限定理的核心结论是?
答案:B。中心极限定理说:无论原始数据分布如何(只要方差有限),随着样本量增大,样本均值的抽样分布趋近于正态分布。
Q3. 独立样本 t 检验的前提条件不包括以下哪项?
答案:C。t 检验不要求两组样本量相等,Welch t 检验连方差齐性都不要求,但要求数据独立且近似正态。
✍️实操题
10-1 补全代码:计算样本均值
sample_ = np.random.choice(population, size=30).mean()
sample_mean = np.random.choice(population, size=30).mean() # 从总体中随机抽取30个样本,计算均值 # 重复多次后,这些均值的分布就是抽样分布
10-2 补全代码:无偏方差(ddof 参数)
variance = a.(ddof=1)
variance = a.var(ddof=1) # ddof=1 表示用 n-1 做分母(无偏方差),是样本方差的正确计算方式 # ddof=0 是总体方差(除以 n)
🏆案例实训
📜 任务:营销活动 A/B 测试分析

某电商平台对首页改版进行了 A/B 测试,请对测试结果进行统计分析并给出决策建议。

要求:

  • 生成 A/B 两组用户的转化数据(每组 1000 人以上)
  • 计算两组的转化率、平均订单价值等核心指标
  • 用假设检验判断差异是否统计显著
  • 计算统计功效和所需样本量
  • 给出是否全量上线新版面的建议
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🤖
Chapter 11

机器学习:回归模型进阶

进阶3周
📖图文教程

1. 多元线性回归

从一元到多元:y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b。使用 正规方程 w = (XᵀX)⁻¹Xᵀy 求解。

Python
import numpy as np

# 创建特征矩阵 (n=100, 3个特征)
np.random.seed(42)
X = np.random.randn(100, 3)
true_w = np.array([3.5, -2.0, 1.8])
true_b = 5.0
y = X @ true_w + true_b + np.random.randn(100) * 0.5

# 正规方程
X_b = np.c_[np.ones((100, 1)), X]  # 加偏置列
w_opt = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y
print("真实系数:", np.concatenate([[true_b], true_w]))
print("估计系数:", w_opt.round(4))

2. 多项式特征与过拟合

用多项式扩展特征可以拟合非线性关系,但次数太高会导致过拟合——训练集表现极好,测试集表现很差。

Python
def poly_features(x, degree):
    return np.column_stack([x**i for i in range(degree+1)])

np.random.seed(42)
x = np.random.uniform(-3, 3, 30)
y_true = np.sin(x) + np.random.normal(0, 0.2, 30)

# 不同多项式次数
for deg in [1, 3, 15]:
    X_poly = poly_features(x, deg)
    w = np.linalg.inv(X_poly.T @ X_poly) @ X_poly.T @ y_true
    y_pred = X_poly @ w
    mse = ((y_true - y_pred)**2).mean()
    print(f"degree={deg:2d}, MSE={mse:.6f}, 参数数量={deg+1}")

3. 训练集 / 测试集划分

train_test_split 划分数据是防止过拟合的关键步骤。

Python
def train_test_split(X, y, test_size=0.2, seed=42):
    np.random.seed(seed)
    n = len(X)
    idx = np.random.permutation(n)
    split = int(n * (1 - test_size))
    train_idx, test_idx = idx[:split], idx[split:]
    return X[train_idx], X[test_idx], y[train_idx], y[test_idx]

X_train, X_test, y_train, y_test = train_test_split(X, y)
print(f"训练集: {len(X_train)} 样本, 测试集: {len(X_test)} 样本")

4. 正则化直觉

正则化通过对大的系数施加惩罚来防止过拟合。L2 正则化(岭回归)在损失函数中加入 λ * Σw²,迫使系数变小但不为零。

Python
# 岭回归(L2正则化)
def ridge_regression(X, y, lambda_=1.0):
    n_features = X.shape[1]
    # (XᵀX + λI)⁻¹Xᵀy
    w = np.linalg.inv(X.T @ X + lambda_ * np.eye(n_features)) @ X.T @ y
    return w

w_ridge = ridge_regression(X_poly, y_true, lambda_=10)
y_ridge = X_poly @ w_ridge
print(f"岭回归 MSE: {((y_true-y_ridge)**2).mean():.6f}")
print(f"系数均值大小: {np.abs(w_ridge).mean():.4f}")
💡详细解释
❓ 过拟合怎么诊断?

典型信号:训练误差不断下降,但测试误差开始上升。学习曲线(training vs validation error)是诊断利器。解决方法:更多数据、正则化、降低模型复杂度、早停(early stopping)。

🛠 为什么多项式次数高会导致过拟合?

高次多项式可以让曲线剧烈震荡来"穿过"每个训练点。就像画一条完美的曲线连接所有点,看起来很完美,但稍微来一个新点就完全不适用了。模型记住了噪声而非信号。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. 过拟合的典型表现是什么?
答案:B。过拟合是指模型过度学习了训练数据的噪声,导致训练误差很小但泛化能力差(测试误差很大)。
Q2. 正规方程 w = (XᵀX)⁻¹Xᵀy 的问题是什么?
答案:B。正规方程需要计算 (XᵀX)⁻¹,时间复杂度 O(n³),当特征数很大时非常慢。此时用梯度下降更合适。
Q3. 岭回归(L2正则化)对系数的影响是?
答案:B。L2正则化(岭回归)通过惩罚系数的平方和使所有系数收缩(变小),但不会像 L1(Lasso)那样把系数变成0。
✍️实操题
11-1 补全代码:添加偏置列
X_b = np.[np.ones((n, 1)), X]
X_b = np.c_[np.ones((n, 1)), X] # np.c_[] 按列拼接,在第一列前加一列全1,对应偏置项 b # 这样正规方程 w = (X_bᵀX_b)⁻¹X_bᵀy 的第一个系数就是 b
11-2 补全代码:计算 R² 分数
r2 = 1 - ((y_true - y_pred)**2).() / ((y_true - y_true.mean())**2).sum()
r2 = 1 - ((y_true - y_pred)**2).sum() / ((y_true - y_true.mean())**2).sum() # R² = 1 - SSE/SST # SSE: 残差平方和(模型没解释的部分) # SST: 总平方和(数据的总体变异)
🏆案例实训
📜 任务:房价预测模型构建

某房产中介希望根据房屋特征构建房价预测模型,请用回归分析方法从零实现。

要求:

  • 生成模拟房屋数据(150 套,含面积、卧室数、位置评分、交通便利度等特征)
  • 实现多元线性回归(正规方程法)
  • 实现 L2 正则化(岭回归)并比较效果
  • 用交叉验证评估模型稳定性
  • 分析哪些特征对房价影响最大
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🎯
Chapter 12

机器学习:分类与集成方法

进阶3周
📖图文教程

1. 分类问题概述

分类是监督学习的核心任务之一,目标是将样本划分到预定义的类别中。与回归预测连续值不同,分类输出的是离散标签。常见的分类算法包括逻辑回归、决策树、随机森林、KNN 等。评估分类器不能只看准确率,还需要关注混淆矩阵及其衍生指标:精确率、召回率、F1 分数。

Python
import numpy as np
# 混淆矩阵元素
y_true = np.array([1,0,1,1,0,1,0,0])
y_pred = np.array([1,0,1,0,0,1,1,0])
TP = ((y_pred==1) & (y_true==1)).sum()
FP = ((y_pred==1) & (y_true==0)).sum()
FN = ((y_pred==0) & (y_true==1)).sum()
TN = ((y_pred==0) & (y_true==0)).sum()
print(f"TP={TP} FP={FP} FN={FN} TN={TN}")
precision = TP/(TP+FP) if TP+FP else 0
recall = TP/(TP+FN) if TP+FN else 0
F1 = 2*precision*recall/(precision+recall) if precision+recall else 0
print(f"精确率={precision:.2%} 召回率={recall:.2%} F1={F1:.2%}")

2. 逻辑回归

逻辑回归虽然名字有"回归",实际上是分类算法。它通过 Sigmoid 函数将线性输出映射到 [0, 1] 概率区间,设定阈值(通常 0.5)进行分类决策。Sigmoid 函数公式为 s(z) = 1 / (1 + e^(-z)),当 z 趋近正无穷时输出接近 1,负无穷时接近 0。

Python
import numpy as np
def sigmoid(z): return 1/(1+np.exp(-z))
np.random.seed(42)
X = np.random.randn(20, 2)
# 模拟逻辑回归预测
z = 1.5*X[:,0] - 0.8*X[:,1] + 0.2
probs = sigmoid(z)
preds = (probs >= 0.5).astype(int)
print("预测概率前5个:", probs[:5].round(3))
print("预测类别前5个:", preds[:5])

3. 决策树与集成方法

决策树通过树状结构对特征进行递归划分,每个节点选择最优分裂特征。但单棵树容易过拟合随机森林通过 Bootstrap 采样构建多棵树并投票,大幅降低方差。集成方法的核心思想是"三个臭皮匠顶个诸葛亮"——组合多个弱学习器得到强学习器。Bagging(如随机森林)并行构建并平均,Boosting(如 AdaBoost)串行迭代修正前一轮错误。

Python
import numpy as np
# 模拟集成投票:3个弱分类器
np.random.seed(42)
n_samples = 10
true_labels = np.array([0,1,0,1,0,1,1,0,1,0])
# 3个分类器各自预测
clf1 = np.array([0,1,0,1,0,1,1,0,0,0])  # 90%准确
clf2 = np.array([0,0,0,1,0,1,1,1,1,0])  # 80%准确
clf3 = np.array([1,1,0,1,0,1,0,0,1,0])  # 70%准确
# 多数投票
all_votes = np.vstack([clf1, clf2, clf3])
ensemble_pred = np.array([np.bincount(all_votes[:, i]).argmax() for i in range(n_samples)])
acc_ensemble = (ensemble_pred == true_labels).mean()
print(f"集成投票准确率: {acc_ensemble:.2%}")
print(f"单个最高准确率: {(clf1==true_labels).mean():.2%}")
💡详细解释
❓ 为什么集成方法通常比单个模型更好?

单个模型可能有偏(高偏差)或不稳定(高方差)。Bagging 通过并行训练多个模型取平均来减小方差。Boosting 通过串行迭代逐步降低偏差。两者都能在泛化能力上超越单个模型。

🛠 精确率 vs 召回率?

精确率:预测为正例的样本中真正为正例的比例(宁缺毋滥)。召回率:所有正例中被正确找出的比例(宁可错杀一千,不放过一个)。F1 是两者的调和平均,综合衡量模型性能。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. Bagging(如随机森林)和 Boosting 的主要区别是什么?
答案:B。Bagging 对多个独立训练的模型取平均(并行),Boosting 依次训练模型,每个新模型重点关注前一轮的错误样本(串行)。
Q2. 在混淆矩阵中,精确率(Precision)的计算公式是?
答案:B。精确率 = TP/(TP+FP),衡量的是"预测为正例中有多少是真的正例"。召回率 = TP/(TP+FN),衡量的是"真正的正例中有多少被找出来了"。
Q3. 决策树的主要缺点是什么?
答案:C。决策树如果不加限制(如最大深度、叶节点最小样本数),容易完美拟合训练数据导致过拟合。随机森林通过集成多棵树来缓解这一问题。
✍️实操题
12-1 补全代码:欧氏距离计算
def euclidean_distance(a, b): return np.sqrt(np.((a - b) ** 2))
def euclidean_distance(a, b): return np.sqrt(np.sum((a - b) ** 2))
12-2 补全代码:精确率的分母
precision = TP / (TP + ) if (TP + ) else 0
precision = TP / (TP + FP) if (TP + FP) else 0 recall = TP / (TP + FN) if (TP + FN) else 0 F1 = 2 * precision * recall / (precision + recall) if (precision + recall) else 0
🏆案例实训
📜 任务:客户流失预测分类器

某电信公司希望预测哪些客户可能流失,请构建分类模型并评估性能。

要求:

  • 生成客户特征数据(300 条,含月费、时长、投诉次数、合约期等)
  • 实现 kNN 分类器(从零实现)
  • 实现逻辑回归分类器(从零实现,含 Sigmoid 函数)
  • 比较两种模型的准确率、精确率和召回率
  • 输出混淆矩阵分析结果
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
📅
Chapter 13

时间序列分析与预测

高级3周
📖图文教程

1. 时间序列的构成

时间序列数据是按时间顺序排列的观测值序列。它由三个核心成分组成:趋势(长期上升或下降方向)、季节性(固定周期波动,如每年夏季用电高峰)和残差(随机噪声,不规则波动)。分解时间序列有助于理解数据的内在结构。

Python
import numpy as np
# 构造含趋势和季节性的时间序列
np.random.seed(42)
t = np.arange(60)
trend = 0.1 * t                 # 上升趋势
seasonal = 5 * np.sin(2*np.pi*t/12)  # 12期季节性
noise = np.random.normal(0, 1, 60)  # 随机噪声
ts = trend + seasonal + noise
print("时间序列前12个值:", ts[:12].round(2))
print(f"趋势分量范围: [{trend.min():.2f}, {trend.max():.2f}]")

2. 移动平均与指数平滑

移动平均通过取固定窗口内的均值来平滑短期波动,揭示长期趋势。窗口越大,平滑效果越强,但对变化反应越迟钝。指数平滑对近期的观测值赋予更高权重,权重呈指数衰减,比简单移动平均更能捕捉最新变化。

Python
import numpy as np
np.random.seed(42)
sales = np.cumsum(np.random.randn(24)*5) + 100
# 3期移动平均
window = 3
weights = np.ones(window) / window
ma_3 = np.convolve(sales, weights, mode='valid')
# 简单指数平滑
alpha = 0.3
ewma = np.zeros_like(sales)
ewma[0] = sales[0]
for i in range(1, len(sales)):
    ewma[i] = alpha*sales[i] + (1-alpha)*ewma[i-1]
print("原始vs平滑(后10个):")
for i in range(-10, 0):
    print(f"  {i+25:2d}: {sales[i]:8.2f} | 平滑:{ewma[i]:.2f}")

3. 平稳性与自相关

平稳性是时间序列建模的重要前提:一个平稳序列的均值、方差在不同时间点保持恒定。自相关衡量序列与其滞后版本的相关程度——如果今天的值与昨天的值高度相关,则说明存在滞后1的自相关。自相关函数(ACF)可以辅助识别时间序列的模式和周期。

Python
import numpy as np
np.random.seed(42)
ts = np.cumsum(np.random.randn(100))  # 非平稳(随机游走)
# 一阶差分使其平稳
ts_diff = np.diff(ts)
# 自相关计算(滞后1)
lag = 1
y = ts_diff[lag:]
y_lag = ts_diff[:-lag]
corr = np.corrcoef(y, y_lag)[0, 1]
print(f"差分后自相关(滞后1): {corr:.4f}")
print(f"差分后均值: {ts_diff.mean():.4f}(接近0表示平稳)")
💡详细解释
❓ 为什么时间序列预测不能用普通的交叉验证?

时间序列有时间顺序依赖性,不能随机打乱数据。不能使用未来数据预测过去(数据泄露)。应使用 时间序列交叉验证:训练集始终在测试集之前,逐步向前扩展。

🛠 如何选择移动平均的窗口大小?

窗口越小,对变化越敏感但噪声也越多。窗口越大,曲线越平滑但会有滞后。一般原则:窗口 = 季节周期的长度(如月度数据用12)。也可用多个窗口对比,选择预测误差最小的。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. 时间序列的三个核心组成成分是什么?
答案:B。时间序列的三个核心成分为趋势(长期方向)、季节性(固定周期波动)和残差(不规则噪声)。
Q2. 什么是时间序列的平稳性?
答案:B。平稳序列的统计性质(均值、方差、自相关)不随时间变化,是许多时间序列建模方法(如ARIMA)的前提条件。
Q3. 移动平均窗口越大,会带来什么效果?
答案:B。更大的窗口能更好地消除噪声,但也会使平滑后的序列对真实变化反应更迟钝(滞后效应),需要在平滑度和响应速度之间权衡。
✍️实操题
13-1 补全代码:移动平均权重
window = 3 weights = np.ones(window) / ma = np.convolve(sales, weights, mode='valid')
window = 3 weights = np.ones(window) / window ma = np.convolve(sales, weights, mode='valid')
13-2 补全代码:均方误差(MSE)
mse = ((actual - forecast) ** 2).()
mse = ((actual - forecast) ** 2).mean() rmse = np.sqrt(mse) mae = np.abs(actual - forecast).mean()
🏆案例实训
📜 任务:股票价格时间序列分析

某投资团队希望分析股票价格的趋势和季节性模式,请用时间序列分析方法进行探索。

要求:

  • 生成模拟股票价格数据(180 天,含趋势和季节性成分)
  • 计算移动平均线(MA5, MA20, MA60)并对比
  • 分析序列的自相关性
  • 检测序列的平稳性
  • 预测未来 7 天的价格走势
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
📝
Chapter 14

文本数据分析入门

高级3周
📖图文教程

1. 文本预处理

原始文本数据包含大量噪声,必须先进行预处理。分词将句子拆分成单词/词语。去除停用词过滤掉"的""了""在"等高频无意义词。词干提取将单词还原为词根形式(如"running"→"run")。预处理质量直接影响后续分析效果。

Python
import numpy as np
text = "数据科学是一门非常有趣和有用的学科"
# 简单分词
words = text.split()
print("分词结果:", words)
# 去除停用词
stop_words = {'的', '和', '非常'}
filtered = [w for w in words if w not in stop_words]
print("去停用词后:", filtered)
print(f"词汇数量: {len(filtered)}")

2. 词袋模型与 TF-IDF

词袋模型(Bag of Words)将文本转换为词频向量,每个文档用一个向量表示,维度为词汇表大小。但词袋只统计频次,未考虑词的重要性。TF-IDF 则通过词频(TF)乘以逆文档频率(IDF)来降低常见词权重、提升罕见词权重,更准确地反映词在文档中的重要性。

Python
import numpy as np
docs = ["我喜欢数据科学", "数据科学很有趣", "我喜欢编程"]
# 构建词袋
all_words = sorted(set(' '.join(docs).split()))
vocab = {w:i for i,w in enumerate(all_words)}
bow = np.zeros((len(docs), len(vocab)), dtype=int)
for i, doc in enumerate(docs):
    for w in doc.split():
        bow[i, vocab[w]] += 1
print("词袋矩阵:\n", bow)
print("词汇表:", list(vocab.keys()))

3. 情感分析与词频分析

情感分析判断文本的情感倾向(正向/负向)。简单方法:准备情感词典,统计文本中正向和负向词的数量。词频分析统计每个词在文本集中出现的频次,常用词云可视化展示。词频分析能快速发现文本的核心主题。

Python
import numpy as np
# 简单情感词典分析
positive_words = {'好','喜欢','优秀','棒','有趣','有用'}
negative_words = {'差','讨厌','糟糕','无聊','没用'}
reviews = ["这个产品非常好 我很喜欢", "质量很差 非常糟糕", "很有趣也很有用 很棒"]
for i, review in enumerate(reviews):
    words = review.split()
    pos = sum(1 for w in words if w in positive_words)
    neg = sum(1 for w in words if w in negative_words)
    sentiment = "正向" if pos > neg else ("负向" if neg > pos else "中立")
    print(f"评论{i+1}: 正向词={pos} 负向词={neg} 情感={sentiment}")
💡详细解释
❓ 为什么不能直接用原始文本做机器学习?

机器学习模型只能处理数值型数据。需要将文本转换为数值特征向量,同时保留语义信息。词袋和 TF-IDF 是最基础的文本向量化方法,更高级的还有 Word2Vec、BERT 等词嵌入技术。

🛠 TF-IDF 为什么比词袋更好?

词袋中"的""是"等高频词频次最高但信息量最低。TF-IDF 通过 IDF = log(总文档数/包含该词的文档数) 降低常见词权重,使"机器学习""数据分析"等有意义的词获得更高权重。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. TF-IDF 中 IDF 的作用是什么?
答案:B。IDF = log(总文档数 / 包含该词的文档数),在大量文档中都出现的词(如"的")IDF 低,只在少数文档中出现的词 IDF 高。
Q2. 词袋模型(Bag of Words)的主要局限性是什么?
答案:B。词袋模型只统计词频,完全丢失了词的顺序和上下文信息。"猫追狗"和"狗追猫"在词袋中表示完全相同的向量。
Q3. 文本预处理中"去除停用词"的目的是什么?
答案:B。像"的""了""在"这类停用词在所有文档中出现频率都很高,但对区分文档主题几乎没有贡献,去除后可以降低维度、提升模型效果。
✍️实操题
14-1 补全代码:去除停用词
filtered = [w for w in words if w not in ]
filtered = [w for w in words if w not in stop_words]
14-2 补全代码:词频统计
word_counts[w] = word_counts.get(w, ) +
word_counts[w] = word_counts.get(w, 0) + 1 sorted_words = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)
🏆案例实训
📜 任务:电商评论情感分析

某电商平台收集了大量用户评论,请用文本分析方法对评论进行情感分析。

要求:

  • 生成 20 条用户评论数据(含正面、负面、中性评论)
  • 构建自定义情感词典(正面词和负面词列表)
  • 对每条评论进行分词并计算情感得分
  • 输出每条评论的情感分类结果
  • 统计正面/负面/中性的比例分布
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
🏆
Chapter 15

综合实战项目:端到端数据分析

高级4周
📖图文教程

1. 数据分析完整工作流

一个完整的数据分析项目通常包含以下步骤:问题定义(明确业务目标)→ 数据收集(获取原始数据)→ 数据清洗(处理缺失值、异常值、重复值)→ 探索性数据分析 EDA(统计描述、可视化探索)→ 特征工程(编码、缩放、创建新特征)→ 建模(选择合适的算法)→ 评估(用指标衡量模型性能)→ 结论(业务建议)。每一步都相互影响,共同决定最终结果的质量。

Python
import numpy as np
import pandas as pd
# 工作流概览
steps = ["问题定义", "数据收集", "数据清洗", "EDA探索",
         "特征工程", "建模", "评估", "结论"]
for i, step in enumerate(steps, 1):
    print(f"Step {i}: {step}")

2. 数据清洗与探索

真实数据很少是干净的。数据清洗包括处理缺失值(填充或删除)、异常值(IQR 或 Z-score 方法)和重复值。EDA 通过描述性统计(均值、中位数、标准差)和相关分析来理解数据的基本特征,发现变量之间的关系。

Python
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({'年龄': np.random.randint(18,65,100),
    '收入': np.random.normal(8000,3000,100).round(0),
    '消费金额': np.random.normal(500,200,100).round(0)})
df.loc[0:4, '收入'] = np.nan
print("缺失值:\n", df.isnull().sum())
df['收入'] = df['收入'].fillna(df['收入'].median())
print("描述统计:\n", df.describe())
print("相关系数:\n", df.corr().round(3))

3. 建模与评估

根据问题类型选择模型(回归用线性回归,分类用逻辑回归/决策树等)。将数据分为训练集测试集(常用 80/20 比例),在训练集上训练模型,在测试集上评估泛化能力。评估指标因任务而异:回归看 MSE/RMSE/R²,分类看准确率/精确率/召回率/F1。最后根据分析结果给出业务建议。

Python
import numpy as np
# 模拟训练/测试划分与评估
np.random.seed(42)
X = np.random.rand(100, 3)
y = 3*X[:,0] + 1.5*X[:,1] - 2*X[:,2] + np.random.normal(0, 0.2, 100)
split = 80
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
# 线性回归(正规方程)
X_train_b = np.c_[np.ones(split), X_train]
theta = np.linalg.inv(X_train_b.T @ X_train_b) @ X_train_b.T @ y_train
X_test_b = np.c_[np.ones(20), X_test]
y_pred = X_test_b @ theta
mse = ((y_test - y_pred)**2).mean()
r2 = 1 - ((y_test - y_pred)**2).sum() / ((y_test - y_test.mean())**2).sum()
print(f"MSE: {mse:.4f}, R²: {r2:.4f}")
💡详细解释
❓ 数据分析项目中时间分配建议?

典型分配:问题定义 10%(明确目标才能做对方向),数据清洗 30%(最耗时但最关键),EDA 20%(发现模式和问题),特征工程 20%(好的特征比好的模型更重要),建模与评估 15%结论 5%(用数据讲故事)。

🛠 过拟合如何预防?

简化模型(减少特征),正则化(L1/L2惩罚),交叉验证(稳定评估),增加数据量(最直接有效)。始终用测试集上的表现而非训练集来判断模型好坏。

💻动手练习
💻 Python 编辑器
⚡ 轻量模拟器:支持 numpy / pandas / print / 变量 / 循环 / 列表操作
练习题
Q1. 在端到端数据分析流程中,EDA(探索性数据分析)的主要目的是?
答案:B。EDA 通过描述性统计、可视化等手段理解数据特征,发现变量之间的关系、异常值和数据质量问题,指导后续的特征工程和建模方向。
Q2. 数据清洗通常在数据分析流程的哪个阶段进行?
答案:B。数据清洗在数据收集之后立即进行,确保数据质量后再进入 EDA 和建模阶段。糟糕的清洗会导致"垃圾进,垃圾出"。
Q3. 以下哪个指标最适合评估客户流失预测中的"不漏掉可能流失的客户"?
答案:B。召回率衡量在所有真正的正例中模型成功识别出的比例。在客户流失场景中,"不漏掉"意味着高召回率——尽可能找出所有可能流失的客户,即使误报一些也没关系。
✍️实操题
15-1 补全代码:缺失值填充
df['月消费额'] = df['月消费额'].fillna(df['月消费额'].())
df['月消费额'] = df['月消费额'].fillna(df['月消费额'].median()) df['满意度'] = df['满意度'].fillna(df['满意度'].median())
15-2 补全代码:精确率分母
precision = TP / (TP + ) if (TP + ) else 0
precision = TP / (TP + FP) if (TP + FP) else 0 recall = TP / (TP + FN) if (TP + FN) else 0 f1 = 2 * precision * recall / (precision + recall) if (precision + recall) else 0
🏆案例实训
📜 任务:综合数据分析实战项目

某公司需要一份完整的数据分析报告,请综合运用所学知识完成从数据生成到结论输出的全流程分析。

要求:

  • 生成用户全生命周期数据(500 条,含注册、活跃、消费、留存等维度)
  • 进行数据清洗和探索性分析
  • 构建用户分层模型(RFM 或评分卡)
  • 预测用户留存概率
  • 输出完整数据分析报告和建议
💻 编写你的代码
⚡ 提示:使用真实 Python 语法,支持 numpy/pandas 等外部库
swer(this)">显示答案