将形状为(M,N,P)的 numpy int 数组高效转换为具有(N,P)形状的二维对象数组

4

我希望能够从形状为(M,N,P)的int数据类型的3D数组中,以合理的效率获取一个形状为(N,P)且数据类型为object的2D数组。

我可以接受这些对象是tuplelistnumpy.ndarray类型之一。

我现在有一个可行的解决方案,但需要通过一个列表来实现。所以感觉可能还有其他更好的方法:

import numpy as np

m = np.mgrid[:8, :12]

l = zip(*(v.ravel() for v in m))
a2 = np.empty(m.shape[1:], dtype=np.object)
a2.ravel()[:] = l

在这个例子中,最终的数组应该有这样的属性:a2[(x, y)] == (x, y)
感觉上可以转置 m 并且让 a2 变成这样:a2 = m.transpose(1,2,0).astype(np.object).reshape(m.shape[1:]),因为numpy并不真正关心对象内部的内容,或者在创建一个np.object类型的numpy数列时能够告诉它应该有多少维度。
a2 = np.array(m.transpose(1,2,0), astype=object, ndim=2)

Numpy知道在第三维度(本例中)不同形状的嵌套可迭代对象的最后一层之前停止,但由于“m”没有任何异常,这似乎是不可能的。
或者创建“a2”,并填充其转置的值:
a2 = np.empty(m.shape[1:], dtype=np.object)
a2[...] = m.transpose(1, 2, 0)

在这种情况下,例如m.transpose(1, 2, 0)[2, 4]就是np.array([2, 4]),将其赋值给a2[2, 4]是完全合法的。然而,这三个更为合理的尝试都没有成功。
1个回答

2

因此,对于较小的m

In [513]: m = np.mgrid[:3,:4]
In [514]: m.shape
Out[514]: (2, 3, 4)
In [515]: m
Out[515]: 
array([[[0, 0, 0, 0],
        [1, 1, 1, 1],
        [2, 2, 2, 2]],

       [[0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3]]])
In [516]: ll = list(zip(*(v.ravel() for v in m)))
In [517]: ll
Out[517]: 
[(0, 0),
 (0, 1),
 (0, 2),
 ...
 (2, 3)]
In [518]: a2=np.empty(m.shape[1:], dtype=object)
In [519]: a2.ravel()[:] = ll
In [520]: a2
Out[520]: 
array([[(0, 0), (0, 1), (0, 2), (0, 3)],
       [(1, 0), (1, 1), (1, 2), (1, 3)],
       [(2, 0), (2, 1), (2, 2), (2, 3)]], dtype=object)

制造一个正确形状的空白数组,然后通过[:]=填充它是控制此类数组的object深度的最佳方法。np.array(...)默认为最高可能维度,本例中为3d。
因此,主要问题是 - 是否有更好的方法构造包含元组的ll列表。
 a2.ravel()[:] = np.array(ll)

无法工作,抱怨(12,2) into shape (12)

倒推一下,如果我从一个像ll的数组开始,将其转换为嵌套列表,那么赋值就可以成功,除了a2的元素是列表而不是元组:

In [533]: a2.ravel()[:] = np.array(ll).tolist()
In [534]: a2
Out[534]: 
array([[[0, 0], [0, 1], [0, 2], [0, 3]],
       [[1, 0], [1, 1], [1, 2], [1, 3]],
       [[2, 0], [2, 1], [2, 2], [2, 3]]], dtype=object)

m形状为(2,3,4),而np.array(ll)的形状为(12,2),那么

m.reshape(2,-1).T产生相同的结果。

a2.ravel()[:] = m.reshape(2,-1).T.tolist()

我可以先转置,然后再重塑:m.transpose(1,2,0).reshape(-1,2)

要获取元组,我需要通过推导将重塑的数组传递:

a2.ravel()[:] = [tuple(l) for l in m.reshape(2,-1).T]

===============

m.transpose(1,2,0).astype(object) 仍然是三维的;它只是用指向整数的指针替换了整数。在数组维度和dtype之间有一个“墙”。像reshapetranspose这样的操作只作用于维度,不穿透或移动该墙。列表从始至终都是指针。对象数组仅在dtype级别使用指针。

不要害怕a2.ravel()[:]=表达式。 ravel是一种廉价的reshape,将数组的扁平版本分配给可能比分配给二维版本更快。毕竟,数据(在本例中为指针)存储在平坦的数据缓冲区中。

但是(玩弄一下后),我可以在没有ravelreshape的情况下进行赋值(仍然需要tolist来移动object边界)。 列表嵌套必须与a2形状匹配,直到'object'级别。

a2[...] = m.transpose(1,2,0).tolist()   # even a2[:] works

这让我想起了一个关于给np.array添加maxdim参数的讨论 - Prevent numpy from creating a multidimensional array

使用tolist似乎是一种低效率的方法。但是,如果a2的元素是元组(或者说是指向元组的指针),那么这些元组必须以某种方式被创建出来。不能将mc数据缓冲区视为一组元组。使用tolist(带有[tuple...]推导式)可能是创建这些对象最有效的方法。

==============

我是否提到转置可以索引,从而产生具有正确数字的2个元素数组?

In [592]: m.transpose(1,2,0)[1,2]
Out[592]: array([1, 2])
In [593]: m.transpose(1,2,0)[0,1]
Out[593]: array([0, 1])

由于结构化数组的 tolist 使用元组,因此我可以这样做:

In [598]: a2[:]=m.transpose(1,2,0).copy().view('i,i').reshape(a2.shape).tolist()

In [599]: a2
Out[599]: 
array([[(0, 0), (0, 1), (0, 2), (0, 3)],
       [(1, 0), (1, 1), (1, 2), (1, 3)],
       [(2, 0), (2, 1), (2, 2), (2, 3)]], dtype=object)

因此可以避免列表推导式。这并不一定更简单或更快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接