嵌套数组的最小值(可能部分为空)

时间:2017-06-27 11:45:24

标签: python arrays numpy multidimensional-array

我想计算轴1上一些嵌套2D数组的最小值。 我的问题如下:数组可能有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]

有一种简单的方法可以用更加pythonic的方式实现这一目标吗?

修改 @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:在minimum.reduceat [0,10]中索引10个越界

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

numpy_indexed包(免责声明:我是它的作者)可以帮助处理锯齿状数组。在幕后,下面的解决方案就像Divakars解决方案,但有更多的开销;但是,如果您系统地将代码中的数据结构从经典的锯齿状阵列布局切换到这种密集的键值数组格式,您可以随后以完全矢量化的方式表达这个锯齿状阵列上的任何操作,这往往会很快得到回报一般而言,在性能,可读性和numpythonicness方面。

Option Explicit

Sub ShowProgressBar()

    Dim lAllCnt         As Long
    Dim rc              As Range

    lAllCnt = Selection.Count

    UserForm1.Show vbModeless
    UserForm1.ProgressBar1.Min = 1
    UserForm1.ProgressBar1.Max = lAllCnt

    For Each rc In Selection
        UserForm1.ProgressBar1.Value = fnBigOrSmallIncrement(UserForm1.ProgressBar1.Value, 1, lAllCnt)
        Application.Wait Now + #12:00:01 AM#
    Next

    Unload UserForm1

End Sub

Public Function fnBigOrSmallIncrement(lngCurrent As Long, lngMin As Long, lngMax As Long) As Long

    fnBigOrSmallIncrement = lngCurrent + 1

    If fnBigOrSmallIncrement < lngMin Then fnBigOrSmallIncrement = lngMin
    If fnBigOrSmallIncrement > lngMax Then fnBigOrSmallIncrement = lngMax

End Function

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

充分利用这一点需要重新思考如何在应用程序中使用锯齿状数组,因为表示之间的重新映射对于可读性或性能并不是特别好。因此,如果您坚持使用一种表示法,则无需重新映射;但如果你想,回到锯齿状阵列也很容易:

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