如何用PyArrow read_json()将结构体列表解析为StructArray
问题描述
我能成功用pyarrow的read_json方法读取金融交易JSON数据为Table,JSON里包含asks和bids两个结构体列表。
当前使用的代码:
schema = pa.schema([('time', pa.timestamp(unit='ns', tz='UTC'))]) parse_options = pj.ParseOptions(explicit_schema=schema) table = pj.read_json(file, parse_options=parse_options)
执行后Table的结构如下:
pyarrow.Table time: timestamp[ns, tz=UTC] instrument: string asks: list<item: struct<price: string, liquidity: int64>> child 0, item: struct<price: string, liquidity: int64> child 0, price: string child 1, liquidity: int64 bids: list<item: struct<price: string, liquidity: int64>> child 0, item: struct<price: string, liquidity: int64> child 0, price: string child 1, liquidity: int64 ask_prices: list<item: struct<price: string, liquidity: int64>> child 0, item: struct<price: string, liquidity: int64> child 0, price: string child 1, liquidity: int64
查看首行的asks或bids字段时,它们是ListArray类型。我希望将这些数据处理为StructArray以便使用field()方法,尝试在schema中指定StructArray但没成功,求可行解决方案。
示例JSON行:
{"type": "PRICE", "time": "2022-01-04T15:55:12.710818633Z", "bids": [{"price": "1.13134", "liquidity": 1000000}, {"price": "1.13133", "liquidity": 2000000}, {"price": "1.13132", "liquidity": 2000000}, {"price": "1.13130", "liquidity": 5000000}], "asks": [{"price": "1.13145", "liquidity": 1000000}, {"price": "1.13147", "liquidity": 2000000}, {"price": "1.13148", "liquidity": 2000000}, {"price": "1.13149", "liquidity": 5000000}], "closeoutBid": "1.13130", "closeoutAsk": "1.13149", "status": "tradeable", "tradeable": true, "instrument": "EUR_USD"}
解决方案
首先明确:你的JSON里asks和bids本身是数组(列表),所以在Arrow中对应的类型必然是list<struct<...>>(ListArray包裹StructArray),无法直接读取为纯StructArray——除非JSON字段本身是单个结构体而非数组。不过可以通过后续转换实现需求,具体如下:
1. 直接访问嵌套结构体字段
不需要转换类型,直接通过ListArray的嵌套语法即可访问结构体里的字段:
# 提取所有asks列表中每个结构体的price字段 ask_prices = table['asks'].flatten()['price'] # 访问第一行asks列表里的第一个结构体的liquidity字段 first_ask_liquidity = table['asks'][0][0]['liquidity'] # 用Arrow计算工具批量处理 import pyarrow.compute as pc # 取每个asks列表中第一个元素的price first_ask_prices = pc.list_element(table['asks'], 0)['price']
2. 展开ListArray为StructArray(多行)
如果需要把每个结构体单独拆成一行,可以用explode方法,此时展开后的字段就是StructArray:
# 展开asks字段,每个结构体对应一行新数据 exploded_table = table.explode('asks') # 此时exploded_table的asks字段类型为struct<price: string, liquidity: int64> print(exploded_table['asks'].type) # 直接用field方法访问结构体字段 exploded_table['asks'].field('price')
3. 读取时指定完整嵌套Schema
如果想在读取阶段就明确指定所有字段的类型(包括嵌套结构),可以定义完整的Schema:
import pyarrow as pa import pyarrow.json as pj # 先定义单个订单的结构体类型 order_struct = pa.struct([ ('price', pa.string()), ('liquidity', pa.int64()) ]) # 定义完整的Table Schema schema = pa.schema([ ('time', pa.timestamp(unit='ns', tz='UTC')), ('instrument', pa.string()), ('asks', pa.list_(order_struct)), ('bids', pa.list_(order_struct)), ('type', pa.string()), ('closeoutBid', pa.string()), ('closeoutAsk', pa.string()), ('status', pa.string()), ('tradeable', pa.bool_()) ]) parse_options = pj.ParseOptions(explicit_schema=schema) table = pj.read_json(file, parse_options=parse_options)
这样读取后的asks和bids依然是ListArray,但结构完全符合预期,后续处理更顺畅。
内容的提问来源于stack exchange,提问作者John Duffy
相关产品推荐
相关产品推荐

