【问题标题】:Minima values of nested array (possibly partially empty)嵌套数组的最小值(可能部分为空)
【发布时间】:2017-06-27 21:43:22
【问题描述】:

我想计算轴 1 上一些嵌套二维数组的最小值。 我的问题如下:数组可能有 1 个(或更多)空轴,如果我尝试在其上应用 np.min 会引发一些错误。

请注意,如果轴为空,我希望代码返回 -1(通过初始化,我的数组不能有负值)。 例如,我可能希望将m 的最小值定义如下:

import numpy as np
m = np.array([np.array([1,2]),np.array([3,4,5,6]),np.array([]),np.array([7,8,9])])
# Expected output in this case: [1,3,-1,7]

我第一次尝试:

_min = np.min(m, axis=1)

这给出了以下错误:

ValueError: 'axis' 条目超出范围

所以,由于错误来自嵌套数组(它的形状为(4,),我尝试了:

_min = [np.min(x) for x in m]

哪个投掷:

ValueError: 零大小数组到没有标识的归约操作最小值

这里的错误来自 m[2] = [] 这是一个零大小的数组。

我想出的只是这个低效且丑陋的解决方案:

_min = []
for x in m:
    if len(x) > 0:
        _min.append(np.min(x))
    else:
        _min.append(-1)
# [1, 3, -1, 7]

有没有一种简单的方法可以用更 Python 的方式来实现?


编辑 @Divakar 提出的 pythonic 解决方案在空轴是最后一个元素时不起作用(m[-1]):

m = np.array([np.array([0.53, 0.56]), np.array([0.33, 0.31, 0.27, 0.48, 0.36, 0.35, 0.27, 0.24]), np.array([])])

给出的错误:

IndexError: index 10 out-of-bounds in minimum.reduceat [0, 10)

【问题讨论】:

  • 这不是一个嵌套的np.array,而是一个list的数组。 NumPy 1.13 对此更加明确,将您的 m 数组显示为 array([list([1, 2]), list([3, 4, 5, 6]), list([]), list([7, 8, 9])], dtype=object)
  • @Eric 当我明确定义为 np.array-s 时,我遇到了同样的错误。例如:m = np.array([np.array([0.53, 0.56]), np.array([0.33, 0.31, 0.27, 0.48, 0.36, 0.35, 0.27, 0.24]), np.array([])])
  • 确实如此,但仍然值得意识到那些东西不一样
  • @Eric 我的错误,在我的代码中它是作为嵌套的 np.array 生成的,我在写我的小例子时忘记添加它。我编辑了问题以解决它。感谢您的关注

标签: python arrays numpy multidimensional-array


【解决方案1】:

原版 Python

一种方法是 -

[min(i) if len(i)>0 else -1 for i in m]

示例运行 -

In [270]: m = np.array([[1,2],[3,4,5,6],[],[7,8,9]])

In [271]: [min(i) if len(i)>0 else -1 for i in m]
Out[271]: [1, 3, -1, 7]

我们可以按照@blacksite 的建议跳过列出len -

In [307]: [min(i) if i else -1 for i in m]
Out[307]: [1, 3, -1, 7]

更多的 NumPythonic 方法

使用 NumPy,我们可以将输入数组展平为一个常规数组,然后使用 np.minimum.reduceat 执行区间最小值查找,就像这样 -

def min_per_elem(m):
    a = np.concatenate(m)
    l = np.array([len(i) for i in m])
    split_idx = np.unique(np.r_[0,l.cumsum()])[:-1]
    out = np.full(len(l),-1,dtype=a.dtype)
    out[l>0] = np.minimum.reduceat(a, split_idx)
    return out

示例运行 -

In [74]: m
Out[74]: 
array([array([], dtype=float64), array([ 0.53,  0.56]),
       array([], dtype=float64),
       array([ 0.33,  0.31,  0.27,  0.48,  0.36,  0.35,  0.27,  0.24]),
       array([], dtype=float64)], dtype=object)

In [75]: min_per_elem(m)
Out[75]: array([-1.  ,  0.53, -1.  ,  0.24, -1.  ])

【讨论】:

  • 你甚至可以做[min(i) if i else -1 for i in m],即取出len测试。
  • @blacksite 谢谢!不知道那个。已编辑。
  • 感谢迪瓦卡! Pythonic 方法是我一直在寻找的。我还需要计算m 的最大值和平均值,np.maximum.reduceat 效果很好,但我找不到平均值的等价物......有什么想法吗?
  • @Nuageux 对于average,使用np.add.reduceat,然后除以长度:l
  • 是的,确实……我的大脑应该已经停止运作了……非常感谢!
【解决方案2】:

numpy_indexed 包(免责声明:我是它的作者)有助于处理锯齿状数组。在引擎盖下,下面的解决方案类似于 Divakars 解决方案,但开销更大;但是,如果您系统地将代码中的数据结构从经典的锯齿状数组布局切换到这种密集的键值数组格式,那么您随后可以以完全矢量化的方式表达对这个锯齿状数组的几乎任何操作,这往往会很快得到回报总体而言,在性能、可读性和 numpythonicness 方面。

import numpy_indexed as npi
keys = np.concatenate([np.ones(len(row)*i for i, row in enumerate(m)])
values = np.concatenate(m)
unique_keys, minima_m = npi.group_by(keys).min(values)
# this doesnt have the -1 entry but if it is important to your data layout it is easy to map back:
result = -np.ones(len(m), dtype=minima_m.dtype)
result[unique_keys] = minima_m

除了性能特征外,还有内存含义;维护键数组可能看起来很浪费;如果你的行很大,它可能会有点。但是,如果您的示例中的行大小实际上具有代表性,那么在您的数据结构的这种表示中实际上会节省内存,因为每行需要的每个额外的 numpy 数组也会吞噬大约 100 字节左右。

要充分利用这一点,需要重新考虑如何在应用程序中处理锯齿状数组,因为表示之间的重新映射对于可读性或性能而言并不是特别好。因此,如果您坚持使用一种表示形式,则无需重新映射;但如果你愿意,回到锯齿状数组也非常简单:

npi.group_by(keys).split(values)

【讨论】:

  • 感谢您的回答,我会查看您的包裹。在 Github 上,有一个关于使用 Python 2.7 安装的未解决问题,还没有解决吗? (我正在使用 Python 2.7.6)
  • 在 pip 上你需要先 pip install pyyaml;就是这样,那么应该可以正常工作。
猜你喜欢
  • 2019-02-17
  • 2018-02-12
  • 2021-09-02
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多