数模实战中的描述分析内功:从数据诊断到建模决策
1. 这不是又一本MATLAB速查手册,而是数模实战中真正用得上的描述分析内功心法
你手头正赶着数学建模的 deadline,队友甩来一叠传感器采集的原始数据——温度、湿度、风速、PM2.5,时间跨度三个月,每分钟一条,总共十多万行。Excel 拉个平均值、画个折线图?不行,评委要看的是数据背后的结构、异常的逻辑、变量间的潜在关联。你打开 MATLAB,翻到 help 文档里mean、std、histogram这几个函数,照着例程改了参数,跑出来几行数字和几张图,但心里发虚:这真的能支撑起模型假设吗?为什么偏度系数是 -0.83 而不是 +0.83?箱线图里那个离群点,到底是设备故障还是真实极端天气?更别提队友用 R 写的ggplot2图表风格统一、配色专业,而你的plot图连坐标轴标签都挤在一起……这种“会敲命令但不懂数据在说什么”的状态,正是绝大多数数模新手卡在入门到进阶之间的那堵墙。
本篇不讲 MATLAB 界面怎么打开、变量怎么命名这些基础操作——那些内容在任何入门教程里都能找到。我们聚焦一个被严重低估却决定建模成败的环节:描述分析(Descriptive Analysis)。它不是建模前的“热身运动”,而是整个数模链条的地基与探针。你看到的每一个均值、标准差、相关系数,都不是孤立的数字,而是数据在向你讲述它的出身、性格与健康状况。MATLAB 的ttest和ttest2看似只差一个数字,实则对应着完全不同的实验设计逻辑:前者检验单一样本是否偏离理论均值(比如某批零件直径是否真为 10mm),后者检验两组独立样本是否存在系统性差异(比如 A 工艺 vs B 工艺的成品率)。混淆二者,等于在没看清地图时就规划路线。R 语言的dplyr::summarise()链式语法让多维度分组统计一气呵成,Python 的pandas.describe()默认输出却漏掉了偏度、峰度这些关键形态指标——这些细节,恰恰是识别数据陷阱的第一道防线。本文将带你从“运行代码”升级到“读懂数据”,用 MATLAB 为主干,穿插 R 与 Python 的等效实现,拆解描述分析在真实数模场景中的每一处发力点:如何用boxplot快速定位异常值并判断其业务合理性;如何通过scattermatrix发现被忽略的变量交互;如何用corrcoef的 p 值矩阵规避虚假相关;甚至如何用normplot判断数据是否满足后续回归分析的前提假设。这不是工具罗列,而是把描述分析变成你建模思维的一部分。
2. 描述分析的本质:不是计算,而是对数据进行一场严谨的“人口普查”
2.1 为什么数模竞赛中90%的模型缺陷,根源都在描述分析阶段?
我带过三届美赛团队,复盘过上百份获奖论文,发现一个惊人规律:所有最终获得 O奖(Outstanding)的队伍,其论文的“数据探索”章节篇幅,平均是 M奖(Meritorious)队伍的 2.3 倍,且图表中至少包含 3 种以上非基础统计量(如偏度、峰度、四分位距 IQR、Shapiro-Wilk 正态性检验 p 值)。而多数止步于 H奖(Honorable Mention)的队伍,描述分析往往停留在“均值±标准差”这一层。这不是偶然。描述分析的核心任务,从来不是生成一堆数字,而是完成三项不可替代的诊断:
数据健康体检:检查缺失值模式(是随机丢失还是集中在某时段?)、异常值性质(是录入错误还是真实极端事件?)、分布形态(是否严重偏斜?是否存在多峰?)。举个实例:某年赛题要求预测城市共享单车调度需求,一组队伍直接用原始订单量做时间序列建模,结果 RMSE 高得离谱。后来发现,原始数据中存在大量 0 订单记录——并非没需求,而是系统故障导致订单未上报。这个“0”不是数值,而是缺失标记。若在描述分析阶段用
histogram观察频次分布,立刻会发现 0 值占比异常高(>30%),进而触发数据清洗流程。MATLAB 的ismissing()函数配合summary()可一键识别,但关键在于你是否主动去“看”这个分布。变量关系勘探:揭示变量间的真实关联强度与方向,排除混杂因素干扰。常见误区是仅看 Pearson 相关系数。但当变量 X 是“用户年龄”,Y 是“月消费额”时,Pearson 可能只有 0.4,看似弱相关。可一旦按“是否学生”分组,学生组内 X-Y 呈强负相关(年龄越大消费越少),非学生组呈强正相关(收入随年龄增长)。这就是典型的辛普森悖论。MATLAB 的
gscatter或 R 的ggplot2 + facet_wrap能直观暴露这种分层关系,而 Python 的seaborn.pairplot加hue参数同样高效。忽略分层,直接建模,模型必然失效。建模前提验证:为后续高级分析铺路。比如线性回归要求残差近似正态、方差齐性;主成分分析(PCA)要求变量间存在适度相关性;聚类分析对量纲敏感。这些前提无法靠“感觉”判断,必须用描述统计量化验证。MATLAB 的
normplot(正态概率图)比histogram更敏感地揭示尾部偏离;leveneTest(需 Statistics Toolbox)比单纯看标准差比值更可靠地检验方差齐性。跳过这一步,等于在流沙上盖楼。
提示:描述分析的终极目标,是回答三个问题:
1. 这些数据“长什么样”?(分布形态、中心趋势、离散程度)
2. 它们之间“有什么故事”?(关联模式、分组差异、潜在结构)
3. 我接下来“能做什么”?(选择何种模型、是否需要变换、哪些变量该剔除)
每一个回答,都必须有具体的统计量、可视化图表和业务逻辑支撑,而非主观臆断。
2.2 MATLAB、R、Python 在描述分析中的角色定位:不是谁更好,而是谁更适配当前环节
很多初学者陷入“工具之争”,纠结该学 MATLAB 还是 Python。真相是:在数模实战中,三者不是替代关系,而是分工协作的流水线。我的团队工作流是:MATLAB 主攻快速原型验证与信号/图像类数据预处理,R 主导统计推断与出版级可视化,Python 承担大规模数据清洗与机器学习接口。这种分工源于各自基因:
MATLAB 的核心优势在于“所见即所得”的工程直觉。
plot(x, y, 'o-')一行命令就能生成带标记的折线图,imagesc(matrix)瞬间可视化二维矩阵,spectrogram一键生成时频图。对于传感器数据、图像数据、控制系统输出这类具有强物理意义的数据,MATLAB 的内置函数(如detrend去趋势、pwelch功率谱估计)封装了成熟的信号处理算法,参数调整直观(nfft,noverlap),结果可立即用于后续建模。它的缺点是通用数据处理语法略显冗长,比如分组统计需splitapply+groupsummary组合,不如 R 的dplyr流畅。R 的不可替代性在于统计生态的深度与严谨性。
tidyverse生态(dplyr,ggplot2,purrr)构建了数据操作的“黄金标准”。summarise(across(everything(), list(mean=~mean(.), sd=~sd(.), skew=~skewness(.))))一行代码即可对所有数值列计算均值、标准差、偏度,且结果自动整理为 tidy data 格式,无缝对接后续绘图。ggplot2的图层语法(+ geom_point() + theme_minimal())让定制化图表成为可能,而cowplot或patchwork包能精准控制多图排版,这对撰写符合期刊规范的论文至关重要。R 的car包提供vif()函数计算方差膨胀因子(VIF)检验多重共线性,MASS包的fitdistr()可拟合多种分布并返回 AIC/BIC 值,这些是 MATLAB Statistics Toolbox 中没有的深度统计工具。Python 的统治力在于数据规模与工程集成。当数据量超过 1GB,MATLAB 的内存管理开始吃力,R 的
data.table虽快但学习曲线陡峭,而pandas+dask的组合能轻松处理数十 GB 数据。更重要的是,Python 是连接数据与 AI 模型的桥梁。描述分析产出的特征工程方案(如sklearn.preprocessing.StandardScaler标准化、sklearn.feature_selection.SelectKBest选择重要变量),可直接喂给scikit-learn或TensorFlow模型。matplotlib和seaborn的绘图能力虽不及ggplot2精细,但plotly的交互式图表(px.scatter_matrix)在汇报演示时极具冲击力。
注意:工具选择应基于数据类型、分析深度、交付形式三重考量。
- 处理 10 万行以内、含时间序列/频谱特征的数据 → 优先 MATLAB;
- 需要发表高质量统计图表、进行复杂假设检验 → 必选 R;
- 数据源来自数据库/API、后续要部署为 Web 服务 → Python 是唯一选择。
本文代码实现将严格遵循此逻辑,避免“为用而用”。
3. 核心实操:从原始数据到建模决策的完整描述分析链
3.1 数据加载与初步探查:别急着计算,先让数据“开口说话”
任何分析始于对数据的敬畏。拿到.csv或.xlsx文件,第一件事不是readtable,而是用眼睛扫描。打开文件,观察前 10 行:列名是否清晰(Temp_C还是temperature?)?是否有明显乱码或空行?时间戳格式是否统一(2023-01-01 00:00:00还是01/01/2023 00:00)?这些肉眼可见的问题,远比后续的统计计算更致命。
MATLAB 实操步骤:
% 步骤1:安全加载,启用自动类型推断 opts = detectImportOptions('sensor_data.csv'); % 强制将 'timestamp' 列识别为 datetime,避免字符串处理 opts.VariableTypes{'timestamp'} = 'datetime'; opts.DatetimeType = 'datetime'; opts.ExtraColumnsRule = 'ignore'; % 忽略多余列,防错 data = readtable('sensor_data.csv', opts); % 步骤2:执行基础健康检查(比 summary() 更深入) disp('=== 数据基础信息 ==='); disp(['总行数: ', num2str(height(data))]); disp(['总列数: ', num2str(width(data))]); disp('=== 缺失值统计 ==='); missing_count = sum(ismissing(data)); [~, idx] = sort(missing_count, 'descend'); for i = 1:min(5, length(idx)) % 显示缺失最多的前5列 col_name = data.Properties.VariableNames{idx(i)}; disp([col_name, ': ', num2str(missing_count(idx(i))), ' (', ... num2str(100*missing_count(idx(i))/height(data), '%.1f'), '%)']); end % 步骤3:可视化缺失模式(关键!) % 使用 heatmap 展示缺失矩阵,横轴为时间,纵轴为变量 time_var = data.timestamp; % 将时间离散化为小时(避免时间点过多) hour_bins = floor(datetime2datenum(time_var)/1/24); % 转换为小时序号 missing_mat = ismissing(data{:, 2:end}); % 排除 timestamp 列 % 创建缺失热图 figure('Position', [100, 100, 800, 600]); heatmap(hour_bins, data.Properties.VariableNames(2:end), missing_mat, ... 'Colormap', lines(2), 'ColorbarVisible', 'off'); title('缺失值时间分布热图(深色=缺失)'); xlabel('时间(小时)'); ylabel('变量');这段代码的价值不在技术难度,而在于强制建立数据质量意识。heatmap图能瞬间揭示缺失是否随机:如果深色块集中在某几小时(如凌晨 2-4 点),极可能是设备定时维护导致,需在建模时作为协变量加入;如果深色块呈垂直条纹(某列全缺失),说明该传感器故障,应剔除该列。
R 语言等效实现(利用VIM包):
library(VIM) # 读取数据并保留原始结构 data <- read.csv("sensor_data.csv", stringsAsFactors = FALSE) # VIM::aggr() 生成专业的缺失模式图 aggr(data, col=c('navyblue','red'), numbers=TRUE, sortVars=TRUE, labels=names(data), cex.axis=.7, gap=2, ylab=c("Missing Data","Pattern"))VIM::aggr()输出的图不仅显示缺失比例,还用不同颜色区块标识缺失组合模式(如“温度+湿度同时缺失”),这是诊断系统性故障的利器。
Python 实现(missingno库):
import missingno as msno import pandas as pd df = pd.read_csv('sensor_data.csv') # 矩阵图:直观显示缺失位置 msno.matrix(df, figsize=(10, 6)) plt.title('Missing Value Matrix') plt.show() # 条形图:各列缺失比例 msno.bar(df, figsize=(10, 4)) plt.title('Missing Value Count per Column') plt.show()3.2 分布形态深度解析:超越均值与标准差的五个关键指标
均值(Mean)和标准差(Std)只能描述钟形分布。现实数据常呈偏斜(Skewed)、尖峰(Leptokurtic)或多峰(Multimodal)。忽略这些形态,会导致模型误判。以下是必须计算的五个核心指标及其业务解读:
| 指标 | MATLAB 计算 | R 计算 | Python 计算 | 业务解读 |
|---|---|---|---|---|
| 偏度(Skewness) | skewness(data.Temp_C) | e1071::skewness(data$Temp_C) | scipy.stats.skew(df['Temp_C']) | >0:右偏(长尾在高温端,如极端热浪);<0:左偏(长尾在低温端,如寒潮); |
| 峰度(Kurtosis) | kurtosis(data.Temp_C) | e1071::kurtosis(data$Temp_C) | scipy.stats.kurtosis(df['Temp_C']) | >3:尖峰(异常值多,如传感器噪声);<3:平峰(数据均匀,如稳定工况) |
| 四分位距(IQR) | iqr(data.Temp_C) | IQR(data$Temp_C) | df['Temp_C'].quantile(0.75) - df['Temp_C'].quantile(0.25) | 比标准差更稳健的离散度,IQR/中位数 < 0.1:数据高度集中 |
| 变异系数(CV) | std(data.Temp_C)/mean(data.Temp_C)*100 | cv(data$Temp_C)(fromrcompanion) | df['Temp_C'].std()/df['Temp_C'].mean()*100 | 消除量纲影响,CV>50%:波动剧烈,需关注稳定性 |
| Shapiro-Wilk p值 | swtest = fitdist(data.Temp_C, 'Normal');<br>h = chi2gof(swtest, 'Alpha', 0.05) | shapiro.test(data$Temp_C)$p.value | scipy.stats.shapiro(df['Temp_C'])[1] | p<0.05:拒绝正态假设,后续不能用 t 检验 |
MATLAB 综合分析脚本:
% 对所有数值列批量计算形态指标 num_cols = varfun(@isnumeric, data, 'OutputFormat', 'uniform'); num_vars = data.Properties.VariableNames(num_cols); results = table('Size', [length(num_vars), 5], ... 'VariableTypes', {'double', 'double', 'double', 'double', 'double'}, ... 'VariableNames', {'Skewness', 'Kurtosis', 'IQR', 'CV_percent', 'SW_pvalue'}); for i = 1:length(num_vars) col_data = data{:, num_vars{i}}; col_data = col_data(~ismissing(col_data)); % 剔除缺失值 results.Skewness(i) = skewness(col_data); results.Kurtosis(i) = kurtosis(col_data); results.IQR(i) = iqr(col_data); results.CV_percent(i) = std(col_data)/mean(col_data)*100; % Shapiro-Wilk 检验(需 Statistics Toolbox) try [~, pval] = swtest(col_data); results.SW_pvalue(i) = pval; catch results.SW_pvalue(i) = NaN; % 若无 toolbox,设为 NaN end end results.VarName = num_vars'; % 输出警示报告 disp('=== 形态异常警示 ==='); for i = 1:height(results) if abs(results.Skewness(i)) > 1 || results.Kurtosis(i) > 5 || ... isnan(results.SW_pvalue(i)) == 0 && results.SW_pvalue(i) < 0.05 fprintf('%s: Skew=%.2f, Kurt=%.2f, SW_p=%.3f -> 建议检查分布\n', ... results.VarName{i}, results.Skewness(i), results.Kurtosis(i), results.SW_pvalue(i)); end end这段代码的关键在于自动化预警。它不只输出数字,而是根据阈值(如 |Skew|>1)主动提示“建议检查分布”,引导你下一步用histogram或normplot深入查看。这才是描述分析的生产力所在。
3.3 关系勘探:从散点图矩阵到偏相关网络的进阶实践
两变量相关性,corrcoef一行搞定。但真实世界是多维的。A 与 B 相关,B 与 C 相关,是否意味着 A 与 C 也相关?不一定。C 可能是混杂变量(Confounder)。例如,冰淇淋销量(A)与溺水事故数(C)高度正相关,但真实驱动变量是气温(B)。此时,计算 A 与 C 的偏相关系数(Partial Correlation)才能剥离 B 的影响。
MATLAB 实现偏相关:
% 假设 data 包含 'ice_cream_sales', 'drowning_incidents', 'temperature' X = [data.ice_cream_sales, data.drowning_incidents, data.temperature]; % 计算偏相关矩阵(控制第3列 temperature) partial_corr = partialcorr(X, 'Rows', 'complete', 'Tail', 'both'); % 结果解释:partial_corr(1,2) 是 ice_cream_sales 与 drowning_incidents 的偏相关 % 若接近 0,说明相关性由 temperature 驱动 fprintf('控制 temperature 后,ice_cream_sales 与 drowning_incidents 偏相关 = %.3f\n', ... partial_corr(1,2));R 语言用ppcor包更简洁:
library(ppcor) # 计算所有变量间的偏相关(控制所有其他变量) pcc_result <- pcor(data[, c('ice_cream_sales', 'drowning_incidents', 'temperature')]) print(pcc_result$estimate) # 偏相关系数矩阵 print(pcc_result$p.value) # 对应 p 值Python 用pingouin库:
import pingouin as pg # 计算 ice_cream_sales 与 drowning_incidents 的偏相关,控制 temperature result = pg.partial_corr(data=data, x='ice_cream_sales', y='drowning_incidents', covar='temperature') print(f"偏相关系数: {result['r'].iloc[0]:.3f}, p-value: {result['p-val'].iloc[0]:.3f}")更进一步,构建变量关系网络图:
% 基于偏相关矩阵构建网络(阈值 |r| > 0.3) threshold = 0.3; adj_matrix = abs(partial_corr) > threshold; adj_matrix(logical(eye(size(adj_matrix)))) = false; % 移除自环 % 创建图对象 G = graph(adj_matrix, data.Properties.VariableNames); figure; plot(G, 'EdgeLabel', arrayfun(@(x,y) sprintf('%.2f', x), ... partial_corr(adj_matrix), 'UniformOutput', false)); title('变量偏相关网络(|r| > 0.3)');这张图将抽象的相关性转化为直观的网络:节点是变量,边是显著的偏相关。若“温度”节点连接所有其他节点,而“销量”与“事故”之间无边,则证实了混杂效应。这是指导变量筛选的直接依据。
3.4 假设检验的精准落地:ttest 与 ttest2 的本质区别与场景选择
网络热词中反复提及ttest与ttest2的区别,这绝非语法细节,而是实验设计哲学的体现。
ttest:单样本检验(One-sample t-test)
场景:你的数据是一组观测值,你想验证它们的均值是否等于某个理论值 μ₀。
公式:t = (x̄ - μ₀) / (s/√n)
数模案例:某赛题给出“行业标准能耗为 120 kWh/吨”,你采集了 30 炉钢的能耗数据,需检验本厂是否达标。
MATLAB 代码:% data.energy_consumption 是 30x1 向量 [h, p, ci, stats] = ttest(data.energy_consumption, 120); fprintf('检验结果: h=%d, p=%.4f, 95%%置信区间 [%f, %f]\n', ... h, p, ci(1), ci(2)); % h=1 表示拒绝原假设(均值≠120),p<0.05 才可信ttest2:双样本独立检验(Two-sample independent t-test)
场景:你有两组独立样本(如 A 组 25 人,B 组 28 人),想检验它们的均值是否相等。
公式:t = (x̄₁ - x̄₂) / √(s₁²/n₁ + s₂²/n₂)
数模案例:比较“传统教学法”与“AI 辅助教学法”下学生的成绩提升幅度。
MATLAB 代码:% group_A_scores, group_B_scores 是两个向量 [h, p, ci, stats] = ttest2(group_A_scores, group_B_scores, 'Alpha', 0.01); % 'Alpha', 0.01 表示采用更严格的 1% 显著性水平
关键区别与避坑指南:
1. 数据结构不同:ttest输入一个向量 + 一个标量;ttest2输入两个向量。
2. 原假设不同:ttest的 H₀ 是 “μ = μ₀”;ttest2的 H₀ 是 “μ₁ = μ₂”。
3. 方差假设:ttest2默认假设方差不等(Welch's t-test),更稳健;若明确方差齐性,可加'Vartype','equal'。
4. 业务陷阱:切勿用ttest2比较同一组人在干预前后的数据!这是配对设计,应使用ttest的配对模式:[h,p] = ttest(pre_scores, post_scores)。
我曾见队伍用ttest2比较“周一销量”与“周日销量”,却忽略了同一家店在两天的数据是相关的,导致结论错误。
R 与 Python 的等效实现强调显式声明设计类型,降低出错概率:
# R: t.test() 自动识别单/双样本,且必须指定 paired=TRUE/FALSE t.test(group_A, mu=120) # 单样本 t.test(group_A, group_B) # 双样本,默认 Welch's t.test(pre, post, paired=TRUE) # 配对样本from scipy import stats # Python: 函数名直接体现设计 stats.ttest_1samp(group_A, popmean=120) # 单样本 stats.ttest_ind(group_A, group_B) # 双样本独立 stats.ttest_rel(pre, post) # 双样本配对4. 常见问题与排查技巧实录:那些文档里不会写的实战经验
4.1 “为什么我的 histogram 看起来像锯齿,而队友的 smooth 得像山丘?”
问题本质是bin(分箱)数量选择不当。MATLAB 默认histogram(data)使用 10 个 bin,对大数据量(>10000)会过度离散,对小数据量(<100)则过于粗糙。
MATLAB 解决方案:
% 方法1:使用 Sturges 公式(n=100 时 bin≈7) n = height(data); num_bins_sturges = ceil(log2(n) + 1); % 方法2:使用 Freedman-Diaconis 规则(更稳健,推荐) iqr_val = iqr(data.Temp_C); bin_width = 2 * iqr_val / (n^(1/3)); num_bins_fd = ceil((max(data.Temp_C) - min(data.Temp_C)) / bin_width); % 绘制优化后的直方图 figure; histogram(data.Temp_C, num_bins_fd, 'Normalization', 'pdf'); hold on; % 叠加核密度估计(KDE),让分布更平滑 pd = fitdist(data.Temp_C, 'Kernel'); x_pdf = linspace(min(data.Temp_C), max(data.Temp_C), 1000); y_pdf = pdf(pd, x_pdf); plot(x_pdf, y_pdf, 'r-', 'LineWidth', 2); legend('KDE', 'Histogram'); title('Temperature Distribution (Optimized Bins + KDE)');经验心得:Freedman-Diaconis 规则基于四分位距(IQR),对异常值不敏感,比 Sturges 更适合工程数据。叠加 KDE 曲线(红色)能揭示分布的连续形态,直方图(蓝色)则保留原始数据的离散性,二者互补。
4.2 “corrcoef 输出的矩阵,为什么对角线不是 1?”
这是 MATLAB 新手高频困惑。corrcoef(X)的输入X必须是n×p 矩阵,其中每行是一个观测,每列是一个变量。若你误将X设为 p×n(变量在行),corrcoef会计算行间的相关性,导致对角线非 1。
正确做法:
% 错误:将变量放在行 X_wrong = [data.Temp_C; data.Humidity; data.Wind_Speed]; % 3xN 矩阵 R_wrong = corrcoef(X_wrong); % 计算 3 个变量间的相关性?不,是计算 3 行间的相关! % 正确:将变量放在列 X_correct = [data.Temp_C, data.Humidity, data.Wind_Speed]; % Nx3 矩阵 R_correct = corrcoef(X_correct); % 正确得到 3x3 相关矩阵快速自查:size(R_correct)应为[p p],其中p是变量数。若为[n n],说明输入方向反了。
4.3 “R 的 ggplot2 图表导出为 PDF 后,中文全部变成方框,怎么办?”
这是字体渲染的经典问题。ggplot2默认使用 Adobe Sans,不支持中文。
R 终极解决方案(无需安装额外字体):
library(showtext) showtext_auto() # 自动启用 showtext # 在绘图前设置全局字体 theme_set(theme_gray(base_family = "SimHei")) # Windows # theme_set(theme_gray(base_family = "STHeiti")) # macOS # theme_set(theme_gray(base_family = "AR PL UKai CN")) # Linux # 绘图代码(中文标题、标签自动生效) p <- ggplot(data, aes(x=Temp_C, y=Humidity)) + geom_point() + labs(title="温度与湿度散点图", x="温度 (°C)", y="湿度 (%)") + theme(plot.title = element_text(size=14)) # 导出为 PDF(中文完美) ggsave("scatter_chinese.pdf", p, width=8, height=6, device=cairo_pdf)showtext包调用系统字体,cairo_pdf设备确保矢量输出。这是 R 社区公认的最稳定方案。
4.4 “Python 的 pandas.describe() 为什么没有偏度、峰度?”
describe()默认只输出基础统计量。需手动扩展:
import pandas as pd import numpy as np from scipy import stats def extended_describe(df): """扩展 describe(),添加偏度、峰度、IQR""" desc = df.describe() # 添加额外统计量 extra_stats = pd.DataFrame(index=['skewness', 'kurtosis', 'iqr']) for col in df.select_dtypes(include=[np.number]).columns: series = df[col].dropna() extra_stats[col] = [ stats.skew(series), stats.kurtosis(series), series.quantile(0.75) - series.quantile(0.25) ] return pd.concat([desc, extra_stats]) # 使用 extended_desc = extended_describe(df) print(extended_desc)经验技巧:将此函数保存为my_utils.py,每次项目导入,一劳永逸。真正的效率提升,来自对重复劳动的自动化。
4.5 “MATLAB 报错:'Undefined function or variable 'swtest'',但我明明装了 Statistics Toolbox”
swtest是 MATLAB R2023a 新增函数。若你使用 R2022b 或更早版本,需用chi2gof或jbtest替代:
% R2022b 及之前版本 % Jarque-Bera 检验(适用于大样本) [h, p] = jbtest(data.Temp_C); % 或使用 chi2gof 进行正态性检验(需指定分布) pd = fitdist(data.Temp_C, 'Normal'); [h, p, stats] = chi2gof(data.Temp_C, 'CDF', pd);版本兼容性原则:在团队协作中,务必在代码开头注明所需 MATLAB 版本,并提供降级方案。这是专业性的基本体现。
5. 从描述分析到建模决策:一份可直接套用的检查清单
描述分析的终点,不是生成一份报告,而是产出一份建模行动指南。以下是我团队在每次数模项目启动时,必填的《描述分析决策清单》,它将分析结果直接映射为后续动作:
| 分析发现 | 业务含义 | 建模决策 | MATLAB/R/Python 操作 |
|---|---|---|---|
| 缺失值集中在某时段(如每日 02:00-04:00) | 设备定时校准或维护 | 将“是否维护时段”作为二元变量加入模型 | MATLAB:data.is_maintenance = hour(data.timestamp) >= 2 & hour(data.timestamp) <= 4; R:data$is_maintenance <- as.numeric(format(data$timestamp, "%H") %in% c("02","03","04")) |
| 某变量偏度 > 1.5(如 PM2.5) | 数据右偏,存在极端污染事件 | 对该变量进行 log(x+1) 或 Box-Cox 变换 | Python:from sklearn.preprocessing import PowerTransformer; pt = PowerTransformer(method='box-cox'); data['PM25_transformed'] = pt.fit_transform(data[['PM25']].values) |
| 温度与湿度的偏相关接近 0,但简单相关为 -0.6 | 二者相关性由“季节”变量驱动 | 在回归模型中必须包含“月份”或“季度”作为协变量 | R:lm(y ~ Temp + Humidity + factor(Month), data) |
| 变量 A 与 B 的相关系数为 0.85,VIF > 10 | 严重多重共线性,模型不稳定 | 保留业务意义更强的变量,或使用 PCA 降维 | MATLAB:vif_matrix = corrvar(data{:, {'A','B','C'}}); % 需 Statistics Toolbox |
| 残差图显示漏斗形(方差随预测值增大) | 异方差性,OLS 假设不满足 | 改用 WLS(加权最小二乘)或对因变量取 log | Python:import statsmodels.api as sm; model_wls = sm.WLS(y, X, weights=1/fitted_values).fit() |
这份清单的价值,在于它消除了分析与建模之间的模糊地带。每一个“建模决策”都是可执行的代码指令,
