当 FIXED_LEN_BYTE_ARRAY 数据类型用于固定长度字节数组列时,为什么 parquet 文件会变大?

问题描述 投票:0回答:1

当尝试将数据集存储在 parquet 文件中以将其上传到 HuggingFace 时,我遇到了一个奇怪的现象:将 50 字节数组存储为列时,当使用 type

FIXED_LEN_BYTE_ARRAY
代替时,文件大小会变大
BYTE_ARRAY

这是演示这一观察结果的 Python 代码:

import os
import numpy as np
import pyarrow as pa
import pyarrow.parquet as pq

tmp_dir = os.path.expanduser("~/tmp/pq") # directory for example files to be stored
n_bytes = 50
n_records = 4096 * 126
np_rng = np.random.default_rng(1)
array = np.frombuffer(
    np_rng.bytes(n_bytes * n_records), dtype=np.dtype((np.bytes_, n_bytes)))
table_byte_array = pa.Table.from_pydict({'byte_data': array})
pq.write_table(table_byte_array, f'{tmp_dir}/byte_array_example.parquet')
table_fixed_len_byte_array = pa.Table.from_pydict(
    {'byte_data': array},
    schema=pa.schema([
        pa.field('byte_data', pa.binary(array.itemsize), nullable=False)]))
pq.write_table(
    table_fixed_len_byte_array,
    f'{tmp_dir}/fixed_len_byte_array_example.parquet')
file_sizes = []
for name in ['byte_array', 'fixed_len_byte_array']:
    filename = f'{tmp_dir}/{name}_example.parquet'
    table = pq.read_table(filename)
    file_size = os.path.getsize(filename)
    file_sizes.append(file_size)
    print(f'{name}: {table.schema.field("byte_data").type} {file_size}')
print(
    f'size ratio: {file_sizes[1] / file_sizes[0]:.3f}; '
    f'size diff: {file_sizes[1] - file_sizes[0]}')

打印:

byte_array: binary 25493170
fixed_len_byte_array: fixed_size_binary[50] 25850348
size ratio: 1.014; size diff: 357178

这似乎非常违反直觉:没有每个元素的长度(即存储更少的数据)会导致文件大小更大。为什么会这样?


上述测试的系统信息:Ubuntu 22.04.4 LTS; Python 3.10.11; pyarrow 版本 12.0.1(使用 parquet-cpp-arrow 版本 12.0.1)。当查看

pqrs schema <name>_example.parquet --detailed
的输出时,我看到两个文件都有相同的
encoding
行:
encodings: RLE_DICTIONARY PLAIN RLE PLAIN

python serialization encoding format parquet
1个回答
0
投票

为什么
BYTE_ARRAY
列类型的文件较小?

因为它不存储所有输入数据:第

table_byte_array = pa.Table.from_pydict({'byte_data': array})
行截断第一个 NULL 字节上的输入条目。这可以通过以下例子来验证:

import numpy as np
import pyarrow as pa
np_array = np.array([
  b'\x00Very important data. Keep it safe!',
  b'Less important data. Keep it safe??'])
table = pa.Table.from_pydict({'sample': np_array})
print(table['sample'][0].as_py())

人们可能期望它打印

b'\x00Very important data. Keep it safe!'
,但它却打印
b''
。此外,如果在第一个示例中修改了
b'\x00'
中条目中
array
之后的一些字节,则输出
.parquet
文件是相同的,从而确认该数据未保存在输出
.parquet
文件中。由于此行为似乎是一个错误,因此我已提交 #41388

所以
FIXED_LEN_BYTE_ARRAY
更好,对吧?

对于某些应用程序 - 也许是这样,但 HuggingFace 的数据集库默认不支持固定大小的二进制数组:尝试在没有自定义加载程序的情况下加载此类文件会导致从

ValueError("Arrow type FixedSizeBinaryType(fixed_size_binary[50]) does not have a datasets dtype equivalent.")
版本
datasets/features/features.py
开始的
datasets
我们如何将 

2.19.0

存储为
np.ndarray
有一个解决方法:在问题的代码中将 

BYTE_ARRAY

行替换为

table_byte_array = pa.Table.from_pydict({'byte_data': array})

© www.soinside.com 2019 - 2024. All rights reserved.