我在使用NumPy时遇到的一个反复出现的错误是,尝试为数组建立索引失败,因为数组的维度之一为单例,因此该维度被清除,无法进行索引。在设计为对任意大小的数组进行操作的函数中,这尤其成问题。我正在寻找最便宜,最通用的方法来避免此错误。
这是一个例子:
import numpy as np
f = (lambda t, u, i=0: t[:,i]*u[::-1])
a = np.eye(3)
b = np.array([1,2,3])
f(a,b)
f(a[:,0],b[1])
第一次呼叫按预期方式工作。第二个调用以两种方式失败:1)
t
不能为[:,0]
编制索引,因为它的形状为(3,)
,2)完全无法为u
编制索引,因为它是标量。这是发生在我身上的修复程序:
1)在
np.atleast_1d
内使用np.atleast_2d
和f
等(可能有条件,以确保尺寸顺序正确),以确保所有参数都具有所需的尺寸。这排除了使用lambda的可能,并且可能需要一些我不希望使用的行。2)使用
f(a[:,0],b[1])
代替上面的f(a[:,[0]],b[[1]])
。很好,但是我总是要记住放在额外的括号中,并且如果索引存储在变量中,您可能不知道是否应该将额外的括号放在其中。例如。:idx = 1
f(a[:,[0]],b[[idx]])
idx = [2,0,1]
f(a[:,[0]],b[idx])
在这种情况下,您似乎必须先在
np.atleast_1d
上调用idx
,这可能比在函数中放入np.atleast_1d
更为麻烦。3)在某些情况下,我可以不用输入索引就可以摆脱困境。例如。:
f = lambda t, u: t[0]*u
f(a,b)
f(a[:,0],b[0])
这可行,并且显然是应用时最精巧的解决方案。但这并不能在每种情况下都有用(特别是,您的尺寸必须以正确的顺序开始)。
那么,有没有比上述更好的方法?
最佳答案
有很多方法可以避免这种行为。
首先,每当用np.ndarray
而不是整数索引到slice
的维时,输出的维数将与输入的维数相同:
import numpy as np
x = np.arange(12).reshape(3, 4)
print x[:, 0].shape # integer indexing
# (3,)
print x[:, 0:1].shape # slice
# (3, 1)
这是我避免该问题的首选方式,因为它可以很容易地将范围从单元素选择推广到多元素选择(例如
x[:, i:i+1]
与x[:, i:i+n]
)。如前所述,您还可以通过使用任何整数序列索引到维来避免维数损失:
print x[:, [0]].shape # list
# (3, 1)
print x[:, (0,)].shape # tuple
# (3, 1)
print x[:, np.array((0,))].shape # array
# (3, 1)
如果选择使用整数索引,则始终可以使用
np.newaxis
(或等效地,None
)插入新的单例尺寸:print x[:, 0][:, np.newaxis]
# (3, 1)
print x[:, 0][:, None]
# (3, 1)
否则,您可以手动将其调整为正确的尺寸(此处使用
-1
自动推断出第一个尺寸的尺寸):print x[:, 0].reshape(-1, 1).shape
# (3, 1)
最后,您可以使用
np.matrix
而不是np.ndarray
。 np.matrix
的行为更像是MATLAB矩阵,每当您使用整数编制索引时,单维度都会保留在其中:y = np.matrix(x)
print y[:, 0].shape
# (3, 1)
但是,您应该注意,在
np.matrix
和np.ndarray
之间有一个number of other important differences,例如*
运算符对数组执行元素逐级乘法,但对矩阵执行矩阵乘法。在大多数情况下,最好坚持使用np.ndarrays
。