ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Pandas数据预处理与探索性分析:数学建模的基石

Pandas数据预处理与探索性分析:数学建模的基石 1. 项目概述从数据搬运工到建模分析师如果你已经跟着上一篇文章把Pandas的基本数据结构DataFrame和Series玩得比较熟了那恭喜你你已经成功从“数据小白”晋级为“数据搬运工”。但数学建模的世界远不止把数据从一个地方搬到另一个地方那么简单。搬运只是第一步更重要的是你得知道搬来的这些“砖头”数据是什么材质、能不能用、该怎么用。这就是我们这次要深入探讨的核心数据预处理与探索性分析——这是连接原始数据和数学模型之间最关键、也最容易被忽视的桥梁。很多新手朋友一拿到数据尤其是从网上爬下来的天气数据、或者公司导出的业务报表就迫不及待地开始套用复杂的回归、分类模型结果往往惨不忍睹。模型报错还是小事更可怕的是模型“跑通了”但结论完全错误这就是“垃圾进垃圾出”。Pandas在这个阶段扮演的角色就是一个无比强大的“数据质检员”和“数据整形师”。它不仅能帮你快速看清数据的全貌更能高效地处理那些脏乱差的数据把它们整理成建模算法“爱吃”的格式。这次我们就聚焦于如何利用Pandas为你的数学建模项目打下坚实、干净的数据基础。2. 数据预处理建模前的“大扫除”拿到一份原始数据比如一份包含日期、城市、最高温、最低温、天气状况、AQI等字段的天气数据CSV文件直接丢给模型是行不通的。我们得先进行一系列清理和转换。2.1 数据读取与初窥知己知彼读取数据是第一步但怎么读却有讲究。除了最常用的pd.read_csv对于Excel文件可以用pd.read_excel需要安装openpyxl或xlrd库对于从网页API获取的JSON数据可以用pd.read_json。import pandas as pd # 假设我们有一个天气数据文件 # 注意编码问题中文路径或内容可能需指定encodingutf-8或gbk df pd.read_csv(weather_data.csv)读进来之后千万别急着动手清洗。先用几个方法快速“瞟一眼”数据建立第一印象df.head()/df.tail()/df.sample(5)看头、看尾、随机看。了解数据大致样子有没有奇怪的符号、明显的错误。df.info()这是最重要的初步诊断工具。它会列出所有列的非空值数量、数据类型。一眼就能看出哪些列缺失严重以及Pandas自动推断的数据类型是否正确。比如一个应该是数字的列被识别成了object字符串这通常意味着里面混入了非数字字符如“N/A”、“-”。df.describe()针对数值型列快速计算计数、均值、标准差、最小值、四分位数、最大值。它能立刻帮你发现异常值。比如气温出现200度AQI出现负值在这里就会原形毕露。实操心得df.info()是我每次必看的第一项。如果数据量很大df.describe(includeall)可以包含非数值列的统计如唯一值数量、最高频值但计算稍慢。对于初步探索先看数值列的描述统计往往更高效。2.2 处理缺失值填坑的艺术缺失值NaN是现实数据的常态。处理方式无非三种删除、填充、保留。Pandas提供了灵活的工具。发现缺失df.isnull().sum()可以统计每列的缺失数量。df[df[某列].isnull()]可以查看所有包含该列缺失值的行。删除缺失df.dropna()会删除任何包含缺失值的行。df.dropna(subset[关键列])只删除在关键列上缺失的行。慎用全局删除容易损失大量数据。填充缺失这是更常用的方法。固定值填充df[列名].fillna(0)或df.fillna({列A: 0, 列B: 未知})。适合分类变量或对业务影响明确的场景。统计值填充df[列名].fillna(df[列名].mean())用均值填充median()用中位数对异常值不敏感mode()[0]用众数。这是数值型字段的常见做法。前后向填充df.fillna(methodffill)用前一个有效值填充或bfill。在处理时间序列数据如按时间排序的天气数据时特别有用假设天气不会突变。# 示例处理一份学生成绩数据 df[数学].fillna(df[数学].median(), inplaceTrue) # 中位数填充数学成绩缺失 df[班级].fillna(未知班级, inplaceTrue) # 字符串列用特定值填充 # 对于时间序列的温度数据可以考虑用前一天的温度填充 df[温度] df[温度].fillna(methodffill)避坑指南填充缺失值会引入偏差尤其是当缺失并非随机时。例如不愿意透露收入的人可能收入普遍较高或较低用均值填充会扭曲分布。在建模报告中必须说明缺失值的处理方式及其潜在影响。2.3 处理异常值找出“害群之马”异常值可能包含重要信息如欺诈检测也可能是错误数据。需要结合业务判断。标准差法假设数据服从正态分布通常认为在均值 ± 3倍标准差之外的值是异常值。四分位距IQR法更稳健不依赖正态分布假设。计算上四分位数Q3和下四分位数Q1定义IQR Q3 - Q1。通常认为小于Q1 - 1.5*IQR或大于Q3 1.5*IQR的值是异常值。# 使用IQR法识别‘AQI’列的异常值 Q1 df[AQI].quantile(0.25) Q3 df[AQI].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR # 找出异常值 outliers df[(df[AQI] lower_bound) | (df[AQI] upper_bound)] print(f发现 {len(outliers)} 个AQI异常值) # 处理异常值这里选择用上下边界值截断Winsorization而非直接删除 df[AQI_processed] df[AQI].clip(lowerlower_bound, upperupper_bound)注意事项直接删除异常值要非常谨慎。在环境分析中极高的AQI值可能是真实的污染事件包含了关键信息。更好的做法是创建一个标识异常值的布尔列在建模时作为特征输入或者使用对异常值不敏感的模型如树模型。2.4 数据类型转换让数据“名正言顺”Pandas自动推断的数据类型有时不准需要手动转换这对后续分析和建模效率至关重要。转换为数值型pd.to_numeric(df[列名], errorscoerce)。errorscoerce会将无法转换的值如字符串‘N/A’变为NaN而不是报错。转换为日期时间pd.to_datetime(df[日期列], format%Y-%m-%d)。指定format参数可以加速转换并避免歧义。转换为分类类型df[城市] df[城市].astype(category)。对于像城市、天气状况这类有限取值的字符串列转换为category类型可以极大节省内存提高分组、排序的速度。# 常见转换操作 df[温度] pd.to_numeric(df[温度], errorscoerce) # 强制转数字非法变NaN df[观测日期] pd.to_datetime(df[观测日期]) df[天气] df[天气].astype(category) # 检查转换结果 print(df.dtypes)3. 探索性数据分析用Pandas“看透”数据数据清洗干净后就要开始探索了。探索性数据分析的目标是发现模式、趋势、异常和关系为特征工程和模型选择提供依据。3.1 单变量分析了解每一个“个体”数值变量除了describe()可视化是王道。虽然Pandas绘图功能基于Matplotlib但直接调用非常方便。import matplotlib.pyplot as plt df[最高温].hist(bins30, edgecolorblack) # 直方图看分布 plt.title(最高温度分布) plt.xlabel(温度(℃)) plt.ylabel(频数) plt.show() df[AQI].plot(kindbox) # 箱线图看分散情况和异常值 plt.title(AQI箱线图) plt.show()直方图看分布形状是否正态、偏斜箱线图一眼看清中位数、四分位距和异常值点。分类变量使用value_counts()和条形图。weather_counts df[天气状况].value_counts() print(weather_counts) weather_counts.plot(kindbar) plt.title(天气状况频数统计) plt.xticks(rotation45) # 旋转x轴标签避免重叠 plt.show()这能立刻看出“晴”、“多云”、“雨”等天气出现的频率。3.2 多变量关系分析发现“连线”建模的核心是找出变量之间的关系。数值 vs 数值散点图和相关矩阵。# 散点图看两个数值变量的关系 df.plot(kindscatter, x最高温, yAQI, alpha0.5) # alpha设置透明度 plt.title(最高温与AQI散点图) plt.show() # 计算所有数值列之间的相关系数Pearson numeric_df df.select_dtypes(include[number]) correlation_matrix numeric_df.corr() print(correlation_matrix) # 用热图可视化相关系数矩阵需要seaborn库 import seaborn as sns plt.figure(figsize(10,8)) sns.heatmap(correlation_matrix, annotTrue, cmapcoolwarm, center0) plt.title(变量相关系数热图) plt.show()相关系数接近1或-1表示强相关接近0表示弱相关。热图能直观展示哪些变量可能互为冗余。分类 vs 数值分组聚合和箱线图。这是分析不同类别下数值指标差异的黄金方法。# 按‘天气状况’分组查看‘AQI’的统计信息 grouped df.groupby(天气状况)[AQI] print(grouped.describe()) # 用箱线图可视化不同天气下的AQI分布 df.boxplot(columnAQI, by天气状况) plt.title(不同天气状况下的AQI分布) plt.suptitle() # 去除自动生成的默认标题 plt.xlabel(天气状况) plt.ylabel(AQI) plt.xticks(rotation45) plt.show()这个方法能清晰回答诸如“雨天和晴天的AQI有显著差异吗”这类问题。3.3 时间序列分析抓住“趋势”对于带有时间戳的数据如每日天气时间序列分析是重点。重采样将高频数据聚合为低频数据或者反之。# 假设df已按日期排序且‘日期’列是datetime类型 df.set_index(观测日期, inplaceTrue) # 将日期设为索引 # 计算每周的平均温度 weekly_avg_temp df[温度].resample(W).mean() weekly_avg_temp.plot(title周平均温度变化趋势) plt.show() # 计算每月的AQI最大值 monthly_max_aqi df[AQI].resample(M).max()滑动窗口计算计算移动平均线平滑短期波动观察长期趋势。# 计算7天移动平均温度 df[温度_7d_avg] df[温度].rolling(window7).mean() df[[温度, 温度_7d_avg]].plot(title温度与7日移动平均线) plt.show()4. 特征工程初探为模型制造“弹药”原始数据字段不一定适合直接喂给模型。特征工程就是创造新特征以更好地表示潜在规律。4.1 从现有特征中创造新特征从日期中提取年月日、星期几、是否周末、是否节假日等对很多模型都很有用。df[年份] df.index.year df[月份] df.index.month df[星期几] df.index.dayofweek # 周一0, 周日6 df[是否周末] df[星期几].apply(lambda x: 1 if x 5 else 0)分箱将连续变量如年龄、收入离散化成几个区间如青年、中年、老年有时能使线性模型更易捕捉非线性关系。# 将温度分为低温、舒适、高温三档 bins [-float(inf), 10, 25, float(inf)] labels [低温, 舒适, 高温] df[温度档位] pd.cut(df[温度], binsbins, labelslabels)交互特征将两个或多个特征相乘或相加捕捉协同效应。例如在电商分析中“浏览次数”和“停留时长”的乘积可能比单独两个特征更能预测购买意愿。4.2 文本特征简单处理如果数据中有文本列如天气状况描述“晴间多云”需要将其转化为数值。独热编码将分类变量转换为二进制向量。Pandas的get_dummies函数一键完成。# 对‘天气状况’列进行独热编码 weather_dummies pd.get_dummies(df[天气状况], prefix天气) df pd.concat([df, weather_dummies], axis1) # 将新列合并回原数据框 print(df[[天气_晴, 天气_多云, 天气_雨]].head())编码后模型就能理解这些分类信息了。5. 实战演练分析城市天气数据让我们用一个综合例子串联以上所有步骤。假设我们有一个city_weather.csv文件包含多个城市一段时间内的每日天气数据。5.1 数据加载与初探import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # 1. 加载数据 df pd.read_csv(city_weather.csv, parse_dates[date]) print(数据形状:, df.shape) print(\n前5行数据:) print(df.head()) print(\n数据信息:) print(df.info()) print(\n数值列描述统计:) print(df.describe())5.2 数据清洗与预处理# 2. 处理缺失值 print(缺失值统计:) print(df.isnull().sum()) # 假设我们决定温度用列均值填充天气状况用前向填充时间序列风速用0填充 df[temp_max].fillna(df[temp_max].median(), inplaceTrue) df[weather].fillna(methodffill, inplaceTrue) df[wind_speed].fillna(0, inplaceTrue) # 3. 处理异常值以最高温为例使用IQR法 Q1 df[temp_max].quantile(0.25) Q3 df[temp_max].quantile(0.75) IQR Q3 - Q1 df[temp_max] df[temp_max].clip(lowerQ1-1.5*IQR, upperQ31.5*IQR) # 4. 数据类型转换 df[city] df[city].astype(category)5.3 探索性数据分析# 5. 单变量分析 plt.figure(figsize(12,4)) plt.subplot(1,2,1) df[temp_max].hist(bins30, edgecolorblack) plt.title(最高温度分布) plt.subplot(1,2,2) df.boxplot(columntemp_max, bycity) plt.suptitle() plt.title(各城市最高温分布) plt.tight_layout() plt.show() # 查看天气状况频次 print(df[weather].value_counts()) # 6. 多变量分析 - 城市与温度的关系分类 vs 数值 city_temp_stats df.groupby(city)[temp_max].agg([mean, std, count]) print(city_temp_stats) # 多变量分析 - 温度与湿度的关系数值 vs 数值 df.plot(kindscatter, xtemp_max, yhumidity, alpha0.5) plt.title(最高温与湿度散点图) plt.show() # 计算相关系数 corr_matrix df.select_dtypes(include[number]).corr() sns.heatmap(corr_matrix, annotTrue, cmapcoolwarm, center0) plt.title(数值变量相关系数热图) plt.show()5.4 特征工程与数据重塑# 7. 特征工程 # 从日期提取特征 df[month] df[date].dt.month df[day_of_week] df[date].dt.dayofweek df[is_weekend] df[day_of_week].apply(lambda x: 1 if x 5 else 0) # 对天气状况进行独热编码 weather_dummies pd.get_dummies(df[weather], prefixw) df pd.concat([df, weather_dummies], axis1) # 8. 数据准备建模示例为每个城市创建单独的数据集或特征矩阵 # 假设我们想预测温度选择特征和标签 features [humidity, wind_speed, month, day_of_week, is_weekend] [col for col in df.columns if col.startswith(w_)] target temp_max X df[features] y df[target] print(特征矩阵形状:, X.shape) print(目标变量形状:, y.shape)6. 常见问题与排查技巧实录在实际操作中你肯定会遇到各种报错和奇怪的现象。这里记录几个高频问题问题1读取CSV文件时出现UnicodeDecodeError。原因文件编码不是默认的utf-8可能是gbk、gb2312或latin-1。解决尝试指定编码pd.read_csv(file.csv, encodinggbk)。如果不知道编码可以用chardet库检测。问题2df.describe()看不到所有列只显示了数值列。原因describe()默认只针对数值列intfloat进行统计。解决使用df.describe(includeall)来包含所有类型的列包括object和category但非数值列的统计项如唯一值、最高频值会不同。问题3分组聚合groupby后数据变成了奇怪的MultiIndex格式难以进一步操作。原因groupby操作默认会将分组键作为索引。解决在聚合时使用as_indexFalse参数或者在聚合后使用reset_index()方法。# 方法1 result df.groupby(city, as_indexFalse)[temp].mean() # 方法2 result df.groupby(city)[temp].mean().reset_index()问题4使用fillna或astype等方法后原DataFrame好像没变原因大多数Pandas操作不是就地修改inplace而是返回一个新的DataFrame除非显式指定inplaceTrue或重新赋值。解决# 方式一重新赋值 df[column] df[column].fillna(value) # 方式二使用inplace参数但该参数在未来版本可能被弃用习惯重新赋值更安全 df[column].fillna(value, inplaceTrue)问题5处理大型DataFrame时内存不足或速度极慢。解决思路指定数据类型读入时用dtype参数指定每列类型或用astype转换特别是将object转为category分类文本或int/float数值。使用分块读取pd.read_csv(big_file.csv, chunksize10000)然后循环处理每个块。只读取需要的列pd.read_csv(big_file.csv, usecols[col1, col2])。考虑使用Dask或Modin库它们提供了类似Pandas的API但能进行并行计算处理超出内存的数据。问题6时间序列重采样或滑动窗口计算时结果开头出现很多NaN。原因这是正常现象。例如计算7天移动平均前6天没有足够的数据来计算平均值所以结果是NaN。解决可以使用min_periods参数来指定计算所需的最小观测数。例如rolling(window7, min_periods1).mean()表示即使只有1个数据点也计算其实就是它本身这样前6天就不会是NaN了。但需理解这改变了计算含义。掌握这些预处理和探索技巧意味着你不再是数据的被动接收者而是主动的审视者和塑造者。你能判断数据的质量理解数据的故事并把它整理成最适合建模的形态。这往往比后续选择哪个高级模型更能决定一个数学建模项目的成败。在下次的分享中我们会更进一步探讨如何利用Pandas进行更复杂的数据整合、分组聚合以及面向建模的最终数据准备。
返回列表