pin_drop当前位置:知识文库 ❯ 图文
pandas DataFrame增删改操作教程 - 数据添加删除修改详解
在数据分析过程中,我们经常需要对DataFrame进行增加、删除和修改操作。掌握这些基础操作,是进行高效数据处理的前提。本文将详细介绍DataFrame的增删改操作,帮助你灵活地处理各种数据场景。
一、添加列数据
在pandas中,添加新列是最常用的操作之一。我们可以通过多种方式为DataFrame增加新列。
1. 直接赋值法
最简单的方式是通过方括号[]直接赋值:
代码示例
import pandas as pd
import numpy as np
# 创建示例数据
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'city': ['北京', '上海', '广州']
})
# 添加新列:薪资
df['salary'] = [8000, 12000, 15000]
# 添加计算列:年龄的平方
df['age_squared'] = df['age'] ** 2
print(df)输出结果:
代码示例
name age city salary age_squared
0 Alice 25 北京 8000 625
1 Bob 30 上海 12000 900
2 Charlie 35 广州 15000 12252. 使用 assign() 方法
assign()方法会返回一个新的DataFrame,不会修改原数据,适合链式操作:
代码示例
# 使用 assign 添加多个列
df_new = df.assign(
salary_bonus=lambda x: x['salary'] * 1.2,
category='员工'
)
print(df_new)提示:直接赋值会修改原DataFrame,而
assign()返回新对象。在函数式编程风格中推荐使用assign()。
二、删除列和行
删除操作主要使用drop()方法,可以按行或按列进行删除。
1. 删除列
代码示例
# 删除单列
df_dropped = df.drop(columns=['age_squared'])
# 删除多列
df_dropped = df.drop(columns=['age_squared', 'city'])
# 使用 axis 参数(axis=1表示列)
df_dropped = df.drop('age_squared', axis=1)
# 原地删除(修改原DataFrame)
df.drop(columns=['age_squared'], inplace=True)2. 删除行
代码示例
# 按索引删除行
df_dropped = df.drop(index=[0, 2])
# 按条件删除行(删除年龄大于30的行)
df_filtered = df[df['age'] <= 30]
# 使用 drop 和布尔索引结合
df_dropped = df.drop(df[df['age'] > 30].index)3. 删除重复行
代码示例
# 创建含重复数据
df_dup = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Charlie'],
'age': [25, 30, 25, 35]
})
# 删除完全重复的行
df_clean = df_dup.drop_duplicates()
# 根据特定列去重,保留第一条
df_clean = df_dup.drop_duplicates(subset=['name'], keep='first')
# 删除所有重复项(不保留任何一条)
df_clean = df_dup.drop_duplicates(subset=['name'], keep=False)三、修改数据
修改DataFrame中的数据有多种方式,根据需求选择合适的方法。
1. 修改单个值
代码示例
# 使用 loc 修改指定位置
df.loc[0, 'city'] = '深圳'
# 使用 at 修改单个值(性能更好)
df.at[1, 'salary'] = 130002. 批量修改列
代码示例
# 整列替换
df['city'] = ['深圳', '杭州', '成都']
# 条件修改:薪资低于10000的涨薪20%
df.loc[df['salary'] < 10000, 'salary'] = df['salary'] * 1.2
# 使用 np.where 进行条件赋值
df['level'] = np.where(df['salary'] >= 10000, '高级', '初级')3. 使用 replace() 替换值
代码示例
# 替换单个值
df['city'] = df['city'].replace('北京', '北京市')
# 批量替换
df['city'] = df['city'].replace({
'北京': '北京市',
'上海': '上海市',
'广州': '广州市'
})
# 替换缺失值
df['city'] = df['city'].replace(np.nan, '未知')4. 使用 apply() 函数修改
代码示例
# 对列应用函数
df['name_upper'] = df['name'].apply(str.upper)
# 使用 lambda 函数
df['age_group'] = df['age'].apply(lambda x: '青年' if x < 30 else '中年')
# 对多列应用函数
def calculate_bonus(row):
if row['age'] > 30:
return row['salary'] * 0.15
return row['salary'] * 0.1
df['bonus'] = df.apply(calculate_bonus, axis=1)四、插入行数据
虽然pandas不推荐频繁插入行(性能较低),但在某些场景下仍然需要。
代码示例
# 使用 loc 在末尾添加行
df.loc[len(df)] = ['David', 28, '武汉', 9000]
# 使用 concat 添加单行
new_row = pd.DataFrame([{'name': 'Eve', 'age': 26, 'city': '南京', 'salary': 8500}])
df = pd.concat([df, new_row], ignore_index=True)
# 使用 concat 添加多行
new_rows = pd.DataFrame([
{'name': 'Frank', 'age': 32, 'city': '西安', 'salary': 11000},
{'name': 'Grace', 'age': 29, 'city': '长沙', 'salary': 9500}
])
df = pd.concat([df, new_rows], ignore_index=True)注意:频繁使用
concat()或append()添加行会导致性能问题。建议先收集所有数据到列表中,最后一次性创建DataFrame。
五、批量操作与链式调用
利用pandas的链式调用特性,可以将多个操作组合在一起,使代码更简洁。
代码示例
# 链式调用示例
result = (df
.assign(salary_bonus=lambda x: x['salary'] * 0.1)
.assign(total_comp=lambda x: x['salary'] + x['salary_bonus'])
.drop(columns=['age_squared'])
.rename(columns={'name': 'employee_name'})
.query('total_comp > 10000')
)
# 使用 pipe 进行自定义链式操作
def filter_and_transform(df, min_salary, cols_to_drop):
return (df
.query(f'salary >= {min_salary}')
.drop(columns=cols_to_drop)
.assign(rating=lambda x: np.where(x['salary'] > 12000, 'A', 'B'))
)
result = df.pipe(filter_and_transform, min_salary=9000, cols_to_drop=['age_squared'])六、实际应用场景
场景一:数据清洗与标准化
代码示例
# 模拟原始数据(含缺失值和异常值)
raw_data = pd.DataFrame({
'name': ['Alice', 'Bob', None, 'David', 'Alice'],
'age': [25, -1, 30, 28, 25],
'salary': [8000, 12000, 10000, -500, 8000]
})
# 数据清洗流程
cleaned = (raw_data
.drop_duplicates() # 删除重复行
.dropna(subset=['name']) # 删除name为空的行
.query('age > 0 and salary > 0') # 过滤异常值
.assign(name=lambda x: x['name'].str.strip()) # 去除空格
.reset_index(drop=True) # 重置索引
)
print(cleaned)场景二:特征工程
代码示例
# 员工数据
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'age': [25, 30, 35, 28],
'salary': [8000, 12000, 15000, 9000],
'department': ['技术', '销售', '技术', '人事']
})
# 创建多个特征列
df = df.assign(
age_group=pd.cut(df['age'], bins=[0, 25, 30, 35, 100],
labels=['25及以下', '26-30', '31-35', '35以上']),
salary_level=pd.qcut(df['salary'], q=3, labels=['低', '中', '高']),
is_tech=df['department'].apply(lambda x: 1 if x == '技术' else 0)
)
print(df)场景三:数据更新与合并
代码示例
# 原始员工表
employees = pd.DataFrame({
'emp_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'salary': [8000, 12000, 15000, 9000]
})
# 新的薪资调整表
updates = pd.DataFrame({
'emp_id': [2, 4, 5],
'new_salary': [13000, 10000, 11000]
})
# 更新现有员工的薪资
employees = employees.merge(updates, on='emp_id', how='left')
employees['salary'] = employees['new_salary'].combine_first(employees['salary'])
employees.drop(columns=['new_salary'], inplace=True)
print(employees)小贴士
性能优化技巧:对于大数据集,尽量避免使用iterrows()逐行遍历,推荐使用向量化操作或apply()。另外,使用inplace=True可以避免创建副本,节省内存。更多详情可参考pandas官方索引文档。
常见问题
直接赋值和 assign() 有什么区别?
直接赋值(如 df['col'] = value)会原地修改原DataFrame,而 assign() 返回一个新的DataFrame副本,不改变原数据。在需要保持数据不变性或进行链式调用时,推荐使用 assign()。
loc 和 iloc 在修改数据时有什么区别?
loc 基于标签索引(行名和列名),iloc 基于位置索引(整数位置)。例如 df.loc[0, 'name'] 使用行索引0和列名'name',而 df.iloc[0, 0] 使用第0行第0列。修改数据时两者都可以使用,但 loc 更具可读性。
如何高效地逐行添加大量数据?
不要使用循环逐个添加行,这会非常慢。正确的做法是先将所有数据收集到列表中,然后一次性用 pd.DataFrame() 或 pd.concat() 创建DataFrame。例如:rows = [dict1, dict2, dict3]; df = pd.DataFrame(rows)。
inplace=True 有什么优缺点?
优点是节省内存,不会创建数据副本;缺点是无法链式调用,且某些操作使用 inplace=True 时性能并不比赋值更好。pandas官方建议尽量减少使用 inplace=True,优先使用返回新对象的写法。
如何安全地修改链式赋值产生的SettingWithCopyWarning警告?
这个警告意味着你正在操作一个可能的副本。解决方法是:使用 .copy() 明确创建副本 df2 = df[df['age'] > 25].copy(),然后再修改;或者使用 .loc 一次性完成筛选和修改 df.loc[df['age'] > 25, 'salary'] = 10000。
练习1
创建一个包含学生信息的DataFrame(姓名、数学成绩、英语成绩),然后添加一个"总分"列和一个"平均分"列,最后删除数学成绩低于60分的学生。
练习2
编写一个函数,接收一个DataFrame,将所有小于0的值替换为0,并添加一个新列标记每行原始负值的数量。
练习3
使用链式调用方式,对员工数据完成以下操作:删除重复行、填充缺失值、添加奖金列、按部门分组统计平均薪资。
本文涉及AI创作
内容由AI创作,请仔细甄别