将具有形状(M,N,P)阵列的numpy int数组有效转换为具有(N,P)形状的2D对象数组

时间:2016-10-05 14:04:21

标签: python numpy

从具有数据类型int的形状(M,N,P)的3D数组中,我想获得数据类型object的形状(N,P)的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应具有a2[(x, y)] == (x, y)

的属性

感觉应该可以转换m并使a2像这样:

a2 = m.transpose(1,2,0).astype(np.object).reshape(m.shape[1:])

因为numpy并不真正关心对象内部的内容,或者在创建类型np.object的numpy-array时能够分辨出应该有多少维度:

a2 = np.array(m.transpose(1,2,0), astype=object, ndim=2)

Numpy知道在嵌套iterables的最终深度之前停止,如果它们在第三维(在这个例子中)具有不同的形状,但由于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 个答案:

答案 0 :(得分:1)

对于较小的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)and np.array(ll)shape is (12,2), then 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)仍为3d;它只是用整数指针改变整数。阵列尺寸和dtype之间有一个“墙”。重塑和转置之类的东西只能在尺寸上运行,不会穿透那堵墙,也不能移动它。列表一直是指针。对象数组仅在dtype级别使用指针。

不要害怕a2.ravel()[:]=表达。 ravel是一个廉价的重塑,并且对数组的展平版本的赋值实际上可能比分配给2d版本更快。毕竟,数据(在这种情况下是指针)存储在一个平面数据缓冲区中。

但是(在玩了一下之后)我可以在没有ravel或reshape的情况下完成任务(仍然需要tolist来移动object边界)。列表嵌套必须将a2形状与“对象”级别匹配。

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

(这会让人想起有关np.array maxdim tolist参数的讨论 - Prevent numpy from creating a multidimensional array)。

使用a2似乎效率低下。但是如果c的元素是元组(或者指向元组的指针),则必须以某些方式创建这些元组。 m的{​​{1}}数据缓冲区不能被视为一组元组。 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)

从而避免列表理解。它不一定更简单或更快。