按值从多维numpy数组中删除元素

4

给定一个numpy数组

a = np.array([[0, -1, 0], [1, 0, 0], [1, 0, -1]])

如何最快地删除所有值为-1的元素,以获得以下形式的数组

np.array([[0, 0], [1, 0, 0], [1, 0]])

2
NumPy数组是刚性网格 - 如果您尝试创建一个不规则的数组,您将进入对象数组的可怕世界,在那里性能下降,什么都无法正常工作。请仔细考虑是否真的有必要这样做,以及是否真的应该使用NumPy。 - user2357112
5个回答

4

你可能考虑另一种方法:

def iterative_numpy(a):
    mask = a != 1
    out = np.array([ a[i,mask[i]] for i xrange(a.shape[0]) ])
    return out

Divakar的方法loop_compr_based计算掩码行中的总和以及该结果的累积总和。此方法避免了这样的求和,但仍需迭代a的行。它还返回一个数组的数组。这种方法的烦恼在于必须使用语法out[1][2]而不是out[1,2]来索引out。将其与随机整数矩阵的时间进行比较:
In [4]: a = np.random.random_integers(-1,1, size = (3,30))

In [5]: %timeit iterative_numpy(a)
100000 loops, best of 3: 11.1 us per loop

In [6]: %timeit loop_compr_based(a)
10000 loops, best of 3: 20.2 us per loop

In [7]: a = np.random.random_integers(-1,1, size = (30,3))

In [8]: %timeit iterative_numpy(a)
10000 loops, best of 3: 59.5 us per loop

In [9]: %timeit loop_compr_based(a)
10000 loops, best of 3: 30.8 us per loop

In [10]: a = np.random.random_integers(-1,1, size = (30,30))

In [11]: %timeit iterative_numpy(a)
10000 loops, best of 3: 64.6 us per loop

In [12]: %timeit loop_compr_based(a)
10000 loops, best of 3: 36 us per loop

当列数大于行数时,iterative_numpy 胜出。当行数大于列数时,loop_compr_based 胜出,但先对 a 进行转置将提高两种方法的性能。当维度相同时,loop_compr_based 是最佳选择。

重要的附带讨论

除了实现之外,需要注意的是任何具有非均匀形状的 numpy 数组都不是实际数组,因为值不占据连续的内存空间,而且通常的数组操作将无法正常工作。

例如:

>>> a = np.array([[1,2,3],[1,2],[1]])
>>> a*2
array([[1, 2, 3, 1, 2, 3], [1, 2, 1, 2], [1, 1]], dtype=object)

请注意,numpy实际上告诉我们这不是通常的numpy数组,并带有提示dtype = object
因此,最好只制作一个numpy数组列表并相应地使用它们。

2

方法一:使用NumPy数组分割 -

def split_based(a, val):
    mask = a!=val
    p = np.split(a[mask],mask.sum(1)[:-1].cumsum())
    out = np.array(list(map(list,p)))
    return out

方法二:使用循环推导,但在循环内进行最少的操作。

def loop_compr_based(a, val):
    mask = a!=val
    stop = mask.sum(1).cumsum()
    start = np.append(0,stop[:-1])
    am = a[mask].tolist()
    out = np.array([am[start[i]:stop[i]] for i  in range(len(start))])
    return out

样例运行 -

In [391]: a
Out[391]: 
array([[ 0, -1,  0],
       [ 1,  0,  0],
       [ 1,  0, -1],
       [-1, -1,  8],
       [ 3,  7,  2]])

In [392]: split_based(a, val=-1)
Out[392]: array([[0, 0], [1, 0, 0], [1, 0], [8], [3, 7, 2]], dtype=object)

In [393]: loop_compr_based(a, val=-1)
Out[393]: array([[0, 0], [1, 0, 0], [1, 0], [8], [3, 7, 2]], dtype=object)

运行时测试 -

In [387]: a = np.random.randint(-2,10,(1000,1000))

In [388]: %timeit split_based(a, val=-1)
10 loops, best of 3: 161 ms per loop

In [389]: %timeit loop_compr_based(a, val=-1)
10 loops, best of 3: 29 ms per loop

对于Python 3,我认为方法#1中的最后一行应该是:out = np.array(list(map(list,p))) - Kamil Sindi
1
@capitalistpug 对我来说,使用 a = np.random.randint(-2,10,(1000,1000)),方法 #2 比方法 #1 快了 5 倍。 - Divakar
1
我犯了一个错误,因为我正在使用py3,正如你所知道的,map是一个迭代器。 - Kamil Sindi

0

这不会在多维数组中保留正确的值。 - Goodies
1
期望的结果不是多维数组,而是一个不规则数组列表(或对象数组)。 - hpaulj

0

这个怎么样?

print([[y for y in x if y > -1] for x in a])
[[0, 0], [1, 0, 0], [1, 0]]

不是numpy解决方案。如果您使用此方法,效率很可能会受到影响。 - Goodies

0

对于你可能想要使用的数组的几乎所有操作,你都可以使用掩码数组

a = np.array([[0, -1, 0], [1, 0, 0], [1, 0, -1]])

b=np.ma.masked_equal(a,-1)

b
Out[5]: 
masked_array(data =
 [[0 -- 0]
 [1 0 0]
 [1 0 --]],
             mask =
 [[False  True False]
 [False False False]
 [False False  True]],
       fill_value = -1)

如果您真的需要不规则数组,可以按行进行.compressed()处理

c=np.array([b[i].compressed() for i in range(b.shape[0])])

c
Out[10]: array([array([0, 0]), array([1, 0, 0]), array([1, 0])], dtype=object)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接