Pandas最佳实践
1. 优先使用向量化操作而非循环
2. 合理使用链式操作提升可读性
3. 注意copy与view的区别
4. 使用inplace参数节省内存
5. 大数据时考虑使用dask或polars
Python数据处理基础
Pandas是Python中最强大的数据分析库,提供了灵活的数据结构和丰富的数据处理功能。 在量化交易中,Pandas是处理金融数据的核心工具。本节将详细介绍Pandas的核心操作和实用技巧。
import pandas as pd
# 创建Series
s = pd.Series([10, 20, 30, 40, 50],
index=['a', 'b', 'c', 'd', 'e'])
# 从字典创建
s2 = pd.Series({'a': 10, 'b': 20, 'c': 30})
# 访问数据
print(s[0]) # 位置索引
print(s['a']) # 标签索引
print(s.iloc[0]) # 位置索引
print(s.loc['a']) # 标签索引
# 基本操作
s.mean() # 平均值
s.describe() # 描述统计
s.value_counts() # 值计数
# 创建DataFrame
df = pd.DataFrame({
'open': [10.5, 11.2, 10.8, 11.5, 10.9],
'high': [10.8, 11.5, 11.2, 11.8, 11.2],
'low': [10.3, 10.9, 10.6, 11.0, 10.7],
'close': [10.7, 11.0, 10.9, 11.3, 11.0]
}, index=pd.date_range('2024-01-01', periods=5))
# 查看数据
df.head() # 前5行
df.tail() # 后5行
df.info() # 数据信息
df.describe() # 描述统计
# 选择单列
df['open'] # 返回Series
df.open # 返回Series(推荐)
# 选择多列
df[['open', 'close']] # 返回DataFrame
# 使用iloc(位置索引)
df.iloc[:, 0] # 第一列
df.iloc[:, :2] # 前两列
# 使用loc(标签索引)
df.loc[:, ['open', 'close']] # 指定列
# 单行
df.iloc[0] # 第一行
df.loc['2024-01-01'] # 按日期
# 多行
df.iloc[1:3] # 第2-3行
df.loc['2024-01-02':'2024-01-04'] # 日期范围
# 条件过滤
df[df['close'] > 11] # 收盘价大于11
df[(df['high'] - df['low']) > 0.5] # 振幅大于0.5
# 多条件
df[(df['close'] > 11) & (df['volume'] > 1000000)]
df[df['symbol'].isin(['AAPL', 'GOOG'])]
# 使用query方法
df.query('close > 11 and volume > 1000000')
# 使用isin
symbols = ['AAPL', 'GOOG', 'MSFT']
df[df['symbol'].isin(symbols)]
# 使用str方法
df[df['name'].str.contains('科技')]
# 排序
df.sort_values('close', ascending=False)
df.sort_index()
# 添加列
df['amplitude'] = df['high'] - df['low']
df['return'] = df['close'].pct_change()
# 删除列
df.drop('amplitude', axis=1) # 不修改原数据
df.drop(columns=['amplitude']) # 不修改原数据
del df['amplitude'] # 修改原数据
# 重命名列
df.rename(columns={'open': '开盘', 'close': '收盘'})
# 修改列类型
df['volume'] = df['volume'].astype(int)
# 添加行
new_row = {'open': 11.0, 'high': 11.3, 'low': 10.8, 'close': 11.1}
df.append(new_row, ignore_index=True)
# 删除行
df.drop(0) # 删除第一行
df.drop(df.index[0]) # 删除第一行
# 条件删除
df = df[~(df['close'] < 10)] # 删除收盘价小于10的行
# 按列聚合
df.sum() # 求和
df.mean() # 平均值
df.median() # 中位数
df.std() # 标准差
# 按组聚合
df.groupby('symbol').mean()
df.groupby('date').agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last'
})
# 多种聚合
df.groupby('symbol').agg({
'close': ['mean', 'std'],
'volume': ['sum', 'count']
})
# 移动平均
df['MA5'] = df['close'].rolling(5).mean()
df['MA20'] = df['close'].rolling(20).mean()
# 收益率
df['daily_return'] = df['close'].pct_change()
df['log_return'] = np.log(df['close'] / df['close'].shift(1))
# 波动率
df['volatility'] = df['daily_return'].rolling(20).std()
# 布林带
df['middle_band'] = df['close'].rolling(20).mean()
df['upper_band'] = df['middle_band'] + 2 * df['close'].rolling(20).std()
df['lower_band'] = df['middle_band'] - 2 * df['close'].rolling(20).std()
# RSI
def calculate_rsi(prices, period=14):
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
df['RSI'] = calculate_rsi(df['close'])
# 使用向量化操作
df['return'] = df['close'].pct_change() # 快
# 而不是循环计算每个收益率
# 使用eval进行复杂表达式
df.eval('amplitude = high - low', inplace=True)
# 使用assign链式操作
df.assign(
amplitude=lambda x: x['high'] - x['low'],
return=lambda x: x['close'].pct_change()
)
# 分类数据优化
df['category'] = df['symbol'].astype('category')
1. 优先使用向量化操作而非循环
2. 合理使用链式操作提升可读性
3. 注意copy与view的区别
4. 使用inplace参数节省内存
5. 大数据时考虑使用dask或polars