numpy.load 给出 ValueError: descr 不是有效的 dtype 描述符:

numpy.load gives ValueError: descr is not a valid dtype descriptor:

本文关键字:有效 dtype 描述 load 给出 ValueError descr numpy      更新时间:2023-10-16

我在C++程序中使用cnpy编写了一个npy文件:

vector < double > vrmsd(max,99.9);
.
.
.
cnpy::npy_save(frmsd,&vrmsd,{nfeat},"w");

按照此处的示例进行操作。

但是当我尝试使用numpy加载文件时,出现错误

y = np.load(frmsd)

ValueError: descr is not a valid dtype descriptor: '<?24'

下面我粘贴一个npy文件的hexdump -C结果,该文件包含长度为 2 的vector<double>(应包含 46.950、43.94(:

00000000  93 4e 55 4d 50 59 01 00  46 00 7b 27 64 65 73 63  |.NUMPY..F.{'desc|
00000010  72 27 3a 20 27 3c 3f 32  34 27 2c 20 27 66 6f 72  |r': '<?24', 'for|
00000020  74 72 61 6e 5f 6f 72 64  65 72 27 3a 20 46 61 6c  |tran_order': Fal|
00000030  73 65 2c 20 27 73 68 61  70 65 27 3a 20 28 32 2c  |se, 'shape': (2,|
00000040  29 2c 20 7d 20 20 20 20  20 20 20 20 20 20 20 0a  |), }           .|
00000050  10 4d 1b 02 00 00 00 00  20 4d 1b 02 00 00 00 00  |.M...... M......|
00000060  20 4d 1b 02 00 00 00 00  00 ff 00 00 00 ff 00 00  | M..............|
00000070  c8 33 19 02 00 00 00 00  94 99 90 5b 00 00 00 00  |.3.........[....|
00000080

此问题也已发布到cnpygithub站点。只是想知道我是否可以在numpy方面做些什么?谢谢。

如果你想从numpy方面解决这个问题......好吧,您可能不想修改 numpy 来理解非标准的 descr 字符串,我怀疑即使您将该 descr 字符串视为它似乎声称无论如何都会得到垃圾的东西。

但是你可以做一个黑客的解决方法。

如果你打开二进制文件,它应该以这样的内容开头:

x93NUMPYx01x00vx00{'descr': '<?24', 'fortran_order': False, 'shape': (30, 20), }

。后跟一些以原始字节之前的换行符结尾的空格。

您可以在十六进制编辑器或文本编辑器中仔细编辑它,或者使用 Python 代码以二进制模式打开文件,读取文件,对字节执行一些正常的字符串操作,然后将其写回去。

特别是,看起来像Python dict repr的位确实就是这样,这些值的意思正是你认为它们的意思。加载文件最终会尝试创建一个np.dtype('<?24'),这就是错误的来源。

如果您只是编辑descrshape值,并确保使dict repr保持相同的长度(通过填充空格(,那将为您提供可以load的东西。

那么,<?24是什么意思呢?好吧,它不是 PEP 3118 和struct指定的有效格式,但它确实符合 numpy 对该格式的扩展模式。例如,在 numpy 中,您可以指定f8,意思是"与f个字节相同,但有 8 个字节"。所以,据推测,这意味着?的 24 字节小端版本,这意味着如果用 C99 编译_Bool,如果不是,char,并且意味着在 Python 中被解释为bool

因此,如果 numpy 允许这个 descr 指定一个 dtype,这意味着每个单元格都是 24 个字节,解释为解释为布尔值的小端整数。当然,numpy 不知道如何处理除 1、2、4 或 8 个字节之外的任何长度的整数,并且它期望布尔值为 1 个字节,因此不允许使用。但是你可以读到同样的东西作为 24 个单独的布尔值。

你是怎么做到的?只需将descr字符串更改为'?',将shape更改为(30, 20, 24),现在您有一个 30x20x24 的布尔数组,如果你切片[..., 0],你会得到一个 30x20 的布尔数组。或者,可能,'24?'会这样做而不需要更改shape

问题是,你的C++值是双精度值,而不是布尔值。

希望它只是以小端格式编写双打,每个双字节后有 16 个额外的 0 字节。如果是这样,只需将descr更改为'<f8',将shape更改为(30, 20, 3),然后查看您得到的结果。如果第一个(30, 20)是您想要的数组,而其他两个都是零,那么您就完成了;只是切片。(如果你想减少内存使用,也许ascontiguous它。

根据您提供的示例的npy_save()参数格式,您有一个错误。

而不是

cnpy::npy_save(frmsd,&vrmsd,{nfeat},"w");

你想要

cnpy::npy_save(frmsd,&vrmsd[0],{nfeat},"w");