资讯详情

pandas数据预处理课后习题实战:六类核心操作与完整处理流程

📅 2026/10/14 15:08:00 | 华诺云谱 👁 阅读
pandas数据预处理课后习题实战:六类核心操作与完整处理流程
1. 从课后习题反推pandas数据预处理到底在练什么很多人做课后习题的习惯是打开题目、翻书找对应函数、照着敲一遍、跑通就关掉。这个流程本身没错但问题在于——你练的是函数默写不是数据预处理。等真正拿到一份脏数据缺失值、重复行、类型错乱、异常值混在一起的时候你会发现课后题里那种给一个干净的DataFrame让你填一个空的模式根本不够用。我带的几个做数据分析的朋友里几乎所有人都有一个共识pandas的课后习题尤其是数据预处理这一章真正的价值不在于你记住了fillna还是dropna而在于你能不能建立起一套固定的处理顺序。顺序错了后面全是返工。比如你先做了类型转换结果发现列里混着未知这种字符串转换直接报错或者你先删了缺失值结果把本来可以通过其他列推断出来的信息也一起删掉了。这一章通常覆盖的核心动作我把它归成六类缺失值处理、重复值处理、数据类型转换、异常值识别与处理、数据标准化/归一化、以及列的重命名与重塑。课后习题基本就是围绕这六类反复出题只是换了个数据场景。所以与其一题一题地做不如先把这六类动作的触发条件和执行顺序理清楚再回头看题目你会发现大部分题都是在考同一个判断这一步该不该做以及为什么现在做。下面我按自己实际处理项目的顺序把这六类动作拆开讲每一类都对应课后习题里最常见的出题方式同时补上题目不会告诉你、但实际会踩的坑。2. 缺失值处理先判断为什么缺再决定怎么补2.1 缺失值的三种来源决定了三种处理策略课后习题里最常见的问法就是请处理表中的缺失值。但题目不会告诉你这些缺失是怎么来的而实际项目里缺失的来源直接决定你能不能补、该怎么补。第一种是随机缺失。比如一份问卷里某个人恰好漏填了年龄。这种缺失和这一列的值本身没关系删掉或者用均值/中位数填补都还算合理。第二种是系统性缺失。比如某个字段是后来才加进系统的早期数据全是空的。这时候你用均值填补等于凭空造了一批假数据而且这批假数据会集中在时间轴的早期直接污染趋势分析。第三种是缺失本身携带信息。比如离职日期这一列在职员工的这一列就是空的。这个空不是没填而是还没离职。你要是把它填成某个日期逻辑上就错了。提示拿到缺失值第一件事不是isnull().sum()而是先问一句这个空代表什么。isnull().sum()只告诉你数量不告诉你含义。2.2 用一张表决定填补方式我习惯用下面这张表来快速决策课后习题里遇到请选择合适的方法处理缺失值这类开放题也可以按这个逻辑答缺失比例缺失类型推荐处理理由 5%随机缺失直接删除行影响小删除成本低5% - 30%随机缺失均值/中位数/众数填补保留样本量偏差可控5% - 30%系统性缺失单独标记 模型填补避免引入虚假趋势 30%任意类型考虑整列删除或作为特征标记填补偏差过大列本身可能无效任意缺失携带信息新增一列标记是否缺失把空变成有用特征这张表不是死规矩但它能帮你在课后题里快速给出一个有依据的答案而不是凭感觉写fillna(0)。2.3 填补时的两个实操细节第一个细节是填补值要用训练集算不能用全量算。课后习题通常只给一份数据你不会意识到这个问题。但实际做建模时如果你用全量数据的均值去填补测试集的信息就泄漏到了训练过程里。正确做法是在训练集上算均值/中位数然后把这个值应用到测试集。这一点在课后题里不会考但面试和实际项目里经常被追问。第二个细节是分类列的缺失不要用均值填。比如城市这一列缺了几个你用均值填是没有意义的因为城市是字符串。这时候要么用众数要么单独归为未知一类。我见过有人把分类列做了标签编码之后用均值填补结果填出来一个2.5既不是原来的类别也没有任何业务含义。import pandas as pd import numpy as np # 模拟一份带缺失的数据 df pd.DataFrame({ age: [23, 25, np.nan, 30, np.nan, 28], city: [A, B, np.nan, A, C, np.nan], salary: [5000, 6000, 7000, np.nan, 8000, 9000] }) # 数值列用中位数填分类列用众数填 df[age] df[age].fillna(df[age].median()) df[city] df[city].fillna(df[city].mode()[0]) # 对于缺失本身有意义的列新增标记列 df[salary_missing] df[salary].isnull().astype(int) df[salary] df[salary].fillna(df[salary].median())这段代码里salary_missing这一列就是把缺失变成特征的做法。课后题如果问如何处理缺失值更合理你把这个思路写上去基本就是加分项。3. 重复值与类型转换最容易被课后题低估的两步3.1 重复值不只是drop_duplicates()课后习题里关于重复值的题通常就是一句请删除重复行然后你写df.drop_duplicates()就完事了。但实际数据里的重复分好几种处理方式完全不同。第一种是完全重复所有列的值都一样。这种直接删没有争议。第二种是主键重复但其他列不同。比如同一个用户ID出现了两次但一次是旧地址一次是新地址。这时候你不能简单删掉一条而要判断哪条是最新的。常见做法是按时间列排序后保留最后一条。第三种是业务意义上的重复。比如张三和张三 带空格在字符串层面不相等但业务上是同一个人。这种需要先做字符串清洗去空格、统一大小写再去重。# 完全重复 df df.drop_duplicates() # 按主键去重保留最新一条 df df.sort_values(update_time).drop_duplicates(subset[user_id], keeplast) # 字符串清洗后再去重 df[name] df[name].str.strip().str.lower() df df.drop_duplicates(subset[name])课后题如果只考第一种那确实简单。但如果你在答案里补一句实际场景中还需考虑主键重复和业务重复这道题的分量就完全不一样了。3.2 类型转换的坑astype不是万能的类型转换是课后习题的常客题目通常给一列看起来像数字的字符串让你转成数值型。新手第一反应是df[col].astype(float)然后遇到报错就懵了。报错的原因通常是列里混了非数字字符比如1,234里的逗号、12%里的百分号、或者干脆是未知这种文字。这时候astype会直接抛ValueError。正确的做法是先用pd.to_numeric并设置errorscoerce把无法转换的值变成NaN再决定怎么处理这些NaN。# 错误做法直接astype遇到脏数据就崩 # df[price] df[price].astype(float) # 正确做法先coerce再处理产生的NaN df[price] pd.to_numeric(df[price], errorscoerce) print(df[price].isnull().sum()) # 看看有多少转换失败还有一个容易被忽略的点是日期类型。课后题里日期经常以字符串形式出现比如2023-01-01。你用astype(datetime64)有时候能成但遇到2023/01/01这种格式就挂了。稳妥的做法是pd.to_datetime并指定format参数或者让它自动推断。df[date] pd.to_datetime(df[date], errorscoerce)注意errorscoerce是个双刃剑。它让代码不报错但也可能悄悄把一批脏数据变成NaN如果你不检查转换后的缺失数量很容易在后续步骤里莫名其妙丢数据。我的习惯是每次转换后都打印一下NaN的数量确认在预期范围内。3.3 类型转换的顺序问题这里有一个课后题不会讲、但实际很重要的顺序问题类型转换应该在缺失值处理之前还是之后我的经验是先转换再处理缺失。因为转换过程中可能产生新的NaN比如上面说的coerce如果你先处理了缺失再转换那新产生的NaN就没人管了。反过来先转换把所有能变成NaN的都变出来再统一处理缺失逻辑更干净。当然也有例外。如果某一列的缺失比例极高你打算直接删列那就没必要先转换了。所以顺序不是死的但你要知道自己在哪个阶段、为什么这么做。4. 异常值课后题给的是检测实际要的是判断4.1 三种检测方法各有各的适用场景课后习题里关于异常值的题通常是让你用IQR或者Z-score找出异常点。这两个方法本身不难难的是找出之后怎么办。题目往往只要求请识别异常值但实际项目里识别只是第一步判断才是关键。先简单回顾两种方法IQR法计算第一四分位数Q1和第三四分位数Q3IQR Q3 - Q1低于Q1 - 1.5×IQR或高于Q3 1.5×IQR的算异常。这个方法不假设数据服从正态分布比较稳健适合偏态分布的数据。Z-score法计算每个值偏离均值多少个标准差通常绝对值大于3算异常。这个方法假设数据近似正态分布对极端值敏感适合分布比较对称的数据。# IQR法 Q1 df[value].quantile(0.25) Q3 df[value].quantile(0.75) IQR Q3 - Q1 lower Q1 - 1.5 * IQR upper Q3 1.5 * IQR outliers df[(df[value] lower) | (df[value] upper)] # Z-score法 from scipy import stats z np.abs(stats.zscore(df[value])) outliers_z df[z 3]4.2 检测出来之后删还是留这是课后题最大的空白区。题目只教你找出来不教你怎么办。而实际工作中这一步才是真正花时间的地方。我的判断逻辑是这样的第一问这个异常是不是错误。比如年龄是200岁这明显是录入错误该删就删或者该修就修。但如果年龄是80岁虽然离均值很远但它是真实的就不能随便删。第二问这个异常是不是你要找的东西。比如做欺诈检测异常交易恰恰是目标你删了还做什么。做设备故障预测异常振动数据就是故障信号删了模型就学不到东西了。第三问删除的代价是什么。如果数据量本来就小删几个异常值可能让统计结果大幅波动。这时候可以考虑用盖帽法把超出边界的值替换为边界值而不是直接删除。# 盖帽法把超出上下界的值替换为边界值 df[value] np.where(df[value] upper, upper, df[value]) df[value] np.where(df[value] lower, lower, df[value])课后题如果问请处理异常值你只写删除答案是不完整的。把先判断再处理的思路写进去才是真正理解了这一步。4.3 一个容易被忽略的坑异常值检测要在分组内做假设你在处理一份全国销售数据北京的门店销售额普遍比三线城市高。如果你在全量数据上做IQR三线城市的高销售额可能被判定为异常但实际上它在三线城市内部是正常的。正确做法是按组分别检测。比如按城市分组在每个城市内部算IQR再判断异常。这个思路课后题基本不会考但实际做区域数据分析时非常常见。# 按城市分组检测异常 def detect_outlier(group): Q1 group[sales].quantile(0.25) Q3 group[sales].quantile(0.75) IQR Q3 - Q1 return group[(group[sales] Q1 - 1.5*IQR) | (group[sales] Q3 1.5*IQR)] outliers df.groupby(city).apply(detect_outlier)5. 标准化与归一化课后题只考公式实际要考用哪个5.1 标准化和归一化的区别一句话说清课后习题里经常把标准化和归一化放在一起考让你写出公式。公式本身不难标准化Z-score标准化(x - mean) / std处理后均值0标准差1。归一化Min-Max归一化(x - min) / (max - min)处理后值落在[0, 1]区间。但题目不会告诉你什么时候用哪个。我的经验是如果数据分布近似正态或者你后续要用到假设正态分布的算法比如线性回归、逻辑回归、SVM用标准化。如果数据分布有明显边界或者你后续要用到对数值范围敏感的算法比如神经网络、KNN用归一化。如果数据里异常值很多归一化会被异常值拉偏因为min和max容易被极端值影响这时候标准化更稳。5.2 用sklearn还是手写课后题通常让你手写公式这是为了让你理解原理。但实际项目里我建议直接用sklearn的StandardScaler和MinMaxScaler原因有三个一是它们能保存训练集的均值和方差方便应用到测试集二是它们处理了除零等边界情况三是它们能无缝接入Pipeline。from sklearn.preprocessing import StandardScaler, MinMaxScaler scaler StandardScaler() df[value_std] scaler.fit_transform(df[[value]]) # 应用到测试集时用transform而不是fit_transform # test_df[value_std] scaler.transform(test_df[[value]])提示fit_transform用在训练集transform用在测试集。这个区别课后题不考但实际项目里用错就是数据泄漏。5.3 标准化之前要不要先处理异常值要。这是一个顺序问题。如果你先标准化再处理异常值标准化后的异常值依然存在而且因为标准化改变了数值范围你之前设定的异常判断阈值可能就不适用了。正确顺序是先处理异常值再做标准化。同理标准化之前也要先处理缺失值因为均值和标准差的计算不能有NaN。所以完整的顺序是类型转换 → 缺失值处理 → 异常值处理 → 标准化/归一化。这个顺序课后题不会明确告诉你但它是实际做数据预处理时的标准流程。6. 列操作与重塑课后题里最碎但最实用的部分6.1 重命名、删除、新增列的正确姿势课后题里关于列操作的题通常很碎重命名一列、删除一列、新增一列计算列。这些操作本身简单但有几个细节值得注意。重命名用rename不要直接改columns属性。rename可以只改指定的列而且支持函数映射。df df.rename(columns{old_name: new_name}) # 批量处理把所有列名转小写并去空格 df.columns df.columns.str.strip().str.lower()删除列用drop注意axis1和inplace参数。我个人的习惯是不用inplaceTrue而是重新赋值这样链式调用更清晰也方便调试。df df.drop(columns[unused_col])新增计算列直接用赋值pandas会自动对齐索引。但要注意如果计算涉及缺失值结果也会是缺失值。比如df[a] df[b]如果a或b有NaN结果就是NaN。这时候可以用fillna(0)先处理或者用add方法并设置fill_value。df[total] df[price].add(df[tax], fill_value0)6.2apply、map、applymap到底用哪个这三个函数是课后题的高频考点也是最容易混淆的地方。我用一句话区分map用在Series上做元素级映射通常配合字典或函数。apply用在Series或DataFrame上Series上是元素级DataFrame上是按行或按列。applymap用在DataFrame上做元素级操作对每个单元格执行。# mapSeries元素映射 df[gender_code] df[gender].map({男: 1, 女: 0}) # apply按列聚合或按行计算 df[name_length] df[name].apply(len) df[row_sum] df.apply(lambda row: row[a] row[b], axis1) # applymap整个DataFrame元素级操作注意新版pandas推荐用DataFrame.map df df.applymap(lambda x: x.strip() if isinstance(x, str) else x)注意applymap在新版pandas里已经被DataFrame.map取代如果你用的是较新版本建议用map。课后题如果还写applymap跑通没问题但要知道这个变化。6.3 重塑pivot和melt的使用场景课后题里关于重塑的题通常出现在最后几道难度稍高。核心就两个函数pivot长变宽和melt宽变长。pivot把某列的值变成新的列名。比如原始数据是日期、指标、数值三列你想变成日期为行指标为列每个单元格是对应数值。pivoted df.pivot(indexdate, columnsmetric, valuesvalue)melt把多列合并成两列一列是原来的列名一列是原来的值。比如你有语文、数学、英语三列想变成科目、分数两列。melted df.melt(id_vars[student_id], value_vars[语文, 数学, 英语], var_namesubject, value_namescore)这两个操作在实际做数据透视和可视化时非常常用。课后题如果考到建议把pivot_table也一起了解一下它比pivot多了聚合功能能处理重复索引的情况。# pivot_table带聚合的透视 pivoted df.pivot_table(indexdate, columnsmetric, valuesvalue, aggfuncmean)7. 把课后题串成一条流水线我的实际处理顺序7.1 完整流程与每步的判断依据把上面六类动作串起来我实际处理一份陌生数据时的顺序是这样的第一步看数据全貌。df.info()看类型和缺失df.describe()看数值分布df.head()看前几行长什么样。这一步不写代码逻辑就是看。第二步列名清洗。去空格、统一大小写、重命名难懂的列名。这一步放在最前面因为后面所有操作都要引用列名列名不干净后面全是麻烦。第三步类型转换。把该是数值的转数值该是日期的转日期。用to_numeric和to_datetime配合errorscoerce。第四步缺失值处理。先统计缺失比例再按前面的表决定删除、填补还是标记。第五步重复值处理。先做字符串清洗再去重。注意主键重复和业务重复。第六步异常值处理。先检测再判断最后决定删除、盖帽还是保留。分组数据要分组检测。第七步标准化/归一化。根据后续算法选择方法注意fit和transform的区别。第八步列的重塑与最终检查。需要透视就透视需要拉长就拉长。最后再跑一次df.info()确认没有遗漏。这个顺序不是绝对的但每一步都有它的理由。课后题是打散的你需要自己把它们串起来才能在实际项目里不慌。7.2 课后题里最值得反复练的三类题如果时间有限我建议优先练这三类课后题第一类给一份脏数据让你写完整的预处理流程。这种题最能检验你有没有形成体系。不要只写单个函数要把顺序和判断依据写出来。第二类缺失值和异常值的处理选择题。这种题通常有多个看似合理的选项考的是你知不知道每种方法的适用边界。第三类apply和map的混合使用题。这种题考的是你对pandas数据结构的理解做多了对实际写代码帮助很大。7.3 一个我踩过的坑SettingWithCopyWarning最后分享一个课后题不会考、但实际一定会遇到的坑。当你从一个大DataFrame里切片出一个小DataFrame然后试图修改小DataFrame时pandas会抛出SettingWithCopyWarning。这个警告的意思是你改的可能是一个副本不是原始数据改了也不生效。# 可能触发警告 subset df[df[age] 30] subset[new_col] 1 # 警告可能在修改副本 # 正确做法显式复制 subset df[df[age] 30].copy() subset[new_col] 1 # 没有警告这个坑我在实际项目里踩过好几次数据改了半天发现没生效排查半天才发现是切片的问题。课后题的数据通常很小你不会遇到这个警告但一旦处理真实数据它几乎必然出现。记住一句话切片之后要修改先.copy()。把课后题当练习题做你学到的是函数把课后题当流程的一部分来理解你学到的是数据处理的思维方式。前者让你考试过关后者让你在拿到一份真实脏数据时知道从哪下手、按什么顺序、每一步为什么这么做。这个区别在真正开始做项目的时候会体现得非常明显。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑