尴尬的数组:如何在存储为 Parquet(不是 BitMasked)后获取 numpy 数组?
Awkward Array: How to get numpy array after storing as Parquet (not BitMasked)?
我想将不同长度的二维数组存储为 AwkwardArray,将它们存储为 Parquet,然后再次访问它们。
问题是,从Parquet加载后,格式为BitMaskedArray
,访问性能有点慢。通过以下代码演示:
import numpy as np
import awkward as awk
# big to feel performance (imitating big audio file); 2D
np_arr0 = np.arange(20000000, dtype=np.float32).reshape(2, -1)
print(np_arr0.shape)
# (2, 10000000)
# different size
np_arr1 = np.arange(20000000, 36000000, dtype=np.float32).reshape(2, -1)
print(np_arr1.shape)
# (2, 8000000)
# slow; turn into AwkwardArray
awk_arr = awk.fromiter([np_arr0, np_arr1])
# fast; returns np.ndarray
awk_arr[0][0]
# store and load from parquet
awk.toparquet("sample.parquet", awk_arr)
pq_array = awk.fromparquet("sample.parquet")
# kinda slow; return BitMaskedArray
pq_array[0][0]
如果我们检查 return,我们会看到:
pq_array[0][0].layout
# layout
# [ ()] BitMaskedArray(mask=layout[0], content=layout[1], maskedwhen=False, lsborder=True)
# [ 0] ndarray(shape=1250000, dtype=dtype('uint8'))
# [ 1] ndarray(shape=10000000, dtype=dtype('float32'))
# trying to access only float32 array [1]
pq_array[0][0][1]
# expected
# array([0.000000e+00, 1.000000e+00, 2.000000e+00, ..., 9.999997e+06, 9.999998e+06, 9.999999e+06], dtype=float32)
# reality
# 1.0
问题
如何从 Parquet 加载 AwkwardArray 并快速访问 numpy 值?
自述文件中的信息 (GitHub)
awkward.fromparquet
is lazy-loading the Parquet file.
很好,这将有助于做例如pq_array[0][0][:1000]
The next layer of new structure is that the jagged array is bit-masked. Even though none of the values are nullable, this is an artifact of the way Parquet formats columnar data.
我想没有办法解决这个问题。但是,这是加载速度有点慢的原因吗?我仍然可以通过直接访问数据(无位掩码)以 numpy.ndarray
的形式访问数据吗?
额外尝试
用 Arrow 加载它,然后很尴尬:
import pyarrow as pa
import pyarrow.parquet as pq
# Parquet as Arrow
pa_array = pq.read_table("sample.parquet")
# returns table instead of JaggedArray
awk.fromarrow(pa_array)
# <Table [<Row 0> <Row 1>] at 0x7fd92c83aa90>
在 Arrow 和 Parquet 中,所有数据都可以为空,因此 Arrow/Parquet 编写者可以随意在任何他们想要的地方添加位掩码。当读回数据时,Awkward 必须将这些位掩码视为有意义的(将它们映射到 awkward.BitMaskedArray
),但它们可能都是有效的,特别是如果您知道您没有将任何值设置为 null
.
如果您愿意忽略位掩码,您可以通过调用
到达它后面
pq_array[0][0].content
至于慢,我可以这么说
import awkward as ak
# slow; turn into AwkwardArray
awk_arr = ak.fromiter([np_arr0, np_arr1])
会很慢,因为 ak.fromiter
是使用 Python for 循环实现的少数函数之一——使用 [=38= 在 NumPy 数组中迭代超过 1000 万个值] for 循环会很痛苦。您可以使用
手动构建相同的东西
>>> ak_arr0 = ak.JaggedArray.fromcounts([np_arr0.shape[1], np_arr0.shape[1]],
... np_arr0.reshape(-1))
>>> ak_arr1 = ak.JaggedArray.fromcounts([np_arr1.shape[1], np_arr1.shape[1]],
... np_arr1.reshape(-1))
>>> ak_arr = ak.JaggedArray.fromcounts([len(ak_arr0), len(ak_arr1)],
... ak.concatenate([ak_arr0, ak_arr1]))
至于Parquet慢,我说不出原因:可能与页面大小或行组大小有关。由于 Parquet 是一种 "medium weight" 文件格式(介于 "heavyweights"(如 HDF5)和 "lightweights"(如 npy/npz)之间,它有一些可调参数(不是很多)。
您可能还想考虑
ak.save("file.awkd", ak_arr)
ak_arr2 = ak.load("file.awkd")
这实际上只是带有 JSON 元数据的 npy/npz 格式,用于将 Awkward 数组映射到平面 NumPy 数组或从平面 NumPy 数组映射。对于此示例,file.awkd
为 138 MB。
我想将不同长度的二维数组存储为 AwkwardArray,将它们存储为 Parquet,然后再次访问它们。
问题是,从Parquet加载后,格式为BitMaskedArray
,访问性能有点慢。通过以下代码演示:
import numpy as np
import awkward as awk
# big to feel performance (imitating big audio file); 2D
np_arr0 = np.arange(20000000, dtype=np.float32).reshape(2, -1)
print(np_arr0.shape)
# (2, 10000000)
# different size
np_arr1 = np.arange(20000000, 36000000, dtype=np.float32).reshape(2, -1)
print(np_arr1.shape)
# (2, 8000000)
# slow; turn into AwkwardArray
awk_arr = awk.fromiter([np_arr0, np_arr1])
# fast; returns np.ndarray
awk_arr[0][0]
# store and load from parquet
awk.toparquet("sample.parquet", awk_arr)
pq_array = awk.fromparquet("sample.parquet")
# kinda slow; return BitMaskedArray
pq_array[0][0]
如果我们检查 return,我们会看到:
pq_array[0][0].layout
# layout
# [ ()] BitMaskedArray(mask=layout[0], content=layout[1], maskedwhen=False, lsborder=True)
# [ 0] ndarray(shape=1250000, dtype=dtype('uint8'))
# [ 1] ndarray(shape=10000000, dtype=dtype('float32'))
# trying to access only float32 array [1]
pq_array[0][0][1]
# expected
# array([0.000000e+00, 1.000000e+00, 2.000000e+00, ..., 9.999997e+06, 9.999998e+06, 9.999999e+06], dtype=float32)
# reality
# 1.0
问题
如何从 Parquet 加载 AwkwardArray 并快速访问 numpy 值?
自述文件中的信息 (GitHub)
awkward.fromparquet
is lazy-loading the Parquet file.
很好,这将有助于做例如pq_array[0][0][:1000]
The next layer of new structure is that the jagged array is bit-masked. Even though none of the values are nullable, this is an artifact of the way Parquet formats columnar data.
我想没有办法解决这个问题。但是,这是加载速度有点慢的原因吗?我仍然可以通过直接访问数据(无位掩码)以 numpy.ndarray
的形式访问数据吗?
额外尝试
用 Arrow 加载它,然后很尴尬:
import pyarrow as pa
import pyarrow.parquet as pq
# Parquet as Arrow
pa_array = pq.read_table("sample.parquet")
# returns table instead of JaggedArray
awk.fromarrow(pa_array)
# <Table [<Row 0> <Row 1>] at 0x7fd92c83aa90>
在 Arrow 和 Parquet 中,所有数据都可以为空,因此 Arrow/Parquet 编写者可以随意在任何他们想要的地方添加位掩码。当读回数据时,Awkward 必须将这些位掩码视为有意义的(将它们映射到 awkward.BitMaskedArray
),但它们可能都是有效的,特别是如果您知道您没有将任何值设置为 null
.
如果您愿意忽略位掩码,您可以通过调用
到达它后面pq_array[0][0].content
至于慢,我可以这么说
import awkward as ak
# slow; turn into AwkwardArray
awk_arr = ak.fromiter([np_arr0, np_arr1])
会很慢,因为 ak.fromiter
是使用 Python for 循环实现的少数函数之一——使用 [=38= 在 NumPy 数组中迭代超过 1000 万个值] for 循环会很痛苦。您可以使用
>>> ak_arr0 = ak.JaggedArray.fromcounts([np_arr0.shape[1], np_arr0.shape[1]],
... np_arr0.reshape(-1))
>>> ak_arr1 = ak.JaggedArray.fromcounts([np_arr1.shape[1], np_arr1.shape[1]],
... np_arr1.reshape(-1))
>>> ak_arr = ak.JaggedArray.fromcounts([len(ak_arr0), len(ak_arr1)],
... ak.concatenate([ak_arr0, ak_arr1]))
至于Parquet慢,我说不出原因:可能与页面大小或行组大小有关。由于 Parquet 是一种 "medium weight" 文件格式(介于 "heavyweights"(如 HDF5)和 "lightweights"(如 npy/npz)之间,它有一些可调参数(不是很多)。
您可能还想考虑
ak.save("file.awkd", ak_arr)
ak_arr2 = ak.load("file.awkd")
这实际上只是带有 JSON 元数据的 npy/npz 格式,用于将 Awkward 数组映射到平面 NumPy 数组或从平面 NumPy 数组映射。对于此示例,file.awkd
为 138 MB。