Python 进阶教程:Pandas 库的使用

📖 Pandas 是 Python 数据分析的核心库,提供了高性能、易用的数据结构和数据分析工具,是每个数据分析师和数据科学家的必备技能。


1. Pandas 简介

1.1 什么是 Pandas

Pandas 是 Python 编写的高性能、易于使用的数据分析库,主要提供:

特性说明
📊 DataFrame表格化数据结构,类似于 Excel 或 SQL 表
📈 Series一维标签数组,类似 Excel 列
🔄 数据对齐自动对齐不同数据源的数据
向量化运算无需循环即可对整列数据进行操作
🛠️ 数据清洗缺失值处理、重复值删除、数据转换
📁 IO工具支持 CSV、Excel、JSON、SQL、HTML 等格式

1.2 Pandas 在数据处理流程中的位置

┌─────────────────────────────────────────────────────────────────┐
│                      数据处理完整流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  数据采集 ──→ 数据存储 ──→ Pandas清洗 ──→ 分析计算 ──→ 可视化    │
│     │                                              │            │
│     │          ┌─────────────────────┐              │            │
│     └─────────►│   Pandas DataFrame  │◄─────────────┘            │
│                 └─────────────────────┘                          │
│                         │                                        │
│           ┌─────────────┼─────────────┐                         │
│           ▼             ▼             ▼                          │
│      读取数据       数据清洗       数据聚合                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.3 Pandas vs 其他工具对比

特性PandasNumPyExcelSQL
数据规模中等(百万级)小(适合数值计算)小(万级)大(亿级)
灵活性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习曲线中等陡峭平缓中等
可视化集成需配合内置需导出
数据类型混合类型仅数值混合类型表格

2. 环境准备

2.1 安装 Pandas

# 使用pip安装
pip install pandas numpy
​
# 或使用conda(如果可用)
conda install pandas numpy
​
# 安装完整数据分析环境
pip install pandas numpy matplotlib seaborn openpyxl xlrd

2.2 版本检查

import pandas as pd
import numpy as np
​
# 检查版本
print(f"Pandas版本: {pd.__version__}")
print(f"NumPy版本: {np.__version__}")
​
# 查看Pandas默认配置
print("\nPandas配置信息:")
print(pd.show_versions())

预期输出:

Pandas版本: 2.2.0
NumPy版本: 1.26.0
​
Pandas配置信息:
INSTALLED:
  pandas 2.2.0
  numpy 1.26.0
  ...

2.3 Jupyter Notebook 集成

# 在Jupyter中设置显示选项
pd.set_option('display.max_columns', 100)  # 最多显示100列
pd.set_option('display.width', 200)         # 每行最大宽度
pd.set_option('display.max_colwidth', 50)   # 列内容最大宽度
​
# 科学计数法设置
pd.set_option('display.float_format', lambda x: f'{x:.2f}')

3. 数据结构:Series 与 DataFrame

3.1 Series

Series 是带标签的一维数组,类似于 Excel 中的单列数据。

import pandas as pd
import numpy as np
​
# 3.1.1 从列表创建
s1 = pd.Series([1, 2, 3, 4, 5])
print("从列表创建:")
print(s1)
print(f"\n值: {s1.values}")
print(f"索引: {s1.index.tolist()}")
​
# 3.1.2 自定义索引
s2 = pd.Series([95, 87, 92, 88, 96], 
               index=['语文', '数学', '英语', '物理', '化学'])
print("\n自定义索引:")
print(s2)
​
# 3.1.3 从字典创建
scores = {'Alice': 92, 'Bob': 85, 'Charlie': 88, 'David': 95}
s3 = pd.Series(scores)
print("\n从字典创建:")
print(s3)
​
# 3.1.4 访问数据
print(f"\nBob的成绩: {s3['Bob']}")
print(f"前2个: {s3.head(2)}")
print(f"大于90的成绩:\n{s3[s3 > 90]}")

输出结果:

从列表创建:
0    1
1    2
2    3
3    4
4    5
​
自定义索引:
语文    95
数学    87
英语    92
物理    88
化学    96
​
从字典创建:
Alice      92
Bob        85
Charlie    88
David      95
​
Bob的成绩: 85
前2个: Alice      92
Bob        85
大于90的成绩:
Alice     92
David     95

3.2 DataFrame

DataFrame 是带标签的二维数据结构,类似于 Excel 表格或 SQL 表。

# 3.2.1 从字典创建
data = {
    '姓名': ['张三', '李四', '王五', '赵六', '钱七'],
    '年龄': [25, 30, 28, 35, 27],
    '城市': ['北京', '上海', '广州', '深圳', '杭州'],
    '薪资': [15000, 22000, 18000, 25000, 16000]
}
df = pd.DataFrame(data)
print("DataFrame示例:")
print(df)
print(f"\n形状: {df.shape} (行数, 列数)")
print(f"列名: {df.columns.tolist()}")
print(f"数据类型:\n{df.dtypes}")

输出结果:

DataFrame示例:
   姓名  年龄  城市     薪资
0  张三  25  北京  15000
1  李四  30  上海  22000
2  王五  28  广州  18000
3  赵六  35  深圳  25000
4  钱七  27  杭州  16000

形状: (5, 4) (行数, 列数)
列名: ['姓名', '年龄', '城市', '薪资']
数据类型:
姓名     object
年龄      int64
城市     object
薪资      int64

3.3 数据结构可视化

┌─────────────────────────────────────────────────────────────────┐
│                        DataFrame 结构                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│          │   姓名(Str)   │   年龄(Int)  │   城市(Str)  │  薪资(Int)│
│  ────────┼──────────────┼───────────────┼──────────────┼─────────┤
│  index=0 │     张三      │      25       │     北京     │  15000  │
│  index=1 │     李四      │      30       │     上海     │  22000  │
│  index=2 │     王五      │      28       │     广州     │  18000  │
│  index=3 │     赵六      │      35       │     深圳     │  25000  │
│  index=4 │     钱七      │      27       │     杭州     │  16000  │
│                                                                 │
│  ┌─────────┐         ┌──────────────────────────────────────┐  │
│  │  列索引  │         │              行索引 (Index)           │  │
│  └─────────┘         └──────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4. 数据读取与保存

4.1 读取数据

import pandas as pd

# 4.1.1 读取CSV文件(最常用)
df_csv = pd.read_csv('data.csv')
df_csv = pd.read_csv('data.csv', encoding='utf-8')  # 指定编码
df_csv = pd.read_csv('data.csv', sep=';')           # 指定分隔符

# 4.1.2 读取Excel文件
df_excel = pd.read_excel('data.xlsx')
df_excel = pd.read_excel('data.xlsx', sheet_name='Sheet1')  # 指定工作表
df_excel = pd.read_excel('data.xlsx', sheet_name=None)      # 读取所有工作表

# 4.1.3 读取JSON文件
df_json = pd.read_json('data.json')

# 4.1.4 读取HTML表格
tables = pd.read_html('webpage.html')  # 返回所有表格列表
df_table = tables[0]                  # 取第一个表格

# 4.1.5 读取常见参数
df = pd.read_csv(
    'data.csv',
    encoding='utf-8',           # 编码格式
    sep=',',                    # 分隔符
    header=0,                   # 表头行(0表示第一行)
    index_col=0,                # 设为索引的列
    usecols=['A', 'B', 'C'],    # 只读取指定列
    nrows=1000,                 # 只读取前1000行
    na_values=['N/A', 'NULL'],  # 识别为缺失值的字符
    dtype={'年龄': int}         # 指定列的数据类型
)

4.2 保存数据

# 4.2.1 保存为CSV
df.to_csv('output.csv', index=False)           # 不保存索引
df.to_csv('output.csv', index=True)            # 保存索引
df.to_csv('output.csv', sep=';')               # 指定分隔符

# 4.2.2 保存为Excel
df.to_excel('output.xlsx', sheet_name='数据')
df.to_excel('output.xlsx', index=False)

# 4.2.3 保存为JSON
df.to_json('output.json', orient='records', force_ascii=False)

# 4.2.4 保存为HTML
df.to_html('output.html', classes='table table-striped')

# 4.2.5 保存常见参数
df.to_csv(
    'output.csv',
    index=False,              # 是否保存索引
    encoding='utf-8-sig',    # 中文Excel兼容性编码
    sep=',',                  # 分隔符
    na_rep='NULL',            # 缺失值表示
    float_format='%.2f'       # 浮点数格式
)

4.3 读写对比速查表

格式读取保存常用参数
CSVread_csv()to_csv()sep, encoding, index
Excelread_excel()to_excel()sheet_name
JSONread_json()to_json()orient
HTMLread_html()to_html()classes
SQLread_sql()to_sql()con, if_exists

5. 数据探索与查看

5.1 基本信息查看

import pandas as pd
import numpy as np

# 创建示例数据
df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九'],
    '年龄': [25, 30, 28, 35, 27, 32, 29],
    '城市': ['北京', '上海', '广州', '深圳', '杭州', '北京', '上海'],
    '部门': ['技术', '销售', '技术', '市场', '销售', '技术', '市场'],
    '薪资': [15000, 22000, 18000, 25000, 16000, 17000, 21000],
    '绩效': [0.85, 0.92, 0.78, 0.88, 0.95, 0.82, 0.90]
})

# 5.1.1 查看数据概览
print("="*50)
print("1. 基本信息")
print("="*50)
print(f"\n数据形状: {df.shape}")
print(f"行数: {df.shape[0]}, 列数: {df.shape[1]}")
print(f"\n列名: {df.columns.tolist()}")
print(f"\n数据类型:\n{df.dtypes}")

# 5.1.2 查看前/后几行
print("\n" + "="*50)
print("2. 查看前5行")
print("="*50)
print(df.head())  # 默认5行
print("\n查看前3行:")
print(df.head(3))

print("\n" + "="*50)
print("3. 查看后3行")
print("="*50)
print(df.tail(3))

# 5.1.3 查看统计信息
print("\n" + "="*50)
print("4. 数值列统计信息")
print("="*50)
print(df.describe())

# 5.1.4 查看所有信息
print("\n" + "="*50)
print("5. 完整信息报告")
print("="*50)
print(df.info())

输出结果:

==================================================
1. 基本信息
==================================================

数据形状: (7, 6)
行数: 7, 列数: 6

列名: ['姓名', '年龄', '城市', '部门', '薪资', '绩效']

数据类型:
姓名     object
年龄      int64
城市     object
部门     object
薪资      int64
绩效    float64

==================================================
2. 查看前5行
==================================================
   姓名  年龄  城市  部门     薪资    绩效
0  张三  25  北京   技术  15000   0.85
1  李四  30  上海   销售  22000   0.92
2  王五  28  广州   技术  18000   0.78
3  赵六  35  深圳   市场  25000   0.88
4  钱七  27  杭州   销售  16000   0.95
...

==================================================
4. 数值列统计信息
==================================================
              年龄         薪资         绩效
count    7.000000    7.000000    7.000000
mean    29.428571  19000.000000   0.871429
std      3.408340    3585.708710   0.060186
min     25.000000  15000.000000   0.780000
25%     27.500000  16500.000000   0.835000
50%     29.000000  18000.000000   0.870000
75%     31.000000  21500.000000   0.895000
max     35.000000  25000.000000   0.950000

5.2 统计方法速查表

方法说明示例
count()非空值数量df['年龄'].count()
mean()平均值df['薪资'].mean()
median()中位数df['薪资'].median()
std()标准差df['薪资'].std()
min() / max()最小/最大值df['年龄'].min()
sum()求和df['薪资'].sum()
quantile(q)分位数df['薪资'].quantile(0.25)
value_counts()值计数df['城市'].value_counts()

6. 数据选择与过滤

6.1 列选择

# 6.1.1 选择单列(返回Series)
ages = df['年龄']
print(f"单列选择: {type(ages)}")
print(ages)

# 6.1.2 选择多列(返回DataFrame)
subset = df[['姓名', '年龄', '薪资']]
print(f"\n多列选择: {type(subset)}")
print(subset)

# 6.1.3 使用点号访问(适用于列名无空格的情况)
cities = df.城市
print(f"\n点号访问: {cities.tolist()}")

6.2 行选择

# 6.2.1 按位置选择 - iloc(索引位置)
print("="*50)
print("iloc 按位置选择")
print("="*50)

print("\n选择第1行:")
print(df.iloc[0])

print("\n选择前3行:")
print(df.iloc[:3])

print("\n选择第2-4行:")
print(df.iloc[1:4])

print("\n选择间隔行 (第0,2,4行):")
print(df.iloc[[0, 2, 4]])

# 6.2.2 按标签选择 - loc(索引名称)
print("\n" + "="*50)
print("loc 按标签选择")
print("="*50)

# 先设置索引为姓名
df_indexed = df.set_index('姓名')

print("\n选择'张三'行:")
print(df_indexed.loc['张三'])

print("\n选择'张三'到'王五'行:")
print(df_indexed.loc['张三':'王五'])

print("\n选择多行:")
print(df_indexed.loc[['张三', '李四', '钱七']])

6.3 条件过滤

print("="*50)
print("条件过滤示例")
print("="*50)

# 6.3.1 单条件过滤
print("\n1. 薪资大于20000的员工:")
high_salary = df[df['薪资'] > 20000]
print(high_salary)

# 6.3.2 多条件过滤(使用 & | 括号)
print("\n2. 北京且薪资大于15000:")
condition = (df['城市'] == '北京') & (df['薪资'] > 15000)
print(df[condition])

# 6.3.3 OR条件
print("\n3. 绩效大于0.9 或 薪资大于22000:")
condition = (df['绩效'] > 0.9) | (df['薪资'] > 22000)
print(df[condition])

# 6.3.4 使用isin过滤
print("\n4. 城市在['北京', '上海']:")
print(df[df['城市'].isin(['北京', '上海'])])

# 6.3.5 字符串方法过滤
print("\n5. 姓名包含'三'的员工:")
# 注意:中文需要先编码再匹配,这里演示字符串方法
df_temp = df.copy()
df_temp['姓名_str'] = df_temp['姓名'].astype(str)
print(df_temp[df_temp['姓名_str'].str.contains('三')])

# 6.3.6 组合使用
print("\n6. 综合查询:技术部门 + 绩效前50% + 选择特定列:")
high_performers = df[
    (df['部门'] == '技术') & 
    (df['绩效'] >= df['绩效'].median())
][['姓名', '部门', '薪资', '绩效']]
print(high_performers)

输出结果:

==================================================
条件过滤示例
==================================================

1. 薪资大于20000的员工:
   姓名  年龄  城市  部门     薪资    绩效
1  李四  30  上海   销售  22000   0.92
3  赵六  35  深圳   市场  25000   0.88

2. 北京且薪资大于15000:
   姓名  年龄  城市  部门     薪资    绩效
0  张三  25  北京   技术  15000   0.85
5  孙八  32  北京   技术  17000   0.82

6.4 数据选择方法对比

┌─────────────────────────────────────────────────────────────────┐
│                      数据选择方法对比                              │
├───────────────────┬─────────────────────────────────────────────┤
│  df['列名']        │  选择单列 → 返回Series                       │
│  df[['列1','列2']] │  选择多列 → 返回DataFrame                    │
│  df.iloc[0]       │  按位置选择行                                 │
│  df.iloc[0:5]     │  按位置切片选择行                              │
│  df.loc['标签']   │  按标签选择行                                 │
│  df.loc['a':'c']  │  按标签切片选择行                              │
│  df[条件]         │  按条件过滤行                                 │
└───────────────────┴─────────────────────────────────────────────┘

7. 数据清洗与处理

7.1 缺失值处理

import pandas as pd
import numpy as np

# 创建含缺失值的数据
df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '钱七'],
    '年龄': [25, 30, np.nan, 35, 27],
    '薪资': [15000, 22000, 18000, np.nan, 16000],
    '部门': ['技术', '销售', '技术', '市场', None],
    '绩效': [0.85, np.nan, 0.78, 0.88, 0.95]
})

print("="*50)
print("原始数据(含缺失值)")
print("="*50)
print(df)
print(f"\n缺失值统计:\n{df.isnull().sum()}")

# 7.1.1 检测缺失值
print("\n" + "="*50)
print("1. 检测缺失值")
print("="*50)
print(f"是否有缺失值: {df.isnull().any().any()}")
print(f"每列缺失值数量:\n{df.isnull().sum()}")

# 7.1.2 删除缺失值
print("\n" + "="*50)
print("2. 删除缺失值")
print("="*50)

print("\n删除任意含缺失值的行:")
print(df.dropna())

print("\n删除所有列都为缺失值的行:")
print(df.dropna(how='all'))

print("\n删除薪资或绩效缺失的行:")
print(df.dropna(subset=['薪资', '绩效']))

# 7.1.3 填充缺失值
print("\n" + "="*50)
print("3. 填充缺失值")
print("="*50)

print("\n用固定值填充年龄:")
print(df['年龄'].fillna(30))

print("\n用均值填充薪资:")
mean_salary = df['薪资'].mean()
print(df['薪资'].fillna(mean_salary))

print("\n用中位数填充薪资:")
median_salary = df['薪资'].median()
print(df['薪资'].fillna(median_salary))

print("\n用前向填充(ffill):")
print(df.fillna(method='ffill'))

print("\n用后向填充(bfill):")
print(df.fillna(method='bfill'))

# 7.1.4 高级填充:按组填充
print("\n" + "="*50)
print("4. 按组填充(部门平均薪资)")
print("="*50)

# 先创建测试数据
df_test = pd.DataFrame({
    '姓名': ['A', 'B', 'C', 'D', 'E'],
    '部门': ['技术', '技术', '销售', '销售', '技术'],
    '薪资': [15000, np.nan, 22000, np.nan, 17000]
})
print(df_test)
print("\n按部门均值填充:")
df_test['薪资'] = df_test.groupby('部门')['薪资'].transform(
    lambda x: x.fillna(x.mean())
)
print(df_test)

输出结果:

==================================================
原始数据(含缺失值)
==================================================
   姓名   年龄     薪资   部门   绩效
0  张三  25.0  15000.0   技术  0.85
1  李四  30.0  22000.0  销售   NaN
2  王五   NaN  18000.0   技术  0.78
3  赵六  35.0      NaN   市场  0.88
4  钱七  27.0  16000.0  None  0.95

==================================================
1. 检测缺失值
==================================================
是否有缺失值: True
每列缺失值数量:
姓名     0
年龄     1
薪资     1
部门     1
绩效     1

7.2 重复值处理

# 创建含重复值的数据
df = pd.DataFrame({
    '姓名': ['张三', '李四', '张三', '王五', '李四', '赵六'],
    '年龄': [25, 30, 25, 28, 30, 35],
    '城市': ['北京', '上海', '北京', '广州', '上海', '深圳']
})

print("="*50)
print("原始数据(含重复)")
print("="*50)
print(df)

# 7.2.1 检测重复
print("\n检测重复行:")
print(df.duplicated())

print("\n检测姓名重复:")
print(df.duplicated(subset=['姓名']))

# 7.2.2 删除重复
print("\n删除重复行(保留第一个):")
print(df.drop_duplicates())

print("\n删除姓名重复的行(保留最后一个):")
print(df.drop_duplicates(subset=['姓名'], keep='last'))

7.3 数据类型转换

# 7.3.1 查看数据类型
print("="*50)
print("数据类型操作")
print("="*50)
print(f"当前数据类型:\n{df.dtypes}")

# 7.3.2 转换数据类型
df['年龄'] = df['年龄'].astype('float32')
print(f"\n年龄转换为float32: {df['年龄'].dtype}")

# 7.3.3 转换为分类类型(节省内存)
df['部门'] = df['部门'].astype('category')
print(f"\n部门转换为category: {df['部门'].dtype}")

# 7.3.4 字符串转换
df['年龄_str'] = df['年龄'].astype(str)
print(f"\n年龄转字符串:\n{df['年龄_str']}")

# 7.3.5 时间类型转换
df['日期'] = pd.to_datetime(['2024-01-01', '2024-02-15', '2024-03-20', 
                              '2024-04-10', '2024-05-05', '2024-06-18'])
print(f"\n日期类型:\n{df['日期']}")
print(f"日期类型: {df['日期'].dtype}")

7.4 字符串处理

# 创建含字符串的数据
df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五'],
    '邮箱': ['zhangsan@email.com', 'lisi@EMAIL.COM', 'wangwu@Email.Com'],
    '电话': ['138-1234-5678', '139-8765-4321', '137-1111-2222']
})

print("="*50)
print("字符串处理示例")
print("="*50)

# 7.4.1 大小写转换
print("\n邮箱小写化:")
print(df['邮箱'].str.lower())

print("\n邮箱大写化:")
print(df['邮箱'].str.upper())

# 7.4.2 去除空格
df['姓名'] = df['姓名'].str.strip()  # 去除首尾空格
df['姓名'] = df['姓名'].str.replace(' ', '')  # 去除所有空格

# 7.4.3 字符串替换
print("\n电话号码格式化(去除-):")
print(df['电话'].str.replace('-', ''))

# 7.4.4 字符串分割
print("\n从邮箱提取用户名:")
print(df['邮箱'].str.split('@').str[0])

# 7.4.5 字符串包含判断
print("\n包含'email'的邮箱:")
print(df[df['邮箱'].str.contains('email', case=False)])

8. 数据转换与运算

8.1 列的添加与删除

import pandas as pd
import numpy as np

df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六'],
    '基本工资': [10000, 15000, 12000, 18000],
    '奖金': [2000, 3000, 2500, 4000]
})

print("="*50)
print("列的添加与删除")
print("="*50)
print(df)

# 8.1.1 添加新列
df['总工资'] = df['基本工资'] + df['奖金']
df['税率'] = 0.1
df['税后工资'] = df['总工资'] * (1 - df['税率'])

print("\n添加计算列后:")
print(df)

# 8.1.2 使用assign添加列(链式操作)
df = df.assign(
    奖金率=lambda x: x['奖金'] / x['基本工资'],
    年薪=lambda x: x['总工资'] * 12
)
print("\n使用assign添加:")
print(df)

# 8.1.3 删除列
df = df.drop(columns=['税率'])  # 删除单列
df = df.drop(columns=['总工资', '税后工资'])  # 删除多列
print("\n删除列后:")
print(df)

# 8.1.4 重命名列
df = df.rename(columns={
    '姓名': 'Name',
    '基本工资': 'Base_Salary',
    '奖金': 'Bonus'
})
print("\n重命名列后:")
print(df)

8.2 行操作

# 8.2.1 添加行<br>new_row = pd.DataFrame({<br>    'Name': ['孙七'],<br>    'Base_Salary': [14000],<br>    'Bonus': [2800]<br>})<br>df = pd.concat([d# 8.2.1 添加行
new_row = pd.DataFrame({
    'Name': ['孙七'],
    'Base_Salary': [14000],
    'Bonus': [2800]
})
df = pd.concat([df, new_row], ignore_index=True)
print("\n添加新行:")
print(df)

# 8.2.2 删除行
df = df.drop(index=[0])  # 删除第1行
df = df.drop(index=[0, 1])  # 删除多行
print("\n删除第1行后:")
print(df)f, new_row], ignore_index=True)<br>print("\n添加新行:")<br>print(df)<br><br># 8.2.2 删除行<br>df = df.drop(index=[0])  # 删除第1行<br>df = df.drop(index=[0, 1])  # 删除多行<br>print("\n删除第1行后:")<br>print(df)

8.3 排序

df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '钱七'],
    '部门': ['技术', '销售', '技术', '市场', '销售'],
    '薪资': [15000, 22000, 18000, 25000, 16000],
    '绩效': [0.85, 0.92, 0.78, 0.88, 0.95]
})

print("="*50)
print("排序操作")
print("="*50)

# 8.3.1 按单列排序
print("\n按薪资升序:")
print(df.sort_values('薪资'))

print("\n按薪资降序:")
print(df.sort_values('薪资', ascending=False))

# 8.3.2 按多列排序
print("\n先按部门升序,再按薪资降序:")
print(df.sort_values(['部门', '薪资'], ascending=[True, False]))

# 8.3.3 按索引排序
df_sorted = df.sort_index(ascending=False)
print("\n按索引降序:")
print(df_sorted)

8.4 数学运算

print("="*50)
print("数学运算示例")
print("="*50)

# 8.4.1 基础统计
print(f"\n薪资总和: {df['薪资'].sum()}")
print(f"薪资均值: {df['薪资'].mean():.2f}")
print(f"薪资中位数: {df['薪资'].median()}")
print(f"薪资标准差: {df['薪资'].std():.2f}")

# 8.4.2 累计计算
print("\n累计和:")
print(df['薪资'].cumsum())

print("\n累计最大值:")
print(df['薪资'].cummax())

# 8.4.3 排名
print("\n薪资排名:")
df['薪资排名'] = df['薪资'].rank(ascending=False, method='min')
print(df[['姓名', '薪资', '薪资排名']].sort_values('薪资排名'))

# 8.4.4 百分位排名
print("\n薪资百分位排名:")
df['薪资百分位'] = df['薪资'].rank(pct=True)
print(df[['姓名', '薪资', '薪资百分位']])

8.5 apply 与 lambda

print("="*50)
print("apply 与 lambda 示例")
print("="*50)

# 8.5.1 对单列应用函数
def classify_salary(salary):
    if salary < 17000:
        return '低'
    elif salary < 20000:
        return '中'
    else:
        return '高'

df['薪资等级'] = df['薪资'].apply(classify_salary)
print("\n薪资等级分类:")
print(df[['姓名', '薪资', '薪资等级']])

# 8.5.2 使用lambda
df['绩效评价'] = df['绩效'].apply(
    lambda x: '优秀' if x >= 0.9 else ('良好' if x >= 0.8 else '一般')
)
print("\n绩效评价:")
print(df[['姓名', '绩效', '绩效评价']])

# 8.5.3 对整行应用函数
def evaluate(row):
    score = row['薪资'] / 10000 + row['绩效'] * 100
    if score > 100:
        return 'A'
    elif score > 80:
        return 'B'
    else:
        return 'C'

df['综合评价'] = df.apply(evaluate, axis=1)
print("\n综合评价:")
print(df[['姓名', '薪资', '绩效', '综合评价']])

9. 分组与聚合

9.1 groupby 基础

import pandas as pd
import numpy as np

df = pd.DataFrame({
    '姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八'],
    '部门': ['技术', '销售', '技术', '市场', '销售', '技术'],
    '城市': ['北京', '上海', '广州', '北京', '上海', '广州'],
    '薪资': [15000, 22000, 18000, 25000, 16000, 17000],
    '绩效': [0.85, 0.92, 0.78, 0.88, 0.95, 0.82]
})

print("="*50)
print("groupby 分组操作")
print("="*50)

# 9.1.1 单列分组
print("\n按部门分组:")
grouped = df.groupby('部门')
print(f"分组对象: {type(grouped)}")
print(f"分组键: {grouped.groups.keys()}")

# 9.1.2 查看每个组
print("\n各组数据:")
for name, group in grouped:
    print(f"\n部门: {name}")
    print(group[['姓名', '薪资']])

9.2 聚合函数

print("="*50)
print("聚合函数")
print("="*50)

# 9.2.1 单列单聚合
print("\n1. 各部门平均薪资:")
print(df.groupby('部门')['薪资'].mean())

# 9.2.2 多列多聚合
print("\n2. 各部门薪资和绩效统计:")
agg_result = df.groupby('部门').agg({
    '薪资': ['sum', 'mean', 'min', 'max'],
    '绩效': ['mean', 'count']
})
print(agg_result)

# 9.2.3 自定义聚合名称
print("\n3. 自定义聚合列名:")
result = df.groupby('部门').agg(
    总薪资=('薪资', 'sum'),
    平均薪资=('薪资', 'mean'),
    平均绩效=('绩效', 'mean'),
    人数=('姓名', 'count')
)
print(result)

# 9.2.4 常用聚合函数
print("\n4. 常用聚合函数示例:")
print(df.groupby('部门').agg({
    '薪资': ['count', 'sum', 'mean', 'std', 'min', 'max']
}))

输出结果:

==================================================
聚合函数
==================================================

1. 各部门平均薪资:
部门
市场    25000.00
技术    16666.67
销售    19000.00

2. 各部门薪资和绩效统计:
        薪资                     绩效
       sum    mean   min   max   mean count
部门
市场  25000  25000  25000  25000  0.88     1
技术  50000  16667  15000  18000  0.82     3
销售  38000  19000  16000  22000  0.94     2

9.3 变换与过滤

print("="*50)
print("分组变换与过滤")
print("="*50)

# 9.3.1 transform - 返回与原数据等长的结果
print("\n1. 各部门薪资排名:")
df['部门薪资排名'] = df.groupby('部门')['薪资'].rank(ascending=False)
print(df[['姓名', '部门', '薪资', '部门薪资排名']])

# 9.3.2 计算各部门薪资占部门总额比例
print("\n2. 各部门薪资占比:")
df['薪资占比'] = df.groupby('部门')['薪资'].transform(
    lambda x: x / x.sum() * 100
)
print(df[['姓名', '部门', '薪资', '薪资占比']].round(2))

# 9.3.3 计算各部门薪资与平均值的差异
print("\n3. 各员工薪资与部门均值差异:")
df['薪资差异'] = df.groupby('部门')['薪资'].transform(
    lambda x: x - x.mean()
)
print(df[['姓名', '部门', '薪资', '薪资差异']].round(2))

# 9.3.4 filter - 按组过滤
print("\n4. 筛选人数大于2的部门:")
filtered = df.groupby('部门').filter(lambda x: len(x) > 2)
print(filtered)

9.4 多级分组

print("="*50)
print("多级分组")
print("="*50)

# 9.4.1 按部门和城市分组
print("\n1. 各部门在各城市的平均薪资:")
result = df.groupby(['部门', '城市']).agg({
    '薪资': 'mean',
    '绩效': 'mean'
}).round(2)
print(result)

# 9.4.2 转换为宽格式
print("\n2. 透视表形式:")
pivot = df.pivot_table(
    values='薪资',
    index='城市',
    columns='部门',
    aggfunc='mean'
).round(0)
print(pivot)

# 9.4.3 分组后迭代
print("\n3. 分组迭代:")
for (dept, city), group in df.groupby(['部门', '城市']):
    print(f"\n部门: {dept}, 城市: {city}")
    print(f"  人数: {len(group)}, 平均薪资: {group['薪资'].mean():.0f}")

10. 数据合并与连接

10.1 concat 纵向合并

import pandas as pd

df1 = pd.DataFrame({
    '姓名': ['张三', '李四'],
    '薪资': [15000, 22000]
})

df2 = pd.DataFrame({
    '姓名': ['王五', '赵六'],
    '薪资': [18000, 25000]
})

df3 = pd.DataFrame({
    '姓名': ['钱七'],
    '绩效': [0.95]
})

print("="*50)
print("concat 纵向合并")
print("="*50)

# 10.1.1 基本合并
print("\n1. 基础合并:")
result = pd.concat([df1, df2], ignore_index=True)
print(result)

# 10.1.2 处理不同列
print("\n2. 合并不同列的DataFrame:")
result = pd.concat([df1, df3], ignore_index=True)
print(result)

# 10.1.3 只合并相同列
print("\n3. 只合并相同列:")
result = pd.concat([df1, df3], join='inner')
print(result)

10.2 merge 横向连接

employees = pd.DataFrame({
    '员工ID': [1, 2, 3, 4],
    '姓名': ['张三', '李四', '王五', '赵六'],
    '部门ID': [101, 102, 101, 103]
})

departments = pd.DataFrame({
    '部门ID': [101, 102, 103],
    '部门名': ['技术部', '销售部', '市场部'],
    '部门经理': ['张总', '李总', '王总']
})

print("="*50)
print("merge 横向连接")
print("="*50)

# 10.2.1 内连接(默认)
print("\n1. 内连接 (inner):")
result = pd.merge(employees, departments, on='部门ID', how='inner')
print(result)

# 10.2.2 左连接
print("\n2. 左连接 (left):")
result = pd.merge(employees, departments, on='部门ID', how='left')
print(result)

# 10.2.3 右连接
print("\n3. 右连接 (right):")
result = pd.merge(employees, departments, on='部门ID', how='right')
print(result)

# 10.2.4 全外连接
print("\n4. 全外连接 (outer):")
result = pd.merge(employees, departments, on='部门ID', how='outer')
print(result)

连接类型可视化:

┌─────────────────────────────────────────────────────────────────┐
│                         merge 连接类型                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  employees              departments                              │
│  ┌────┬────┐           ┌────┬────┐                               │
│  │ID  │部门 │           │ID  │经理 │                               │
│  ├───┼────┤           ├───┼────┤                               │
│  │101 │技术 │           │101 │张总 │                               │
│  │102 │销售 │           │102 │李总 │                               │
│  │103 │市场 │           │104 │赵总 │                               │
│  └────┴────┘           └────┴────┘                               │
│                                                                 │
│  inner:  保留两表都有ID的 (101, 102)                              │
│  left:   保留左表全部 (101, 102, 103)                             │
│  right:  保留右表全部 (101, 102, 104)                             │
│  outer:  保留两表全部 (101, 102, 103, 104)                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

10.3 join 索引连接

# 10.3.1 使用join连接
df1 = pd.DataFrame({'薪资': [15000, 22000]}, index=['张三', '李四'])
df2 = pd.DataFrame({'绩效': [0.85, 0.92]}, index=['张三', '李四'])

print("\n" + "="*50)
print("join 索引连接")
print("="*50)
print("\n使用join合并:")
result = df1.join(df2)
print(result)

10.4 合并策略对比

方法用途合并轴连接键
pd.concat()追加行/列纵/横
pd.merge()横向连接表指定列
df.join()索引连接索引

11. 时间序列处理

11.1 时间类型转换

import pandas as pd

# 11.1.1 创建时间数据
print("="*50)
print("时间序列处理")
print("="*50)

# 从字符串转换
dates = pd.to_datetime(['2024-01-01', '2024-02-15', '2024-03-20'])
print("\n日期数组:")
print(dates)

# 从范围创建
print("\n日期范围:")
date_range = pd.date_range('2024-01-01', periods=10, freq='D')
print(date_range)

# 11.1.2 创建带时间的DataFrame
df = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=100, freq='D'),
    '销量': np.random.randint(100, 500, 100)
})
df['日期'] = pd.to_datetime(df['日期'])

print("\n时间数据DataFrame:")
print(df.head(10))

11.2 时间属性提取

print("="*50)
print("时间属性提取")
print("="*50)

df = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=7, freq='D'),
    '销量': [120, 150, 130, 180, 200, 170, 160]
})

print("\n时间属性示例:")
print(df)

# 提取年、月、日
df['年'] = df['日期'].dt.year
df['月'] = df['日期'].dt.month
df['日'] = df['日期'].dt.day
df['星期'] = df['日期'].dt.dayofweek  # 0=周一, 6=周日
df['星期名称'] = df['日期'].dt.day_name()
df['季度'] = df['日期'].dt.quarter
df['是否周末'] = df['日期'].dt.is_weekend()

print("\n提取时间属性后:")
print(df)

11.3 时间重采样

# 创建月度销售数据
df = pd.DataFrame({
    '月份': pd.date_range('2023-01-01', periods=24, freq='ME'),
    '销售额': np.random.randint(10000, 50000, 24)
})

print("\n" + "="*50)
print("时间重采样")
print("="*50)

# 设置日期为索引
df.set_index('月份', inplace=True)

# 月转季度
print("\n月度转季度:")
quarterly = df.resample('QE').sum()
print(quarterly)

# 月转年度
print("\n年度汇总:")
yearly = df.resample('YE').agg({
    '销售额': ['sum', 'mean', 'max', 'min']
})
print(yearly)

# 移动平均
print("\n3个月移动平均:")
df['移动平均'] = df['销售额'].rolling(window=3).mean()
print(df)

11.4 时间偏移

print("\n" + "="*50)
print("时间偏移操作")
print("="*50)

df = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=5, freq='D'),
    '值': [1, 2, 3, 4, 5]
})

print("\n原始数据:")
print(df)

# 向前偏移
print("\n日期偏移(向前3天):")
df['偏移后'] = df['日期'] + pd.Timedelta(days=3)
print(df)

# 计算时间差
df['差值'] = df['偏移后'] - df['日期']
print("\n时间差:")
print(df['差值'])

12. 数据可视化

12.1 Pandas 内置绘图

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 创建示例数据
df = pd.DataFrame({
    '月份': ['1月', '2月', '3月', '4月', '5月', '6月'],
    '销售额': [12000, 15000, 18000, 16000, 22000, 25000],
    '成本': [8000, 9500, 11000, 10000, 13000, 14000],
    '利润率': [0.33, 0.37, 0.39, 0.38, 0.41, 0.44]
}, index=['1月', '2月', '3月', '4月', '5月', '6月'])

print("="*50)
print("Pandas 数据可视化")
print("="*50)

# 12.1.1 折线图
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 折线图
df[['销售额', '成本']].plot(kind='line', ax=axes[0, 0], marker='o')
axes[0, 0].set_title('销售额与成本趋势')
axes[0, 0].set_xlabel('月份')
axes[0, 0].set_ylabel('金额')

# 柱状图
df[['销售额', '成本']].plot(kind='bar', ax=axes[0, 1], color=['#2ecc71', '#e74c3c'])
axes[0, 1].set_title('月度销售对比')
axes[0, 1].set_xlabel('月份')
axes[0, 1].tick_params(axis='x', rotation=0)

# 饼图
df['销售额'].plot(kind='pie', ax=axes[1, 0], autopct='%1.1f%%',
                  startangle=90, explode=[0.05]*6)
axes[1, 0].set_title('各月销售额占比')
axes[1, 0].set_ylabel('')

# 面积图
df['利润率'].plot(kind='area', ax=axes[1, 1], alpha=0.7, color='skyblue')
axes[1, 1].set_title('利润率变化')
axes[1, 1].set_ylabel('利润率')

plt.tight_layout()
plt.savefig('images/pandas_visualization.png', dpi=150, bbox_inches='tight')
plt.show()
print("\n图表已保存至: images/pandas_visualization.png")

可视化效果:

┌─────────────────────────────────────────────────────────────────┐
│                    Pandas 数据可视化示例                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  折线图                    │        柱状图                        │
│  ┌──────────────────┐     │     ┌──────────────────┐            │
│  │  ● 销售额         │     │     │ ██                │            │
│  │  ○ 成本           │     │     │ ██ ██             │            │
│  └──────────────────┘     │     └──────────────────┘            │
│                                                                 │
│  饼图                     │        面积图                       │
│  ┌──────────────────┐     │     ┌──────────────────┐            │
│  │     ●            │     │     │ ████████         │            │
│  │   ●   ●          │     │     │ ████████         │            │
│  └──────────────────┘     │     └──────────────────┘            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

12.2 常见图表类型

类型方法适用场景
折线图df.plot.line()趋势变化
柱状图df.plot.bar()类别对比
饼图df.plot.pie()占比展示
散点图df.plot.scatter()相关性分析
箱线图df.plot.box()分布统计
直方图df.plot.hist()频率分布
热力图df.corr().plot()相关性矩阵

12.3 样式设置

# 12.3.1 样式主题
import matplotlib.pyplot as plt
plt.style.use('seaborn-v0_8-darkgrid')  # seaborn风格

# 12.3.2 颜色设置
df['销售额'].plot(color='#3498db', linewidth=2, linestyle='--')

# 12.3.3 网格和标签
plt.grid(True, linestyle='--', alpha=0.7)
plt.xlabel('时间', fontsize=12)
plt.ylabel('金额', fontsize=12)
plt.title('销售趋势', fontsize=14, fontweight='bold')

# 12.3.4 图例设置
plt.legend(['销售额', '成本'], loc='upper left', fontsize=10)

# 12.3.5 保存高清图
plt.savefig('output.png', dpi=300, bbox_inches='tight', 
            facecolor='white', edgecolor='none')

13. 实战项目:电商数据分析

13.1 项目背景

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

print("="*60)
print("       电商数据分析实战项目")
print("="*60)

# 13.1.1 生成模拟数据
np.random.seed(42)

# 客户数据
customers = pd.DataFrame({
    '客户ID': range(1001, 2001),
    '姓名': [f'客户{i}' for i in range(1000)],
    '年龄': np.random.randint(18, 65, 1000),
    '城市': np.random.choice(['北京', '上海', '广州', '深圳', '杭州'], 1000),
    '会员等级': np.random.choice(['普通', '银卡', '金卡', '钻石'], 
                                  1000, p=[0.5, 0.25, 0.15, 0.1]),
    '注册日期': pd.date_range('2023-01-01', periods=1000, freq='6H')
})

# 订单数据
orders = pd.DataFrame({
    '订单ID': range(5001, 8001),
    '客户ID': np.random.choice(customers['客户ID'], 3000),
    '商品类别': np.random.choice(['电子产品', '服装', '食品', '家居', '美妆'], 3000),
    '商品名称': np.random.choice(['手机', '电脑', 'T恤', '裙子', '零食', 
                                  '沙发', '床品', '口红', '面膜'], 3000),
    '单价': np.random.randint(50, 2000, 3000),
    '数量': np.random.randint(1, 5, 3000),
    '订单日期': pd.date_range('2024-01-01', periods=3000, freq='3H')
})

# 计算订单金额
orders['订单金额'] = orders['单价'] * orders['数量']

# 关联客户数据
df = orders.merge(customers[['客户ID', '姓名', '城市', '会员等级']], 
                  on='客户ID', how='left')

print("\n1. 数据概览")
print("-"*60)
print(f"客户总数: {customers['客户ID'].nunique()}")
print(f"订单总数: {len(orders)}")
print(f"总销售额: ¥{df['订单金额'].sum():,.2f}")
print(f"平均订单金额: ¥{df['订单金额'].mean():.2f}")

13.2 数据清洗

print("\n2. 数据清洗")
print("-"*60)

# 检测缺失值
print(f"缺失值统计:\n{df.isnull().sum()}")

# 删除重复订单
before = len(df)
df = df.drop_duplicates(subset=['订单ID'])
print(f"\n删除重复订单: {before - len(df)}条")

# 过滤异常金额
before = len(df)
df = df[df['订单金额'] > 0]
print(f"过滤异常订单: {before - len(df)}条")

print(f"\n清洗后数据量: {len(df)}条")

13.3 业务分析

print("\n3. 业务指标分析")
print("-"*60)

# 3.1 各品类销售分析
print("\n【各品类销售统计】")
category_stats = df.groupby('商品类别').agg({
    '订单ID': 'count',
    '订单金额': ['sum', 'mean']
}).round(2)
category_stats.columns = ['订单数', '销售额', '平均订单额']
category_stats = category_stats.sort_values('销售额', ascending=False)
print(category_stats)

# 3.2 各城市分析
print("\n【各城市销售统计】")
city_stats = df.groupby('城市').agg({
    '订单ID': 'count',
    '订单金额': 'sum',
    '客户ID': 'nunique'
}).round(2)
city_stats.columns = ['订单数', '销售额', '客户数']
city_stats['客单价'] = (city_stats['销售额'] / city_stats['客户数']).round(2)
print(city_stats)

# 3.3 会员等级分析
print("\n【会员等级分析】")
vip_stats = df.groupby('会员等级').agg({
    '订单ID': 'count',
    '订单金额': ['sum', 'mean'],
    '客户ID': 'nunique'
}).round(2)
vip_stats.columns = ['订单数', '销售额', '平均订单额', '客户数']
vip_stats['销售占比'] = (vip_stats['销售额'] / vip_stats['销售额'].sum() * 100).round(2)
vip_stats['客单价'] = (vip_stats['销售额'] / vip_stats['客户数']).round(2)
vip_stats = vip_stats.sort_values('销售额', ascending=False)
print(vip_stats)

# 3.4 月度趋势
print("\n【月度销售趋势】")
df['月份'] = df['订单日期'].dt.to_period('M')
monthly_stats = df.groupby('月份').agg({
    '订单ID': 'count',
    '订单金额': 'sum'
})
monthly_stats['环比增长'] = monthly_stats['订单金额'].pct_change().round(4) * 100
print(monthly_stats)

13.4 数据可视化

print("\n4. 数据可视化")
print("-"*60)

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 4.1 品类销售柱状图
ax1 = axes[0, 0]
category_stats['销售额'].plot(kind='bar', ax=ax1, color='#3498db')
ax1.set_title('各品类销售额', fontsize=12, fontweight='bold')
ax1.set_xlabel('商品类别')
ax1.set_ylabel('销售额')
ax1.tick_params(axis='x', rotation=45)
for i, v in enumerate(category_stats['销售额']):
    ax1.text(i, v + 50000, f'¥{v/10000:.0f}万', ha='center', fontsize=9)

# 4.2 城市占比饼图
ax2 = axes[0, 1]
city_stats['销售额'].plot(kind='pie', ax=ax2, autopct='%1.1f%%',
                          startangle=90, explode=[0.02]*5)
ax2.set_title('各城市销售占比', fontsize=12, fontweight='bold')
ax2.set_ylabel('')

# 4.3 月度趋势折线图
ax3 = axes[1, 0]
monthly_stats['订单金额'].plot(kind='line', ax=ax3, marker='o', 
                                linewidth=2, color='#2ecc71')
ax3.fill_between(monthly_stats.index.astype(str), monthly_stats['订单金额'], 
                  alpha=0.3, color='#2ecc71')
ax3.set_title('月度销售趋势', fontsize=12, fontweight='bold')
ax3.set_xlabel('月份')
ax3.set_ylabel('销售额')

# 4.4 会员等级对比
ax4 = axes[1, 1]
vip_stats[['销售额', '客单价']].plot(kind='bar', ax=ax4, 
                                     color=['#9b59b6', '#e74c3c'])
ax4.set_title('会员等级销售对比', fontsize=12, fontweight='bold')
ax4.set_xlabel('会员等级')
ax4.set_ylabel('金额')
ax4.tick_params(axis='x', rotation=0)

plt.tight_layout()
plt.savefig('images/ecommerce_analysis.png', dpi=150, bbox_inches='tight')
plt.show()
print("\n分析图表已保存至: images/ecommerce_analysis.png")

13.5 分析结论

print("\n5. 分析结论")
print("="*60)

# 计算关键指标
top_category = category_stats['销售额'].idxmax()
top_city = city_stats['销售额'].idxmax()
top_vip = vip_stats['销售额'].idxmax()
vip_sales_ratio = vip_stats.loc['钻石', '销售额'] / vip_stats['销售额'].sum() * 100

print(f"""
📊 【关键发现】

1. 【品类分析】
   - 最畅销品类: {top_category} (销售额最高)
   - 最高平均订单额: {category_stats['平均订单额'].idxmax()} (¥{category_stats['平均订单额'].max():.2f})

2. 【地区分析】
   - 最大市场: {top_city} (销售额¥{city_stats.loc[top_city, '销售额']:,.0f})
   - 最高客单价: {city_stats['客单价'].idxmax()} (¥{city_stats['客单价'].max():.2f})

3. 【会员分析】
   - 最高价值会员: {top_vip}
   - 钻石会员销售占比: {vip_sales_ratio:.1f}%

4. 【月度趋势】
   - 销售高峰月份: {monthly_stats['订单金额'].idxmax()} (¥{monthly_stats['订单金额'].max():,.0f})
   - 环比增长最快: {monthly_stats['环比增长'].idxmax()} (+{monthly_stats['环比增长'].max():.1f}%)

💡 【优化建议】

1. 重点发展{top_city}市场,考虑开设线下体验店
2. 针对钻石会员推出专属促销活动,提升复购率
3. 优化{top_category}品类库存,保证供货充足
4. 关注月度趋势,提前规划节假日营销活动
""")

14. 性能优化技巧

14.1 数据类型优化

import pandas as pd
import numpy as np
import time

print("="*50)
print("性能优化技巧")
print("="*50)

# 14.1.1 内存使用分析
df = pd.DataFrame({
    'ID': np.arange(1, 100001),
    '值': np.random.randint(0, 1000, 100000),
    '分数': np.random.random(100000)
})

print(f"\n原始内存使用: {df.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
print(f"数据类型:\n{df.dtypes}")

# 14.1.2 类型优化
df['ID'] = df['ID'].astype('int32')
df['值'] = df['值'].astype('int16')
df['分数'] = df['分数'].astype('float32')

print(f"\n优化后内存: {df.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
print(f"优化后类型:\n{df.dtypes}")

14.2 读取优化

# 14.2.1 使用chunksize分块读取大文件
print("\n分块读取示例:")
start = time.time()

chunks = []
for chunk in pd.read_csv('data.csv', chunksize=10000):
    # 对每个chunk进行处理
    chunk = chunk[chunk['值'] > 500]  # 示例过滤
    chunks.append(chunk)
    
result = pd.concat(chunks, ignore_index=True)
print(f"分块读取耗时: {time.time() - start:.2f}秒")
print(f"结果行数: {len(result)}")

# 14.2.2 只读取需要的列
df = pd.read_csv('data.csv', usecols=['列1', '列2', '列3'])

14.3 操作优化

# 14.3.1 使用内置方法替代apply
print("\n" + "="*50)
print("操作优化对比")
print("="*50)

df = pd.DataFrame({'数值': np.random.randint(0, 1000, 100000)})

# 慢方法:apply + 自定义函数
start = time.time()
df['结果1'] = df['数值'].apply(lambda x: x * 2 if x > 500 else x)
time1 = time.time() - start
print(f"apply方法耗时: {time1:.4f}秒")

# 快方法:向量化操作
start = time.time()
df['结果2'] = df['数值'].where(df['数值'] > 500, df['数值']) * 2
time2 = time.time() - start
print(f"向量化方法耗时: {time2:.4f}秒")
print(f"速度提升: {time1/time2:.1f}倍")

# 14.3.2 使用query替代布尔索引
start = time.time()
result1 = df[df['数值'] > 500]
time1 = time.time() - start

start = time.time()
result2 = df.query('数值 > 500')
time2 = time.time() - start

print(f"\n布尔索引耗时: {time1:.4f}秒")
print(f"query耗时: {time2:.4f}秒")

14.4 优化技巧速查表

场景优化方法效果
内存不足降低数据类型精度内存减少50%+
大文件读取使用chunksize避免内存溢出
循环处理使用向量化速度提升10-100倍
条件过滤使用query()代码更简洁
字符串操作使用.str.xxx()速度更快
数据合并使用join代替merge更快
重复计算使用groupby.transform避免循环

15. 常见问题与解决方案

15.1 SettingWithCopyWarning

print("="*50)
print("常见问题与解决方案")
print("="*50)

print("""
【问题1】SettingWithCopyWarning

原因:创建了DataFrame的视图而不是副本,导致修改警告。

❌ 错误示例:
df = pd.DataFrame({'A': [1, 2, 3]})
df2 = df[df['A'] > 1]
df2['B'] = [4, 5]  # 警告!

✅ 正确示例:
df = pd.DataFrame({'A': [1, 2, 3]})
df2 = df[df['A'] > 1].copy()
df2['B'] = [4, 5]  # 无警告
""")

15.2 类型转换问题

print("""
【问题2】字符串与数值混合导致的类型问题

原因:列中包含字符串和数值混合,pandas自动推断为object类型。

❌ 错误示例:
df['年龄'] = ['25', '30', 'abc', '28']
df['年龄'].mean()  # 报错!

✅ 正确示例:
df['年龄'] = pd.to_numeric(df['年龄'], errors='coerce')  # 无效值变为NaN
df['年龄'].mean()  # 正常工作
""")

15.3 索引混乱

print("""
【问题3】索引混乱问题

原因:删除行后索引不连续,导致数据对齐问题。

❌ 错误示例:
df = pd.DataFrame({'A': [1, 2, 3, 4, 5]})
df2 = df[df['A'] > 2]
# df2索引是[2, 3, 4],不是[0, 1, 2]

✅ 正确示例:
df2 = df[df['A'] > 2].reset_index(drop=True)  # 重置索引
# 或者
df = df.reset_index(drop=True)  # 重置后再过滤
""")

15.4 时间解析问题

print("""
【问题4】时间解析错误

原因:日期格式不标准或包含时区信息。

❌ 错误示例:
df['日期'] = pd.to_datetime(df['日期'])  # 可能失败

✅ 正确示例:
# 指定格式
df['日期'] = pd.to_datetime(df['日期'], format='%Y/%m/%d')

# 自动推断
df['日期'] = pd.to_datetime(df['日期'], infer_datetime_format=True)

# 处理混合格式
df['日期'] = pd.to_datetime(df['日期'], errors='coerce')
""")

15.5 内存不足

print("""
【问题5】内存不足

✅ 解决方案:

1. 降低数据类型精度
   df['ID'] = df['ID'].astype('int32')
   df['值'] = df['值'].astype('float32')

2. 删除不需要的列
   df = df.drop(columns=['列1', '列2'])

3. 分块处理
   for chunk in pd.read_csv('file.csv', chunksize=10000):
       process(chunk)

4. 使用更高效的数据格式
   df.to_parquet('file.parquet')  # 比CSV节省空间

5. 只读取需要的列
   df = pd.read_csv('file.csv', usecols=['A', 'B', 'C'])
""")

附录

A. 常用命令速查

功能命令示例
读取CSVpd.read_csv()pd.read_csv('data.csv')
保存CSVdf.to_csv()df.to_csv('out.csv')
查看前5行df.head()df.head()
查看形状df.shapedf.shape
查看列名df.columnsdf.columns
查看类型df.dtypesdf.dtypes
统计描述df.describe()df.describe()
缺失值df.isnull()df.isnull().sum()
去重df.drop_duplicates()df.drop_duplicates()
分组df.groupby()df.groupby('A').sum()
排序df.sort_values()df.sort_values('A')
筛选df[df['A']>5]df[df['A']>5]
合并pd.merge()pd.merge(df1, df2, on='A')

B. 推荐学习资源

资源类型链接
Pandas官方文档官方https://pandas.pydata.org/docs/
Pandas练习题练习https://github.com/guipsamora/pandas_exercises
Kaggle Pandas教程互动https://www.kaggle.com/learn/pandas
10分钟入门Pandas教程Pandas官方文档

C. 版本信息

import pandas as pd
import numpy as np

print("\n" + "="*50)
print("环境信息")
print("="*50)
print(f"Pandas版本: {pd.__version__}")
print(f"NumPy版本: {np.__version__}")
print(f"Python版本: {__import__('sys').version}")

📝 教程总结

本教程涵盖了 Pandas 的核心知识点:

  • 数据结构:Series 和 DataFrame 的创建与操作
  • 数据读写:CSV、Excel、JSON 等格式的读取与保存
  • 数据探索:数据查看、统计、描述性分析
  • 数据选择:列选择、行选择、条件过滤
  • 数据清洗:缺失值、重复值、数据类型转换
  • 数据转换:列操作、排序、运算
  • 分组聚合:groupby、agg、transform、filter
  • 数据合并:concat、merge、join
  • 时间序列:时间类型、重采样、偏移
  • 数据可视化:Pandas 内置绘图
  • 性能优化:内存优化、读写优化、操作优化

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容