Pandas 从入门到精通:完整数据分析指南
·
第一部分:Pandas 基础与核心概念
1.1 Pandas 简介与安装
Pandas 是 Python 的核心数据分析库,提供快速、灵活、易用的数据结构,特别适合处理表格数据和时间序列数据。
1.1.1 安装方法:
# 基础安装
pip install pandas
# 完整安装(包含可选依赖)
pip install pandas[all]
# 通过conda安装
conda install pandas
# 安装指定版本
pip install pandas==2.1.0
1.1.2 安装验证:
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
- 从列表创建
# 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]
- 指定索引
# 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
- 从字典创建
# 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
- 从标量创建
# 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属性与方法
# 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
# 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
# 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
# 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
# 自动索引对齐
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
- 从字典创建(最常用)
# 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
- 指定行索引
# 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
- 从列表创建
# 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数组创建
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 0.222079 -0.767977 0.142465 -0.034652
1 1.134339 -0.104746 -0.525123 1.912771
2 -2.026720 1.119424 0.779193 -1.101098
3 1.130228 0.373119 -0.386473 -1.158770
4 0.566113 -0.704453 -1.377939 -0.353117
- 从字典列表创建
# 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 基本属性和方法
# 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
# 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 数据查看方法
# 创建示例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
# 查看数据
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
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
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
print("\n7. 统计摘要:")
print(df.describe(include='all'))
7. 统计摘要:
ID 姓名 年龄 部门 工资 奖金 \
count 10 10 10.00000 10 10.000000 10.000000
unique 10 10 NaN 3 NaN NaN
top E100 张三 NaN 技术部 NaN NaN
freq 1 1 NaN 4 NaN NaN
mean NaN NaN 36.30000 NaN 16160.500000 2791.100000
min NaN NaN 26.00000 NaN 8769.000000 1021.000000
25% NaN NaN 27.75000 NaN 10931.250000 1799.000000
50% NaN NaN 36.50000 NaN 14816.500000 2742.000000
75% NaN NaN 43.75000 NaN 21658.000000 3759.000000
max NaN NaN 48.00000 NaN 24850.000000 4632.000000
std NaN NaN 8.90755 NaN 6130.652317 1281.338749
入职日期 总薪资 绩效
count 10 10.000000 10
unique NaN NaN 3
top NaN NaN C
freq NaN NaN 4
mean 2018-06-15 09:36:00 18951.600000 NaN
min 2018-01-31 00:00:00 10516.000000 NaN
25% 2018-04-07 12:00:00 14690.250000 NaN
50% 2018-06-15 00:00:00 17643.000000 NaN
75% 2018-08-23 06:00:00 23660.750000 NaN
max 2018-10-31 00:00:00 28408.000000 NaN
std NaN 6041.436956 NaN
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 列选择
# 列选择
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 行选择
# 行选择
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进行高级选择
# 使用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进行位置选择
# 使用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快速访问
快速标量访问
# 使用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 布尔索引
# 布尔索引
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 缺失值处理
# 创建包含缺失值的数据
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 检测缺失值
# 检测缺失值
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 删除缺失值
- 删除缺失行
# 处理缺失值
print("\n=== 缺失值处理 ===")
# 1. 删除缺失值
print("1. 删除缺失行:")
df_dropna_rows = df_na.dropna()
print(df_dropna_rows)
=== 缺失值处理 ===
1. 删除缺失行:
姓名 年龄 工资 部门 奖金
0 张三 25.0 15000.0 技术部 3000.0
- 删除缺失列
print("\n2. 删除缺失列:")
df_dropna_cols = df_na.dropna(axis=1)
print(df_dropna_cols)
2. 删除缺失列:
姓名
0 张三
1 李四
2 王五
3 赵六
4 钱七
- 删除全为缺失值的行
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
- 删除特定列有缺失值的行
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 填充缺失值
- 用固定值填充
# 用固定值填充
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
- 用前值填充
# 用前向填充
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
- 用后值填充
# 用后向填充
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
- 用均值填充
# 用均值填充
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
- 用众数填充
# 用众数填充
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 插值
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填充
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 数据类型转换
# 数据类型转换
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 数据去重
# 创建重复数据
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 深圳
# 检测重复值
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
# 查看重复的数据
df_dup[df_dup.duplicated()]
| 姓名 | 年龄 | 城市 | |
|---|---|---|---|
| 2 | 张三 | 25 | 北京 |
| 4 | 李四 | 30 | 上海 |
# 删除重复值
print("\n删除所有重复行:")
df_no_dup = df_dup.drop_duplicates()
print(df_no_dup)
删除所有重复行:
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
3 王五 35 广州
5 赵六 28 深圳
print("\n删除特定列重复(保留第一个):")
df_no_dup_name = df_dup.drop_duplicates(subset=['姓名'])
print(df_no_dup_name)
删除特定列重复(保留第一个):
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
3 王五 35 广州
5 赵六 28 深圳
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 深圳
print("\n删除所有重复(不保留任何重复):")
df_no_dup_all = df_dup.drop_duplicates(keep=False)
print(df_no_dup_all)
删除所有重复(不保留任何重复):
姓名 年龄 城市
3 王五 35 广州
5 赵六 28 深圳
3.4 数据转换
# 创建示例数据
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 重命名列
# 重命名列
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 重命名索引
# 重命名索引
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 重置索引
# 重置索引
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
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 设置索引
# 设置索引
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 添加列
# 添加列
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 删除列
# 删除列
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 修改值
# 修改值
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 应用函数
# 应用函数
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
# 对多列应用函数
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 使用向量化操作
# 使用向量化操作
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 数据排序
# 创建示例数据
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 单列排序
# 单列排序
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 多列排序
# 多列排序
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 按索引排序
# 按索引排序
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 自定义排序
# 自定义排序
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
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 分组操作
# 单列分组
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
# 多列分组
# 当分类列中有 分类 数据时,需提供 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 聚合操作
# 单聚合函数
region_sales = grouped_region['销售额'].sum()
print("各地区总销售额:")
print(region_sales)
各地区总销售额:
地区
上海 530000
北京 330000
广州 95000
Name: 销售额, dtype: int64
# 多聚合函数
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
# 不同列使用不同聚合函数
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
# 重命名聚合结果
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 分组后应用函数
# 分组后应用函数
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 分组过滤
# 过滤
# 筛选订单数大于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 透视表
# 透视表
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 交叉表
# 交叉表
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 数据合并与连接
# 创建多个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
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
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
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
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
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 上海
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 - 沿轴连接
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 孙八 市场部
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 - 基于索引合并
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 窗口函数与滚动计算
# 创建时间序列数据
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 滚动窗口计算
# 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 扩展窗口
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 指数加权移动平均
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 滚动应用自定义函数
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
第五部分:高级功能与技巧
5.1 时间序列处理
# 创建时间序列数据
print("=== 时间序列处理 ===")
date_rng = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
ts_data = pd.DataFrame({
'日期': date_rng,
'销售额': np.random.randint(1000, 5000, len(date_rng)),
'访问量': np.random.randint(100, 1000, len(date_rng)),
'转化率': np.random.uniform(0.01, 0.05, len(date_rng))
})
ts_data.set_index('日期', inplace=True)
print("原始时间序列数据:")
print(ts_data.head())
=== 时间序列处理 ===
原始时间序列数据:
销售额 访问量 转化率
日期
2023-01-01 2698 884 0.011726
2023-01-02 1418 203 0.045646
2023-01-03 3336 492 0.031108
2023-01-04 1378 910 0.049719
2023-01-05 2796 345 0.012952
5.1.1 时间重采样
# 时间重采样
print("=== 时间重采样 ===")
# 按周重采样
weekly_data = ts_data.resample('W').agg({
'销售额': 'sum',
'访问量': 'sum',
'转化率': 'mean'
})
print("周度数据:")
print(weekly_data.head())
=== 时间重采样 ===
周度数据:
销售额 访问量 转化率
日期
2023-01-01 2698 884 0.011726
2023-01-08 20832 2939 0.035896
2023-01-15 20188 5290 0.032504
2023-01-22 22379 3914 0.030931
2023-01-29 22885 4532 0.033641
# 按月重采样
monthly_data = ts_data.resample('ME').agg({
'销售额': ['sum', 'mean', 'std'],
'访问量': 'sum',
'转化率': 'mean'
})
print("月度数据:")
print(monthly_data.head())
月度数据:
销售额 访问量 转化率
sum mean std sum mean
日期
2023-01-31 95127 3068.612903 1022.441805 18203 0.032141
2023-02-28 83578 2984.928571 1263.374052 15695 0.034019
2023-03-31 99910 3222.903226 939.847447 19590 0.032810
2023-04-30 91586 3052.866667 1141.719536 16948 0.028032
2023-05-31 99021 3194.225806 1145.362729 16720 0.025523
5.1.2 时间偏移
# 时间偏移
print("=== 时间偏移 ===")
ts_data['前一天销售额'] = ts_data['销售额'].shift(1)
ts_data['7天平均销售额'] = ts_data['销售额'].rolling(window=7).mean()
ts_data['月累计销售额'] = ts_data['销售额'].resample('ME').transform('sum')
print("时间偏移后的数据:")
print(ts_data[['销售额', '前一天销售额', '7天平均销售额']].head(10))
=== 时间偏移 ===
时间偏移后的数据:
销售额 前一天销售额 7天平均销售额
日期
2023-01-01 2698 NaN NaN
2023-01-02 1418 2698.0 NaN
2023-01-03 3336 1418.0 NaN
2023-01-04 1378 3336.0 NaN
2023-01-05 2796 1378.0 NaN
2023-01-06 4561 2796.0 NaN
2023-01-07 3278 4561.0 2780.714286
2023-01-08 4065 3278.0 2976.000000
2023-01-09 3088 4065.0 3214.571429
2023-01-10 4099 3088.0 3323.571429
5.1.3 日期时间属性
# 日期时间属性
print("=== 日期时间属性 ===")
ts_data['年份'] = ts_data.index.year
ts_data['月份'] = ts_data.index.month
ts_data['季度'] = ts_data.index.quarter
ts_data['星期'] = ts_data.index.dayofweek
ts_data['是否周末'] = ts_data.index.dayofweek >= 5
ts_data['月内天数'] = ts_data.index.day
ts_data['是否月末'] = ts_data.index.is_month_end
print("日期属性:")
print(ts_data[['销售额', '年份', '月份', '季度', '星期', '是否周末']].head())
=== 日期时间属性 ===
日期属性:
销售额 年份 月份 季度 星期 是否周末
日期
2023-01-01 2698 2023 1 1 6 True
2023-01-02 1418 2023 1 1 0 False
2023-01-03 3336 2023 1 1 1 False
2023-01-04 1378 2023 1 1 2 False
2023-01-05 2796 2023 1 1 3 False
5.1.4 时间段分析
# 时间段分析
print("=== 时间段分析 ===")
# 按季度分析
quarterly_sales = ts_data.groupby('季度')['销售额'].agg(['sum', 'mean', 'std'])
print("季度销售额分析:")
print(quarterly_sales)
=== 时间段分析 ===
季度销售额分析:
sum mean std
季度
1 278615 3095.722222 1069.621408
2 279331 3069.571429 1168.631794
3 274679 2985.641304 1068.107071
4 270872 2944.260870 1153.283329
# 按星期分析
weekday_sales = ts_data.groupby('星期')['销售额'].mean()
print("星期平均销售额:")
print(weekday_sales)
星期平均销售额:
星期
0 2813.942308
1 3164.326923
2 2811.346154
3 3246.403846
4 3073.288462
5 3138.230769
6 2917.452830
Name: 销售额, dtype: float64
# 节假日分析
from pandas.tseries.holiday import USFederalHolidayCalendar
cal = USFederalHolidayCalendar()
holidays = cal.holidays(start=ts_data.index.min(), end=ts_data.index.max())
ts_data['是否假日'] = ts_data.index.isin(holidays)
holiday_sales = ts_data.groupby('是否假日')['销售额'].mean()
print("假日 vs 非假日销售额:")
print(holiday_sales)
假日 vs 非假日销售额:
是否假日
False 3030.491525
True 2791.181818
Name: 销售额, dtype: float64
5.2 分类数据与分箱
# 创建示例数据
customer_data = pd.DataFrame({
'客户ID': range(1, 101),
'年龄': np.random.randint(18, 70, 100),
'年收入': np.random.randint(30000, 200000, 100),
'消费次数': np.random.randint(1, 50, 100),
'总消费额': np.random.randint(1000, 50000, 100)
})
print("客户数据:")
print(customer_data.head())
客户数据:
客户ID 年龄 年收入 消费次数 总消费额
0 1 66 36150 9 28345
1 2 46 34425 9 49288
2 3 60 46032 40 29687
3 4 39 32198 17 14737
4 5 43 181151 1 10323
5.2.1 分箱处理
# 分箱
print("=== 分箱处理 ===")
# 等宽分箱
customer_data['年龄分组'] = pd.cut(customer_data['年龄'],
bins=[0, 30, 40, 50, 60, 100],
labels=['<30', '30-40', '40-50', '50-60', '>60'])
customer_data['收入分组'] = pd.cut(customer_data['年收入'],
bins=5,
labels=['低', '中低', '中等', '中高', '高'])
# 等频分箱
customer_data['消费次数分组'] = pd.qcut(customer_data['消费次数'],
q=4,
labels=['低', '中低', '中高', '高'])
print("分箱后的数据:")
print(customer_data[['年龄', '年龄分组', '年收入', '收入分组', '消费次数', '消费次数分组']].head(10))
=== 分箱处理 ===
分箱后的数据:
年龄 年龄分组 年收入 收入分组 消费次数 消费次数分组
0 66 >60 36150 低 9 低
1 46 40-50 34425 低 9 低
2 60 50-60 46032 低 40 高
3 39 30-40 32198 低 17 中低
4 43 40-50 181151 高 1 低
5 45 40-50 197292 高 25 中低
6 67 >60 174931 高 43 高
7 38 30-40 192491 高 40 高
8 66 >60 148865 中高 42 高
9 24 <30 169777 高 25 中低
5.2.2 分箱统计
# 分箱统计
print("=== 分箱统计 ===")
age_group_stats = customer_data.groupby('年龄分组', observed=True).agg({
'年收入': 'mean',
'总消费额': ['mean', 'sum', 'count']
}).round(2)
print("年龄分组统计:")
print(age_group_stats)
=== 分箱统计 ===
年龄分组统计:
年收入 总消费额
mean mean sum count
年龄分组
<30 125017.75 29192.95 583859 20
30-40 115296.80 25240.35 504807 20
40-50 111921.42 25025.92 650674 26
50-60 110103.08 27246.08 326953 12
>60 146865.68 29349.91 645698 22
5.2.3 创建虚拟变量
# 创建虚拟变量
print("=== 创建虚拟变量 ===")
dummy_vars = pd.get_dummies(customer_data['年龄分组'], prefix='年龄')
print("年龄分组的虚拟变量:")
print(dummy_vars.head())
=== 创建虚拟变量 ===
年龄分组的虚拟变量:
年龄_<30 年龄_30-40 年龄_40-50 年龄_50-60 年龄_>60
0 False False False False True
1 False False True False False
2 False False False True False
3 False True False False False
4 False False True False False
pd.get_dummies是一个用于将分类变量(categorical variable)转换为哑变量(dummy variables)/ 独热编码(one-hot encoding)的函数pd.get_dummies(data,
prefix=None,
prefix_sep='_',
dummy_na=False,
columns=None,
sparse=False,
drop_first=False,
dtype=None)
- data:数组、Series 或 DataFrame。这是你要转换的数据。 - prefix:字符串、字符串列表或字典,默认为 None。用于指定生成的新列名的前缀。如果不指定,默认使用原始列名。 - prefix_sep:字符串,默认为 '_'。前缀和原始值之间的分隔符。 - dummy_na:布尔值,默认为 False。是否增加一列表示缺失值(NaN)。 - columns:列表,默认为 None。指定要对DataFrame中的哪些列进行编码。如果不指定,则默认对所有对象类型(object)或类别类型(category)的列进行编码。 - drop_first:布尔值,默认为 False。是否通过删除第一个类别来避免多重共线性(k-1 个哑变量)。这在回归模型中非常重要,可以避免虚拟变量陷阱。 - dtype:数据类型,默认为 None。指定新生成的0/1列的数据类型(例如 dtype=int 得到0和1,默认通常是 uint8 或 float)。
# 与原始数据合并
customer_data_with_dummies = pd.concat([customer_data, dummy_vars], axis=1)
print("合并虚拟变量后的数据:")
print(customer_data_with_dummies.head())
合并虚拟变量后的数据:
客户ID 年龄 年收入 消费次数 总消费额 年龄分组 收入分组 消费次数分组 年龄_<30 年龄_30-40 \
0 1 66 36150 9 28345 >60 低 低 False False
1 2 46 34425 9 49288 40-50 低 低 False False
2 3 60 46032 40 29687 50-60 低 高 False False
3 4 39 32198 17 14737 30-40 低 中低 False True
4 5 43 181151 1 10323 40-50 高 低 False False
年龄_40-50 年龄_50-60 年龄_>60
0 False False True
1 True False False
2 False True False
3 False False False
4 True False False
5.2.4 交叉分析
# 交叉分析
print("=== 交叉分析 ===")
cross_tab = pd.crosstab(
index=customer_data['年龄分组'],
columns=customer_data['收入分组'],
values=customer_data['总消费额'],
aggfunc='mean',
margins=True
)
print("年龄分组 vs 收入分组 - 平均消费额:")
print(cross_tab)
=== 交叉分析 ===
年龄分组 vs 收入分组 - 平均消费额:
收入分组 低 中低 中等 中高 高 \
年龄分组
<30 29882.500000 33867.000000 15899.00 34562.750000 24958.714286
30-40 28370.200000 28767.333333 31131.75 12542.750000 25489.000000
40-50 29254.333333 24799.000000 33819.40 24881.750000 11546.000000
50-60 24622.000000 40893.500000 38573.00 24902.600000 8214.000000
>60 28345.000000 31018.400000 26280.00 25519.333333 29951.916667
All 28374.631579 30372.150000 31198.00 24451.000000 24208.068966
收入分组 All
年龄分组
<30 29192.950000
30-40 25240.350000
40-50 25025.923077
50-60 27246.083333
>60 29349.909091
All 27119.910000
5.3 性能优化技巧
# 创建大数据集
np.random.seed(42)
n_rows = 1000000
big_data = pd.DataFrame({
'ID': range(n_rows),
'数值1': np.random.randn(n_rows),
'数值2': np.random.randn(n_rows),
'类别': np.random.choice(['A', 'B', 'C', 'D'], n_rows),
'日期': pd.date_range('2023-01-01', periods=n_rows, freq='min')
})
print(f"数据集大小: {n_rows:,} 行")
print(f"内存使用: {big_data.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
数据集大小: 1,000,000 行
内存使用: 78.20 MB
5.3.1 数据类型优化
print("=== 数据类型优化 ===")
def optimize_dtypes(df):
"""优化数据类型减少内存使用"""
df_opt = df.copy()
# 整数类型优化
int_cols = df_opt.select_dtypes(include=['int64']).columns
for col in int_cols:
c_min = df_opt[col].min()
c_max = df_opt[col].max()
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df_opt[col] = df_opt[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df_opt[col] = df_opt[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df_opt[col] = df_opt[col].astype(np.int32)
# 浮点数类型优化
float_cols = df_opt.select_dtypes(include=['float64']).columns
for col in float_cols:
df_opt[col] = df_opt[col].astype(np.float32)
# 对象类型优化
obj_cols = df_opt.select_dtypes(include=['object']).columns
for col in obj_cols:
num_unique = len(df_opt[col].unique())
if num_unique < 0.5 * len(df_opt):
df_opt[col] = df_opt[col].astype('category')
return df_opt
# 优化前后对比
import time
start = time.time()
optimized_data = optimize_dtypes(big_data)
end = time.time()
print(f"优化前内存: {big_data.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
print(f"优化后内存: {optimized_data.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
print(f"优化时间: {end-start:.2f} 秒")
=== 数据类型优化 ===
优化前内存: 78.20 MB
优化后内存: 20.03 MB
优化时间: 0.41 秒
5.3.2 矢量化操作 vs 循环
print("=== 矢量化操作优化 ===")
# 非矢量化操作
start = time.time()
big_data['计算1'] = 0.0
for i in range(len(big_data)):
big_data.loc[i, '计算1'] = big_data.loc[i, '数值1'] * 2 + 10
loop_time = time.time() - start
# 矢量化操作
start = time.time()
big_data['计算2'] = big_data['数值1'] * 2 + 10
vectorized_time = time.time() - start
print(f"循环操作时间: {loop_time:.4f} 秒")
print(f"矢量化操作时间: {vectorized_time:.4f} 秒")
print(f"速度提升: {loop_time/vectorized_time:.1f} 倍")
=== 矢量化操作优化 ===
循环操作时间: 202.1564 秒
矢量化操作时间: 0.0908 秒
速度提升: 2227.5 倍
5.3.3 使用query方法
print("=== query方法优化 ===")
# 传统方法
start = time.time()
filtered1 = big_data[(big_data['数值1'] > 0) & (big_data['数值2'] < 0)]
method1_time = time.time() - start
# query方法
start = time.time()
filtered2 = big_data.query('数值1 > 0 and 数值2 < 0')
method2_time = time.time() - start
print(f"传统过滤时间: {method1_time:.4f} 秒")
print(f"query方法时间: {method2_time:.4f} 秒")
print(f"速度提升: {method1_time/method2_time:.1f} 倍")
=== query方法优化 ===
传统过滤时间: 0.0683 秒
query方法时间: 0.0434 秒
速度提升: 1.6 倍
5.3.4 分块处理大数据
print("=== 分块处理大数据 ===")
def process_in_chunks(file_path, chunk_size=10000):
"""分块读取和处理大数据"""
chunks = []
for chunk in pd.read_csv(file_path, chunksize=chunk_size):
# 处理每个块
chunk['processed'] = chunk['数值1'] * 2
chunks.append(chunk)
return pd.concat(chunks, ignore_index=True)
=== 分块处理大数据 ===
5.3.5 使用eval方法
print("=== eval方法优化 ===")
# 传统计算
start = time.time()
result1 = big_data['数值1'] * 2 + big_data['数值2'] * 3
eval1_time = time.time() - start
# eval方法
start = time.time()
result2 = big_data.eval('数值1 * 2 + 数值2 * 3')
eval2_time = time.time() - start
print(f"传统计算时间: {eval1_time:.4f} 秒")
print(f"eval方法时间: {eval2_time:.4f} 秒")
print(f"速度提升: {eval1_time/eval2_time:.1f} 倍")
=== eval方法优化 ===
传统计算时间: 0.0225 秒
eval方法时间: 0.0184 秒
速度提升: 1.2 倍
5.4 高级查询与过滤
# 创建示例数据
product_data = pd.DataFrame({
'产品ID': [f'P{1000+i}' for i in range(20)],
'产品名称': [f'产品{i}' for i in range(20)],
'类别': np.random.choice(['电子', '服装', '食品', '家居'], 20),
'价格': np.random.uniform(10, 1000, 20).round(2),
'库存': np.random.randint(0, 100, 20),
'销量': np.random.randint(0, 1000, 20),
'评分': np.random.uniform(1, 5, 20).round(1),
'上架日期': pd.date_range('2023-01-01', periods=20, freq='D')
})
print("产品数据:")
print(product_data)
产品数据:
产品ID 产品名称 类别 价格 库存 销量 评分 上架日期
0 P1000 产品0 食品 137.63 87 957 4.5 2023-01-01
1 P1001 产品1 家居 744.97 8 417 1.6 2023-01-02
2 P1002 产品2 服装 906.45 25 249 3.6 2023-01-03
3 P1003 产品3 家居 576.89 75 92 2.2 2023-01-04
4 P1004 产品4 服装 638.05 84 962 2.0 2023-01-05
5 P1005 产品5 家居 281.70 93 226 1.1 2023-01-06
6 P1006 产品6 食品 41.37 50 76 4.6 2023-01-07
7 P1007 产品7 电子 659.45 30 946 4.1 2023-01-08
8 P1008 产品8 家居 761.57 10 986 4.9 2023-01-09
9 P1009 产品9 电子 500.01 28 796 1.5 2023-01-10
10 P1010 产品10 家居 854.09 3 89 5.0 2023-01-11
11 P1011 产品11 服装 70.20 42 544 4.6 2023-01-12
12 P1012 产品12 家居 37.58 29 208 1.5 2023-01-13
13 P1013 产品13 食品 25.80 97 86 4.3 2023-01-14
14 P1014 产品14 服装 803.56 86 132 2.2 2023-01-15
15 P1015 产品15 食品 945.21 89 366 1.1 2023-01-16
16 P1016 产品16 食品 178.11 11 56 4.1 2023-01-17
17 P1017 产品17 电子 421.68 35 539 1.1 2023-01-18
18 P1018 产品18 电子 632.95 65 164 1.2 2023-01-19
19 P1019 产品19 家居 780.86 3 282 2.7 2023-01-20
5.4.1 链式查询
high_sales = (product_data[product_data['销量'] > 500]
.sort_values('销量', ascending=False)
.head(5))
print("销量最高的5个产品:")
print(high_sales[['产品名称', '类别', '价格', '销量', '评分']])
销量最高的5个产品:
产品名称 类别 价格 销量 评分
8 产品8 家居 761.57 986 4.9
4 产品4 服装 638.05 962 2.0
0 产品0 食品 137.63 957 4.5
7 产品7 电子 659.45 946 4.1
9 产品9 电子 500.01 796 1.5
5.4.2 query方法高级使用
query_result = product_data.query('价格 > 100 and 销量 > 300 and 评分 >= 4.0')
print("高价、高销量、高评分产品:")
print(query_result[['产品名称', '价格', '销量', '评分']])
高价、高销量、高评分产品:
产品名称 价格 销量 评分
0 产品0 137.63 957 4.5
7 产品7 659.45 946 4.1
8 产品8 761.57 986 4.9
5.4.3 使用between
price_range = product_data[product_data['价格'].between(50, 200)]
print("价格在50-200之间的产品:")
print(price_range[['产品名称', '价格', '库存']])
价格在50-200之间的产品:
产品名称 价格 库存
0 产品0 137.63 87
11 产品11 70.20 42
16 产品16 178.11 11
5.4.4 使用isin
categories = ['电子', '食品']
category_filter = product_data[product_data['类别'].isin(categories)]
print("电子和食品类产品:")
print(category_filter[['产品名称', '类别', '价格']])
电子和食品类产品:
产品名称 类别 价格
0 产品0 食品 137.63
6 产品6 食品 41.37
7 产品7 电子 659.45
9 产品9 电子 500.01
13 产品13 食品 25.80
15 产品15 食品 945.21
16 产品16 食品 178.11
17 产品17 电子 421.68
18 产品18 电子 632.95
5.4.5 使用contains
name_filter = product_data[product_data['产品名称'].str.contains('产品1')]
print("名称包含'产品1'的产品:")
print(name_filter[['产品名称', '价格']])
名称包含'产品1'的产品:
产品名称 价格
1 产品1 744.97
10 产品10 854.09
11 产品11 70.20
12 产品12 37.58
13 产品13 25.80
14 产品14 803.56
15 产品15 945.21
16 产品16 178.11
17 产品17 421.68
18 产品18 632.95
19 产品19 780.86
5.4.6 多条件复杂查询
complex_query = product_data[
((product_data['价格'] > 100) | (product_data['销量'] > 500)) &
(product_data['评分'] >= 3.5) &
(product_data['库存'] > 0) &
(~product_data['类别'].isin(['服装']))
]
print("复杂条件查询结果:")
print(complex_query[['产品名称', '类别', '价格', '销量', '评分', '库存']])
复杂条件查询结果:
产品名称 类别 价格 销量 评分 库存
0 产品0 食品 137.63 957 4.5 87
7 产品7 电子 659.45 946 4.1 30
8 产品8 家居 761.57 986 4.9 10
10 产品10 家居 854.09 89 5.0 3
16 产品16 食品 178.11 56 4.1 11
上面的复杂查询也可使用 query 实现:
print(product_data.query( "(价格 > 100 or 销量 > 500) and " "评分 >= 3.5 and " "库存 > 0 and " "类别 not in ['服装']"))
5.4.7 使用query和eval组合
product_data['是否热销'] = product_data['销量'] > 300
product_data['是否高价'] = product_data['价格'] > 200
product_data['是否高评分'] = product_data['评分'] >= 4.0
query_expr = "(是否热销 == True) and "\
"(是否高价 == True) and "\
"(是否高评分 == True)"
final_result = product_data.query(query_expr)
print("热销、高价、高评分产品:")
print(final_result[['产品名称', '类别', '价格', '销量', '评分']])
热销、高价、高评分产品:
产品名称 类别 价格 销量 评分
7 产品7 电子 659.45 946 4.1
8 产品8 家居 761.57 986 4.9
5.4.8 使用nlargest和nsmallest
print("价格最高的3个产品:")
print(product_data.nlargest(3, '价格')[['产品名称', '价格']])
价格最高的3个产品:
产品名称 价格
15 产品15 945.21
2 产品2 906.45
10 产品10 854.09
print("评分最低的3个产品:")
print(product_data.nsmallest(3, '评分')[['产品名称', '评分']])
评分最低的3个产品:
产品名称 评分
5 产品5 1.1
15 产品15 1.1
17 产品17 1.1
5.4.9 使用where和mask
print("=== where和mask使用 ===")
# where: 条件为True保留原值,为False替换
product_data['价格等级'] = np.where(
product_data['价格'] > 500, '高价',
np.where(product_data['价格'] > 100, '中价', '低价')
)
# mask: 条件为True替换
product_data['折扣价格'] = product_data['价格'].mask(
product_data['销量'] > 500,
product_data['价格'] * 0.9
)
print("价格等级和折扣价格:")
print(product_data[['产品名称', '价格', '销量', '价格等级', '折扣价格']])
=== where和mask使用 ===
价格等级和折扣价格:
产品名称 价格 销量 价格等级 折扣价格
0 产品0 137.63 957 中价 123.867
1 产品1 744.97 417 高价 744.970
2 产品2 906.45 249 高价 906.450
3 产品3 576.89 92 高价 576.890
4 产品4 638.05 962 高价 574.245
5 产品5 281.70 226 中价 281.700
6 产品6 41.37 76 低价 41.370
7 产品7 659.45 946 高价 593.505
8 产品8 761.57 986 高价 685.413
9 产品9 500.01 796 高价 450.009
10 产品10 854.09 89 高价 854.090
11 产品11 70.20 544 低价 63.180
12 产品12 37.58 208 低价 37.580
13 产品13 25.80 86 低价 25.800
14 产品14 803.56 132 高价 803.560
15 产品15 945.21 366 高价 945.210
16 产品16 178.11 56 中价 178.110
17 产品17 421.68 539 中价 379.512
18 产品18 632.95 164 高价 632.950
19 产品19 780.86 282 高价 780.860
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)