首页 > 解决方案 > Numpy将非连续数组的连续部分视为更大尺寸的dtype

问题描述

我试图从一个超长字符数组中生成一个三元组(即连续三字母组合):

# data is actually load from a source file
a = np.random.randint(0, 256, 2**28, 'B').view('c')

由于制作副本效率不高(并且会产生诸如缓存未命中之类的问题),因此我使用步幅技巧直接生成了三元组:

tri = np.lib.stride_tricks.as_strided(a, (len(a) - 2, 3), a.strides * 2)

这会生成一个形状为三元组的三元组列表,(2**28 - 2, 3)其中每一行都是一个三元组。现在我想将三元组转换为字符串列表(即S3),以便 numpy 更“合理地”显示它(而不是单个字符)。

tri = tri.view('S3')

它给出了例外:

ValueError: To change to a dtype of a different size, the array must be C-contiguous

我通常理解数据应该是连续的,以便创建有意义的视图,但是这些数据在“应该在哪里”是连续的:每三个元素都是连续的。

所以我想知道如何将view非连续部分连续np.ndarray作为更大尺寸的 dtype?更“标准”的方式会更好,同时也欢迎使用骇人听闻的方式。似乎我可以自由地设置shape,但我不能强迫成为某事,这就是这里的问题。stridenp.lib.stride_tricks.as_strideddtype

编辑

不连续的数组可以通过简单的切片来制作。例如:

np.empty((8, 4), 'uint32')[:, :2].view('uint64')

将在上面抛出相同的异常(而从内存的角度来看,我应该能够做到这一点)。这种情况比我上面的例子更常见。

标签: pythonarraysnumpymemory-layout

解决方案


如果您有权访问从中派生非连续数组的连续数组,则通常应该可以解决此限制。

例如,您的三元组可以像这样获得:

>>> a = np.random.randint(0, 256, 2**28, 'B').view('c')
>>> a
array([b')', b'\xf2', b'\xf7', ..., b'\xf4', b'\xf1', b'z'], dtype='|S1')
>>> np.lib.stride_tricks.as_strided(a[:0].view('S3'), ((2**28)-2,), (1,))
array([b')\xf2\xf7', b'\xf2\xf7\x14', b'\xf7\x14\x1b', ...,
       b'\xc9\x14\xf4', b'\x14\xf4\xf1', b'\xf4\xf1z'], dtype='|S3')

事实上,这个例子展示了我们所需要的只是在内存缓冲区的基础上的一个连续的“存根”用于视图转换,因为之后,因为as_strided不做很多检查,我们基本上可以自由地做任何我们喜欢的事情。

似乎我们总是可以通过切片到大小为 0 的数组来获得这样的存根。对于您的第二个示例:

>>> X = np.empty((8, 4), 'uint32')[:, :2]
>>> np.lib.stride_tricks.as_strided(X[:0].view(np.uint64), (8, 1), X.strides)
array([[140133325248280],
       [             32],
       [       32083728],
       [       31978800],
       [              0],
       [       29686448],
       [             32],
       [       32362720]], dtype=uint64)

推荐阅读