为什么 BSON 序列化的 numpy 数组比原始数组大得多?
Why is a BSON serialized numpy array much bigger than the original?
我正在处理 numpy 数组形式的图像。我需要 serialize/deserialize 他们 to/from JSON (我正在使用 MongoDB)
numpy 数组不能用 json.dump 序列化;我知道 this 但我想知道是否有更好的方法,因为将字节 numpy 数组转换为 BSON 会将字节数乘以近 12(我不明白为什么):
import numpy as np
import bson
from io import StringIO as sio
RC = 500
npdata = np.zeros(shape=(RC,RC,3), dtype='B')
rows, cols, depth = npdata.shape
npsize = rows*cols*depth
npdata=npdata.reshape((npsize,))
listdata = npdata.tolist()
bsondata = bson.BSON.encode({"rows": rows, "cols": cols, "data": listdata})
lb = len(bsondata)
print(lb, npsize, lb/npsize)
> 8888926 750000 11.851901333333334
字节数增加的原因是BSON如何保存数据。您可以在 BSON specification 中找到此信息,但让我们看一个具体示例:
import numpy as np
import bson
npdata = np.arange(5, dtype='B') * 11
listdata = npdata.tolist()
bsondata = bson.BSON.encode({"rows": rows, "cols": cols, "data": listdata})
print([hex(b) for b in bsondata])
在这里,我们将一个值为 [0, 11, 22, 33, 44, 55]
的数组存储为 BSON 并打印生成的二进制数据。下面我对结果进行了注释以解释发生了什么:
['0x47', '0x0', '0x0', '0x0', # total number of bytes in the document
# First element in document
'0x4', # Array
'0x64', '0x61', '0x74', '0x61', '0x0', # key: "data"
# subdocument (data array)
'0x4b', '0x0', '0x0', '0x0', # total number of bytes
# first element in data array
'0x10', # 32 bit integer
'0x30', '0x0', # key: "0"
'0x0', '0x0', '0x0', '0x0', # value: 0
# second element in data array
'0x10', # 32 bit integer
'0x31', '0x0', # key: "1"
'0xb', '0x0', '0x0', '0x0', # value: 11
# third element in data array
'0x10', # 32 bit integer
'0x32', '0x0', # key: "2"
'0x16', '0x0', '0x0', '0x0', # value: 22
# ...
]
除了一些格式开销之外,数组的每个值都相当浪费地使用 7 个字节进行编码:1 个字节用于指定数据类型,2 个字节用于包含索引的字符串(3 个字节用于索引 >=10,四个字节用于索引 >=100, ...) 和 4 个字节用于 32 位整数值。
这至少解释了为什么 BSON 数据比原始数组大得多。
我发现了两个库 GitHub - mongodb/bson-numpy and GitHub - ajdavis/bson-numpy,它们在 BSON 中对 numby 数组进行编码可能做得更好。但是,我没有尝试过它们,所以我不能说是否是这种情况,或者它们是否可以正常工作。
我正在处理 numpy 数组形式的图像。我需要 serialize/deserialize 他们 to/from JSON (我正在使用 MongoDB)
numpy 数组不能用 json.dump 序列化;我知道 this 但我想知道是否有更好的方法,因为将字节 numpy 数组转换为 BSON 会将字节数乘以近 12(我不明白为什么):
import numpy as np
import bson
from io import StringIO as sio
RC = 500
npdata = np.zeros(shape=(RC,RC,3), dtype='B')
rows, cols, depth = npdata.shape
npsize = rows*cols*depth
npdata=npdata.reshape((npsize,))
listdata = npdata.tolist()
bsondata = bson.BSON.encode({"rows": rows, "cols": cols, "data": listdata})
lb = len(bsondata)
print(lb, npsize, lb/npsize)
> 8888926 750000 11.851901333333334
字节数增加的原因是BSON如何保存数据。您可以在 BSON specification 中找到此信息,但让我们看一个具体示例:
import numpy as np
import bson
npdata = np.arange(5, dtype='B') * 11
listdata = npdata.tolist()
bsondata = bson.BSON.encode({"rows": rows, "cols": cols, "data": listdata})
print([hex(b) for b in bsondata])
在这里,我们将一个值为 [0, 11, 22, 33, 44, 55]
的数组存储为 BSON 并打印生成的二进制数据。下面我对结果进行了注释以解释发生了什么:
['0x47', '0x0', '0x0', '0x0', # total number of bytes in the document
# First element in document
'0x4', # Array
'0x64', '0x61', '0x74', '0x61', '0x0', # key: "data"
# subdocument (data array)
'0x4b', '0x0', '0x0', '0x0', # total number of bytes
# first element in data array
'0x10', # 32 bit integer
'0x30', '0x0', # key: "0"
'0x0', '0x0', '0x0', '0x0', # value: 0
# second element in data array
'0x10', # 32 bit integer
'0x31', '0x0', # key: "1"
'0xb', '0x0', '0x0', '0x0', # value: 11
# third element in data array
'0x10', # 32 bit integer
'0x32', '0x0', # key: "2"
'0x16', '0x0', '0x0', '0x0', # value: 22
# ...
]
除了一些格式开销之外,数组的每个值都相当浪费地使用 7 个字节进行编码:1 个字节用于指定数据类型,2 个字节用于包含索引的字符串(3 个字节用于索引 >=10,四个字节用于索引 >=100, ...) 和 4 个字节用于 32 位整数值。
这至少解释了为什么 BSON 数据比原始数组大得多。
我发现了两个库 GitHub - mongodb/bson-numpy and GitHub - ajdavis/bson-numpy,它们在 BSON 中对 numby 数组进行编码可能做得更好。但是,我没有尝试过它们,所以我不能说是否是这种情况,或者它们是否可以正常工作。