第一部分:Pandas 基础与核心概念
1.1 Pandas 简介与安装
Pandas 是 Python 的核心数据分析库,提供快速、灵活、易用的数据结构,特别适合处理表格数据和时间序列数据。
1.1.1 安装方法:
bash
# 基础安装
pip install pandas
# 完整安装(包含可选依赖)
pip install pandas[all]
# 通过conda安装
conda install pandas
# 安装指定版本
pip install pandas==2.1.0
1.1.2 安装验证:
python
import numpy as np
import pandas as pd
print(f"Numpy版本: {np.__version__}")
print(f"Pandas版本: {pd.__version__}")
Numpy版本: 2.3.3
Pandas版本: 2.3.3
1.2 核心数据结构:Series 和 DataFrame
1.2.1 Series:一维带标签数组
1.2.1.1 创建Series
- 从列表创建
python
# 1. 从列表创建
s1 = pd.Series([1, 3, 5, 7, 9])
print("基本Series:")
print(s1)
print(f"索引: {s1.index}")
print(f"值: {s1.values}")
基本Series:
0 1
1 3
2 5
3 7
4 9
dtype: int64
索引: RangeIndex(start=0, stop=5, step=1)
值: [1 3 5 7 9]
- 指定索引
python
# 2. 指定索引
s2 = pd.Series([10, 20, 30, 40],
index=['a', 'b', 'c', 'd'])
print("\n带自定义索引的Series:")
print(s2)
带自定义索引的Series:
a 10
b 20
c 30
d 40
dtype: int64
- 从字典创建
python
# 3. 从字典创建
data_dict = {'A': 100, 'B': 200, 'C': 300}
s3 = pd.Series(data_dict)
print("\n从字典创建的Series:")
print(s3)
从字典创建的Series:
A 100
B 200
C 300
dtype: int64
- 从标量创建
python
# 4. 从标量创建
s4 = pd.Series(5.0, index=['x', 'y', 'z'])
print("\n从标量创建的Series:")
print(s4)
从标量创建的Series:
x 5.0
y 5.0
z 5.0
dtype: float64
1.2.1.2 Series属性与方法
python
# Series属性
print("\n=== Series属性 ===")
print(f"数据类型: {s2.dtype}")
print(f"形状: {s2.shape}")
print(f"大小: {s2.size}")
print(f"索引类型: {type(s2.index)}")
print(f"是否唯一: {s2.is_unique}")
print(f"是否单调递增: {s2.is_monotonic_increasing}")
=== Series属性 ===
数据类型: int64
形状: (4,)
大小: 4
索引类型: <class 'pandas.core.indexes.base.Index'>
是否唯一: True
是否单调递增: True
python
# Series基本操作
print("\n=== Series操作 ===")
print(f"前2个元素:\n{s2.head(2)}")
print(f"后2个元素:\n{s2.tail(2)}")
print(f"描述性统计:\n{s2.describe()}")
print(f"求和: {s2.sum()}")
print(f"均值: {s2.mean()}")
print(f"标准差: {s2.std()}")
=== Series操作 ===
前2个元素:
a 10
b 20
dtype: int64
后2个元素:
c 30
d 40
dtype: int64
描述性统计:
count 4.000000
mean 25.000000
std 12.909944
min 10.000000
25% 17.500000
50% 25.000000
75% 32.500000
max 40.000000
dtype: float64
求和: 100
均值: 25.0
标准差: 12.909944487358056
python
# Series索引
print("\n=== Series索引 ===")
print(f"位置索引 s2.iloc[0]: {s2.iloc[0]}")
print(f"标签索引 s2['a']: {s2['a']}")
print(f"切片 s2[1:3]:\n{s2[1:3]}")
print(f"布尔索引 s2[s2 > 20]:\n{s2[s2 > 20]}")
=== Series索引 ===
位置索引 s2.iloc[0]: 10
标签索引 s2['a']: 10
切片 s2[1:3]:
b 20
c 30
dtype: int64
布尔索引 s2[s2 > 20]:
c 30
d 40
dtype: int64
python
# Series运算
s5 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s6 = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print("\n=== Series运算 ===")
print(f"加法:\n{s5 + s6}")
print(f"乘法1:\n{s5 * 2}")
print(f"乘法2:\n{s5 * s6}")
print(f"广播运算:\n{s5 + 100}")
=== Series运算 ===
加法:
a 11
b 22
c 33
d 44
dtype: int64
乘法1:
a 2
b 4
c 6
d 8
dtype: int64
乘法2:
a 10
b 40
c 90
d 160
dtype: int64
广播运算:
a 101
b 102
c 103
d 104
dtype: int64
python
# 自动索引对齐
s7 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s8 = pd.Series([4, 5, 6], index=['b', 'c', 'd'])
print(f"\n索引对齐加法:\n{s7 + s8}")
索引对齐加法:
a NaN
b 6.0
c 8.0
d NaN
dtype: float64
1.2.2 DataFrame:二维表格数据结构
1.2.2.1 创建DataFrame
- 从字典创建(最常用)
python
# 1. 从字典创建(最常用)
data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'年龄': [25, 30, 35, 28],
'城市': ['北京', '上海', '广州', '深圳'],
'工资': [15000, 20000, 18000, 22000],
'入职年份': [2019, 2018, 2020, 2017]
}
df = pd.DataFrame(data)
print("基本DataFrame:")
print(df)
基本DataFrame:
姓名 年龄 城市 工资 入职年份
0 张三 25 北京 15000 2019
1 李四 30 上海 20000 2018
2 王五 35 广州 18000 2020
3 赵六 28 深圳 22000 2017
- 指定行索引
python
# 2. 指定行索引
df_with_index = pd.DataFrame(data,
index=['E001', 'E002', 'E003', 'E004'])
print("带自定义索引的DataFrame:")
print(df_with_index)
带自定义索引的DataFrame:
姓名 年龄 城市 工资 入职年份
E001 张三 25 北京 15000 2019
E002 李四 30 上海 20000 2018
E003 王五 35 广州 18000 2020
E004 赵六 28 深圳 22000 2017
- 从列表创建
python
# 3. 从列表创建
data_list = [
['张三', 25, '北京', 15000, 2019],
['李四', 30, '上海', 20000, 2018],
['王五', 35, '广州', 18000, 2020],
['赵六', 28, '深圳', 22000, 2017]
]
columns = ['姓名', '年龄', '城市', '工资', '入职年份']
df_from_list = pd.DataFrame(data_list, columns=columns)
print("从列表创建的DataFrame:")
print(df_from_list)
从列表创建的DataFrame:
姓名 年龄 城市 工资 入职年份
0 张三 25 北京 15000 2019
1 李四 30 上海 20000 2018
2 王五 35 广州 18000 2020
3 赵六 28 深圳 22000 2017
- 从NumPy数组创建
python
np_array = np.random.randn(5, 4)
df_from_numpy = pd.DataFrame(np_array,
columns=['A', 'B', 'C', 'D'])
print("从NumPy数组创建的DataFrame:")
print(df_from_numpy)
从NumPy数组创建的DataFrame:
A B C D
0 -1.927079 -0.698070 1.029268 0.430813
1 -0.642815 1.747273 0.738184 0.492974
2 -1.392473 -0.219943 0.868221 0.715244
3 -0.208143 2.142948 -0.029559 0.806000
4 -2.601245 -0.076942 -1.085496 1.108939
- 从字典列表创建
python
# 5. 从字典列表创建
dict_list = [
{'姓名': '张三', '年龄': 25, '城市': '北京'},
{'姓名': '李四', '年龄': 30, '城市': '上海'},
{'姓名': '王五', '年龄': 35, '城市': '广州'}
]
df_from_dict_list = pd.DataFrame(dict_list)
print("从字典列表创建的DataFrame:")
print(df_from_dict_list)
从字典列表创建的DataFrame:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
2 王五 35 广州
1.2.2.2 DataFrame 基本属性和方法
python
# DataFrame属性
print("=== DataFrame属性 ===")
print(f"形状: {df.shape}")
print(f"维度: {df.ndim}")
print(f"大小: {df.size}")
print(f"列名: {df.columns.tolist()}")
print(f"索引: {df.index}")
print(f"数据类型:\n{df.dtypes}")
print(f"内存使用:\n{df.memory_usage()}")
=== DataFrame属性 ===
形状: (4, 5)
维度: 2
大小: 20
列名: ['姓名', '年龄', '城市', '工资', '入职年份']
索引: RangeIndex(start=0, stop=4, step=1)
数据类型:
姓名 object
年龄 int64
城市 object
工资 int64
入职年份 int64
dtype: object
内存使用:
Index 132
姓名 32
年龄 32
城市 32
工资 32
入职年份 32
dtype: int64
python
# DataFrame信息概览
print("=== 数据概览 ===")
print("前3行:")
print(df.head(3))
print("\n后2行:")
print(df.tail(2))
print("\n基本信息:")
df.info()
print("\n描述性统计:")
print(df.describe())
print("\n数值列描述性统计:")
print(df.describe(include=[np.number]))
print("\n非数值列描述性统计:")
print(df.describe(exclude=[np.number]))
=== 数据概览 ===
前3行:
姓名 年龄 城市 工资 入职年份
0 张三 25 北京 15000 2019
1 李四 30 上海 20000 2018
2 王五 35 广州 18000 2020
后2行:
姓名 年龄 城市 工资 入职年份
2 王五 35 广州 18000 2020
3 赵六 28 深圳 22000 2017
基本信息:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 姓名 4 non-null object
1 年龄 4 non-null int64
2 城市 4 non-null object
3 工资 4 non-null int64
4 入职年份 4 non-null int64
dtypes: int64(3), object(2)
memory usage: 292.0+ bytes
描述性统计:
年龄 工资 入职年份
count 4.000000 4.000000 4.000000
mean 29.500000 18750.000000 2018.500000
std 4.203173 2986.078811 1.290994
min 25.000000 15000.000000 2017.000000
25% 27.250000 17250.000000 2017.750000
50% 29.000000 19000.000000 2018.500000
75% 31.250000 20500.000000 2019.250000
max 35.000000 22000.000000 2020.000000
数值列描述性统计:
年龄 工资 入职年份
count 4.000000 4.000000 4.000000
mean 29.500000 18750.000000 2018.500000
std 4.203173 2986.078811 1.290994
min 25.000000 15000.000000 2017.000000
25% 27.250000 17250.000000 2017.750000
50% 29.000000 19000.000000 2018.500000
75% 31.250000 20500.000000 2019.250000
max 35.000000 22000.000000 2020.000000
非数值列描述性统计:
姓名 城市
count 4 4
unique 4 4
top 张三 北京
freq 1 1
第二部分:数据查看与选择
2.1 数据查看方法
python
# 创建示例DataFrame
np.random.seed(42)
data = {
'ID': [f'E{100+i}' for i in range(10)],
'姓名': ['张三', '李四', '王五', '赵六', '钱七',
'孙八', '周九', '吴十', '郑一', '王二'],
'年龄': np.random.randint(20, 50, 10),
'部门': ['技术部', '市场部', '技术部', '人事部', '市场部',
'技术部', '人事部', '市场部', '技术部', '人事部'],
'工资': np.random.randint(8000, 25000, 10),
'奖金': np.random.randint(1000, 5000, 10),
'入职日期': pd.date_range('2018-01-01', periods=10, freq='ME')
}
df = pd.DataFrame(data)
df['总薪资'] = df['工资'] + df['奖金']
df['绩效'] = np.random.choice(['A', 'B', 'C', 'D'], 10)
print("完整DataFrame:")
print(df)
完整DataFrame:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
1 E101 李四 39 市场部 12426 3324 2018-02-28 15750 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
3 E103 赵六 34 人事部 19363 1459 2018-04-30 20822 B
4 E104 钱七 30 市场部 24023 4385 2018-05-31 28408 D
5 E105 孙八 27 技术部 16322 1021 2018-06-30 17343 D
6 E106 周九 48 人事部 9685 3300 2018-07-31 12985 D
7 E107 吴十 40 市场部 8769 1747 2018-08-31 10516 D
8 E108 郑一 26 技术部 10433 3904 2018-09-30 14337 C
9 E109 王二 45 人事部 13311 4632 2018-10-31 17943 B
python
# 查看数据
print("=== 数据查看方法 ===")
print("1. 查看前N行:")
print(df.head(3))
print("\n2. 查看后N行:")
print(df.tail(3))
print("\n3. 随机查看N行:")
print(df.sample(3))
print("\n4. 查看特定行:")
print(df.iloc[[0, 3, 5]]) # 查看第0,3,5行
=== 数据查看方法 ===
1. 查看前N行:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
1 E101 李四 39 市场部 12426 3324 2018-02-28 15750 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
2. 查看后N行:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
7 E107 吴十 40 市场部 8769 1747 2018-08-31 10516 D
8 E108 郑一 26 技术部 10433 3904 2018-09-30 14337 C
9 E109 王二 45 人事部 13311 4632 2018-10-31 17943 B
3. 随机查看N行:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
1 E101 李四 39 市场部 12426 3324 2018-02-28 15750 C
8 E108 郑一 26 技术部 10433 3904 2018-09-30 14337 C
7 E107 吴十 40 市场部 8769 1747 2018-08-31 10516 D
4. 查看特定行:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
3 E103 赵六 34 人事部 19363 1459 2018-04-30 20822 B
5 E105 孙八 27 技术部 16322 1021 2018-06-30 17343 D
python
print("\n5. 查看列:")
print("所有列:", df.columns.tolist())
print("特定几列:")
print(df[['姓名', '部门', '工资']])
5. 查看列:
所有列: ['ID', '姓名', '年龄', '部门', '工资', '奖金', '入职日期', '总薪资', '绩效']
特定几列:
姓名 部门 工资
0 张三 技术部 24850
1 李四 市场部 12426
2 王五 技术部 22423
3 赵六 人事部 19363
4 钱七 市场部 24023
5 孙八 技术部 16322
6 周九 人事部 9685
7 吴十 市场部 8769
8 郑一 技术部 10433
9 王二 人事部 13311
python
print("\n6. 数据信息:")
df.info()
6. 数据信息:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 10 non-null object
1 姓名 10 non-null object
2 年龄 10 non-null int32
3 部门 10 non-null object
4 工资 10 non-null int32
5 奖金 10 non-null int32
6 入职日期 10 non-null datetime64[ns]
7 总薪资 10 non-null int32
8 绩效 10 non-null object
dtypes: datetime64[ns](1), int32(4), object(4)
memory usage: 692.0+ bytes
python
print("\n7. 统计摘要:")
df.describe(include='all')
7. 统计摘要:
| | ID | 姓名 | 年龄 | 部门 | 工资 | 奖金 | 入职日期 | 总薪资 | 绩效 |
| count | 10 | 10 | 10.00000 | 10 | 10.000000 | 10.000000 | 10 | 10.000000 | 10 |
| unique | 10 | 10 | NaN | 3 | NaN | NaN | NaN | NaN | 3 |
| top | E100 | 张三 | NaN | 技术部 | NaN | NaN | NaN | NaN | C |
| freq | 1 | 1 | NaN | 4 | NaN | NaN | NaN | NaN | 4 |
| mean | NaN | NaN | 36.30000 | NaN | 16160.500000 | 2791.100000 | 2018-06-15 09:36:00 | 18951.600000 | NaN |
| min | NaN | NaN | 26.00000 | NaN | 8769.000000 | 1021.000000 | 2018-01-31 00:00:00 | 10516.000000 | NaN |
| 25% | NaN | NaN | 27.75000 | NaN | 10931.250000 | 1799.000000 | 2018-04-07 12:00:00 | 14690.250000 | NaN |
| 50% | NaN | NaN | 36.50000 | NaN | 14816.500000 | 2742.000000 | 2018-06-15 00:00:00 | 17643.000000 | NaN |
| 75% | NaN | NaN | 43.75000 | NaN | 21658.000000 | 3759.000000 | 2018-08-23 06:00:00 | 23660.750000 | NaN |
| max | NaN | NaN | 48.00000 | NaN | 24850.000000 | 4632.000000 | 2018-10-31 00:00:00 | 28408.000000 | NaN |
| std | NaN | NaN | 8.90755 | NaN | 6130.652317 | 1281.338749 | NaN | 6041.436956 | NaN |
|---|
python
print("\n8. 唯一值统计:")
for col in ['部门', '绩效']:
print(f"{col}的唯一值: {df[col].unique()}")
print(f"{col}的值计数:\n{df[col].value_counts()}")
print()
8. 唯一值统计:
部门的唯一值: ['技术部' '市场部' '人事部']
部门的值计数:
部门
技术部 4
市场部 3
人事部 3
Name: count, dtype: int64
绩效的唯一值: ['C' 'B' 'D']
绩效的值计数:
绩效
C 4
D 4
B 2
Name: count, dtype: int64
2.2 数据选择与索引
2.2.1 列选择
python
# 列选择
print("=== 列选择 ===")
print("1. 单列选择(返回Series):")
names = df['姓名']
print(type(names))
print(names)
print("\n2. 多列选择(返回DataFrame):")
subset = df[['姓名', '部门', '工资']]
print(type(subset))
print(subset)
print("\n3. 点号表示法(仅当列名是有效的Python标识符):")
ages = df.年龄
print(ages)
=== 列选择 ===
1. 单列选择(返回Series):
<class 'pandas.core.series.Series'>
0 张三
1 李四
2 王五
3 赵六
4 钱七
5 孙八
6 周九
7 吴十
8 郑一
9 王二
Name: 姓名, dtype: object
2. 多列选择(返回DataFrame):
<class 'pandas.core.frame.DataFrame'>
姓名 部门 工资
0 张三 技术部 24850
1 李四 市场部 12426
2 王五 技术部 22423
3 赵六 人事部 19363
4 钱七 市场部 24023
5 孙八 技术部 16322
6 周九 人事部 9685
7 吴十 市场部 8769
8 郑一 技术部 10433
9 王二 人事部 13311
3. 点号表示法(仅当列名是有效的Python标识符):
0 26
1 39
2 48
3 34
4 30
5 27
6 48
7 40
8 26
9 45
Name: 年龄, dtype: int32
2.2.2 行选择
python
# 行选择
print("\n=== 行选择 ===")
print("1. 通过索引标签选择:")
print(df.loc[0]) # 选择第一行
print(df.loc[[0, 2, 4]]) # 选择多行
print("\n2. 通过位置选择:")
print(df.iloc[0]) # 选择第一行
print(df.iloc[0:3]) # 选择前3行
print(df.iloc[[0, 2, 4]]) # 选择第0,2,4行
print("\n3. 条件选择:")
# 单条件
tech_dept = df[df['部门'] == '技术部']
print("技术部员工:")
print(tech_dept)
# 多条件
high_salary_tech = df[(df['部门'] == '技术部') & (df['工资'] > 15000)]
print("\n技术部高薪员工:")
print(high_salary_tech)
# 复杂条件
condition = (df['年龄'] > 30) | (df['总薪资'] > 20000)
result = df[condition]
print("\n年龄>30或总薪资>20000的员工:")
print(result)
# 使用query方法
result_query = df.query('年龄 > 30 and 工资 > 15000')
print("\n使用query方法:")
print(result_query)
=== 行选择 ===
1. 通过索引标签选择:
ID E100
姓名 张三
年龄 26
部门 技术部
工资 24850
奖金 1955
入职日期 2018-01-31 00:00:00
总薪资 26805
绩效 C
Name: 0, dtype: object
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
4 E104 钱七 30 市场部 24023 4385 2018-05-31 28408 D
2. 通过位置选择:
ID E100
姓名 张三
年龄 26
部门 技术部
工资 24850
奖金 1955
入职日期 2018-01-31 00:00:00
总薪资 26805
绩效 C
Name: 0, dtype: object
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
1 E101 李四 39 市场部 12426 3324 2018-02-28 15750 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
4 E104 钱七 30 市场部 24023 4385 2018-05-31 28408 D
3. 条件选择:
技术部员工:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
5 E105 孙八 27 技术部 16322 1021 2018-06-30 17343 D
8 E108 郑一 26 技术部 10433 3904 2018-09-30 14337 C
技术部高薪员工:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
5 E105 孙八 27 技术部 16322 1021 2018-06-30 17343 D
年龄>30或总薪资>20000的员工:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
1 E101 李四 39 市场部 12426 3324 2018-02-28 15750 C
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
3 E103 赵六 34 人事部 19363 1459 2018-04-30 20822 B
4 E104 钱七 30 市场部 24023 4385 2018-05-31 28408 D
6 E106 周九 48 人事部 9685 3300 2018-07-31 12985 D
7 E107 吴十 40 市场部 8769 1747 2018-08-31 10516 D
9 E109 王二 45 人事部 13311 4632 2018-10-31 17943 B
使用query方法:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
3 E103 赵六 34 人事部 19363 1459 2018-04-30 20822 B
2.3 高级索引技巧
2.3.1 使用loc进行高级选择
python
# 使用loc进行高级选择
print("=== 使用loc进行高级选择 ===")
# 选择特定行和列
print("选择特定行和列:")
print(df.loc[[0, 2, 4], ['姓名', '部门', '工资']])
# 使用切片
print("\n使用行切片和列选择:")
print(df.loc[2:5, '姓名':'工资'])
# 使用条件
print("\n使用条件选择:")
print(df.loc[df['年龄'] > 30, ['姓名', '年龄', '部门']])
=== 使用loc进行高级选择 ===
选择特定行和列:
姓名 部门 工资
0 张三 技术部 24850
2 王五 技术部 22423
4 钱七 市场部 24023
使用行切片和列选择:
姓名 年龄 部门 工资
2 王五 48 技术部 22423
3 赵六 34 人事部 19363
4 钱七 30 市场部 24023
5 孙八 27 技术部 16322
使用条件选择:
姓名 年龄 部门
1 李四 39 市场部
2 王五 48 技术部
3 赵六 34 人事部
6 周九 48 人事部
7 吴十 40 市场部
9 王二 45 人事部
2.3.2 使用iloc进行位置选择
python
# 使用iloc进行位置选择
print("\n=== 使用iloc进行位置选择 ===")
print("选择特定位置:")
print(df.iloc[0, 1]) # 第0行第1列(单个元素数据值)
print(df.iloc[0:3, 0:3]) # 前3行前3列
print(df.iloc[[0, 2, 4], [1, 3, 5]]) # 不连续行列
=== 使用iloc进行位置选择 ===
选择特定位置:
张三
ID 姓名 年龄
0 E100 张三 26
1 E101 李四 39
2 E102 王五 48
姓名 部门 奖金
0 张三 技术部 1955
2 王五 技术部 2184
4 钱七 市场部 4385
2.3.3 使用at和iat快速访问
快速标量访问
python
# 使用at和iat快速访问
print("\n=== 快速标量访问 ===")
print("使用at(标签访问):")
print(df.at[0, '姓名']) # 比df.loc[0, '姓名']更快
print("\n使用iat(位置访问):")
print(df.iat[0, 1]) # 比df.iloc[0, 1]更快
=== 快速标量访问 ===
使用at(标签访问):
张三
使用iat(位置访问):
张三
2.3.4 布尔索引
python
# 布尔索引
print("\n=== 布尔索引 ===")
# 创建布尔序列
is_tech = df['部门'] == '技术部'
high_perf = df['绩效'].isin(['A', 'B'])
age_over_30 = df['年龄'] > 30
# 组合条件
combined = df[is_tech & high_perf & age_over_30]
print("技术部、高绩效、年龄>30的员工:")
print(combined)
# 使用between
print("\n使用between:")
age_range = df[df['年龄'].between(25, 35)]
print("年龄在25-35之间的员工:")
print(age_range)
# 使用str访问器
print("\n使用str访问器:")
name_start_wang = df[df['姓名'].str.startswith('王')]
print("姓王的员工:")
print(name_start_wang)
=== 布尔索引 ===
技术部、高绩效、年龄>30的员工:
Empty DataFrame
Columns: [ID, 姓名, 年龄, 部门, 工资, 奖金, 入职日期, 总薪资, 绩效]
Index: []
使用between:
年龄在25-35之间的员工:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
0 E100 张三 26 技术部 24850 1955 2018-01-31 26805 C
3 E103 赵六 34 人事部 19363 1459 2018-04-30 20822 B
4 E104 钱七 30 市场部 24023 4385 2018-05-31 28408 D
5 E105 孙八 27 技术部 16322 1021 2018-06-30 17343 D
8 E108 郑一 26 技术部 10433 3904 2018-09-30 14337 C
使用str访问器:
姓王的员工:
ID 姓名 年龄 部门 工资 奖金 入职日期 总薪资 绩效
2 E102 王五 48 技术部 22423 2184 2018-03-31 24607 C
9 E109 王二 45 人事部 13311 4632 2018-10-31 17943 B
第三部分:数据处理与清洗
3.1 缺失值处理
python
# 创建包含缺失值的数据
data_with_na = {
'姓名': ['张三', '李四', '王五', '赵六', '钱七'],
'年龄': [25, np.nan, 35, 28, np.nan],
'工资': [15000, 20000, np.nan, 22000, 18000],
'部门': ['技术部', '市场部', None, '人事部', '技术部'],
'奖金': [3000, np.nan, 2000, np.nan, 2500]
}
df_na = pd.DataFrame(data_with_na)
print("包含缺失值的数据:")
print(df_na)
包含缺失值的数据:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 NaN 20000.0 市场部 NaN
2 王五 35.0 NaN None 2000.0
3 赵六 28.0 22000.0 人事部 NaN
4 钱七 NaN 18000.0 技术部 2500.0
3.1.1 检测缺失值
python
# 检测缺失值
print("=== 缺失值检测 ===")
print("是否存在缺失值:")
print(df_na.isna().any())
print("\n每列缺失值数量:")
print(df_na.isna().sum())
print("\n每行缺失值数量:")
print(df_na.isna().sum(axis=1))
print("\n缺失值比例:")
print(df_na.isna().mean())
=== 缺失值检测 ===
是否存在缺失值:
姓名 False
年龄 True
工资 True
部门 True
奖金 True
dtype: bool
每列缺失值数量:
姓名 0
年龄 2
工资 1
部门 1
奖金 2
dtype: int64
每行缺失值数量:
0 0
1 2
2 2
3 1
4 1
dtype: int64
缺失值比例:
姓名 0.0
年龄 0.4
工资 0.2
部门 0.2
奖金 0.4
dtype: float64
3.1.2 缺失值处理
3.1.2.1 删除缺失值
- 删除缺失行
python
# 处理缺失值
print("\n=== 缺失值处理 ===")
# 1. 删除缺失值
print("1. 删除缺失行:")
df_dropna_rows = df_na.dropna()
print(df_dropna_rows)
=== 缺失值处理 ===
1. 删除缺失行:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
- 删除缺失列
python
print("\n2. 删除缺失列:")
df_dropna_cols = df_na.dropna(axis=1)
print(df_dropna_cols)
2. 删除缺失列:
姓名
0 张三
1 李四
2 王五
3 赵六
4 钱七
- 删除全为缺失值的行
python
print("\n3. 删除全为缺失值的行:")
df_dropna_all = df_na.dropna(how='all')
print(df_dropna_all)
3. 删除全为缺失值的行:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 NaN 20000.0 市场部 NaN
2 王五 35.0 NaN None 2000.0
3 赵六 28.0 22000.0 人事部 NaN
4 钱七 NaN 18000.0 技术部 2500.0
- 删除特定列有缺失值的行
python
print("\n4. 删除特定列有缺失值的行:")
df_dropna_subset = df_na.dropna(subset=['年龄', '工资'])
print(df_dropna_subset)
4. 删除特定列有缺失值的行:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
3 赵六 28.0 22000.0 人事部 NaN
3.1.2.2 填充缺失值
- 用固定值填充
python
# 用固定值填充
df_fill_value = df_na.fillna(0)
print("用0填充:")
print(df_fill_value)
用0填充:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 0.0 20000.0 市场部 0.0
2 王五 35.0 0.0 0 2000.0
3 赵六 28.0 22000.0 人事部 0.0
4 钱七 0.0 18000.0 技术部 2500.0
- 用前值填充
python
# 用前向填充
df_ffill = df_na.ffill()
print("\n前向填充:")
print(df_ffill)
前向填充:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 25.0 20000.0 市场部 3000.0
2 王五 35.0 20000.0 市场部 2000.0
3 赵六 28.0 22000.0 人事部 2000.0
4 钱七 28.0 18000.0 技术部 2500.0
- 用后值填充
python
# 用后向填充
df_bfill = df_na.bfill()
print("\n后向填充:")
print(df_bfill)
后向填充:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 35.0 20000.0 市场部 2000.0
2 王五 35.0 22000.0 人事部 2000.0
3 赵六 28.0 22000.0 人事部 2500.0
4 钱七 NaN 18000.0 技术部 2500.0
- 用均值填充
python
# 用均值填充
df_fill_mean = df_na.copy()
df_fill_mean['年龄'] = df_fill_mean['年龄'].fillna(df_fill_mean['年龄'].mean())
df_fill_mean['工资'] = df_fill_mean['工资'].fillna(df_fill_mean['工资'].mean())
print("\n用均值填充数值列:")
print(df_fill_mean)
用均值填充数值列:
姓名 年龄 工资 部门 奖金
0 张三 25.000000 15000.0 技术部 3000.0
1 李四 29.333333 20000.0 市场部 NaN
2 王五 35.000000 18750.0 None 2000.0
3 赵六 28.000000 22000.0 人事部 NaN
4 钱七 29.333333 18000.0 技术部 2500.0
- 用众数填充
python
# 用众数填充
mode_dept = df_na['部门'].mode()[0]
df_fill_mode = df_na.copy()
df_fill_mode['部门'] = df_fill_mode['部门'].fillna(mode_dept)
print(f"\n用众数'{mode_dept}'填充部门列:")
print(df_fill_mode)
用众数'技术部'填充部门列:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 NaN 20000.0 市场部 NaN
2 王五 35.0 NaN 技术部 2000.0
3 赵六 28.0 22000.0 人事部 NaN
4 钱七 NaN 18000.0 技术部 2500.0
3.1.2.3 插值
python
print("\n插值法:")
df_interp = df_na.copy()
df_interp['年龄'] = df_interp['年龄'].interpolate()
print("线性插值:")
print(df_interp)
插值法:
线性插值:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
1 李四 30.0 20000.0 市场部 NaN
2 王五 35.0 NaN None 2000.0
3 赵六 28.0 22000.0 人事部 NaN
4 钱七 28.0 18000.0 技术部 2500.0
3.1.2.4 使用KNN填充
python
try:
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=2)
df_knn = pd.DataFrame(imputer.fit_transform(df_na.select_dtypes(include=[np.number])),
columns=df_na.select_dtypes(include=[np.number]).columns)
print("\nKNN填充:")
print(df_knn)
except ImportError:
print("\n需要scikit-learn进行KNN填充")
KNN填充:
年龄 工资 奖金
0 25.0 15000.0 3000.0
1 26.5 20000.0 2750.0
2 35.0 20000.0 2000.0
3 28.0 22000.0 2250.0
4 30.0 18000.0 2500.0
3.2 数据类型转换
python
# 数据类型转换
print("=== 数据类型转换 ===")
print("原始数据类型:")
print(df_na.dtypes)
# 转换数据类型
df_convert = df_na.copy()
df_convert['年龄'] = df_convert['年龄'].astype('float32')
df_convert['工资'] = pd.to_numeric(df_convert['工资'], errors='coerce')
print("\n转换后数据类型:")
print(df_convert.dtypes)
# 使用分类数据类型
df_convert['部门'] = df_convert['部门'].astype('category')
print("\n部门列的分类信息:")
print(df_convert['部门'].cat.categories)
print("内存使用对比:")
print(f"原内存: {df_na['部门'].memory_usage(deep=True)} bytes")
print(f"分类内存: {df_convert['部门'].memory_usage(deep=True)} bytes")
# 日期时间转换
df_convert['入职日期'] = pd.to_datetime('2023-01-01')
print("\n添加日期列:")
print(df_convert.dtypes)
=== 数据类型转换 ===
原始数据类型:
姓名 object
年龄 float64
工资 float64
部门 object
奖金 float64
dtype: object
转换后数据类型:
姓名 object
年龄 float32
工资 float64
部门 object
奖金 float64
dtype: object
部门列的分类信息:
Index(['人事部', '市场部', '技术部'], dtype='object')
内存使用对比:
原内存: 444 bytes
分类内存: 461 bytes
添加日期列:
姓名 object
年龄 float32
工资 float64
部门 category
奖金 float64
入职日期 datetime64[ns]
dtype: object
3.3 数据去重
python
# 创建重复数据
duplicate_data = {
'姓名': ['张三', '李四', '张三', '王五', '李四', '赵六'],
'年龄': [25, 30, 25, 35, 30, 28],
'城市': ['北京', '上海', '北京', '广州', '上海', '深圳']
}
df_dup = pd.DataFrame(duplicate_data)
print("包含重复值的数据:")
print(df_dup)
包含重复值的数据:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
2 张三 25 北京
3 王五 35 广州
4 李四 30 上海
5 赵六 28 深圳
python
# 检测重复值
print("检测重复行:")
# 所有列都一样判定为重复行
print(df_dup.duplicated())
print("\n检测特定列重复:")
print(df_dup.duplicated(subset=['姓名']))
检测重复行:
0 False
1 False
2 True
3 False
4 True
5 False
dtype: bool
检测特定列重复:
0 False
1 False
2 True
3 False
4 True
5 False
dtype: bool
python
# 查看重复的数据
df_dup[df_dup.duplicated()]
| | 姓名 | 年龄 | 城市 |
| 2 | 张三 | 25 | 北京 |
| 4 | 李四 | 30 | 上海 |
|---|
python
# 删除重复值
print("\n删除所有重复行:")
df_no_dup = df_dup.drop_duplicates()
print(df_no_dup)
删除所有重复行:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
3 王五 35 广州
5 赵六 28 深圳
python
print("\n删除特定列重复(保留第一个):")
df_no_dup_name = df_dup.drop_duplicates(subset=['姓名'])
print(df_no_dup_name)
删除特定列重复(保留第一个):
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
3 王五 35 广州
5 赵六 28 深圳
python
print("\n删除特定列重复(保留最后一个):")
df_no_dup_name_last = df_dup.drop_duplicates(subset=['姓名'], keep='last')
print(df_no_dup_name_last)
删除特定列重复(保留最后一个):
姓名 年龄 城市
2 张三 25 北京
3 王五 35 广州
4 李四 30 上海
5 赵六 28 深圳
python
print("\n删除所有重复(不保留任何重复):")
df_no_dup_all = df_dup.drop_duplicates(keep=False)
print(df_no_dup_all)
删除所有重复(不保留任何重复):
姓名 年龄 城市
3 王五 35 广州
5 赵六 28 深圳
3.4 数据转换
python
# 创建示例数据
df_trans = pd.DataFrame({
'订单号': ['ORD001', 'ORD002', 'ORD003', 'ORD004'],
'金额': [1500.5, 2800.0, 3200.75, 980.25],
'日期': ['2023-01-15', '2023-02-20', '2023-01-15', '2023-03-10'],
'产品': ['A', 'B', 'A', 'C']
})
print("原始数据:")
print(df_trans)
原始数据:
订单号 金额 日期 产品
0 ORD001 1500.50 2023-01-15 A
1 ORD002 2800.00 2023-02-20 B
2 ORD003 3200.75 2023-01-15 A
3 ORD004 980.25 2023-03-10 C
3.4.1 重命名列
python
# 重命名列
print("=== 重命名列 ===")
df_renamed = df_trans.rename(columns={
'订单号': 'order_id',
'金额': 'amount',
'日期': 'date',
'产品': 'product'
})
print("重命名后:")
print(df_renamed)
=== 重命名列 ===
重命名后:
order_id amount date product
0 ORD001 1500.50 2023-01-15 A
1 ORD002 2800.00 2023-02-20 B
2 ORD003 3200.75 2023-01-15 A
3 ORD004 980.25 2023-03-10 C
3.4.2 重命名索引
python
# 重命名索引
df_renamed_idx = df_trans.rename(index=lambda x: f'row_{x}')
print("重命名索引后:")
print(df_renamed_idx)
重命名索引后:
订单号 金额 日期 产品
row_0 ORD001 1500.50 2023-01-15 A
row_1 ORD002 2800.00 2023-02-20 B
row_2 ORD003 3200.75 2023-01-15 A
row_3 ORD004 980.25 2023-03-10 C
3.4.3 重置索引
python
# 重置索引
print("=== 重置索引 ===")
df_reset = df_trans.reset_index()
print("重置索引(保留原索引):")
print(df_reset)
=== 重置索引 ===
重置索引(保留原索引):
index 订单号 金额 日期 产品
0 0 ORD001 1500.50 2023-01-15 A
1 1 ORD002 2800.00 2023-02-20 B
2 2 ORD003 3200.75 2023-01-15 A
3 3 ORD004 980.25 2023-03-10 C
python
df_reset_drop = df_trans.reset_index(drop=True)
print("\n重置索引(删除原索引):")
print(df_reset_drop)
重置索引(删除原索引):
订单号 金额 日期 产品
0 ORD001 1500.50 2023-01-15 A
1 ORD002 2800.00 2023-02-20 B
2 ORD003 3200.75 2023-01-15 A
3 ORD004 980.25 2023-03-10 C
3.4.4 设置索引
python
# 设置索引
print("=== 设置索引 ===")
df_set_index = df_trans.set_index('订单号')
print("设置'订单号'为索引:")
print(df_set_index)
=== 设置索引 ===
设置'订单号'为索引:
金额 日期 产品
订单号
ORD001 1500.50 2023-01-15 A
ORD002 2800.00 2023-02-20 B
ORD003 3200.75 2023-01-15 A
ORD004 980.25 2023-03-10 C
3.4.5 添加列
python
# 添加列
print("=== 添加列 ===")
df_trans['折扣'] = 0.1
df_trans['实际金额'] = df_trans['金额'] * (1 - df_trans['折扣'])
print("添加折扣和实际金额列:")
print(df_trans)
=== 添加列 ===
添加折扣和实际金额列:
订单号 金额 日期 产品 折扣 实际金额
0 ORD001 1500.50 2023-01-15 A 0.1 1350.450
1 ORD002 2800.00 2023-02-20 B 0.1 2520.000
2 ORD003 3200.75 2023-01-15 A 0.1 2880.675
3 ORD004 980.25 2023-03-10 C 0.1 882.225
3.4.6 删除列
python
# 删除列
print("=== 删除列 ===")
df_dropped = df_trans.drop(columns=['折扣'])
print("删除折扣列:")
print(df_dropped)
=== 删除列 ===
删除折扣列:
订单号 金额 日期 产品 实际金额
0 ORD001 1500.50 2023-01-15 A 1350.450
1 ORD002 2800.00 2023-02-20 B 2520.000
2 ORD003 3200.75 2023-01-15 A 2880.675
3 ORD004 980.25 2023-03-10 C 882.225
3.4.7 修改值
python
# 修改值
print("=== 修改值 ===")
df_modified = df_trans.copy()
df_modified.loc[df_modified['产品'] == 'A', '金额'] *= 1.1
print("产品A的金额增加10%:")
print(df_modified)
=== 修改值 ===
产品A的金额增加10%:
订单号 金额 日期 产品 折扣 实际金额
0 ORD001 1650.550 2023-01-15 A 0.1 1350.450
1 ORD002 2800.000 2023-02-20 B 0.1 2520.000
2 ORD003 3520.825 2023-01-15 A 0.1 2880.675
3 ORD004 980.250 2023-03-10 C 0.1 882.225
3.4.8 应用函数
python
# 应用函数
print("=== 应用函数 ===")
# 对单列应用函数
df_trans['金额_千元'] = df_trans['金额'].apply(lambda x: f'{x/1000:.2f}K')
print("金额转换为千元:")
print(df_trans[['金额', '金额_千元']])
=== 应用函数 ===
金额转换为千元:
金额 金额_千元
0 1500.50 1.50K
1 2800.00 2.80K
2 3200.75 3.20K
3 980.25 0.98K
python
# 对多列应用函数
df_trans['大额订单'] = df_trans.apply(
lambda row: '是' if row['金额'] > 2000 else '否', axis=1
)
print("\n标记大额订单:")
print(df_trans[['订单号', '金额', '大额订单']])
标记大额订单:
订单号 金额 大额订单
0 ORD001 1500.50 否
1 ORD002 2800.00 是
2 ORD003 3200.75 是
3 ORD004 980.25 否
3.4.9 使用向量化操作
python
# 使用向量化操作
df_trans['税率'] = 0.13
df_trans['税额'] = df_trans['金额'] * df_trans['税率']
df_trans['税后金额'] = df_trans['金额'] + df_trans['税额']
print("\n税后金额计算:")
print(df_trans[['订单号', '金额', '税额', '税后金额']])
税后金额计算:
订单号 金额 税额 税后金额
0 ORD001 1500.50 195.0650 1695.5650
1 ORD002 2800.00 364.0000 3164.0000
2 ORD003 3200.75 416.0975 3616.8475
3 ORD004 980.25 127.4325 1107.6825
第四部分:数据操作与计算
4.1 数据排序
python
# 创建示例数据
sales_data = {
'销售员': ['张三', '李四', '王五', '赵六', '钱七'],
'销售额': [150000, 220000, 180000, 95000, 310000],
'订单数': [45, 38, 52, 28, 61],
'地区': ['北京', '上海', '北京', '广州', '上海'],
'季度': ['Q1', 'Q2', 'Q1', 'Q3', 'Q2']
}
sales_df = pd.DataFrame(sales_data)
sales_df['客单价'] = sales_df['销售额'] / sales_df['订单数']
print("销售数据:")
print(sales_df)
销售数据:
销售员 销售额 订单数 地区 季度 客单价
0 张三 150000 45 北京 Q1 3333.333333
1 李四 220000 38 上海 Q2 5789.473684
2 王五 180000 52 北京 Q1 3461.538462
3 赵六 95000 28 广州 Q3 3392.857143
4 钱七 310000 61 上海 Q2 5081.967213
4.1.1 单列排序
python
# 单列排序
print("=== 单列排序 ===")
df_sorted_sales = sales_df.sort_values('销售额')
print("按销售额升序排序:")
print(df_sorted_sales)
df_sorted_sales_desc = sales_df.sort_values('销售额', ascending=False)
print("\n按销售额降序排序:")
print(df_sorted_sales_desc)
=== 单列排序 ===
按销售额升序排序:
销售员 销售额 订单数 地区 季度 客单价
3 赵六 95000 28 广州 Q3 3392.857143
0 张三 150000 45 北京 Q1 3333.333333
2 王五 180000 52 北京 Q1 3461.538462
1 李四 220000 38 上海 Q2 5789.473684
4 钱七 310000 61 上海 Q2 5081.967213
按销售额降序排序:
销售员 销售额 订单数 地区 季度 客单价
4 钱七 310000 61 上海 Q2 5081.967213
1 李四 220000 38 上海 Q2 5789.473684
2 王五 180000 52 北京 Q1 3461.538462
0 张三 150000 45 北京 Q1 3333.333333
3 赵六 95000 28 广州 Q3 3392.857143
4.1.2 多列排序
python
# 多列排序
print("=== 多列排序 ===")
df_sorted_multi = sales_df.sort_values(['地区', '销售额'], ascending=[True, False])
print("先按地区升序,再按销售额降序:")
print(df_sorted_multi)
=== 多列排序 ===
先按地区升序,再按销售额降序:
销售员 销售额 订单数 地区 季度 客单价
4 钱七 310000 61 上海 Q2 5081.967213
1 李四 220000 38 上海 Q2 5789.473684
2 王五 180000 52 北京 Q1 3461.538462
0 张三 150000 45 北京 Q1 3333.333333
3 赵六 95000 28 广州 Q3 3392.857143
4.1.3 按索引排序
python
# 按索引排序
print("=== 索引排序 ===")
df_sorted_index = sales_df.sort_index(ascending=False)
print("按索引降序排序:")
print(df_sorted_index)
=== 索引排序 ===
按索引降序排序:
销售员 销售额 订单数 地区 季度 客单价
4 钱七 310000 61 上海 Q2 5081.967213
3 赵六 95000 28 广州 Q3 3392.857143
2 王五 180000 52 北京 Q1 3461.538462
1 李四 220000 38 上海 Q2 5789.473684
0 张三 150000 45 北京 Q1 3333.333333
4.1.4 自定义排序
python
# 自定义排序
print("=== 自定义排序 ===")
quarter_order = ['Q1', 'Q2', 'Q3', 'Q4']
sales_df['季度'] = pd.Categorical(sales_df['季度'], categories=quarter_order, ordered=True)
df_sorted_custom = sales_df.sort_values('季度')
print("按季度自定义顺序排序:")
print(df_sorted_custom)
=== 自定义排序 ===
按季度自定义顺序排序:
销售员 销售额 订单数 地区 季度 客单价
0 张三 150000 45 北京 Q1 3333.333333
2 王五 180000 52 北京 Q1 3461.538462
1 李四 220000 38 上海 Q2 5789.473684
4 钱七 310000 61 上海 Q2 5081.967213
3 赵六 95000 28 广州 Q3 3392.857143
python
sales_df['季度'] = pd.Categorical(sales_df['季度'], categories=quarter_order, ordered=True)
作用 :将'季度'列从普通文本转换为有序分类数据。
参数说明:
- pd.Categorical(): Pandas的分类数据类型
- categories=quarter_order: 指定所有可能的类别及其逻辑顺序
- ordered=True: 表示这是一个有顺序的分类变量
为什么要这样做?
如果不转换,Pandas默认按字母顺序排序(Q1、Q2、Q3的顺序是正确的,但如果有Q10,字母排序就会出错)。转换为有序分类后,Pandas就会按照我们指定的quarter_order顺序来理解季度的大小关系。
4.2 数据分组与聚合
4.2.1 分组操作
python
# 单列分组
grouped_region = sales_df.groupby('地区')
print("按地区分组:")
for region, group in grouped_region:
print(f"\n地区: {region}")
print(group)
按地区分组:
地区: 上海
销售员 销售额 订单数 地区 季度 客单价
1 李四 220000 38 上海 Q2 5789.473684
4 钱七 310000 61 上海 Q2 5081.967213
地区: 北京
销售员 销售额 订单数 地区 季度 客单价
0 张三 150000 45 北京 Q1 3333.333333
2 王五 180000 52 北京 Q1 3461.538462
地区: 广州
销售员 销售额 订单数 地区 季度 客单价
3 赵六 95000 28 广州 Q3 3392.857143
python
# 多列分组
# 当分类列中有 分类 数据时,需提供 observed 参数
grouped_multi = sales_df.groupby(['地区', '季度'],
observed=True)
print("\n\n按地区和季度分组:")
for (region, quarter), group in grouped_multi:
print(f"\n地区: {region}, 季度: {quarter}")
print(group)
按地区和季度分组:
地区: 上海, 季度: Q2
销售员 销售额 订单数 地区 季度 客单价
1 李四 220000 38 上海 Q2 5789.473684
4 钱七 310000 61 上海 Q2 5081.967213
地区: 北京, 季度: Q1
销售员 销售额 订单数 地区 季度 客单价
0 张三 150000 45 北京 Q1 3333.333333
2 王五 180000 52 北京 Q1 3461.538462
地区: 广州, 季度: Q3
销售员 销售额 订单数 地区 季度 客单价
3 赵六 95000 28 广州 Q3 3392.857143
4.2.2 聚合操作
python
# 单聚合函数
region_sales = grouped_region['销售额'].sum()
print("各地区总销售额:")
print(region_sales)
各地区总销售额:
地区
上海 530000
北京 330000
广州 95000
Name: 销售额, dtype: int64
python
# 多聚合函数
region_stats = grouped_region['销售额'].agg(['sum', 'mean', 'count', 'std', 'min', 'max'])
print("各地区销售额统计:")
print(region_stats)
各地区销售额统计:
sum mean count std min max
地区
上海 530000 265000.0 2 63639.610307 220000 310000
北京 330000 165000.0 2 21213.203436 150000 180000
广州 95000 95000.0 1 NaN 95000 95000
python
# 不同列使用不同聚合函数
detailed_stats = grouped_region.agg({
'销售额': ['sum', 'mean'],
'订单数': 'sum',
'客单价': 'mean'
})
print("详细统计:")
print(detailed_stats)
详细统计:
销售额 订单数 客单价
sum mean sum mean
地区
上海 530000 265000.0 99 5435.720449
北京 330000 165000.0 97 3397.435897
广州 95000 95000.0 28 3392.857143
python
# 重命名聚合结果
renamed_stats = grouped_region.agg(
总销售额=('销售额', 'sum'),
平均销售额=('销售额', 'mean'),
总订单数=('订单数', 'sum'),
平均客单价=('客单价', 'mean')
)
print("重命名聚合结果:")
print(renamed_stats)
重命名聚合结果:
总销售额 平均销售额 总订单数 平均客单价
地区
上海 530000 265000.0 99 5435.720449
北京 330000 165000.0 97 3397.435897
广州 95000 95000.0 28 3392.857143
4.2.3 分组后应用函数
python
# 分组后应用函数
def normalize(x):
"""标准化函数"""
return (x - x.mean()) / x.std()
normalized_sales = sales_df.groupby('地区')['销售额'].transform(normalize)
sales_df['销售额标准化'] = normalized_sales
print("标准化后的销售额:")
print(sales_df[['销售员', '地区', '销售额', '销售额标准化']])
标准化后的销售额:
销售员 地区 销售额 销售额标准化
0 张三 北京 150000 -0.707107
1 李四 上海 220000 -0.707107
2 王五 北京 180000 0.707107
3 赵六 广州 95000 NaN
4 钱七 上海 310000 0.707107
4.2.4 分组过滤
python
# 过滤
# 筛选订单数大于40的组
high_order_groups = sales_df.groupby('地区').filter(lambda x: x['订单数'].sum() > 40)
print("订单总数大于40的地区:")
print(high_order_groups)
订单总数大于40的地区:
销售员 销售额 订单数 地区 季度 客单价 销售额标准化
0 张三 150000 45 北京 Q1 3333.333333 -0.707107
1 李四 220000 38 上海 Q2 5789.473684 -0.707107
2 王五 180000 52 北京 Q1 3461.538462 0.707107
4 钱七 310000 61 上海 Q2 5081.967213 0.707107
4.2.5 透视表
python
# 透视表
pivot_table = sales_df.pivot_table(
values='销售额',
index='地区',
columns='季度',
aggfunc='sum',
fill_value=0,
margins=True,
margins_name='总计',
observed=True
)
print("销售额透视表:")
print(pivot_table)
销售额透视表:
季度 Q1 Q2 Q3 总计
地区
上海 0 530000 0 530000
北京 330000 0 0 330000
广州 0 0 95000 95000
总计 330000 530000 95000 955000
4.2.6 交叉表
python
# 交叉表
cross_tab = pd.crosstab(
index=sales_df['地区'],
columns=sales_df['季度'],
values=sales_df['销售额'],
aggfunc='sum',
margins=True,
margins_name='总计'
)
print("交叉表:")
print(cross_tab)
交叉表:
季度 Q1 Q2 Q3 Q4 总计
地区
上海 0 530000 0 0.0 530000
北京 330000 0 0 0.0 330000
广州 0 0 95000 0.0 95000
总计 330000 530000 95000 NaN 955000
4.3 数据合并与连接
python
# 创建多个DataFrame
df1 = pd.DataFrame({
'员工号': ['E001', 'E002', 'E003', 'E004'],
'姓名': ['张三', '李四', '王五', '赵六'],
'部门': ['技术部', '市场部', '技术部', '人事部']
})
df2 = pd.DataFrame({
'员工号': ['E001', 'E002', 'E005'],
'工资': [15000, 18000, 22000],
'奖金': [3000, 4000, 5000]
})
df3 = pd.DataFrame({
'员工号': ['E003', 'E004', 'E006'],
'入职日期': ['2020-01-15', '2019-03-20', '2021-05-10'],
'城市': ['北京', '上海', '广州']
})
print("DataFrame 1:")
print(df1)
print("\nDataFrame 2:")
print(df2)
print("\nDataFrame 3:")
print(df3)
DataFrame 1:
员工号 姓名 部门
0 E001 张三 技术部
1 E002 李四 市场部
2 E003 王五 技术部
3 E004 赵六 人事部
DataFrame 2:
员工号 工资 奖金
0 E001 15000 3000
1 E002 18000 4000
2 E005 22000 5000
DataFrame 3:
员工号 入职日期 城市
0 E003 2020-01-15 北京
1 E004 2019-03-20 上海
2 E006 2021-05-10 广州
4.3.1 合并操作
merge - 类似SQL JOIN
python
print("1. 内连接 (INNER JOIN):")
inner_join = pd.merge(df1, df2, on='员工号', how='inner')
print(inner_join)
1. 内连接 (INNER JOIN):
员工号 姓名 部门 工资 奖金
0 E001 张三 技术部 15000 3000
1 E002 李四 市场部 18000 4000
python
print("\n2. 左连接 (LEFT JOIN):")
left_join = pd.merge(df1, df2, on='员工号', how='left')
print(left_join)
2. 左连接 (LEFT JOIN):
员工号 姓名 部门 工资 奖金
0 E001 张三 技术部 15000.0 3000.0
1 E002 李四 市场部 18000.0 4000.0
2 E003 王五 技术部 NaN NaN
3 E004 赵六 人事部 NaN NaN
python
print("\n3. 右连接 (RIGHT JOIN):")
right_join = pd.merge(df1, df2, on='员工号', how='right')
print(right_join)
3. 右连接 (RIGHT JOIN):
员工号 姓名 部门 工资 奖金
0 E001 张三 技术部 15000 3000
1 E002 李四 市场部 18000 4000
2 E005 NaN NaN 22000 5000
python
print("\n4. 外连接 (OUTER JOIN):")
outer_join = pd.merge(df1, df2, on='员工号', how='outer')
print(outer_join)
4. 外连接 (OUTER JOIN):
员工号 姓名 部门 工资 奖金
0 E001 张三 技术部 15000.0 3000.0
1 E002 李四 市场部 18000.0 4000.0
2 E003 王五 技术部 NaN NaN
3 E004 赵六 人事部 NaN NaN
4 E005 NaN NaN 22000.0 5000.0
python
print("\n5. 多DataFrame合并:")
multi_merge = pd.merge(pd.merge(df1, df2, on='员工号', how='left'),
df3, on='员工号', how='left')
print(multi_merge)
5. 多DataFrame合并:
员工号 姓名 部门 工资 奖金 入职日期 城市
0 E001 张三 技术部 15000.0 3000.0 NaN NaN
1 E002 李四 市场部 18000.0 4000.0 NaN NaN
2 E003 王五 技术部 NaN NaN 2020-01-15 北京
3 E004 赵六 人事部 NaN NaN 2019-03-20 上海
python
print("\n6. 按不同列名合并:")
df2_renamed = df2.rename(columns={'员工号': '编号'})
merge_different_cols = pd.merge(df1, df2_renamed,
left_on='员工号',
right_on='编号',
how='left')
print(merge_different_cols)
6. 按不同列名合并:
员工号 姓名 部门 编号 工资 奖金
0 E001 张三 技术部 E001 15000.0 3000.0
1 E002 李四 市场部 E002 18000.0 4000.0
2 E003 王五 技术部 NaN NaN NaN
3 E004 赵六 人事部 NaN NaN NaN
4.3.2 连接操作
concat - 沿轴连接
python
df4 = pd.DataFrame({
'员工号': ['E007', 'E008'],
'姓名': ['钱七', '孙八'],
'部门': ['技术部', '市场部']
})
print("7. 垂直连接 (追加行):")
vertical_concat = pd.concat([df1, df4], ignore_index=True)
print(vertical_concat)
7. 垂直连接 (追加行):
员工号 姓名 部门
0 E001 张三 技术部
1 E002 李四 市场部
2 E003 王五 技术部
3 E004 赵六 人事部
4 E007 钱七 技术部
5 E008 孙八 市场部
python
print("\n8. 水平连接 (追加列):")
df1_subset = df1[['员工号', '姓名']]
df2_subset = df2[['员工号', '工资']]
horizontal_concat = pd.concat([df1_subset.set_index('员工号'),
df2_subset.set_index('员工号')],
axis=1)
print(horizontal_concat)
8. 水平连接 (追加列):
姓名 工资
员工号
E001 张三 15000.0
E002 李四 18000.0
E003 王五 NaN
E004 赵六 NaN
E005 NaN 22000.0
4.3.3 基于索引合并
join - 基于索引合并
python
df1_indexed = df1.set_index('员工号')
df2_indexed = df2.set_index('员工号')
df3_indexed = df3.set_index('员工号')
join_result = df1_indexed.join([df2_indexed, df3_indexed], how='outer')
print("基于索引合并:")
print(join_result)
基于索引合并:
姓名 部门 工资 奖金 入职日期 城市
员工号
E001 张三 技术部 15000.0 3000.0 NaN NaN
E002 李四 市场部 18000.0 4000.0 NaN NaN
E003 王五 技术部 NaN NaN 2020-01-15 北京
E004 赵六 人事部 NaN NaN 2019-03-20 上海
E005 NaN NaN 22000.0 5000.0 NaN NaN
E006 NaN NaN NaN NaN 2021-05-10 广州
4.4 窗口函数与滚动计算
python
# 创建时间序列数据
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=30, freq='D')
stock_data = pd.DataFrame({
'日期': dates,
'股票代码': 'AAPL',
'开盘价': np.random.uniform(150, 180, 30).round(2),
'收盘价': np.random.uniform(155, 185, 30).round(2),
'成交量': np.random.randint(1000000, 5000000, 30)
})
# 添加涨跌幅
stock_data['涨跌幅'] = (stock_data['收盘价'] - stock_data['开盘价']) / stock_data['开盘价']
stock_data.set_index('日期', inplace=True)
print("股票数据:")
print(stock_data.head(10))
股票数据:
股票代码 开盘价 收盘价 成交量 涨跌幅
日期
2023-01-01 AAPL 161.24 173.23 1023247 0.074361
2023-01-02 AAPL 178.52 160.12 2072876 -0.103070
2023-01-03 AAPL 171.96 156.95 4613009 -0.087288
2023-01-04 AAPL 167.96 183.47 3704238 0.092343
2023-01-05 AAPL 154.68 183.97 3630708 0.189359
2023-01-06 AAPL 154.68 179.25 4494679 0.158844
2023-01-07 AAPL 151.74 164.14 2322905 0.081719
2023-01-08 AAPL 175.99 157.93 2767640 -0.102619
2023-01-09 AAPL 168.03 175.53 3839291 0.044635
2023-01-10 AAPL 171.24 168.20 3529467 -0.017753
4.4.1 滚动窗口计算
python
# 5日滚动平均
stock_data['5日平均收盘价'] = stock_data['收盘价'].rolling(window=5).mean()
stock_data['5日成交量平均'] = stock_data['成交量'].rolling(window=5).mean()
# 滚动最大值/最小值
stock_data['5日最高价'] = stock_data['收盘价'].rolling(window=5).max()
stock_data['5日最低价'] = stock_data['收盘价'].rolling(window=5).min()
# 滚动标准差
stock_data['5日价格波动'] = stock_data['收盘价'].rolling(window=5).std()
# 滚动求和
stock_data['5日成交量总和'] = stock_data['成交量'].rolling(window=5).sum()
print("滚动计算后的数据:")
print(stock_data[['收盘价', '5日平均收盘价', '5日最高价', '5日最低价', '5日价格波动']].head(10))
滚动计算后的数据:
收盘价 5日平均收盘价 5日最高价 5日最低价 5日价格波动
日期
2023-01-01 173.23 NaN NaN NaN NaN
2023-01-02 160.12 NaN NaN NaN NaN
2023-01-03 156.95 NaN NaN NaN NaN
2023-01-04 183.47 NaN NaN NaN NaN
2023-01-05 183.97 171.548 183.97 156.95 12.678427
2023-01-06 179.25 172.752 183.97 156.95 13.154977
2023-01-07 164.14 173.556 183.97 156.95 12.283940
2023-01-08 157.93 173.752 183.97 157.93 11.956183
2023-01-09 175.53 172.164 183.97 157.93 10.815664
2023-01-10 168.20 169.010 179.25 157.93 8.580609
4.4.2 扩展窗口
python
stock_data['累计成交量'] = stock_data['成交量'].expanding().sum()
stock_data['累计平均收盘价'] = stock_data['收盘价'].expanding().mean()
print("扩展窗口计算:")
print(stock_data[['成交量', '累计成交量', '收盘价', '累计平均收盘价']].head(10))
扩展窗口计算:
成交量 累计成交量 收盘价 累计平均收盘价
日期
2023-01-01 1023247 1023247.0 173.23 173.230000
2023-01-02 2072876 3096123.0 160.12 166.675000
2023-01-03 4613009 7709132.0 156.95 163.433333
2023-01-04 3704238 11413370.0 183.47 168.442500
2023-01-05 3630708 15044078.0 183.97 171.548000
2023-01-06 4494679 19538757.0 179.25 172.831667
2023-01-07 2322905 21861662.0 164.14 171.590000
2023-01-08 2767640 24629302.0 157.93 169.882500
2023-01-09 3839291 28468593.0 175.53 170.510000
2023-01-10 3529467 31998060.0 168.20 170.279000
4.4.3 指数加权移动平均
python
stock_data['EWM_收盘价'] = stock_data['收盘价'].ewm(span=5).mean()
stock_data['EWM_成交量'] = stock_data['成交量'].ewm(span=5).mean()
print("指数加权移动平均:")
print(stock_data[['收盘价', 'EWM_收盘价', '成交量', 'EWM_成交量']].head(10))
指数加权移动平均:
收盘价 EWM_收盘价 成交量 EWM_成交量
日期
2023-01-01 173.23 173.230000 1023247 1.023247e+06
2023-01-02 160.12 165.364000 2072876 1.653024e+06
2023-01-03 156.95 161.378421 4613009 3.055122e+06
2023-01-04 183.47 170.554923 3704238 3.324755e+06
2023-01-05 183.97 175.704787 3630708 3.442206e+06
2023-01-06 179.25 177.000256 4494679 3.826794e+06
2023-01-07 164.14 172.447013 2322905 3.294334e+06
2023-01-08 157.93 167.411532 2767640 3.111641e+06
2023-01-09 175.53 170.189961 3839291 3.360669e+06
2023-01-10 168.20 169.514935 3529467 3.417928e+06
4.4.4 滚动应用自定义函数
python
def price_range(series):
"""计算价格范围"""
return series.max() - series.min()
stock_data['5日价格范围'] = stock_data['收盘价'].rolling(window=5).apply(price_range)
stock_data['5日涨跌幅波动'] = stock_data['涨跌幅'].rolling(window=5).std()
print("滚动应用自定义函数:")
print(stock_data[['收盘价', '5日价格范围', '涨跌幅', '5日涨跌幅波动']].head(10))
滚动应用自定义函数:
收盘价 5日价格范围 涨跌幅 5日涨跌幅波动
日期
2023-01-01 173.23 NaN 0.074361 NaN
2023-01-02 160.12 NaN -0.103070 NaN
2023-01-03 156.95 NaN -0.087288 NaN
2023-01-04 183.47 NaN 0.092343 NaN
2023-01-05 183.97 27.02 0.189359 0.125164
2023-01-06 179.25 27.02 0.158844 0.137240
2023-01-07 164.14 27.02 0.081719 0.107321
2023-01-08 157.93 26.04 -0.102619 0.113582
2023-01-09 175.53 26.04 0.044635 0.114697
2023-01-10 168.20 21.32 -0.017753 0.099139