如何在dtype=object的numpy数组上广播函数?
如果我有一个数字值的数组,因为数组的长度不一样,所以我必须用对象指针来代替直接使用值:
In [145]: import numpy as np
In [147]: a = np.array([[1,2],[3,4,5]])
In [148]: a
Out[148]: array([[1, 2], [3, 4, 5]], dtype=object)
In [150]: np.sin(a)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-150-58d97006f018> in <module>()
----> 1 np.sin(a)
In [152]: np.sin(a[0])
Out[152]: array([ 0.84147098, 0.90929743])
我该如何对这些实际的数字值应用一个函数,而不需要手动一个一个地遍历这个数组呢?
2 个回答
1
就像其他人提到的,最好避免使用数组 dtype=object
。
还有一种避免这个问题的方法,令人惊讶的是到目前为止没有人提到,那就是用 NaN 填充,以便让数组达到相同的形状。
a = np.array([[1,2],[3,4,5]])
maxlen = max(len(x) for x in a)
b = np.array([ x+[np.NaN]*(maxlen-len(x)) for x in a ])
b
=> array([[ 1., 2., nan], [ 3., 4., 5.]])
b.shape
=> (2, 3)
np.sin(b)
=> array([[ 0.84147098, 0.90929743, nan],
[ 0.14112001, -0.7568025 , -0.95892427]])
当然,处理包含 NaN 的数组时要小心,比如你可能想用 nanmax
代替 max
,等等。
1
这里有几个不同的问题。首先,在numpy中对python对象进行广播并没有太大好处;在这种情况下,使用纯python可能会更有效。
>>> a = np.array([[1, 2, 3], [4, 5, 6]], dtype=object)
>>> b = np.arange(1, 7).reshape(2, 3)
>>> c = [[1, 2, 3], [4, 5, 6]]
>>> %timeit a * 5
100000 loops, best of 3: 4.28 µs per loop
>>> %timeit b * 5
100000 loops, best of 3: 2.08 µs per loop
>>> %timeit [[x * 5 for x in l] for l in c]
1000000 loops, best of 3: 998 ns per loop
这些速度的变化可能不太均匀,但你大概明白了。
其次,问题并不是直接和广播有关。numpy
可以很高兴地对python列表进行广播,但结果可能和你预期的不一样:
>>> a = np.array([[1, 2, 3], [4, 5]], dtype=object)
>>> a * 5
array([[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
[4, 5, 4, 5, 4, 5, 4, 5, 4, 5]], dtype=object)
numpy
允许数组中的对象定义它们自己的操作符或函数的版本。在这个例子中,python列表把*
定义为重复!即使在混合类型的数组中也是如此;你可以试试这个:np.array([5, [1, 2]], dtype=object) * 5
。之所以sin
在这种情况下不能广播,是因为python列表根本没有定义sin
。
你可能会发现使用固定宽度的数组加上掩码会更好。
>>> np.ma.array([[1, 2, 3], [4, 5, 6]], mask=[[0, 0, 0], [0, 0, 1]])
masked_array(data =
[[1 2 3]
[4 5 --]],
mask =
[[False False False]
[False False True]],
fill_value = 999999)
如你所见,你可以通过这种方式“模拟”一个不规则数组,它的表现会和你预期的一样。
>>> a = np.ma.array([[1, 2, 3], [4, 5, 6]], mask=[[0, 0, 0], [0, 0, 1]])
>>> np.sin(a)
masked_array(data =
[[0.841470984808 0.909297426826 0.14112000806]
[-0.756802495308 -0.958924274663 --]],
mask =
[[False False False]
[False False True]],
fill_value = 1e+20)
值得提到的是,有几种方法可以创建掩码数组。在你的情况下,masked_invalid
可能会很有用。
>>> np.ma.masked_invalid([[1, 2, 3], [4, 5, np.NaN]])
masked_array(data =
[[1.0 2.0 3.0]
[4.0 5.0 --]],
mask =
[[False False False]
[False False True]],
fill_value = 1e+20)
你也可以通过条件来创建掩码数组:
>>> x = np.array([[1, 2, 3], [4, 5, 6]])
>>> np.ma.masked_where(x > 5, x)
masked_array(data =
[[1 2 3]
[4 5 --]],
mask =
[[False False False]
[False False True]],
fill_value = 999999)
想要了解这些技术的完整列表,可以查看这里。