Pandas统计分析基础与数据预处理实战指南
简介这份实验报告面向学习大数据分析、希望系统掌握Pandas库核心用法的数据科学初学者完整呈现了统计分析基础与数据预处理全流程。内容涵盖read_table、read_csv、read_excel等数据存取方法ndim、shape、memory_usage等常用属性以及to_datetime时间处理、groupby与agg分组聚合、pivot_table和crosstab长宽表转换等关键技能。报告深入演示了缺失值拉格朗日插值、基于id和date的主键内连接整合、标准差标准化、主成分分析PCA降维以及通过pymysql和sqlalchemy实现MySQL数据库的数据导入导出操作每一步都附有详细代码与运行说明。资源为单个doc文档大小3.56MB既适合课程实验和毕业设计参考也可作为企业数据分析人员的自学模板。已有387人学习下载是快速掌握Pandas实操的实用资料。1. Pandas统计分析基础与数据预处理要做的事拿到一份从业务库导出的 CSV几万行、二十多列里面混着空值、重复记录、格式不统一的日期和带货币符号的金额——这种场景下大多数人第一反应是打开 Excel 手工筛但数据量一旦过了十万行Excel 就开始卡顿更别提把同样的清洗逻辑复用到下周的增量数据上。Pandas 解决的就是这个问题它把统计分析的基础操作和数据预处理放在同一个 DataFrame 模型里从读取原始文件到输出统计结果中间不需要切换任何工具。这篇文章围绕大数据分析技术里最常用的 Pandas 统计分析基础与数据预处理展开适合正在学数据分析、或者刚把 Pandas 引入报表流程的工程师新手能照着命令跑通熟手也能在分组聚合和清洗顺序上找到可以优化的细节。2. 描述性统计与分组聚合先摸清数据再谈分析2.1 用 describe() 快速摸清数值列的底细Pandas 做统计分析的第一步不是写复杂公式而是让数据自己说话。describe()会一次性输出计数、均值、标准差、最小值、四分位数和最大值这几项足以判断数据有没有明显的离群点、量纲是否一致、是否有缺失。默认情况下它只统计数值列但通过include参数可以纳入对象列或布尔列。import pandas as pd df pd.read_csv(sales.csv, encodingutf-8) desc df.describe(percentiles[.25, .5, .75, .9]) print(desc) # 把对象列也纳入统计看非空个数和唯一值数量 obj_desc df.describe(include[object])percentiles参数控制输出的分位点默认是 25%、50%、75%加上 90% 可以观察尾部数据比如大额订单是否集中在后 10%。如果count数量明显小于总行数这一列就有缺失值后面第 3 章的处理就要优先安排。include[object]对文本列输出的是非空个数、唯一值个数和出现频率最高的值这个信息在检查“城市”这类业务字段时很有效——如果唯一值数量异常少说明数据录入可能有默认值污染。对于任何一张新接手的数据表先跑一次describe()再决定后续步骤比直接进入清洗要稳得多。因为统计结果会直接暴露两个问题各列的量纲差异是否大得需要标准化以及是否存在明显偏离业务范围的值。这两个问题不解决后面的分组聚合和建模都会受到干扰。2.2 groupby() 与 agg() 搭起分组统计的骨架分组统计是数据分析里最常用的操作业务上的“按地区看销售额”“按品类看平均折扣”都对应groupby()加聚合函数。agg()的价值在于可以在一次分组里同时计算多个统计量而且允许为每个结果指定列名避免生成一堆层级索引后再去 rename。result ( df.groupby(region, as_indexFalse) .agg( total_revenue(amount, sum), avg_order(amount, mean), order_cnt(order_id, nunique), ) )as_indexFalse让分组列保留为普通列而不是索引这样导出的结果可以直接给报表系统用。agg()内的写法是“新列名(原列名, 聚合函数)”的元组形式sum和mean后面不加括号因为它们是传给 Pandas 的回调函数不是立即执行。nunique统计唯一订单数而不只是行数因为同一订单可能拆成多行明细直接count会把订单数算大。更复杂的需求可以传入字典或函数列表。比如同时计算销售额和销量并对金额列取中位数result df.groupby(category).agg( revenue_sum(amount, sum), revenue_median(amount, median), quantity_sum(qty, sum), ).reset_index()分组聚合的性能问题在高基数分组列上会暴露比如按“订单号”分组有几万个组这时可以先把该列转为category类型再分组速度会有明显提升。2.3 透视表 pivot_table 与逆透视 melt长表和宽表的转换统计分析和数据预处理里长表和宽表的转换是躲不掉的场景。宽表是每一行一个样本、每一列一个属性适合建模长表是每一行一个观测适合数据库存储和时序分析。业务系统导出的数据往往是长表但数据透视需求要的是宽表pivot_table()就是为此设计的。pivot pd.pivot_table( df, valuesamount, indexregion, columnscategory, aggfuncsum, fill_value0, )aggfunc默认是mean如果不指定金额列会被当作均值而不是总和这是最常见的误用。fill_value0把透视后缺失的组合填零避免输出里出现大量 NaN。逆透视用melt()把多列合并成一列melted pivot.reset_index().melt( id_varsregion, value_vars[电子产品, 家居用品], var_namecategory, value_nameamount, )表格对比两者的适用场景操作输入结构输出结构典型场景pivot_table长表宽表按维度展开指标做交叉分析melt宽表长表多列指标合并适配数据库导入stack / unstack多层索引行列互换索引层级的透视操作3. 数据清洗缺失值、重复值与数据类型修正3.1 缺失值处理的三个方向丢弃、填充、插值缺失值处理是数据预处理的第一个硬骨头但很多教程一上来就强调dropna()有多方便实际上无脑删行是风险最高的做法。删除行会让样本量缩水更严重的是如果缺失发生在特定业务条件下删除后剩下的数据就带上了选择性偏差。处理缺失值的第一步不是选方法而是搞清楚三个问题哪些列有缺失、缺失率多高、缺失是否与业务含义关联。缺失情况推荐做法理由单列缺失率低于 5%且与其他列无关直接删除该行对整体分布影响可以忽略关键列缺失比如客户 ID删除该行缺失后无法关联业务实体时间序列的中间空洞插值填充保留时序连续性分类特征的缺失填充“未知”类别缺失本身可能代表一种状态3.2 用 isnull() 和 dropna() 定位与删除缺失正式动手前要先量化缺失情况isnull()配合sum()和mean()能同时得到缺失的数量和比例。missing_cnt df.isnull().sum() missing_ratio df.isnull().mean().round(4) print(missing_cnt[missing_cnt 0]) print(missing_ratio[missing_ratio 0])mean()在这里的语义是缺失比例因为布尔值求和后除以行数。筛选出有缺失的列后再决定是删除还是填充。df_dropped df.dropna(subset[customer_id], howany)subset指定判断缺失时只看哪些列避免无关列的空值牵连整行被删。how参数有两个取值any表示只要有缺失就删all表示整行全空才删。如果处理包含大量空列的数据还可以用thresh参数设置保留条件比如某行至少有 15 个非空值才保留。3.3 fillna() 的参数细节与填充边界fillna()是最常用的填充方法但参数用不好会造成数据污染。常见的低级错误是不区分业务场景对所有列统一填充 0 或统一填充均值。数值型连续变量用均值或中位数填充需要先看分布——偏态明显的用中位数更稳正态分布的用均值更合理。分类变量则建议单独填充一个“unknown”值。df[score] df[score].fillna(df[score].median()) df[category] df[category].fillna(unknown) df[last_login] pd.to_datetime(df[last_login]).fillna(pd.NaT)时间列填充pd.NaT而不是字符串目的是保持时间类型的一致性。另一个容易忽略的点是fillna()默认返回新对象需要赋值回原变量除非传入inplaceTrue——而inplaceTrue在链式操作里经常失效所以习惯上用赋值方式更安全。3.4 重复值处理duplicated() 与 drop_duplicates() 的参数陷阱重复值判断不等于简单的整行去重。业务上同一订单在不同时间点被更新整行数据可能只有更新时间不同同一用户重复注册手机号相同但用户名不同。这些情况都要通过subset指定判断重复的字段集。# 找出重复的订单明细 dup_mask df.duplicated(subset[order_id, sku], keepFalse) # 查看重复部分 print(df[dup_mask].sort_values(order_id)) # 保留第一条删除其余 df_clean df[~dup_mask].drop_duplicates( subset[order_id, sku], keepfirst )keep参数有三个选项first保留第一条、last保留最后一条、False标记所有重复行。这里先通过dup_mask查看重复数据确认业务上是否真的只该保留一条避免盲目删除。如果重复行的其他列有不同的值删除前需要决定保留哪一条或者干脆用groupby()把多条记录聚合成一条。4. 数据转换类型修正、编码处理与标准化4.1 结合数据类型转换修正隐藏的脏数据数据类型转换可能是数据预处理里最枯燥但价值最高的环节。业务系统导出的数据经常把数值列做成字符串日期列做成object类型金额列里混着“¥1,200”这样的格式。这些问题在统计分析时不会报错但结果悄然出错——把字符串列做sum()会得到一个拼接的字符串把日期当字符串排序会得到错误的时间顺序。# 查看每列类型 print(df.dtypes) # 金额列清洗后转 float df[amount] ( df[amount] .astype(str) .str.replace(¥, , regexFalse) .str.replace(,, , regexFalse) .astype(float) ) # 日期列统一格式 df[order_date] pd.to_datetime( df[order_date], format%Y-%m-%d, errorscoerce )errorscoerce是关键参数它在格式不匹配时把该值置为NaT而不是抛异常终止整个转换之后再结合缺失值处理统一收口。astype(str)前置转换是为了防止某些列本身就是浮点型直接调用.str方法会报错。类型转换的顺序也有讲究如果金额列清洗和日期解析同时进行先做字符串清洗再做类型转换更稳因为清洗过程本身依赖字符串方法。4.2 类别型变量的编码转换map、replace 与 get_dummies统计分析中经常遇到文字类别列比如“省份”“商品分类”“支付方式”。Pandas 提供了多个转换工具但各自适用场景明显不同。map()适合用字典做一一映射比如把“男”“女”映射为 0、1replace()适合多对一替换比如把“北京”“上海”统一替换为“一线城市”get_dummies()则生成独热编码把类别展开成多列 0/1 特征。# 有序类别用 map 编码 df[gender_code] df[gender].map({男: 1, 女: 0}) # 无序类别用 get_dummies 展开 region_dummies pd.get_dummies( df[region], prefixregion, dummy_naTrue ) df pd.concat([df, region_dummies], axis1)dummy_naTrue会为缺失值单独生成一列避免编码过程中丢失缺失信息。map()遇到字典中不存在的键会返回 NaN所以使用前需要确认类别的实际取值可以通过df[gender].value_counts()先查看。类别编码的方向取决于后续用途做回归或树模型有序类别用数值映射更合适做逻辑回归或距离类模型独热编码是必须的否则类别之间的数值差会被模型误解。4.3 数值型变量的量纲统一标准化与归一化统计分析里经常出现“销售额”和“订单数量”两个列数量级相差几百倍。虽然 Pandas 本身的统计函数不受影响但后续如果接入机器学习模型量纲差异会影响梯度下降的收敛速度。标准化和归一化是两个不同概念标准化StandardScaler把数据变为均值为 0、标准差为 1归一化MinMaxScaler把数据缩放到 [0, 1] 区间。选择标准是看后续算法对分布有没有假设。from sklearn.preprocessing import StandardScaler, MinMaxScaler scaler StandardScaler() df[amount_std] scaler.fit_transform(df[[amount]]) minmax MinMaxScaler() df[amount_norm] minmax.fit_transform(df[[amount]])fit_transform传入的必须是二维结构所以df[[amount]]用双层括号。标准化保留异常值的影响归一化会把异常值压缩到边界附近。如果数据里存在明显离群点先对离群点做截断处理再归一化否则大部分数据会被压缩在一个很窄的区间里。两个缩放器都支持inverse_transform还原这在需要把预测结果映射回原始量纲时很有用。4.4 数据降维的轻量做法相关性分析与去冗余数据预处理的数据降维不必一上来就上 PCA最常见的做法是先通过相关性矩阵找出高度相关的列组再决定保留哪一列。corr()输出的是两两相关系数矩阵对于标准化后的数据相关系数绝对值大于 0.8 的列组就存在冗余。corr_matrix df[[amount, quantity, amount_per_unit]].corr() high_corr corr_matrix[corr_matrix.abs() 0.8] print(high_corr)如果amount和amount_per_unit相关度过高而amount还受quantity影响业务上更合理的做法是保留amount和quantity删除amount_per_unit。基于业务判断的多此一举在上线后会很值得因为冗余特征不仅增加计算量还可能让模型对特征产生错误的依赖关系。5. 多列联合清洗的完整流程与结果验证5.1 把散落的清洗步骤整理成可复用的流程前面的清洗步骤各管一段但真实项目里这些操作要按固定顺序串联起来。我一般按照“去全空行 → 类型转换 → 缺失值收口 → 去重 → 异常值校验”的顺序来组织。顺序有讲究先做类型转换后面的缺失值填充和去重才能基于正确的类型判断去重放在缺失值收口之后是因为一些重复行会在填充后变得完全相同此时去重更彻底。df[amount] df[amount].astype(str).str.replace(¥, , regexFalse).astype(float) df[order_date] pd.to_datetime(df[order_date], errorscoerce) df df.dropna(subset[order_id, sku]) df[amount] df[amount].fillna(df[amount].median()) df df.drop_duplicates(subset[order_id, sku], keeplast)5.2 用断言函数验证清洗后的数据质量清洗做完并不意味着可以进入分析还差一步验证。Pandas 官方提供了pd.testing.assert_frame_equal()用于验证两个 DataFrame 是否完全相等但在清洗场景里更常用的是针对业务规则写自定义断言缺失率是否已收敛、金额是否全部大于 0、日期是否在合理区间。assert df[amount].isnull().sum() 0 assert df[amount].min() 0 assert df[order_date].max() pd.Timestamp.today() assert df.duplicated(subset[order_id, sku]).sum() 0 summary pd.DataFrame({ rows: [len(df)], cols: [len(df.columns)], missing_rate: [df.isnull().mean().max()], })断言失败时抛出的异常会直接打断流程让问题在一次执行里暴露完整而不是等分析结果出错后再回头排查。把这一段整理成函数后续每次拿到新数据先执行一次能得到稳定的数据质量基线。统计分析建立在干净数据之上预处理的每一步都该留下验证痕迹。本文还有配套的精品资源点击获取