数据分析技术训练营
从零基础到实战 · 6大模块 · 视频+教程+代码+练习
数据清洗与预处理
1. 认识脏数据
在真实世界中,数据几乎从来不是完美的。打开一个 CSV 文件,你可能会发现缺失值(某些格子是空的)、重复行、异常值(比如年龄=200)、格式错误。使用 Pandas 的 df.info() 和 df.describe() 可以快速了解数据全貌。
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. 处理缺失值
缺失值处理有三种策略:删除、填充(均值/中位数/众数)、插值。
# 删除缺失行 df_clean = df.dropna() # 中位数填充(对异常值更鲁棒) df_filled = df.fillna({'年龄': df['年龄'].median()}) print("填充后:", df_filled)
3. 重复值与异常值
使用 df.duplicated() 检测重复行。使用 IQR 方法检测异常值。
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% 时最简单。均值:适合正态分布。中位数:有异常值时更鲁棒。众数:适合分类数据。
某电商平台提供了 100 条用户注册数据,包含姓名、年龄、邮箱、注册日期、消费金额等字段,数据中存在缺失值、重复值、格式错误等问题。
要求:
- 生成模拟原始数据(包含 100 条记录,至少含 5 个字段)
- 在数据中人为引入缺失值、重复记录和异常值
- 检测并统计缺失值的数量和比例
- 删除重复记录(保留第一条)
- 对异常值进行处理(如年龄超出合理范围、金额为负数等)
- 输出清洗前后的数据对比报告
特征工程实战
1. 标准化与归一化
标准化将数据转为均值0、标准差1。归一化缩放到 [0, 1]。特征量纲差异大时必须先做标准化。
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. 编码技术
标签编码将分类值映射为整数。独热编码为每个类别创建二进制列。
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. 特征创建
从现有特征创造新特征:数学组合、日期拆分、文本提取。
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)
"特征决定上限,模型只是逼近上限"。好的特征能让简单模型超越复杂模型。
标准化适合 SVM、逻辑回归。归一化适合 KNN、图像处理。有异常值时优先用标准化。
某房产网站收集了房屋数据,请对原始特征进行特征工程处理,构造有用特征并分析特征重要性。
要求:
- 生成模拟房产数据(200 条,含面积、卧室数、楼层、房龄、距离地铁站距离等字段)
- 构造组合特征(如均价 = 总价 / 面积)
- 对分类特征进行编码转换
- 对数值特征进行标准化/归一化处理
- 计算各特征与房价的相关性,排序展示
数据建模与分析
1. 线性回归
用 y = wx + b 拟合数据,最小化均方误差(MSE)。
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] 概率值,用于二分类。
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。
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 份轮流测试,取平均值,结果更稳定可靠。
过拟合:训练好但测试差(死记硬背)。欠拟合:训练和测试都差(太简单)。
某连锁超市收集了各个分店的销售数据,请构建数据分析模型,分析不同因素对销售额的影响。
要求:
- 生成模拟销售数据(100 家门店,含客流量、客单价、折扣率、员工数、门店面积等)
- 计算各门店的销售额 = 客流量 * 客单价
- 分析不同因素与销售额的关系(散点图 + 相关系数)
- 构建多元线性回归模型(从零实现)
- 输出模型系数和 R² 评估指标
SQL 高级查询技巧
1. SQL 基础查询
SELECT 选择列,FROM 指定表,WHERE 过滤条件。
SELECT name, salary FROM employees WHERE salary > 5000 AND department = '技术部' ORDER BY salary DESC LIMIT 10;
2. 聚合与分组
GROUP BY 分组,HAVING 过滤组。
SELECT department, COUNT(*) AS 人数, AVG(salary) AS 平均工资 FROM employees GROUP BY department HAVING AVG(salary) > 5000;
3. 窗口函数
不折叠行,为每行计算额外值。
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 分组前过滤行。HAVING 分组后过滤组。执行顺序:FROM->WHERE->GROUP BY->HAVING->SELECT。
ROW_NUMBER:连续 1,2,3,4。RANK:并列跳号 1,2,2,4。DENSE_RANK:并列不跳 1,2,2,3。
某电商平台需要分析用户订单数据,请使用 Python 模拟 SQL 操作完成多表关联查询与分析。
要求:
- 创建用户表、订单表、商品表、类目表(各 50-200 条记录)
- 用字典/列表模拟 SQL 表连接操作
- 完成分组统计(按类目统计销售额、按用户统计订单数)
- 找出销售额 TOP 5 的商品
- 计算各月的销售趋势
Python 数据分析库
1. NumPy 基础
ndarray 数组,向量化运算比循环快 10-100 倍。
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 数据操作
核心操作:筛选、分组、聚合、合并。
import pandas as pd df = pd.DataFrame({'部门':['技术','市场','技术'],'工资':[8000,6000,12000]}) print(df[df['工资'] > 7000]) print(df.groupby('部门').agg(平均工资=('工资','mean')))
3. 文本可视化
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 底层用 C 语言实现,利用连续内存和 SIMD 指令集,向量化运算避免了 Python 循环开销。
df.loc[] 基于标签索引。df.iloc[] 基于位置索引。记忆:loc=label,iloc=integer location。
某 APP 记录了用户的每日行为数据,请使用 pandas 风格的列表操作进行数据分析。
要求:
- 生成 30 天 50 个用户的每日行为数据(登录次数、停留时长、操作次数等)
- 计算每个用户的日均指标
- 找出高活跃用户(登录 > 15 天)和低活跃用户(登录 < 5 天)
- 比较两组用户的行为差异
- 给出用户分层运营建议
数据挖掘算法应用
1. K-Means 聚类
把数据分成 K 组,使组内相似、组间不同。步骤:选中心->分配->更新->重复。
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 降维
把高维数据压缩到低维,保留主要信息。
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. 时间序列分析
移动平均、指数平滑,用于趋势预测。
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 值的簇内误差平方和(SSE),画图找"拐点"。拐点对应的 K 就是最优值。
会。方差解释率告诉我们保留了多少信息。通常保留累计解释率 > 85% 的主成分即可。
某零售公司要求制作一份销售数据可视化分析报告,用文本图表展示各维度的销售表现。
要求:
- 生成月度销售数据(12 个月,含销售额、利润、成本等指标)
- 用文本柱状图展示月度销售额趋势
- 按产品类别对比销售占比
- 展示各区域销售排名
- 输出分析结论与建议
某电商平台希望通过用户消费数据对用户进行聚类分析,以便制定差异化的营销策略。
要求:
- 生成 200 名用户的消费数据(年消费额、购买频次、客单价、会员时长等)
- 对数据进行标准化处理
- 使用 K-Means 算法从零实现聚类(K=3 或 4)
- 分析每个聚类群体的特征差异
- 为每个群体设计针对性的营销策略
高级数据可视化与 Seaborn
1. Seaborn 主题与样式
Seaborn 基于 Matplotlib,提供更美观的默认主题。使用 sns.set_theme() 设置样式,sns.set_palette() 设置配色方案。
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() 结合箱线图与核密度。
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() 用颜色展示相关系数矩阵。
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() 是分类图的统一接口。
# 创建分类数据 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 是对 Matplotlib 的高级封装,优点是代码简洁、默认好看、自带数据集和统计功能。Matplotlib 更底层,自由度更高。实践中建议 Seaborn 做探索性分析,Matplotlib 做定制化图表。
颜色越深表示相关性越强。红色正相关(同增同减),蓝色负相关(此消彼长)。数字越接近 1 或 -1 相关性越强,接近 0 表示几乎无关。对角线永远是 1(自己和自己的关系)。
某工厂需要对新生产的一批产品进行质量检验,请用统计方法分析样本数据并给出检验结论。
要求:
- 生成产品样本数据(200 个产品,含重量、尺寸、硬度等指标)
- 计算样本的均值、标准差、中位数等统计量
- 绘制分布频次文本图(直方图)
- 与标准规格进行对比(假设检验)
- 给出产品合格率分析报告
探索性数据分析 (EDA) 实战
1. 数据概览:info() 与 describe()
df.info() 查看列类型和非空计数。df.describe() 统计数值列分布。这是任何 EDA 的第一步。
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() 检查偏度和峰度。
# 分类变量 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() 做交叉表。
# 相关系数 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 方法系统检测。
# 缺失值热力图(列维度) 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 不是一次性的,而是一个循环迭代的过程。
偏度=0:对称分布。偏度>0:右偏(长尾在右边,均值>中位数)。偏度<0:左偏(长尾在左边,均值<中位数)。|偏度|>1 表明严重偏斜,可能需要做变换(如 log 变换)。
某电商平台收集了不同客户群体的满意度调查数据,请进行探索性分析并得出结论。
要求:
- 生成模拟调查数据(200 名客户,含年龄段、会员等级、满意度评分等字段)
- 按年龄段分组统计平均满意度,用文本柱状图展示
- 比较不同会员等级(普通/银卡/金卡/钻石)的满意度差异
- 计算各特征与满意度的相关系数,找出最重要的影响因素
- 给出 3 条业务改进建议
统计分析与假设检验
1. 描述性统计
描述性统计分两类:集中趋势(均值、中位数、众数)和离散程度(方差、标准差、IQR)。均值受异常值影响大,中位数更鲁棒。
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. 正态分布与中心极限定理
中心极限定理:不管原始分布是什么,样本均值的分布会趋近正态分布。这使我们能用正态分布做统计推断。
# 验证中心极限定理 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 为统计显著。
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 值 -> 做决策。
# 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 值的正确理解是:假设两组实际上没有差异,观察到当前这么大的差异(甚至更大)的概率。p=0.03 的意思是:如果实际上两组没差异,只有 3% 的概率看到当前结果。误用 p 值是数据分析中最常见的错误之一。
t 检验:样本量小(n<30)或总体标准差未知。z 检验:样本量大且总体标准差已知。实际数据分析中 t 检验更常用,因为总体标准差几乎总是未知的。
某电商平台对首页改版进行了 A/B 测试,请对测试结果进行统计分析并给出决策建议。
要求:
- 生成 A/B 两组用户的转化数据(每组 1000 人以上)
- 计算两组的转化率、平均订单价值等核心指标
- 用假设检验判断差异是否统计显著
- 计算统计功效和所需样本量
- 给出是否全量上线新版面的建议
机器学习:回归模型进阶
1. 多元线性回归
从一元到多元:y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b。使用 正规方程 w = (XᵀX)⁻¹Xᵀy 求解。
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. 多项式特征与过拟合
用多项式扩展特征可以拟合非线性关系,但次数太高会导致过拟合——训练集表现极好,测试集表现很差。
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 划分数据是防止过拟合的关键步骤。
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²,迫使系数变小但不为零。
# 岭回归(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)。
高次多项式可以让曲线剧烈震荡来"穿过"每个训练点。就像画一条完美的曲线连接所有点,看起来很完美,但稍微来一个新点就完全不适用了。模型记住了噪声而非信号。
某房产中介希望根据房屋特征构建房价预测模型,请用回归分析方法从零实现。
要求:
- 生成模拟房屋数据(150 套,含面积、卧室数、位置评分、交通便利度等特征)
- 实现多元线性回归(正规方程法)
- 实现 L2 正则化(岭回归)并比较效果
- 用交叉验证评估模型稳定性
- 分析哪些特征对房价影响最大
机器学习:分类与集成方法
1. 分类问题概述
分类是监督学习的核心任务之一,目标是将样本划分到预定义的类别中。与回归预测连续值不同,分类输出的是离散标签。常见的分类算法包括逻辑回归、决策树、随机森林、KNN 等。评估分类器不能只看准确率,还需要关注混淆矩阵及其衍生指标:精确率、召回率、F1 分数。
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。
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)串行迭代修正前一轮错误。
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 通过串行迭代逐步降低偏差。两者都能在泛化能力上超越单个模型。
精确率:预测为正例的样本中真正为正例的比例(宁缺毋滥)。召回率:所有正例中被正确找出的比例(宁可错杀一千,不放过一个)。F1 是两者的调和平均,综合衡量模型性能。
某电信公司希望预测哪些客户可能流失,请构建分类模型并评估性能。
要求:
- 生成客户特征数据(300 条,含月费、时长、投诉次数、合约期等)
- 实现 kNN 分类器(从零实现)
- 实现逻辑回归分类器(从零实现,含 Sigmoid 函数)
- 比较两种模型的准确率、精确率和召回率
- 输出混淆矩阵分析结果
时间序列分析与预测
1. 时间序列的构成
时间序列数据是按时间顺序排列的观测值序列。它由三个核心成分组成:趋势(长期上升或下降方向)、季节性(固定周期波动,如每年夏季用电高峰)和残差(随机噪声,不规则波动)。分解时间序列有助于理解数据的内在结构。
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. 移动平均与指数平滑
移动平均通过取固定窗口内的均值来平滑短期波动,揭示长期趋势。窗口越大,平滑效果越强,但对变化反应越迟钝。指数平滑对近期的观测值赋予更高权重,权重呈指数衰减,比简单移动平均更能捕捉最新变化。
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)可以辅助识别时间序列的模式和周期。
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)。也可用多个窗口对比,选择预测误差最小的。
某投资团队希望分析股票价格的趋势和季节性模式,请用时间序列分析方法进行探索。
要求:
- 生成模拟股票价格数据(180 天,含趋势和季节性成分)
- 计算移动平均线(MA5, MA20, MA60)并对比
- 分析序列的自相关性
- 检测序列的平稳性
- 预测未来 7 天的价格走势
文本数据分析入门
1. 文本预处理
原始文本数据包含大量噪声,必须先进行预处理。分词将句子拆分成单词/词语。去除停用词过滤掉"的""了""在"等高频无意义词。词干提取将单词还原为词根形式(如"running"→"run")。预处理质量直接影响后续分析效果。
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)来降低常见词权重、提升罕见词权重,更准确地反映词在文档中的重要性。
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. 情感分析与词频分析
情感分析判断文本的情感倾向(正向/负向)。简单方法:准备情感词典,统计文本中正向和负向词的数量。词频分析统计每个词在文本集中出现的频次,常用词云可视化展示。词频分析能快速发现文本的核心主题。
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 通过 IDF = log(总文档数/包含该词的文档数) 降低常见词权重,使"机器学习""数据分析"等有意义的词获得更高权重。
某电商平台收集了大量用户评论,请用文本分析方法对评论进行情感分析。
要求:
- 生成 20 条用户评论数据(含正面、负面、中性评论)
- 构建自定义情感词典(正面词和负面词列表)
- 对每条评论进行分词并计算情感得分
- 输出每条评论的情感分类结果
- 统计正面/负面/中性的比例分布
综合实战项目:端到端数据分析
1. 数据分析完整工作流
一个完整的数据分析项目通常包含以下步骤:问题定义(明确业务目标)→ 数据收集(获取原始数据)→ 数据清洗(处理缺失值、异常值、重复值)→ 探索性数据分析 EDA(统计描述、可视化探索)→ 特征工程(编码、缩放、创建新特征)→ 建模(选择合适的算法)→ 评估(用指标衡量模型性能)→ 结论(业务建议)。每一步都相互影响,共同决定最终结果的质量。
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 通过描述性统计(均值、中位数、标准差)和相关分析来理解数据的基本特征,发现变量之间的关系。
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。最后根据分析结果给出业务建议。
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惩罚),交叉验证(稳定评估),增加数据量(最直接有效)。始终用测试集上的表现而非训练集来判断模型好坏。
某公司需要一份完整的数据分析报告,请综合运用所学知识完成从数据生成到结论输出的全流程分析。
要求:
- 生成用户全生命周期数据(500 条,含注册、活跃、消费、留存等维度)
- 进行数据清洗和探索性分析
- 构建用户分层模型(RFM 或评分卡)
- 预测用户留存概率
- 输出完整数据分析报告和建议