【问题标题】:How to broadcast a function over a numpy array, when dtype=object?当 dtype = object 时,如何通过 numpy 数组广播函数?
【发布时间】:2014-05-22 21:31:52
【问题描述】:

如果我有一个数值数组,由于向量长度不等,它必须使用对象指针而不是值作为数据类型:

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])

如何在不手动遍历数组的情况下通过实际数值广播函数?

【问题讨论】:

  • 几乎从不有充分的理由使用dtype=object!在 Python list 上使用 numpy 数组的意义在于,它允许您加速需要通过向量化循环遍历数组元素的操作——有效地通过使用较低级别的语言执行循环来避免开销Python 中固有的。但是,使用object 类型会失去这些性能优势。数组中的元素仅被视为“哑” Python 对象(在这种情况下,您的数组仅包含两个普通的 lists,即带有 shape=(2,))。
  • 如果我知道更多关于数组代表什么,以及为什么行长不均匀,我可能会提出一个更好的解决方案。就目前而言,如果您真的非常需要一个参差不齐的数组,那么您不妨使用嵌套的lists,这样构建起来会便宜得多。

标签: python arrays numpy broadcast


【解决方案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)

有关这些技术变体的完整列表,请参阅here

【讨论】:

    【解决方案2】:

    就像其他人建议的那样,最好避免使用数组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 等。

    【讨论】:

    • 屏蔽数组是做这类事情的首选方式。他们避免了不得不求助于nanmax 和朋友的尴尬。
    猜你喜欢
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 2019-09-29
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多