如何用Pandas高效将L2订单簿数据转为带层级列的DataFrame?
问题描述
我有形式为[[[key1,value1],[key2,value2],...,500],...,n]的L2订单簿数据(包含500个层级),想要将其转换为包含key_1至key_500、value_1至value_500列的DataFrame——也就是每个层级对应一组price和volume列(比如level_1_volume、level_2_volume……level_500_volume)。
目前我用df.iterrows()循环遍历现有DataFrame(该DataFrame仅将上述列表作为一列),把值提取到单独列表后再创建新DataFrame合并列,但这个方法效率远低于数据集上的其他操作。请问能不能用Pandas内置方法实现?
示例数据结构
List1 = [[key1.1,val1.1],[key2.1,val2.1],...] List2 = [[key1.2,val1.2],[key2.2,val2.2],...] # 转换后目标结构 key1_column, val1_column, key2_column, val2_column Row_List1 key1.1 val1.1 key2.1 val2.1 Row_List2 key1.2 val1.2 key2.2 val2.2
当前实现代码
(其中["bids"]和["asks"]列包含500个键值对的字典/JSON对象)
# 初始化价格和成交量的空列表 prices_bids = [[] for _ in range(500)] volumes_bids = [[] for _ in range(500)] prices_asks = [[] for _ in range(500)] volumes_asks = [[] for _ in range(500)] # 遍历每一行 for index, row in df.iterrows(): attributes = row["bids"] result_bids = [[key,attributes[key]] for key in sorted(attributes.keys(), reverse=True)] attributes = row["asks"] result_asks = [[key,attributes[key]] for key in sorted(attributes.keys(), reverse=False)] for i in range(500): prices_bids[i].append(np.float64(result_bids[i][0])) volumes_bids[i].append(np.float64(result_bids[i][1])) prices_asks[i].append(np.float64(result_asks[i][0])) volumes_asks[i].append(np.float64(result_asks[i][1])) # 从列表创建DataFrame列 for i in range(500): # 将价格转换为点差形式 df[f"bid_level_{i}_price"] = pd.Series((df["mid_price"]/pd.Series(prices_bids[i],dtype='float64')-1)*10000,dtype="float64") df[f"bid_level_{i}_volume"] = pd.Series(volumes_bids[i],dtype='float64') df[f"ask_level_{i}_price"] = pd.Series((df["mid_price"]/pd.Series(prices_asks[i],dtype='float64')-1)*10000,dtype="float64") df[f"ask_level_{i}_volume"] = pd.Series(volumes_asks[i],dtype='float64')
优化方案
完全可以用Pandas的向量化操作替代循环,大幅提升效率。核心思路是利用apply结合pd.DataFrame批量解析每行的订单簿数据,再通过列重命名、拼接完成转换,最后统一计算价差:
步骤1:批量解析bids和asks列
import pandas as pd import numpy as np # 处理bids列:按价格降序排序键值对,转成结构化数据后堆叠,方便后续展开为列 def parse_bids(x): sorted_items = sorted(x.items(), key=lambda item: -float(item[0])) return pd.DataFrame(sorted_items, columns=['price', 'volume']).T.stack() # 处理asks列:按价格升序排序键值对 def parse_asks(x): sorted_items = sorted(x.items(), key=lambda item: float(item[0])) return pd.DataFrame(sorted_items, columns=['price', 'volume']).T.stack() # 生成解析后的结构化DataFrame bids_df = df['bids'].apply(parse_bids).unstack() asks_df = df['asks'].apply(parse_asks).unstack() # 给解析后的列重命名,匹配需求的格式 bids_df.columns = [f"bid_level_{i}_{col}" for col in ['price', 'volume'] for i in range(500)] asks_df.columns = [f"ask_level_{i}_{col}" for col in ['price', 'volume'] for i in range(500)]
步骤2:计算价差并合并到原DataFrame
# 把解析好的订单簿数据合并到原DataFrame df = pd.concat([df, bids_df, asks_df], axis=1) # 批量计算bid/ask的价差(替代原循环) for i in range(500): df[f"bid_level_{i}_price"] = (df["mid_price"] / df[f"bid_level_{i}_price"].astype(np.float64) - 1) * 10000 df[f"ask_level_{i}_price"] = (df["mid_price"] / df[f"ask_level_{i}_price"].astype(np.float64) - 1) * 10000 # 统一转换成交量列的数值类型 for i in range(500): df[f"bid_level_{i}_volume"] = df[f"bid_level_{i}_volume"].astype(np.float64) df[f"ask_level_{i}_volume"] = df[f"ask_level_{i}_volume"].astype(np.float64)
效率说明
- 彻底避免了
iterrows()和嵌套循环的低效操作,用Pandas内置的向量化逻辑处理,速度能提升数倍甚至一个数量级(取决于数据集大小)。 apply结合pd.DataFrame的方式,本质是把每行的字典批量转成结构化数据,再通过unstack展开成列,比手动维护列表更简洁可靠。
内容的提问来源于stack exchange,提问作者Dontwannausemynormalnick
相关产品推荐
相关产品推荐

