当尝试将数据集存储在 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
。
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})