第一部分: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. 从列表创建
# 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]
  1. 指定索引
# 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
  1. 从字典创建
# 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
  1. 从标量创建
# 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. 从字典创建(最常用)
# 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
  1. 指定行索引
# 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
  1. 从列表创建
# 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
  1. 从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
  1. 从字典列表创建
# 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 删除缺失值
  1. 删除缺失行
# 处理缺失值
print("\n=== 缺失值处理 ===")
# 1. 删除缺失值
print("1. 删除缺失行:")
df_dropna_rows = df_na.dropna()
print(df_dropna_rows)
=== 缺失值处理 ===
1. 删除缺失行:
   姓名    年龄       工资   部门      奖金
0  张三  25.0  15000.0  技术部  3000.0
  1. 删除缺失列
print("\n2. 删除缺失列:")
df_dropna_cols = df_na.dropna(axis=1)
print(df_dropna_cols)
2. 删除缺失列:
   姓名
0  张三
1  李四
2  王五
3  赵六
4  钱七
  1. 删除全为缺失值的行
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
  1. 删除特定列有缺失值的行
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 填充缺失值
  1. 用固定值填充
# 用固定值填充
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
  1. 用前值填充
# 用前向填充
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
  1. 用后值填充
# 用后向填充
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
  1. 用均值填充
# 用均值填充
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
  1. 用众数填充
# 用众数填充
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
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐