刚接触数据分析时,我最头疼的就是处理脏数据。记得有次拿到一份电商用户数据,里面混杂着测试账号、缺失的年龄信息、重复的订单记录,就像一团乱麻。直到我发现了.drop()这把"手术刀",才真正体会到数据清洗的效率之美。
.drop()是Pandas中最基础却最强大的数据清理工具之一。它的核心功能很简单:从DataFrame中删除指定的行或列。但就像外科医生的手术刀,简单的工具在高手手中能玩出各种花样。举个例子,假设我们有个学生成绩表:
python复制import pandas as pd
students = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [18, 19, 20, None],
'成绩': [85, 92, 78, 65],
'班级': ['A', 'B', 'A', 'C']
})
想删除"赵六"这条记录(因为年龄缺失),一行代码就能搞定:
python复制clean_students = students.drop(3) # 删除索引为3的行
但.drop()的厉害之处在于它的灵活性。你可以:
实际项目中,我常用的删除行方式有三种:
第一种:按索引删除
python复制# 删除单行(索引为2的行)
df.drop(2)
# 删除多行(非连续)
df.drop([1, 3, 5])
# 删除连续行(利用切片)
df.drop(df.index[10:20]) # 删除第10到19行
第二种:按条件删除
这是我用得最多的方式,特别适合清理异常值:
python复制# 删除成绩低于60分的记录
df = df.drop(df[df['成绩'] < 60].index)
# 复合条件:删除年龄大于30且成绩低于70的
condition = (df['年龄'] > 30) & (df['成绩'] < 70)
df = df.drop(df[condition].index)
第三种:快速删除重复行
虽然.drop_duplicates()更专业,但.drop()也能实现:
python复制# 删除完全重复的行
df = df.drop(df[df.duplicated()].index)
# 删除特定列重复的行
df = df.drop(df[df.duplicated(subset=['姓名'])].index)
删除列时最容易犯的错误是忘记设置axis=1。我有个同事曾花了半小时debug,就是因为漏了这个参数:
python复制# 正确姿势
df.drop('成绩', axis=1) # 删除单列
df.drop(['年龄', '班级'], axis=1) # 删除多列
# 更直观的写法(推荐)
df.drop(columns=['年龄', '班级'])
实际工作中,我经常需要处理包含数十列的数据集。这时可以用些小技巧:
python复制# 删除包含特定字符的列
cols_to_drop = [col for col in df.columns if 'temp_' in col]
df.drop(columns=cols_to_drop)
# 删除数据类型为object的列(字符串列)
str_cols = df.select_dtypes(include='object').columns
df.drop(columns=str_cols)
处理真实数据时,单一条件往往不够。上周我分析用户行为数据时,就需要删除"7天内无活跃且未完成注册"的用户:
python复制condition = (df['最后活跃'] < '2023-01-01') & (df['注册状态'] == '未完成')
df = df.drop(df[condition].index)
这里有几个实用技巧:
|表示OR条件,&表示AND条件~实现NOT条件python复制# 删除非VIP用户或近30天无消费的记录
condition = (~df['is_vip']) | (df['最后消费'] < pd.Timestamp.now() - pd.Timedelta(days=30))
df = df.drop(df[condition].index)
当遇到多层索引(MultiIndex)数据时,.drop()的level参数就派上用场了。比如处理这样的销售数据:
python复制sales = pd.DataFrame({
'季度': ['Q1', 'Q1', 'Q2', 'Q2'],
'区域': ['北区', '南区', '北区', '南区'],
'销售额': [100, 150, 120, 180]
}).set_index(['季度', '区域'])
# 删除所有Q1的数据(保留Q2)
sales.drop('Q1', level='季度')
多层索引删除的常见坑点:
处理百万级数据时,不当的删除操作可能导致内存爆炸。我总结了几条经验:
第一:避免链式操作
python复制# 不好:创建了多个中间DataFrame
df = df.drop(1).drop(2).reset_index()
# 好:一次性操作
df = df.drop([1, 2]).reset_index()
第二:谨慎使用inplace
虽然inplace=True能节省内存,但会丢失原始数据。我的习惯是:
第三:善用索引
python复制# 慢:按条件删除
df = df.drop(df[df['age'] > 100].index)
# 快:先建立布尔索引
mask = df['age'] > 100
df = df[~mask]
坑点1:误删数据
有次我误用了errors='ignore',导致系统静默忽略了无效标签,后续分析全错了。现在我的原则是:
errors='raise'严格检查errors='ignore'加日志监控坑点2:索引混乱
删除操作可能打乱原有索引。我常用的解决方案:
python复制# 方法1:重置索引
df = df.drop([1,3]).reset_index(drop=True)
# 方法2:使用loc+条件筛选
df = df.loc[~condition] # 等效于drop但保留索引连续性
坑点3:内存泄漏
对于超大数据集,我会分块处理:
python复制chunk_size = 100000
for chunk in pd.read_csv('big_data.csv', chunksize=chunk_size):
chunk = chunk.drop(columns=['unused_col'])
process(chunk)
去年我处理过一个电商用户数据集,完整展示了.drop()的实战价值:
python复制df = df.drop(df[df['用户名'].str.contains('test')].index)
python复制invalid_orders = (df['订单金额'] <= 0) | (df['商品数量'].isna())
df = df.drop(df[invalid_orders].index)
python复制# 删除金额超过3个标准差