Pandas
Pandas 是 Python 数据分析的核心库,提供了快速、灵活和富有表现力的数据结构,旨在使处理"关系型"或"标记型"数据既简单又直观。
简介
Pandas 特性
Pandas 核心特性:
- DataFrame: 强大的二维数据结构
- Series: 带标签的一维数组
- 数据对齐: 自动标签对齐
- 处理缺失数据: 完整的缺失数据处理
- 数据清洗: 强大的数据清洗工具
- 数据 IO: 支持多种文件格式
- 时间序列: 专门的时间序列功能
- 数据聚合: 灵活的分组聚合操作
- 数据合并: 类似 SQL 的合并操作
- 高性能: 基于 NumPy 优化
适用场景:
- 数据清洗和预处理
- 数据探索和分析
- 数据可视化准备
- 时间序列分析
- 统计分析
- 机器学习数据准备
- Excel/CSV 数据处理
安装 Pandas
# 创建虚拟环境
python -m venv venv
# Windows 激活
venv\Scripts\activate
# Linux/Mac 激活
source venv/bin/activate
# 安装 Pandas
pip install pandas
# 安装特定版本
pip install pandas==2.0.3
# 安装常用扩展
pip install numpy # 数值计算
pip install matplotlib # 数据可视化
pip install openpyxl # Excel 支持
pip install xlrd # Excel 读取
pip install sqlalchemy # 数据库支持
pip install psycopg2-binary # PostgreSQL
pip install pymysql # MySQL
pip install pyarrow # Arrow 格式
pip install fastparquet # Parquet 格式
# 查看版本
python -c "import pandas; print(pandas.__version__)"
快速开始
导入和配置
import pandas as pd
import numpy as np
# 查看版本
print(pd.__version__)
# 配置选项
pd.set_option('display.max_rows', 100) # 最大显示行数
pd.set_option('display.max_columns', 20) # 最大显示列数
pd.set_option('display.width', 1000) # 显示宽度
pd.set_option('display.precision', 2) # 小数位数
pd.set_option('display.float_format', '{:.2f}'.format) # 浮点数格式
# 重置选项
pd.reset_option('all')
# 获取所有选项
pd.describe_option()
Series
创建 Series
import pandas as pd
import numpy as np
# 从列表创建
s = pd.Series([1, 3, 5, np.nan, 7, 9])
print(s)
# 指定索引
s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
print(s)
# 从字典创建
data = {'a': 1, 'b': 3, 'c': 5}
s = pd.Series(data)
print(s)
# 从标量创建
s = pd.Series(5, index=['a', 'b', 'c'])
print(s)
# 指定名称和类型
s = pd.Series(
[1, 3, 5, 7, 9],
index=['a', 'b', 'c', 'd', 'e'],
name='numbers',
dtype='float64'
)
print(s)
# 从 NumPy 数组创建
arr = np.array([1, 3, 5, 7, 9])
s = pd.Series(arr, index=['a', 'b', 'c', 'd', 'e'])
Series 索引
s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
# 位置索引
print(s[0]) # 第一个元素
print(s[:3]) # 前三个元素
print(s[1:4]) # 第2到第4个
# 标签索引
print(s['a']) # 标签为 'a' 的元素
print(s[['a', 'c', 'e']]) # 多个标签
# loc: 标签索引
print(s.loc['a']) # 单个标签
print(s.loc['a':'c']) # 标签切片(包含两端)
print(s.loc[['a', 'c']]) # 多个标签
# iloc: 位置索引
print(s.iloc[0]) # 第一个位置
print(s.iloc[0:3]) # 位置切片(不包含右端)
print(s.iloc[[0, 2, 4]]) # 多个位置
# 布尔索引
print(s[s > 3]) # 大于3的元素
print(s[(s > 2) & (s < 8)]) # 2-8之间的元素
# 条件索引
print(s[s > 5] = 10) # 大于5的设为10
Series 运算
s1 = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series([5, 6, 7, 8], index=['a', 'b', 'c', 'd'])
# 算术运算
print(s1 + s2) # 加法
print(s1 - s2) # 减法
print(s1 * s2) # 乘法
print(s1 / s2) # 除法
print(s1 // s2) # 整除
print(s1 % s2) # 取模
print(s1 ** 2) # 幂运算
# 标量运算
print(s1 + 10) # 所有元素加10
print(s1 * 2) # 所有元素乘2
# 比较运算
print(s1 > 2) # 大于
print(s1 == s2) # 等于
print(s1 != 3) # 不等于
# 统计函数
print(s1.sum()) # 求和
print(s1.mean()) # 平均值
print(s1.median()) # 中位数
print(s1.std()) # 标准差
print(s1.var()) # 方差
print(s1.min()) # 最小值
print(s1.max()) # 最大值
print(s1.quantile(0.25)) # 25%分位数
# 其他方法
print(s1.value_counts()) # 值计数
print(s1.unique()) # 唯一值
print(s1.nunique()) # 唯一值数量
print(s1.isin([2, 4])) # 是否在列表中
Series 字符串操作
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
# 字符串方法
print(s.str.lower()) # 转小写
print(s.str.upper()) # 转大写
print(s.str.len()) # 长度
print(s.str.strip()) # 去除空格
print(s.str.split(',')) # 分割
print(s.str.replace('a', 'X')) # 替换
print(s.str.contains('a')) # 包含
print(s.str.startswith('A')) # 以...开头
print(s.str.endswith('a')) # 以...结尾
print(s.str[:3]) # 切片
print(s.str.cat(sep=',')) # 连接
# 正则表达式
print(s.str.extract(r'([A-Z][a-z]+)')) # 提取
print(s.str.findall(r'[A-Z]')) # 查找所有
print(s.str.match(r'[A-Z][a-z]+')) # 匹配
DataFrame
创建 DataFrame
import pandas as pd
import numpy as np
# 从字典创建
data = {
'name': ['Tom', 'Jerry', 'Mickey'],
'age': [20, 25, 30],
'city': ['New York', 'London', 'Paris']
}
df = pd.DataFrame(data)
print(df)
# 指定列顺序
df = pd.DataFrame(data, columns=['name', 'city', 'age'])
print(df)
# 指定索引
df = pd.DataFrame(data, index=['a', 'b', 'c'])
print(df)
# 从列表的字典创建
data = [
{'name': 'Tom', 'age': 20, 'city': 'New York'},
{'name': 'Jerry', 'age': 25, 'city': 'London'},
{'name': 'Mickey', 'age': 30, 'city': 'Paris'}
]
df = pd.DataFrame(data)
print(df)
# 从 NumPy 数组创建
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
df = pd.DataFrame(arr, columns=['A', 'B', 'C'])
print(df)
# 从列表创建
df = pd.DataFrame(
[[1, 'Tom', 20], [2, 'Jerry', 25], [3, 'Mickey', 30]],
columns=['id', 'name', 'age']
)
print(df)
# 创建空 DataFrame
df = pd.DataFrame(columns=['A', 'B', 'C'])
print(df)
# 创建带索引的 DataFrame
df = pd.DataFrame(
data,
index=pd.Index(['a', 'b', 'c'], name='idx')
)
print(df)
查看数据
# 创建示例数据
data = {
'name': ['Tom', 'Jerry', 'Mickey', 'Minney', 'Donald'],
'age': [20, 25, 30, 28, 35],
'city': ['New York', 'London', 'Paris', 'Paris', 'Tokyo'],
'salary': [50000, 60000, 70000, 65000, 80000]
}
df = pd.DataFrame(data)
# 查看基本信息
print(df.head()) # 前5行
print(df.head(3)) # 前3行
print(df.tail()) # 后5行
print(df.tail(2)) # 后2行
# 数据信息
print(df.info()) # 数据信息
print(df.shape) # 形状(行数,列数)
print(df.columns) # 列名
print(df.index) # 索引
print(df.dtypes) # 数据类型
print(df.values) # 值数组
print(df.ndim) # 维度
print(df.size) # 元素总数
# 统计信息
print(df.describe()) # 数值列统计
print(df.describe(include='all')) # 所有列统计
print(df.describe(include='object')) # 对象列统计
print(df.describe(exclude='object')) # 非对象列统计
# 快速统计
print(df.count()) # 非空值计数
print(df.nunique()) # 唯一值数量
print(df.value_counts()) # 值计数(针对单列)
print(df['city'].value_counts()) # 某列的值计数
# 内存使用
print(df.memory_usage()) # 每列内存
print(df.memory_usage(deep=True)) # 深度内存分析
索引与选择
# 选择列
print(df['name']) # 单列(返回Series)
print(df.name) # 单列(同上,但列名需合法)
print(df[['name', 'age']]) # 多列(返回DataFrame)
# 选择行(位置)
print(df[0:2]) # 前两行
print(df[1:3]) # 第2-3行
print(df[:2]) # 前两行
# loc: 标签索引
print(df.loc[0]) # 单行
print(df.loc[0:2]) # 多行(包含两端)
print(df.loc[0, 'name']) # 标量值
print(df.loc[:, ['name', 'age']]) # 所有行,指定列
# iloc: 位置索引
print(df.iloc[0]) # 第一行
print(df.iloc[0:2]) # 前两行(不包含右端)
print(df.iloc[0, 0]) # 标量值
print(df.iloc[0:2, 0:2]) # 前两行,前两列
# 条件选择
print(df[df['age'] > 25]) # 年龄大于25
print(df[(df['age'] > 25) & (df['salary'] > 60000)]) # 多条件
print(df[df['city'].isin(['Paris', 'Tokyo'])]) # 在列表中
print(df[df['name'].str.startswith('T')]) # 字符串条件
# query 方法
print(df.query('age > 25')) # 查询
print(df.query('age > 25 and city == "Paris"')) # 多条件
# between
print(df[df['age'].between(25, 30)]) # 年龄在25-30之间
# 条件赋值
df.loc[df['age'] > 30, 'age'] = 31 # 将大于30的设为31
df.loc[df['city'] == 'Paris', 'salary'] *= 1.1 # 巴黎的工资增加10%
数据操作
# 添加列
df['bonus'] = df['salary'] * 0.1 # 计算列
df['total'] = df['salary'] + df['bonus'] # 基于其他列
df['new_col'] = 100 # 常量列
# 删除列
df.drop('bonus', axis=1, inplace=True) # 删除单列
df.drop(['bonus', 'total'], axis=1, inplace=True) # 删除多列
del df['new_col'] # 删除列
# 重命名列
df.rename(columns={'name': '姓名', 'age': '年龄'}, inplace=True)
df.columns = ['ID', 'Name', 'Age', 'City', 'Salary'] # 重命名所有列
# 添加行
df.loc[len(df)] = [6, 'Daisy', 26, 'Berlin', 55000] # 添加单行
df = pd.concat([df, pd.DataFrame([[7, 'Lily', 27, 'Rome', 58000]],
columns=df.columns)], ignore_index=True) # 添加多行
# 删除行
df.drop(0, axis=0, inplace=True) # 删除单行
df.drop([0, 1], axis=0, inplace=True) # 删除多行
df = df[df['age'] != 30] # 条件删除
# 排序
df.sort_values(by='age', inplace=True) # 按年龄排序
df.sort_values(by=['city', 'age'], ascending=[True, False]) # 多列排序
df.sort_index(inplace=True) # 按索引排序
# 去重
df.drop_duplicates(subset='city', keep='first') # 去重(保留第一个)
df.drop_duplicates(subset=['city', 'age']) # 多列去重
数据清洗
处理缺失值
# 创建包含缺失值的数据
import numpy as np
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [5, np.nan, np.nan, 8, 10],
'C': ['a', 'b', 'c', np.nan, 'e'],
'D': [1.1, np.nan, 3.3, 4.4, np.nan]
})
# 检测缺失值
print(df.isnull()) # 是否为缺失值
print(df.isna()) # 同上
print(df.notnull()) # 是否不为缺失值
print(df.notna()) # 同上
# 缺失值数量
print(df.isnull().sum()) # 每列缺失值数量
print(df.isnull().sum().sum()) # 总缺失值数量
# 删除缺失值
df.dropna() # 删除包含缺失值的行
df.dropna(axis=1) # 删除包含缺失值的列
df.dropna(how='all') # 删除全部为缺失值的行
df.dropna(thresh=2) # 至少有2个非缺失值
df.dropna(subset=['A', 'B']) # 指定列检查缺失值
# 填充缺失值
df.fillna(0) # 用0填充
df.fillna(method='ffill') # 前向填充
df.fillna(method='bfill') # 后向填充
df.fillna({'A': 0, 'B': 1, 'C': 'unknown'}) # 不同列不同填充值
# 插值填充
df.interpolate() # 线性插值
df.interpolate(method='linear', limit_direction='both')
# 均值/中位数填充
df['A'].fillna(df['A'].mean(), inplace=True)
df['B'].fillna(df['B'].median(), inplace=True)
# 替换
df.replace(np.nan, 0) # 替换NaN
df.replace([1, 2], 100) # 替换多个值
df.replace({'A': 1, 'B': 2}, 100) # 字典替换
数据类型转换
df = pd.DataFrame({
'A': ['1', '2', '3'],
'B': ['1.1', '2.2', '3.3'],
'C': ['2023-01-01', '2023-01-02', '2023-01-03'],
'D': ['1', '2', '3']
})
# 查看类型
print(df.dtypes)
# 转换类型
df['A'] = df['A'].astype(int) # 转整数
df['B'] = df['B'].astype(float) # 转浮点数
df['C'] = pd.to_datetime(df['C']) # 转日期
df['D'] = df['D'].astype('category') # 转分类类型
df['D'] = df['D'].astype(str) # 转字符串
# to_numeric 转数值
pd.to_numeric(df['A'], errors='coerce') # 无效值转为NaN
pd.to_numeric(df['A'], errors='ignore') # 忽略无效值
# 转换多个列
df = df.astype({'A': int, 'B': float})
# 分类类型
df['category'] = df['category'].astype('category')
df['category'].cat.categories # 查看分类
df['category'].cat.codes # 查看编码
字符串处理
df = pd.DataFrame({
'name': [' Tom ', 'Jerry', ' Mickey ', 'MINNEY', 'donald'],
'email': ['tom@example.com', 'jerry@test.com', 'invalid-email', 'minney@example.com', 'donald@example.com']
})
# 去除空格
df['name'] = df['name'].str.strip() # 去除两端空格
df['name'] = df['name'].str.lstrip() # 去除左端空格
df['name'] = df['name'].str.rstrip() # 去除右端空格
# 大小写转换
df['name'] = df['name'].str.upper() # 转大写
df['name'] = df['name'].str.lower() # 转小写
df['name'] = df['name'].str.title() # 转标题格式
df['name'] = df['name'].str.capitalize() # 首字母大写
# 替换
df['name'] = df['name'].str.replace('Tom', 'Thomas')
# 提取
df['domain'] = df['email'].str.split('@').str[1] # 提取域名
df['username'] = df['email'].str.extract(r'^(.+)@')[0] # 提取用户名
# 检查
df['valid_email'] = df['email'].str.contains('@') # 包含@
df['starts_with_t'] = df['name'].str.startswith('T') # 以T开头
# 长度
df['name_length'] = df['name'].str.len()
数据操作
排序
df = pd.DataFrame({
'name': ['Tom', 'Jerry', 'Mickey', 'Minney'],
'age': [25, 20, 30, 28],
'salary': [50000, 60000, 55000, 65000],
'city': ['New York', 'London', 'Paris', 'Paris']
})
# 按值排序
df.sort_values(by='age') # 按年龄升序
df.sort_values(by='age', ascending=False) # 按年龄降序
df.sort_values(by=['city', 'age']) # 多列排序
df.sort_values(by=['city', 'age'], ascending=[True, False]) # 混合排序
# 按索引排序
df.sort_index() # 升序
df.sort_index(ascending=False) # 降序
df.sort_index(axis=1) # 按列名排序
# 按值排名
df['age_rank'] = df['age'].rank() # 平均排名
df['age_rank'] = df['age'].rank(method='first') # 第一次出现排名
df['age_rank'] = df['age'].rank(method='min') # 最小排名
df['age_rank'] = df['age'].rank(method='max') # 最大排名
df['age_rank'] = df['age'].rank(ascending=False) # 降序排名
# nlargest/nsmallest
df.nlargest(2, 'salary') # 工资最高的2人
df.nsmallest(2, 'age') # 年龄最小的2人
分组聚合
df = pd.DataFrame({
'department': ['Sales', 'Sales', 'IT', 'IT', 'HR', 'HR'],
'employee': ['Tom', 'Jerry', 'Mickey', 'Minney', 'Donald', 'Daisy'],
'salary': [50000, 55000, 60000, 65000, 45000, 47000],
'age': [25, 28, 30, 32, 26, 27]
})
# 基本分组
grouped = df.groupby('department')
print(grouped.groups) # 查看分组
# 聚合函数
print(grouped.sum()) # 求和
print(grouped.mean()) # 平均值
print(grouped.median()) # 中位数
print(grouped.std()) # 标准差
print(grouped.var()) # 方差
print(grouped.min()) # 最小值
print(grouped.max()) # 最大值
print(grouped.count()) # 计数
print(grouped.size()) # 每组大小
# 单列聚合
print(df.groupby('department')['salary'].mean())
# 多种聚合
print(grouped.agg({
'salary': ['mean', 'sum', 'max'],
'age': ['mean', 'min']
}))
# 自定义聚合函数
print(grouped.agg({
'salary': lambda x: x.max() - x.min()
}))
# 多种聚合方法
print(grouped['salary'].agg(['mean', 'sum', 'count']))
# 多列分组
print(df.groupby(['department', 'age']).sum())
# as_index=False
print(df.groupby('department', as_index=False)['salary'].mean())
# transform
df['salary_mean'] = df.groupby('department')['salary'].transform('mean')
# filter
filtered = df.groupby('department').filter(lambda x: x['salary'].mean() > 50000)
# apply
def get_range(x):
return x.max() - x.min()
print(df.groupby('department')['salary'].apply(get_range))
数据合并
# concat: 拼接
df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})
# 纵向拼接
pd.concat([df1, df2]) # 保留原索引
pd.concat([df1, df2], ignore_index=True) # 重置索引
# 横向拼接
pd.concat([df1, df2], axis=1)
# merge: 类似 SQL JOIN
df1 = pd.DataFrame({
'id': [1, 2, 3, 4],
'name': ['Tom', 'Jerry', 'Mickey', 'Minney']
})
df2 = pd.DataFrame({
'id': [1, 2, 3, 5],
'salary': [50000, 60000, 70000, 80000]
})
# 内连接
pd.merge(df1, df2, on='id') # 内连接(默认)
pd.merge(df1, df2, on='id', how='inner')
# 左连接
pd.merge(df1, df2, on='id', how='left')
# 右连接
pd.merge(df1, df2, on='id', how='right')
# 外连接
pd.merge(df1, df2, on='id', how='outer')
# 不同列名
pd.merge(df1, df2, left_on='id', right_on='emp_id')
# 多列连接
pd.merge(df1, df2, on=['id', 'name'])
# join: 基于索引合并
df1.join(df2, lsuffix='_left', rsuffix='_right')
# 合并 indicator
pd.merge(df1, df2, on='id', how='outer', indicator=True)
数据透视
df = pd.DataFrame({
'date': ['2023-01-01', '2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03'],
'category': ['A', 'B', 'A', 'B', 'A'],
'value': [10, 20, 30, 40, 50]
})
# 透视表
print(pd.pivot_table(df,
values='value',
index='date',
columns='category',
aggfunc='sum'
))
# 多聚合函数
print(pd.pivot_table(df,
values='value',
index='date',
columns='category',
aggfunc=['sum', 'mean']
))
# 交叉表
print(pd.crosstab(
df['date'],
df['category'],
values=df['value'],
aggfunc='sum'
))
# melt: 宽表转长表
df_wide = pd.DataFrame({
'id': [1, 2, 3],
'A': [10, 20, 30],
'B': [40, 50, 60]
})
print(pd.melt(df_wide,
id_vars=['id'],
value_vars=['A', 'B'],
var_name='category',
value_name='value'
))
# pivot: 长表转宽表
df_long = pd.DataFrame({
'id': [1, 1, 2, 2],
'category': ['A', 'B', 'A', 'B'],
'value': [10, 20, 30, 40]
})
print(df_long.pivot(index='id', columns='category', values='value'))
时间序列
日期时间处理
# 创建日期时间
df = pd.DataFrame({
'date': ['2023-01-01', '2023-01-02', '2023-01-03'],
'value': [10, 20, 30]
})
# 转换为日期时间
df['date'] = pd.to_datetime(df['date'])
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
# 获取日期部分
df['year'] = df['date'].dt.year # 年
df['month'] = df['date'].dt.month # 月
df['day'] = df['date'].dt.day # 日
df['hour'] = df['date'].dt.hour # 时
df['minute'] = df['date'].dt.minute # 分
df['second'] = df['date'].dt.second # 秒
df['dayofweek'] = df['date'].dt.dayofweek # 星期几(0=周一)
df['day_name'] = df['date'].dt.day_name() # 星期名称
df['quarter'] = df['date'].dt.quarter # 季度
# 日期运算
df['date'] + pd.Timedelta(days=1) # 加1天
df['date'] + pd.DateOffset(months=1) # 加1月
df['date'].dt.to_period('M') # 转为月份周期
# 日期范围
dates = pd.date_range('2023-01-01', periods=5, freq='D')
print(dates)
# 不同频率
dates = pd.date_range('2023-01-01', periods=5, freq='H') # 小时
dates = pd.date_range('2023-01-01', periods=5, freq='W') # 周
dates = pd.date_range('2023-01-01', periods=5, freq='M') # 月
dates = pd.date_range('2023-01-01', periods=5, freq='Y') # 年
# 设置为索引
df.set_index('date', inplace=True)
# 按时间筛选
df.loc['2023-01'] # 2023年1月
df.loc['2023-01-01':'2023-01-03'] # 日期范围
df[df.index.day == 1] # 每月1日
# 重采样
df.resample('D').sum() # 按天重采样
df.resample('W').mean() # 按周重采样
df.resample('M').count() # 按月重采样
df.resample('Q').sum() # 按季度重采样
df.resample('Y').mean() # 按年重采样
# 滚动窗口
df['rolling_mean'] = df['value'].rolling(window=3).mean() # 3天移动平均
df['rolling_sum'] = df['value'].rolling(window=3).sum() # 3天移动求和
df['rolling_max'] = df['value'].rolling(window=3).max() # 3天移动最大值
# 扩展窗口
df['expanding_mean'] = df['value'].expanding().mean() # 累计平均
df['cumsum'] = df['value'].cumsum() # 累计求和
# 时间差
df['time_diff'] = df['date'].diff() # 相邻时间差
df['time_diff_days'] = df['time_diff'].dt.days # 天数差
数据输入输出
CSV 文件
# 读取 CSV
df = pd.read_csv('data.csv')
df = pd.read_csv('data.csv', sep=',') # 指定分隔符
df = pd.read_csv('data.csv', encoding='utf-8') # 指定编码
df = pd.read_csv('data.csv', index_col=0) # 指定索引列
df = pd.read_csv('data.csv', header=0) # 指定表头行
df = pd.read_csv('data.csv', names=['A', 'B', 'C']) # 指定列名
df = pd.read_csv('data.csv', usecols=['A', 'B']) # 只读取指定列
df = pd.read_csv('data.csv', nrows=100) # 只读取前n行
df = pd.read_csv('data.csv', skiprows=[0, 2]) # 跳过指定行
df = pd.read_csv('data.csv', na_values=['NA', 'null']) # 指定缺失值
# 写入 CSV
df.to_csv('output.csv', index=False) # 不写入索引
df.to_csv('output.csv', encoding='utf-8') # 指定编码
df.to_csv('output.csv', sep=';') # 指定分隔符
df.to_csv('output.csv', na_rep='NULL') # 缺失值表示
df.to_csv('output.csv', columns=['A', 'B']) # 只写入指定列
df.to_csv('output.csv', header=False) # 不写入表头
df.to_csv('output.csv', mode='a') # 追加模式
# 分块读取大文件
chunk_iter = pd.read_csv('large_file.csv', chunksize=10000)
for chunk in chunk_iter:
process(chunk)
Excel 文件
# 读取 Excel
df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
df = pd.read_excel('data.xlsx', header=0) # 指定表头行
df = pd.read_excel('data.xlsx', usecols='A:C') # 读取指定列
df = pd.read_excel('data.xlsx', skiprows=2) # 跳过前2行
# 写入 Excel
df.to_excel('output.xlsx', sheet_name='Sheet1', index=False)
df.to_excel('output.xlsx', startrow=2, startcol=1) # 指定起始位置
# 写入多个sheet
with pd.ExcelWriter('output.xlsx') as writer:
df1.to_excel(writer, sheet_name='Sheet1')
df2.to_excel(writer, sheet_name='Sheet2')
JSON 文件
# 读取 JSON
df = pd.read_json('data.json')
df = pd.read_json('data.json', orient='records') # 指定格式
df = pd.read_json('data.json', lines=True) # JSON Lines
# 写入 JSON
df.to_json('output.json', orient='records', indent=2)
df.to_json('output.json', orient='split') # 分割格式
df.to_json('output.json', lines=True) # JSON Lines
数据库
from sqlalchemy import create_engine
import psycopg2 # PostgreSQL
import pymysql # MySQL
# 创建连接
engine = create_engine('postgresql://user:password@localhost:5432/database')
# 读取数据
df = pd.read_sql('SELECT * FROM table_name', engine)
df = pd.read_sql_table('table_name', engine)
df = pd.read_sql_query('SELECT * FROM table_name WHERE age > 25', engine)
# 写入数据
df.to_sql('table_name', engine, if_exists='replace') # replace, fail, append
df.to_sql('table_name', engine, if_exists='append', index=False)
其他格式
# HTML 表格
df = pd.read_html('https://example.com/table')[0]
# Pickle
df.to_pickle('data.pkl')
df = pd.read_pickle('data.pkl')
# Parquet
df.to_parquet('data.parquet')
df = pd.read_parquet('data.parquet')
# Feather
df.to_feather('data.feather')
df = pd.read_feather('data.feather')
# HDF5
df.to_hdf('data.h5', key='df', mode='w')
df = pd.read_hdf('data.h5', key='df')
# Clipboard
df.to_clipboard() # 复制到剪贴板
df = pd.read_clipboard() # 从剪贴板读取
数据分析
描述统计
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [10, 20, 30, 40, 50],
'C': [100, 200, 300, 400, 500]
})
# 基本统计
print(df.describe()) # 数值列统计
print(df.count()) # 非空值计数
print(df.sum()) # 求和
print(df.mean()) # 平均值
print(df.median()) # 中位数
print(df.std()) # 标准差
print(df.var()) # 方差
print(df.min()) # 最小值
print(df.max()) # 最大值
print(df.quantile(0.25)) # 25%分位数
print(df.quantile([0.25, 0.5, 0.75])) # 多分位数
# 累计统计
print(df.cumsum()) # 累计求和
print(df.cumprod()) # 累计乘积
print(df.cummax()) # 累计最大值
print(df.cummin()) # 累计最小值
# 值统计
print(df['A'].value_counts()) # 值计数
print(df['A'].nunique()) # 唯一值数量
print(df['A'].unique()) # 唯一值
print(df['A'].mode()) # 众数
# 差分/变化
print(df.diff()) # 一阶差分
print(df.pct_change()) # 百分比变化
相关性
# 相关系数
print(df.corr()) # Pearson相关系数
print(df.corr(method='kendall')) # Kendall相关系数
print(df.corr(method='spearman')) # Spearman相关系数
# 协方差
print(df.cov())
# 单列相关性
print(df['A'].corr(df['B']))
# 相关性热力图
import seaborn as sns
import matplotlib.pyplot as plt
sns.heatmap(df.corr(), annot=True, cmap='coolwarm')
plt.show()
数据分组分析
df = pd.DataFrame({
'category': ['A', 'A', 'B', 'B', 'C', 'C'],
'value1': [10, 20, 30, 40, 50, 60],
'value2': [100, 200, 300, 400, 500, 600]
})
# 分组统计
print(df.groupby('category').mean())
print(df.groupby('category').sum())
print(df.groupby('category').agg({
'value1': ['mean', 'sum'],
'value2': ['max', 'min']
}))
# 分组应用
def normalize(x):
return (x - x.mean()) / x.std()
print(df.groupby('category')['value1'].apply(normalize))
性能优化
内存优化
# 查看内存使用
df.memory_usage()
df.memory_usage(deep=True)
# 优化数据类型
df['column'] = df['column'].astype('int8') # 小整数
df['column'] = df['column'].astype('int16') # 中整数
df['column'] = df['column'].astype('float32') # 单精度浮点
df['column'] = df['column'].astype('category') # 分类类型
# 分类类型转换
df['category'] = df['category'].astype('category')
# 稀疏数据
df['column'] = df['column'].astype(pd.SparseDtype('float', fill_value=0))
# 删除不需要的列
df.drop(columns=['unused_col'], inplace=True)
计算优化
# 向量化操作
# 避免: 使用循环
# 推荐: 使用 Pandas/Numpy 向量操作
# 使用 eval 进行表达式计算
df.eval('D = A + B + C', inplace=True)
# 使用 query 进行筛选
df.query('A > 10 and B < 100')
# 使用 assign 链式操作
df.assign(D=lambda x: x['A'] + x['B']) \
.assign(E=lambda x: x['D'] * 2)
# 避免链式索引
# 不推荐: df[df['A'] > 0]['B'] = 1
# 推荐: df.loc[df['A'] > 0, 'B'] = 1
# 使用 loc/iloc 而不是 []
# 不推荐: df[df['A'] > 0]['B']
# 推荐: df.loc[df['A'] > 0, 'B']
# 批量操作
df[['A', 'B', 'C']].apply(lambda x: x * 2)
# 使用 inplace 减少内存
df.dropna(inplace=True)
df.sort_values(inplace=True)
最佳实践
数据分析流程
# 1. 数据加载
df = pd.read_csv('data.csv')
# 2. 数据探索
print(df.head())
print(df.info())
print(df.describe())
# 3. 数据清洗
df.dropna(inplace=True)
df['date'] = pd.to_datetime(df['date'])
df['category'] = df['category'].astype('category')
# 4. 特征工程
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
# 5. 数据分析
result = df.groupby('category')['value'].mean()
# 6. 数据可视化
import matplotlib.pyplot as plt
result.plot(kind='bar')
plt.show()
编码规范
# 使用有意义的变量名
customer_data = pd.read_csv('customers.csv') # 推荐
df = pd.read_csv('customers.csv') # 不推荐
# 链式方法提高可读性
(df.groupby('category')
.agg({'value': 'mean'})
.sort_values('value')
.plot(kind='bar'))
# 使用类型提示
def process_data(df: pd.DataFrame) -> pd.DataFrame:
return df[df['value'] > 0]
# 添加注释
# 计算每个分类的平均值
category_mean = df.groupby('category')['value'].mean()
常见陷阱
# 1. 链式赋值问题
# 不推荐
df[df['A'] > 0]['B'] = 1 # 可能不生效
# 推荐
df.loc[df['A'] > 0, 'B'] = 1
# 2. 忘记 inplace
df.dropna() # 原数据不变
df.dropna(inplace=True) # 原数据改变
# 3. 索引重置
df = df.append(new_row) # 索引可能重复
df = df.append(new_row).reset_index(drop=True) # 重置索引
# 4. 数据类型
df['column'] = 'string' # 不会改变类型
df['column'] = df['column'].astype(str) # 正确方式
# 5. 内存使用
df_list = []
for chunk in pd.read_csv('large.csv', chunksize=10000):
processed = process(chunk)
df_list.append(processed)
df = pd.concat(df_list) # 一次性合并
总结
Pandas 是 Python 数据分析的核心库:
核心数据结构
- Series: 带标签的一维数组
- DataFrame: 带标签的二维表格
- Index: 行列标签系统
主要功能
- 数据加载: 支持多种文件格式
- 数据清洗: 处理缺失值、重复值、类型转换
- 数据操作: 筛选、排序、分组、聚合
- 数据合并: concat、merge、join
- 时间序列: 日期处理、重采样、滚动窗口
- 数据分析: 统计、相关性、透视表
最佳实践
- 使用向量化操作而非循环
- 使用 loc/iloc 而非 []
- 注意数据类型和内存使用
- 合理使用分组聚合
- 掌握时间序列处理
- 使用链式操作提高可读性
Pandas 是数据科学和机器学习的基础工具,掌握 Pandas 是高效进行数据分析的关键。