正在解码Decimal类型的镶木地板最小/最大统计信息



我使用pyarrow创建了一个具有十进制列类型pa.decimal128(12, 4)的镶木地板文件。在我读取文件并访问其元数据后,我得到以下输出:

<pyarrow._parquet.ColumnChunkMetaData object at 0x7f4752644310>
file_offset: 26077
file_path: 
physical_type: FIXED_LEN_BYTE_ARRAY
num_values: 3061
path_in_schema: Price
is_stats_set: True
statistics:
<pyarrow._parquet.Statistics object at 0x7f4752644360>
has_min_max: True
min: b'x00x00x00x00x9bxdc'
max: b'x00x00w5x93x9c'
null_count: 0
distinct_count: 0
num_values: 3061
physical_type: FIXED_LEN_BYTE_ARRAY
logical_type: Decimal(precision=12, scale=4)
converted_type (legacy): DECIMAL
compression: SNAPPY
encodings: ('PLAIN_DICTIONARY', 'PLAIN', 'RLE')
has_dictionary_page: True
dictionary_page_offset: 22555
data_page_offset: 23225
total_compressed_size: 3522
total_uncompressed_size: 3980

正如您所看到的,最小/最大值实际上是字节对象。如何将这些解码为实际的十进制值?

我试着用铸造

pc.cast(statistics.max, pa.decimal128(12, 4))

但得到了以下错误消息,而不是

pyarrow.lib.ArrowNotImplementedError:不支持使用函数cast_decimal 从二进制转换为十进制

统计数据基于物理类型而非逻辑类型。对于Decimal(precision=12, scale=4),物理类型是FIXED_LEN_BYTE_ARRAY,这是最小值和最大值。不幸的是,为了转换回十进制,您需要知道Arrow是如何编码为固定长度的字节数组的。

它首先根据精度确定需要多少字节。你不需要对这个零件进行逆向工程。然后,它转换为big-endian,截断到所需的字节并写入它们。所以这应该允许你转换回来。

def pad(b):
# Left pad 0 or 1 based on leading digit (2's complement rules)
if b[-1] & 128 == 0:
return b.ljust(16, b'x00')
else:
return b.ljust(16, b'xff')
def to_pyarrow_bytes(b):
# converts from big-endian (parquet's repr) to little endian (arrow's repr)
# and then pads to 16 bytes
return pad(b[::-1])
def decode_stats_decimal(b):
pyarrow_bytes = to_pyarrow_bytes(b)
arr = pa.Array.from_buffers(dtype, 1, [None, pa.py_buffer(pyarrow_bytes)], 0)
return arr[0].as_py()
decode_stats_decimal(statistics.max)
# Decimal('199999.9900')
decode_stats_decimal(statistics.min)
# Decimal('3.9900')

最新更新