第三章:DataFrame 对象详解
📋 章节概述
本章深入讲解 Pandas DataFrame 对象的高级用法,包括行列操作、数据选择、索引管理、条件筛选、数据转换等核心功能。通过本章学习,你将掌握 DataFrame 的完整操作技能。
🎯 学习目标
- 掌握 DataFrame 的行列操作(增删改查)
- 深入理解 loc/iloc 在 DataFrame 中的高级用法
- 学会设置、重置和修改索引
- 掌握条件筛选和过滤技巧
- 理解 DataFrame 的数据对齐机制
- 学会使用 apply 和 applymap 进行数据转换
📊 知识结构图
DataFrame 详解
创建与结构
列操作
行操作
数据选择
索引管理
条件筛选
数据转换
字典创建
列表创建
NumPy创建
选择列
添加列
修改列
删除列
选择行
添加行
修改行
删除行
loc
标签索引
iloc
位置索引
布尔索引
set_index
reset_index
rename
单条件
多条件
query
apply
applymap
map
python
import pandas as pd
import numpy as np
3.1 DataFrame 结构解析
DataFrame 是 Pandas 最重要的数据结构,可以理解为:
- 一个 Excel 工作表
- 一个 SQL 数据表
- 多个 Series 组成的字典(共享同一个行索引)
结构组成
- 行索引(index):每行的标签
- 列索引(columns):每列的标签
- 数据(values):二维数组形式的数据
DataFrame结构
行索引
Index
数据区域
列索引
Columns
列1: Series
列2: Series
列3: Series
示例1:从字典创建 DataFrame(最常用)
python
# 方式1:从字典创建(最常用)
# 字典的 key 成为列名,value 成为该列数据
df_dict = pd.DataFrame({
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部"],
"薪资": [12000, 15000, 18000, 8000, 9000],
"入职日期": pd.to_datetime(["2026-01-15", "2026-02-20", "2026-03-10", "2026-01-05", "2026-02-01"])
})
df_dict
| | 姓名 | 部门 | 薪资 | 入职日期 |
| 0 | 张伟 | 技术部 | 12000 | 2026-01-15 |
| 1 | 李娜 | 销售部 | 15000 | 2026-02-20 |
| 2 | 王强 | 技术部 | 18000 | 2026-03-10 |
| 3 | 刘洋 | 人事部 | 8000 | 2026-01-05 |
| 4 | 陈静 | 财务部 | 9000 | 2026-02-01 |
|---|
示例2:从列表创建 DataFrame
python
# 方式2:从列表的列表创建
df_list = pd.DataFrame(
data=[["E001", "张伟", 28], ["E002", "李娜", 32], ["E003", "王强", 25]],
columns=["工号", "姓名", "年龄"],
index=["row1", "row2", "row3"] # 自定义行索引
)
df_list
| | 工号 | 姓名 | 年龄 |
| row1 | E001 | 张伟 | 28 |
| row2 | E002 | 李娜 | 32 |
| row3 | E003 | 王强 | 25 |
|---|
示例3:从 NumPy 数组创建 DataFrame
python
# 方式3:从 NumPy 数组创建
np.random.seed(42) # 设置随机种子保证可重复
df_numpy = pd.DataFrame(
data=np.random.randn(4, 3), # 4行3列的标准正态分布数据
columns=["A", "B", "C"],
index=["第1行", "第2行", "第3行", "第4行"]
)
df_numpy.round(2)
| | A | B | C |
| 第1行 | 0.50 | -0.14 | 0.65 |
| 第2行 | 1.52 | -0.23 | -0.23 |
| 第3行 | 1.58 | 0.77 | -0.47 |
| 第4行 | 0.54 | -0.46 | -0.47 |
|---|
示例4:DataFrame 基本属性
python
print("DataFrame 基本属性:")
print(f" shape(形状): {df_dict.shape} -> (行数, 列数)")
print(f" index(行索引): {list(df_dict.index)}")
print(f" columns(列索引): {list(df_dict.columns)}")
print(f" dtypes(数据类型):")
print(df_dict.dtypes)
print(f"\n values(数据值,NumPy数组):")
print(df_dict.values)
DataFrame 基本属性:
shape(形状): (5, 4) -> (行数, 列数)
index(行索引): [0, 1, 2, 3, 4]
columns(列索引): ['姓名', '部门', '薪资', '入职日期']
dtypes(数据类型):
姓名 str
部门 str
薪资 int64
入职日期 datetime64[us]
dtype: object
values(数据值,NumPy数组):
[['张伟' '技术部' 12000 Timestamp('2026-01-15 00:00:00')]
['李娜' '销售部' 15000 Timestamp('2026-02-20 00:00:00')]
['王强' '技术部' 18000 Timestamp('2026-03-10 00:00:00')]
['刘洋' '人事部' 8000 Timestamp('2026-01-05 00:00:00')]
['陈静' '财务部' 9000 Timestamp('2026-02-01 00:00:00')]]
3.2 列操作(增删改查)
列操作
选择列
添加列
修改列
删除列
df单括号col
返回Series
df双括号col
返回DataFrame
直接赋值
assign方法
整列修改
apply修改
del删除
pop删除
drop删除
python
# 创建基础数据框
df = pd.DataFrame({
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部"],
"基本工资": [10000, 12000, 15000, 7000, 8000]
})
df
| | 姓名 | 部门 | 基本工资 |
| 0 | 张伟 | 技术部 | 10000 |
| 1 | 李娜 | 销售部 | 12000 |
| 2 | 王强 | 技术部 | 15000 |
| 3 | 刘洋 | 人事部 | 7000 |
| 4 | 陈静 | 财务部 | 8000 |
|---|
3.2.1 选择列
python
# 选择单列(返回 Series)
name_col = df["姓名"] # 或 df.姓名(不推荐)
print(f"类型: {type(name_col)}")
name_col
类型: <class 'pandas.Series'>
0 张伟
1 李娜
2 王强
3 刘洋
4 陈静
Name: 姓名, dtype: str
python
# 选择多列(返回 DataFrame)
subset = df[["姓名", "部门"]]
print(f"类型: {type(subset)}")
subset
类型: <class 'pandas.DataFrame'>
| | 姓名 | 部门 |
| 0 | 张伟 | 技术部 |
| 1 | 李娜 | 销售部 |
| 2 | 王强 | 技术部 |
| 3 | 刘洋 | 人事部 |
| 4 | 陈静 | 财务部 |
|---|
3.2.2 添加新列
python
df_new = df.copy()
# 方式1:直接赋值(标量值)
df_new["绩效奖金"] = 2000
print("方式1 - 直接赋值标量:")
df_new[["姓名", "绩效奖金"]]
方式1 - 直接赋值标量:
| | 姓名 | 绩效奖金 |
| 0 | 张伟 | 2000 |
| 1 | 李娜 | 2000 |
| 2 | 王强 | 2000 |
| 3 | 刘洋 | 2000 |
| 4 | 陈静 | 2000 |
|---|
python
# 方式2:赋值 Series 或列表
df_new["项目数"] = [3, 5, 4, 2, 3]
print("方式2 - 赋值列表:")
df_new[["姓名", "项目数"]]
方式2 - 赋值列表:
| | 姓名 | 项目数 |
| 0 | 张伟 | 3 |
| 1 | 李娜 | 5 |
| 2 | 王强 | 4 |
| 3 | 刘洋 | 2 |
| 4 | 陈静 | 3 |
|---|
python
# 方式3:基于现有列计算
df_new["总薪资"] = df_new["基本工资"] + df_new["绩效奖金"]
print("方式3 - 基于现有列计算:")
df_new[["姓名", "基本工资", "绩效奖金", "总薪资"]]
方式3 - 基于现有列计算:
| | 姓名 | 基本工资 | 绩效奖金 | 总薪资 |
| 0 | 张伟 | 10000 | 2000 | 12000 |
| 1 | 李娜 | 12000 | 2000 | 14000 |
| 2 | 王强 | 15000 | 2000 | 17000 |
| 3 | 刘洋 | 7000 | 2000 | 9000 |
| 4 | 陈静 | 8000 | 2000 | 10000 |
|---|
python
# 方式4:使用 assign() 方法(返回新 DataFrame,链式操作)
df_assigned = df_new.assign(
税后薪资=lambda x: x["总薪资"] * 0.9, # 假设税率10%
评级=lambda x: np.where(x["总薪资"] >= 15000, "A", "B")
)
print("方式4 - 使用 assign() 方法:")
df_assigned[["姓名", "总薪资", "税后薪资", "评级"]]
方式4 - 使用 assign() 方法:
| | 姓名 | 总薪资 | 税后薪资 | 评级 |
| 0 | 张伟 | 12000 | 10800.0 | B |
| 1 | 李娜 | 14000 | 12600.0 | B |
| 2 | 王强 | 17000 | 15300.0 | A |
| 3 | 刘洋 | 9000 | 8100.0 | B |
| 4 | 陈静 | 10000 | 9000.0 | B |
|---|
3.2.3 修改列
python
df_modify = df_new.copy()
print("修改前 - 基本工资:")
print(df_modify["基本工资"].tolist())
# 方式1:直接修改整列
df_modify["基本工资"] = df_modify["基本工资"] * 1.1 # 涨薪10%
print("\n修改后 - 基本工资(涨薪10%):")
print(df_modify["基本工资"].tolist())
修改前 - 基本工资:
[10000, 12000, 15000, 7000, 8000]
修改后 - 基本工资(涨薪10%):
[11000.0, 13200.000000000002, 16500.0, 7700.000000000001, 8800.0]
python
# 方式2:使用 apply 方法(更灵活)
df_modify["部门"] = df_modify["部门"].apply(lambda x: x.replace("部", "部门"))
print("使用 apply 修改部门名称:")
print(df_modify["部门"].tolist())
使用 apply 修改部门名称:
['技术部门', '销售部门', '技术部门', '人事部门', '财务部门']
3.2.4 删除列
python
print(f"原始列: {list(df_new.columns)}")
# 方式1:使用 del(原地删除)
df_del = df_new.copy()
del df_del["项目数"]
print(f"方式1 - del 删除后: {list(df_del.columns)}")
# 方式2:使用 pop()(原地删除,返回被删除的列)
df_pop = df_new.copy()
popped_col = df_pop.pop("绩效奖金")
print(f"方式2 - pop 删除后: {list(df_pop.columns)}")
print(f" 被删除的列: {popped_col.tolist()}")
# 方式3:使用 drop()(返回新 DataFrame,推荐)
df_dropped = df_new.drop(columns=["项目数", "总薪资"])
print(f"方式3 - drop 删除后: {list(df_dropped.columns)}")
print(f" 原 DataFrame 列数: {len(df_new.columns)}")
原始列: ['姓名', '部门', '基本工资', '绩效奖金', '项目数', '总薪资']
方式1 - del 删除后: ['姓名', '部门', '基本工资', '绩效奖金', '总薪资']
方式2 - pop 删除后: ['姓名', '部门', '基本工资', '项目数', '总薪资']
被删除的列: [2000, 2000, 2000, 2000, 2000]
方式3 - drop 删除后: ['姓名', '部门', '基本工资', '绩效奖金']
原 DataFrame 列数: 6
3.3 行操作(增删改查)
行操作
选择行
添加行
修改行
删除行
loc标签
iloc位置
loc新增
concat
loc修改
drop方法
python
# 创建带自定义索引的数据框
df_indexed = pd.DataFrame({
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部"],
"薪资": [12000, 15000, 18000, 8000, 9000]
}, index=["E001", "E002", "E003", "E004", "E005"])
df_indexed
| | 姓名 | 部门 | 薪资 |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
| E004 | 刘洋 | 人事部 | 8000 |
| E005 | 陈静 | 财务部 | 9000 |
|---|
3.3.1 选择行
python
# 使用 loc 选择行(按标签)
print("选择单行:")
print(df_indexed.loc["E001"])
print("\n选择多行:")
df_indexed.loc["E001":"E003"]
选择单行:
姓名 张伟
部门 技术部
薪资 12000
Name: E001, dtype: object
选择多行:
| | 姓名 | 部门 | 薪资 |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
|---|
python
# 使用 iloc 选择行(按位置)
print("选择第一行:")
print(df_indexed.iloc[0])
print("\n选择前3行:")
df_indexed.iloc[0:3]
选择第一行:
姓名 张伟
部门 技术部
薪资 12000
Name: E001, dtype: object
选择前3行:
| | 姓名 | 部门 | 薪资 |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
|---|
3.3.2 添加新行
python
# 方式1:使用 loc 添加(推荐)
df_add = df_indexed.copy()
df_add.loc["E006"] = ["赵敏", "技术部", 13000]
print("方式1 - 使用 loc 添加新行:")
df_add.tail(3)
方式1 - 使用 loc 添加新行:
| | 姓名 | 部门 | 薪资 |
| E004 | 刘洋 | 人事部 | 8000 |
| E005 | 陈静 | 财务部 | 9000 |
| E006 | 赵敏 | 技术部 | 13000 |
|---|
python
# 方式2:使用 concat 添加多行
df_add2 = df_indexed.copy()
new_rows = pd.DataFrame({
"姓名": ["周杰", "吴刚"],
"部门": ["销售部", "人事部"],
"薪资": [11000, 9500]
}, index=["E006", "E007"])
df_concat = pd.concat([df_add2, new_rows])
print("方式2 - 使用 concat 添加多行:")
df_concat.tail(4)
方式2 - 使用 concat 添加多行:
| | 姓名 | 部门 | 薪资 |
| E004 | 刘洋 | 人事部 | 8000 |
| E005 | 陈静 | 财务部 | 9000 |
| E006 | 周杰 | 销售部 | 11000 |
| E007 | 吴刚 | 人事部 | 9500 |
|---|
3.3.3 修改行
python
df_modify_row = df_indexed.copy()
print("修改前 E001:")
print(df_modify_row.loc["E001"])
df_modify_row.loc["E001"] = ["张伟(已更新)", "技术部", 15000]
print("\n修改后 E001:")
print(df_modify_row.loc["E001"])
修改前 E001:
姓名 张伟
部门 技术部
薪资 12000
Name: E001, dtype: object
修改后 E001:
姓名 张伟(已更新)
部门 技术部
薪资 15000
Name: E001, dtype: object
3.3.4 删除行
python
print(f"原始行数: {len(df_indexed)}")
# 使用 drop 删除行
df_drop_row = df_indexed.drop(index=["E004", "E005"])
print(f"删除后行数: {len(df_drop_row)}")
df_drop_row
原始行数: 5
删除后行数: 3
| | 姓名 | 部门 | 薪资 |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
|---|
3.4 loc 和 iloc 高级用法
loc 和 iloc 对比总结
| 特性 | loc | iloc |
|---|---|---|
| 索引方式 | 标签索引 | 位置索引 |
| 切片规则 | 左闭右闭 | 左闭右开 |
| 支持类型 | 字符串、数字、布尔 | 整数 |
| 布尔索引 | 支持 | 支持 |
记忆口诀:
- loc = label-based(标签)
- iloc = integer-based(整数位置)
数据选择
loc
标签索引
左闭右闭
iloc
位置索引
左闭右开
布尔索引
条件筛选
df.loc标签切片
包含终点
df.iloc位置切片
不含终点
python
# 创建示例数据
df_demo = pd.DataFrame({
"A": [1, 2, 3, 4, 5],
"B": [10, 20, 30, 40, 50],
"C": [100, 200, 300, 400, 500]
}, index=["a", "b", "c", "d", "e"])
df_demo
| | A | B | C |
| a | 1 | 10 | 100 |
| b | 2 | 20 | 200 |
| c | 3 | 30 | 300 |
| d | 4 | 40 | 400 |
| e | 5 | 50 | 500 |
|---|
3.4.1 loc 高级用法
python
# loc['a', 'A'] - 选择单行单列
print("loc['a', 'A'] - 选择单行单列:")
print(df_demo.loc["a", "A"])
print()
# loc['a':'c', :] - 选择多行,所有列
print("loc['a':'c', :] - 选择多行,所有列:")
print(df_demo.loc["a":"c", :])
print()
# loc[:, 'A':'B'] - 所有行,选择多列
print("loc[:, 'A':'B'] - 所有行,选择多列:")
print(df_demo.loc[:, "A":"B"])
print()
# loc[['a','c','e'], ['A','C']] - 选择指定行和列
print("loc[['a','c','e'], ['A','C']] - 选择指定行和列:")
df_demo.loc[["a", "c", "e"], ["A", "C"]]
loc['a', 'A'] - 选择单行单列:
1
loc['a':'c', :] - 选择多行,所有列:
A B C
a 1 10 100
b 2 20 200
c 3 30 300
loc[:, 'A':'B'] - 所有行,选择多列:
A B
a 1 10
b 2 20
c 3 30
d 4 40
e 5 50
loc[['a','c','e'], ['A','C']] - 选择指定行和列:
| | A | C |
| a | 1 | 100 |
| c | 3 | 300 |
| e | 5 | 500 |
|---|
3.4.2 iloc 高级用法
python
# iloc[0, 0] - 第1行第1列
print("iloc[0, 0] - 第1行第1列:")
print(df_demo.iloc[0, 0])
print()
# iloc[0:3, :] - 前3行,所有列
print("iloc[0:3, :] - 前3行,所有列:")
print(df_demo.iloc[0:3, :])
print()
# iloc[:, 0:2] - 所有行,前2列
print("iloc[:, 0:2] - 所有行,前2列:")
print(df_demo.iloc[:, 0:2])
print()
# iloc[[0,2,4], [0,2]] - 指定行和列(按位置)
print("iloc[[0,2,4], [0,2]] - 指定行和列(按位置):")
df_demo.iloc[[0, 2, 4], [0, 2]]
iloc[0, 0] - 第1行第1列:
1
iloc[0:3, :] - 前3行,所有列:
A B C
a 1 10 100
b 2 20 200
c 3 30 300
iloc[:, 0:2] - 所有行,前2列:
A B
a 1 10
b 2 20
c 3 30
d 4 40
e 5 50
iloc[[0,2,4], [0,2]] - 指定行和列(按位置):
| | A | C |
| a | 1 | 100 |
| c | 3 | 300 |
| e | 5 | 500 |
|---|
3.4.3 布尔索引(条件筛选)
python
# 选择 A 列大于 2 的行
print("选择 A 列大于 2 的行:")
print(df_demo.loc[df_demo["A"] > 2])
print()
# 选择 A 列大于 2 的行的 B 列
print("选择 A 列大于 2 的行的 B 列:")
df_demo.loc[df_demo["A"] > 2, "B"]
选择 A 列大于 2 的行:
A B C
c 3 30 300
d 4 40 400
e 5 50 500
选择 A 列大于 2 的行的 B 列:
c 30
d 40
e 50
Name: B, dtype: int64
3.5 索引管理
索引管理
set_index
设置索引
reset_index
重置索引
rename
重命名
单列索引
多级索引
保留原索引
删除原索引
修改行索引名
修改列名
python
df_base = pd.DataFrame({
"工号": ["E001", "E002", "E003", "E004", "E005"],
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部"],
"薪资": [12000, 15000, 18000, 8000, 9000]
})
print("原始数据(默认整数索引):")
df_base
原始数据(默认整数索引):
| | 工号 | 姓名 | 部门 | 薪资 |
| 0 | E001 | 张伟 | 技术部 | 12000 |
| 1 | E002 | 李娜 | 销售部 | 15000 |
| 2 | E003 | 王强 | 技术部 | 18000 |
| 3 | E004 | 刘洋 | 人事部 | 8000 |
| 4 | E005 | 陈静 | 财务部 | 9000 |
|---|
3.5.1 设置索引
python
# 使用 set_index() 设置索引
df_set_idx = df_base.set_index("工号")
print("设置工号为索引后:")
df_set_idx
设置工号为索引后:
| | 姓名 | 部门 | 薪资 |
| 工号 | | | |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
| E004 | 刘洋 | 人事部 | 8000 |
| E005 | 陈静 | 财务部 | 9000 |
|---|
python
# 设置多级索引
df_multi = df_base.set_index(["部门", "工号"])
print("设置部门和工号为多级索引:")
df_multi
设置部门和工号为多级索引:
| | | 姓名 | 薪资 |
| 部门 | 工号 | | |
| 技术部 | E001 | 张伟 | 12000 |
| 销售部 | E002 | 李娜 | 15000 |
| 技术部 | E003 | 王强 | 18000 |
| 人事部 | E004 | 刘洋 | 8000 |
| 财务部 | E005 | 陈静 | 9000 |
|---|
3.5.2 重置索引
python
# 使用 reset_index() 重置索引
df_reset = df_set_idx.reset_index()
print("重置索引后(工号变回列):")
df_reset
重置索引后(工号变回列):
| | 工号 | 姓名 | 部门 | 薪资 |
| 0 | E001 | 张伟 | 技术部 | 12000 |
| 1 | E002 | 李娜 | 销售部 | 15000 |
| 2 | E003 | 王强 | 技术部 | 18000 |
| 3 | E004 | 刘洋 | 人事部 | 8000 |
| 4 | E005 | 陈静 | 财务部 | 9000 |
|---|
python
# 重置索引并删除原索引
df_reset_drop = df_set_idx.reset_index(drop=True)
print("重置索引并删除(恢复默认整数索引):")
df_reset_drop
重置索引并删除(恢复默认整数索引):
| | 姓名 | 部门 | 薪资 |
| 0 | 张伟 | 技术部 | 12000 |
| 1 | 李娜 | 销售部 | 15000 |
| 2 | 王强 | 技术部 | 18000 |
| 3 | 刘洋 | 人事部 | 8000 |
| 4 | 陈静 | 财务部 | 9000 |
|---|
3.5.3 修改索引名称
python
df_rename = df_set_idx.copy()
# 修改索引名称
df_rename.index.name = "员工编号"
print(f"索引名称: {df_rename.index.name}")
# 修改列名
df_rename.columns = ["员工姓名", "所属部门", "月薪"]
print("\n修改列名后:")
df_rename.head(3)
索引名称: 员工编号
修改列名后:
| | 员工姓名 | 所属部门 | 月薪 |
| 员工编号 | | | |
| E001 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
|---|
python
# 使用 rename() 修改特定行/列名
df_renamed = df_rename.rename(
index={"E001": "E001-主管"},
columns={"月薪": "基本薪资"}
)
print("修改特定名称后:")
df_renamed.head(3)
修改特定名称后:
| | 员工姓名 | 所属部门 | 基本薪资 |
| 员工编号 | | | |
| E001-主管 | 张伟 | 技术部 | 12000 |
| E002 | 李娜 | 销售部 | 15000 |
| E003 | 王强 | 技术部 | 18000 |
|---|
3.6 条件筛选与过滤
条件筛选运算符
| 运算符 | 含义 |
|---|---|
& |
与(and) |
| ` | ` |
~ |
非(not) |
isin() |
在列表中 |
between() |
在范围内 |
条件筛选
单条件
多条件
特殊方法
df列大于值
AND与
OR或
NOT非
isin
between
query
python
# 创建员工数据
df_employees = pd.DataFrame({
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静", "赵敏", "周杰", "吴刚"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部", "技术部", "销售部", "人事部"],
"职位": ["工程师", "销售经理", "高级工程师", "HR专员", "会计", "工程师", "销售代表", "HR经理"],
"薪资": [12000, 15000, 18000, 8000, 9000, 13000, 11000, 10000],
"工龄": [2, 3, 1, 4, 2, 3, 2, 5],
"绩效评分": [4.5, 4.8, 4.9, 4.2, 4.0, 4.6, 4.3, 4.7]
})
df_employees
| | 姓名 | 部门 | 职位 | 薪资 | 工龄 | 绩效评分 |
| 0 | 张伟 | 技术部 | 工程师 | 12000 | 2 | 4.5 |
| 1 | 李娜 | 销售部 | 销售经理 | 15000 | 3 | 4.8 |
| 2 | 王强 | 技术部 | 高级工程师 | 18000 | 1 | 4.9 |
| 3 | 刘洋 | 人事部 | HR专员 | 8000 | 4 | 4.2 |
| 4 | 陈静 | 财务部 | 会计 | 9000 | 2 | 4.0 |
| 5 | 赵敏 | 技术部 | 工程师 | 13000 | 3 | 4.6 |
| 6 | 周杰 | 销售部 | 销售代表 | 11000 | 2 | 4.3 |
| 7 | 吴刚 | 人事部 | HR经理 | 10000 | 5 | 4.7 |
|---|
3.6.1 单条件筛选
python
# 薪资大于 12000 的员工
print("薪资大于 12000 的员工:")
high_salary = df_employees[df_employees["薪资"] > 12000]
high_salary[["姓名", "部门", "薪资"]]
薪资大于 12000 的员工:
| | 姓名 | 部门 | 薪资 |
| 1 | 李娜 | 销售部 | 15000 |
| 2 | 王强 | 技术部 | 18000 |
| 5 | 赵敏 | 技术部 | 13000 |
|---|
python
# 技术部的员工
print("技术部的员工:")
tech_dept = df_employees[df_employees["部门"] == "技术部"]
tech_dept[["姓名", "职位", "薪资"]]
技术部的员工:
| | 姓名 | 职位 | 薪资 |
| 0 | 张伟 | 工程师 | 12000 |
| 2 | 王强 | 高级工程师 | 18000 |
| 5 | 赵敏 | 工程师 | 13000 |
|---|
3.6.2 多条件筛选
python
# 技术部且薪资大于 13000 的员工(与 &)
print("技术部且薪资大于 13000 的员工:")
result = df_employees[(df_employees["部门"] == "技术部") & (df_employees["薪资"] > 13000)]
result[["姓名", "部门", "薪资"]]
技术部且薪资大于 13000 的员工:
| | 姓名 | 部门 | 薪资 |
| 2 | 王强 | 技术部 | 18000 |
|---|
python
# 销售部或绩效评分 >= 4.8 的员工(或 |)
print("销售部或绩效评分 >= 4.8 的员工:")
result2 = df_employees[(df_employees["部门"] == "销售部") | (df_employees["绩效评分"] >= 4.8)]
result2[["姓名", "部门", "绩效评分"]]
销售部或绩效评分 >= 4.8 的员工:
| | 姓名 | 部门 | 绩效评分 |
| 1 | 李娜 | 销售部 | 4.8 |
| 2 | 王强 | 技术部 | 4.9 |
| 6 | 周杰 | 销售部 | 4.3 |
|---|
python
# 非技术部的员工(非 ~)
print("非技术部的员工:")
non_tech = df_employees[~(df_employees["部门"] == "技术部")]
non_tech[["姓名", "部门"]]
非技术部的员工:
| | 姓名 | 部门 |
| 1 | 李娜 | 销售部 |
| 3 | 刘洋 | 人事部 |
| 4 | 陈静 | 财务部 |
| 6 | 周杰 | 销售部 |
| 7 | 吴刚 | 人事部 |
|---|
3.6.3 使用 isin 和 between
python
# 使用 isin() 筛选
print("部门为技术部或销售部的员工:")
selected = df_employees[df_employees["部门"].isin(["技术部", "销售部"])]
selected[["姓名", "部门"]]
部门为技术部或销售部的员工:
| | 姓名 | 部门 |
| 0 | 张伟 | 技术部 |
| 1 | 李娜 | 销售部 |
| 2 | 王强 | 技术部 |
| 5 | 赵敏 | 技术部 |
| 6 | 周杰 | 销售部 |
|---|
python
# 使用 between() 筛选
print("薪资在 10000 到 15000 之间的员工:")
middle_salary = df_employees[df_employees["薪资"].between(10000, 15000)]
middle_salary[["姓名", "薪资"]]
薪资在 10000 到 15000 之间的员工:
| | 姓名 | 薪资 |
| 0 | 张伟 | 12000 |
| 1 | 李娜 | 15000 |
| 5 | 赵敏 | 13000 |
| 6 | 周杰 | 11000 |
| 7 | 吴刚 | 10000 |
|---|
3.6.4 使用 query() 方法
python
# 使用 query() 进行筛选
print("薪资大于 12000 且绩效 >= 4.5:")
query_result = df_employees.query("薪资 > 12000 and 绩效评分 >= 4.5")
query_result[["姓名", "薪资", "绩效评分"]]
薪资大于 12000 且绩效 >= 4.5:
| | 姓名 | 薪资 | 绩效评分 |
| 1 | 李娜 | 15000 | 4.8 |
| 2 | 王强 | 18000 | 4.9 |
| 5 | 赵敏 | 13000 | 4.6 |
|---|
python
# 使用变量查询
min_salary = 12000
query_result2 = df_employees.query("薪资 > @min_salary") # @符号引用外部变量
print(f"薪资大于 {min_salary} 的员工:")
query_result2[["姓名", "薪资"]]
薪资大于 12000 的员工:
| | 姓名 | 薪资 |
| 1 | 李娜 | 15000 |
| 2 | 王强 | 18000 |
| 5 | 赵敏 | 13000 |
|---|
3.7 apply 和 applymap 数据转换
数据转换方法对比
| 方法 | 作用 | 适用对象 |
|---|---|---|
apply() |
按行/列应用函数 | DataFrame/Series |
applymap() |
逐元素应用函数 | DataFrame |
map() |
值映射 | Series |
数据转换
apply
按行/列
applymap
逐元素
map
Series映射
axis=0
按列
axis=1
按行
3.7.1 apply 方法(按行或按列应用函数)
python
df_apply = df_employees.copy()
# 对列应用函数(默认 axis=0)
print("计算每列的数值范围(max-min):")
range_result = df_apply[["薪资", "工龄", "绩效评分"]].apply(lambda x: x.max() - x.min())
range_result
计算每列的数值范围(max-min):
薪资 10000.0
工龄 4.0
绩效评分 0.9
dtype: float64
python
# 对行应用函数(axis=1)
print("计算每个员工的综合得分(薪资/1000 + 工龄 + 绩效*10):")
df_apply["综合得分"] = df_apply.apply(
lambda row: row["薪资"] / 1000 + row["工龄"] + row["绩效评分"] * 10,
axis=1
).round(2)
df_apply[["姓名", "薪资", "工龄", "绩效评分", "综合得分"]]
计算每个员工的综合得分(薪资/1000 + 工龄 + 绩效*10):
| | 姓名 | 薪资 | 工龄 | 绩效评分 | 综合得分 |
| 0 | 张伟 | 12000 | 2 | 4.5 | 59.0 |
| 1 | 李娜 | 15000 | 3 | 4.8 | 66.0 |
| 2 | 王强 | 18000 | 1 | 4.9 | 68.0 |
| 3 | 刘洋 | 8000 | 4 | 4.2 | 54.0 |
| 4 | 陈静 | 9000 | 2 | 4.0 | 51.0 |
| 5 | 赵敏 | 13000 | 3 | 4.6 | 62.0 |
| 6 | 周杰 | 11000 | 2 | 4.3 | 56.0 |
| 7 | 吴刚 | 10000 | 5 | 4.7 | 62.0 |
|---|
3.7.2 applymap 方法(逐元素应用函数)
python
# 对 DataFrame 所有元素应用函数
df_numeric = df_demo.copy()
print("原始数据:")
print(df_numeric)
print("\n所有元素格式化(保留1位小数):")
# Pandas 3.0+ 使用 map 替代 applymap
formatted = df_numeric.map(lambda x: f"{x:.1f}")
formatted
原始数据:
A B C
a 1 10 100
b 2 20 200
c 3 30 300
d 4 40 400
e 5 50 500
所有元素格式化(保留1位小数):
| | A | B | C |
| a | 1.0 | 10.0 | 100.0 |
| b | 2.0 | 20.0 | 200.0 |
| c | 3.0 | 30.0 | 300.0 |
| d | 4.0 | 40.0 | 400.0 |
| e | 5.0 | 50.0 | 500.0 |
|---|
3.7.3 map 方法(Series 专用)
python
# 使用 map 进行值映射
print("部门名称映射为英文:")
dept_map = {"技术部": "Tech", "销售部": "Sales", "人事部": "HR", "财务部": "Finance"}
df_apply["部门英文"] = df_apply["部门"].map(dept_map)
df_apply[["姓名", "部门", "部门英文"]]
部门名称映射为英文:
| | 姓名 | 部门 | 部门英文 |
| 0 | 张伟 | 技术部 | Tech |
| 1 | 李娜 | 销售部 | Sales |
| 2 | 王强 | 技术部 | Tech |
| 3 | 刘洋 | 人事部 | HR |
| 4 | 陈静 | 财务部 | Finance |
| 5 | 赵敏 | 技术部 | Tech |
| 6 | 周杰 | 销售部 | Sales |
| 7 | 吴刚 | 人事部 | HR |
|---|
python
# 使用 map 进行条件映射
print("根据薪资分级:")
df_apply["薪资等级"] = df_apply["薪资"].map(
lambda x: "高" if x >= 15000 else "中" if x >= 10000 else "低"
)
df_apply[["姓名", "薪资", "薪资等级"]]
根据薪资分级:
| | 姓名 | 薪资 | 薪资等级 |
| 0 | 张伟 | 12000 | 中 |
| 1 | 李娜 | 15000 | 高 |
| 2 | 王强 | 18000 | 高 |
| 3 | 刘洋 | 8000 | 低 |
| 4 | 陈静 | 9000 | 低 |
| 5 | 赵敏 | 13000 | 中 |
| 6 | 周杰 | 11000 | 中 |
| 7 | 吴刚 | 10000 | 中 |
|---|
3.8 实战案例:员工绩效管理系统
场景描述
你是一家公司的 HR 经理,需要管理员工的绩效数据。你需要完成以下任务:
- 创建完整的员工绩效数据表
- 计算绩效奖金(基于绩效评分和项目数)
- 筛选高绩效员工(用于年终评优)
- 按部门统计绩效情况
- 生成员工绩效报告
员工绩效管理
创建数据
计算奖金
筛选员工
部门统计
绩效评级
生成报告
步骤1:创建员工绩效数据表
python
# 创建员工绩效数据
hr_df = pd.DataFrame({
"工号": ["E001", "E002", "E003", "E004", "E005", "E006", "E007", "E008", "E009", "E010"],
"姓名": ["张伟", "李娜", "王强", "刘洋", "陈静", "赵敏", "周杰", "吴刚", "郑华", "孙丽"],
"部门": ["技术部", "销售部", "技术部", "人事部", "财务部", "技术部", "销售部", "人事部", "技术部", "销售部"],
"职位": ["工程师", "销售经理", "高级工程师", "HR专员", "会计", "工程师", "销售代表", "HR经理", "架构师", "销售总监"],
"基本工资": [12000, 15000, 18000, 8000, 9000, 13000, 11000, 10000, 20000, 22000],
"项目数": [3, 5, 4, 2, 3, 4, 6, 3, 5, 7],
"绩效评分": [4.5, 4.8, 4.9, 4.2, 4.0, 4.6, 4.3, 4.7, 4.8, 4.9],
"客户满意度": [88, 92, 95, 85, 82, 90, 87, 91, 94, 96]
})
print("员工绩效数据表:")
hr_df
员工绩效数据表:
| | 工号 | 姓名 | 部门 | 职位 | 基本工资 | 项目数 | 绩效评分 | 客户满意度 |
| 0 | E001 | 张伟 | 技术部 | 工程师 | 12000 | 3 | 4.5 | 88 |
| 1 | E002 | 李娜 | 销售部 | 销售经理 | 15000 | 5 | 4.8 | 92 |
| 2 | E003 | 王强 | 技术部 | 高级工程师 | 18000 | 4 | 4.9 | 95 |
| 3 | E004 | 刘洋 | 人事部 | HR专员 | 8000 | 2 | 4.2 | 85 |
| 4 | E005 | 陈静 | 财务部 | 会计 | 9000 | 3 | 4.0 | 82 |
| 5 | E006 | 赵敏 | 技术部 | 工程师 | 13000 | 4 | 4.6 | 90 |
| 6 | E007 | 周杰 | 销售部 | 销售代表 | 11000 | 6 | 4.3 | 87 |
| 7 | E008 | 吴刚 | 人事部 | HR经理 | 10000 | 3 | 4.7 | 91 |
| 8 | E009 | 郑华 | 技术部 | 架构师 | 20000 | 5 | 4.8 | 94 |
| 9 | E010 | 孙丽 | 销售部 | 销售总监 | 22000 | 7 | 4.9 | 96 |
|---|
步骤2:计算绩效奖金
绩效奖金计算规则:
- 基础奖金 = 基本工资 * 0.1
- 绩效系数 = 绩效评分 / 5.0
- 项目奖金 = 项目数 * 500
- 总奖金 = (基础奖金 * 绩效系数) + 项目奖金
python
hr_df["基础奖金"] = hr_df["基本工资"] * 0.1
hr_df["绩效系数"] = hr_df["绩效评分"] / 5.0
hr_df["项目奖金"] = hr_df["项目数"] * 500
hr_df["绩效奖金"] = (hr_df["基础奖金"] * hr_df["绩效系数"] + hr_df["项目奖金"]).round(2)
hr_df["总薪资"] = hr_df["基本工资"] + hr_df["绩效奖金"]
print("绩效奖金计算结果(前5行):")
hr_df[["姓名", "基本工资", "绩效评分", "项目数", "绩效奖金", "总薪资"]].head()
绩效奖金计算结果(前5行):
| | 姓名 | 基本工资 | 绩效评分 | 项目数 | 绩效奖金 | 总薪资 |
| 0 | 张伟 | 12000 | 4.5 | 3 | 2580.0 | 14580.0 |
| 1 | 李娜 | 15000 | 4.8 | 5 | 3940.0 | 18940.0 |
| 2 | 王强 | 18000 | 4.9 | 4 | 3764.0 | 21764.0 |
| 3 | 刘洋 | 8000 | 4.2 | 2 | 1672.0 | 9672.0 |
| 4 | 陈静 | 9000 | 4.0 | 3 | 2220.0 | 11220.0 |
|---|
步骤3:筛选高绩效员工(年终评优候选人)
python
# 定义高绩效标准:绩效评分 >= 4.7 且 客户满意度 >= 90
high_performers = hr_df[(hr_df["绩效评分"] >= 4.7) & (hr_df["客户满意度"] >= 90)]
high_performers = high_performers.sort_values("绩效评分", ascending=False)
print("高绩效员工名单(绩效评分 >= 4.7 且 客户满意度 >= 90):")
print(high_performers[["姓名", "部门", "职位", "绩效评分", "客户满意度", "绩效奖金"]])
print(f"\n共 {len(high_performers)} 人获得年终评优资格")
高绩效员工名单(绩效评分 >= 4.7 且 客户满意度 >= 90):
姓名 部门 职位 绩效评分 客户满意度 绩效奖金
2 王强 技术部 高级工程师 4.9 95 3764.0
9 孙丽 销售部 销售总监 4.9 96 5656.0
1 李娜 销售部 销售经理 4.8 92 3940.0
8 郑华 技术部 架构师 4.8 94 4420.0
7 吴刚 人事部 HR经理 4.7 91 2440.0
共 5 人获得年终评优资格
步骤4:部门绩效统计
python
dept_stats = hr_df.groupby("部门").agg({
"绩效评分": ["mean", "max", "min"],
"客户满意度": "mean",
"项目数": "sum",
"绩效奖金": "mean",
"姓名": "count"
}).round(2)
# 简化列名
dept_stats.columns = ["平均绩效", "最高绩效", "最低绩效", "平均满意度", "总项目数", "平均奖金", "人数"]
print("部门绩效统计:")
dept_stats
部门绩效统计:
| | 平均绩效 | 最高绩效 | 最低绩效 | 平均满意度 | 总项目数 | 平均奖金 | 人数 |
| 部门 | | | | | | | |
| 人事部 | 4.45 | 4.7 | 4.2 | 88.00 | 5 | 2056.0 | 2 |
| 技术部 | 4.70 | 4.9 | 4.5 | 91.75 | 16 | 3490.0 | 4 |
| 财务部 | 4.00 | 4.0 | 4.0 | 82.00 | 3 | 2220.0 | 1 |
| 销售部 | 4.67 | 4.9 | 4.3 | 91.67 | 18 | 4514.0 | 3 |
|---|
步骤5:生成员工绩效评级
绩效评级规则:
- A级:绩效评分 >= 4.8 且 客户满意度 >= 95
- B级:绩效评分 >= 4.5 且 客户满意度 >= 88
- C级:绩效评分 >= 4.0 且 客户满意度 >= 85
- D级:其他
python
def get_performance_level(row):
score = row["绩效评分"]
satisfaction = row["客户满意度"]
if score >= 4.8 and satisfaction >= 95:
return "A级-卓越"
elif score >= 4.5 and satisfaction >= 88:
return "B级-优秀"
elif score >= 4.0 and satisfaction >= 85:
return "C级-良好"
else:
return "D级-待改进"
hr_df["绩效评级"] = hr_df.apply(get_performance_level, axis=1)
print("员工绩效评级分布:")
print(hr_df["绩效评级"].value_counts())
print("\n各评级员工名单:")
for level in ["A级-卓越", "B级-优秀", "C级-良好", "D级-待改进"]:
employees = hr_df[hr_df["绩效评级"] == level]["姓名"].tolist()
if employees:
print(f" {level}: {', '.join(employees)}")
员工绩效评级分布:
绩效评级
B级-优秀 5
A级-卓越 2
C级-良好 2
D级-待改进 1
Name: count, dtype: int64
各评级员工名单:
A级-卓越: 王强, 孙丽
B级-优秀: 张伟, 李娜, 赵敏, 吴刚, 郑华
C级-良好: 刘洋, 周杰
D级-待改进: 陈静
步骤6:生成综合报告
python
report_data = {
"员工总数": len(hr_df),
"平均绩效评分": hr_df["绩效评分"].mean(),
"最高绩效评分": hr_df["绩效评分"].max(),
"平均客户满意度": hr_df["客户满意度"].mean(),
"总项目数": hr_df["项目数"].sum(),
"总绩效奖金": hr_df["绩效奖金"].sum(),
"平均绩效奖金": hr_df["绩效奖金"].mean(),
"A级员工数": len(hr_df[hr_df["绩效评级"] == "A级-卓越"]),
"B级员工数": len(hr_df[hr_df["绩效评级"] == "B级-优秀"]),
"最佳部门": dept_stats["平均绩效"].idxmax()
}
print(f"""
┌─────────────────────────────────────────┐
│ 2026年度员工绩效综合报告 │
├─────────────────────────────────────────┤
│ 员工总数: {report_data['员工总数']:>6} 人 │
│ 平均绩效评分: {report_data['平均绩效评分']:>6.2f} 分 │
│ 最高绩效评分: {report_data['最高绩效评分']:>6.2f} 分 │
│ 平均客户满意度: {report_data['平均客户满意度']:>6.1f} % │
│ 总项目数: {report_data['总项目数']:>6} 个 │
│ 总绩效奖金: {report_data['总绩效奖金']:>10.2f} 元 │
│ 平均绩效奖金: {report_data['平均绩效奖金']:>10.2f} 元 │
│ A级员工数: {report_data['A级员工数']:>6} 人 │
│ B级员工数: {report_data['B级员工数']:>6} 人 │
│ 最佳绩效部门: {report_data['最佳部门']:>10} │
└─────────────────────────────────────────┘
""")
┌─────────────────────────────────────────┐
│ 2026年度员工绩效综合报告 │
├─────────────────────────────────────────┤
│ 员工总数: 10 人 │
│ 平均绩效评分: 4.57 分 │
│ 最高绩效评分: 4.90 分 │
│ 平均客户满意度: 90.0 % │
│ 总项目数: 42 个 │
│ 总绩效奖金: 33834.00 元 │
│ 平均绩效奖金: 3383.40 元 │
│ A级员工数: 2 人 │
│ B级员工数: 5 人 │
│ 最佳绩效部门: 技术部 │
└─────────────────────────────────────────┘
本章小结
学习内容回顾
1. DataFrame 结构
- 由多个 Series 组成,共享同一个行索引
- 基本属性:shape, index, columns, dtypes, values
2. 列操作
- 选择列:
df['col']或df[['col1', 'col2']] - 添加列:直接赋值、assign() 方法
- 修改列:直接赋值、apply() 方法
- 删除列:del, pop(), drop()
3. 行操作
- 选择行:loc[label], iloc[pos]
- 添加行:loc[new_label] = values, concat()
- 修改行:loc[label] = values
- 删除行:drop(index=[...])
4. loc 和 iloc
- loc:标签索引,左闭右闭
- iloc:位置索引,左闭右开
- 布尔索引:
df[df['col'] > value]
5. 索引管理
- 设置索引:set_index()
- 重置索引:reset_index()
- 修改名称:index.name, columns, rename()
6. 条件筛选
- 单条件:
df[df['col'] > value] - 多条件:
&(与),|(或),~(非) - 特殊方法:isin(), between(), query()
7. 数据转换
- apply():按行/列应用函数
- applymap():逐元素应用函数
- map():Series 值映射
下章预告
第四章:数据读取与保存
将学习多种数据格式的读写操作,包括:
- CSV 文件读写
- Excel 文件读写
- JSON 数据读写
- SQL 数据库读写
课后练习
练习1:创建一个包含学生信息的 DataFrame(姓名、班级、语文、数学、英语成绩),然后:
- 添加"总分"和"平均分"列
- 筛选出平均分 >= 90 的优秀学生
- 按班级统计平均分
练习2:基于上面的员工数据 hr_df,完成以下操作:
- 将工号设置为索引
- 使用 loc 查询 E003 和 E007 的信息
- 使用 iloc 查询第 3-5 行的姓名和薪资列
- 删除"基础奖金"和"绩效系数"列
练习3:使用 query() 方法查询:
- 技术部且绩效评分 >= 4.5 的员工
- 销售部或财务部且薪资 > 10000 的员工
练习4 :创建一个销售数据 DataFrame,包含产品、地区、销售额,
然后使用 apply 计算每个地区的销售占比。