Numpy 索引:返回其余部分
Posted
技术标签:
【中文标题】Numpy 索引:返回其余部分【英文标题】:Numpy Indexing: Return the rest 【发布时间】:2012-09-13 03:20:27 【问题描述】:一个简单的numpy索引示例:
In: a = numpy.arange(10)
In: sel_id = numpy.arange(5)
In: a[sel_id]
Out: array([0,1,2,3,4])
如何返回未被 sel_id 索引的数组的其余部分?我能想到的是:
In: numpy.array([x for x in a if x not in a[id]])
out: array([5,6,7,8,9])
有没有更简单的方法?
【问题讨论】:
这是一次操作吗?或者你会在路上重复使用sel_id
(这是否定的)?另外,您是对多维案例感兴趣,还是只对一维案例感兴趣?
在我的应用中,会在多维海量数组上操作,是的,我会复用sel_id。
刚刚意识到我上面的解决方案是错误的。如果它是一个包含十个 1 的数组,那么给定的代码将给出一个 None 数组,而不是一个包含五个 1 的数组。
【参考方案1】:
对于这个简单的一维案例,我实际上会使用布尔掩码:
a = numpy.arange(10)
include_index = numpy.arange(4)
include_idx = set(include_index) #Set is more efficient, but doesn't reorder your elements if that is desireable
mask = numpy.array([(i in include_idx) for i in xrange(len(a))])
现在你可以得到你的价值观了:
included = a[mask] # array([0, 1, 2, 3])
excluded = a[~mask] # array([4, 5, 6, 7, 8, 9])
请注意,a[mask]
不一定产生与a[include_index]
相同的结果,因为include_index
的顺序对那种情况下的输出很重要(它应该大致相当于a[sorted(include_index)]
)。但是,由于您排除项目的顺序没有明确定义,这应该可以正常工作。
编辑
创建蒙版的更好方法是:
mask = np.zeros(a.shape,dtype=bool)
mask[include_idx] = True
(感谢 seberg)。
【讨论】:
@BiRico -- 错误。我将include_index
转换为set
(称为include_idx
),它有一个在O(1) 中的__contains__
方法。此解决方案具有O(N)
复杂性。
+1,这几乎正是我要建议的,但我不得不离开电脑。使用布尔掩码非常适合此类操作,因为您不必做任何额外的工作来计算相对补码。仅供参考,根据我的测试,在生成器上使用 fromiter
而不是在列表理解上使用 array
会产生小的速度提升。
抱歉,使用套装来制作面具是为了一个小问题而拿出大型武器......
可能是另一种创建布尔数组的方式可以是:mask = numpy.zeros(numpy.shape(a),bool);掩码 [sel_id] = 真;我同意使用布尔数组可能是最好的解决方案。谢谢!
@mgilson,如果您不想罚款,但是您如何编辑答案以使用mask[sel_id] = True
。这将使它成为某人找到的非常好的答案。 (是的,我真的认为布景在这里很糟糕,原因有很多)【参考方案2】:
你可以用布尔掩码很好地做到这一点:
a = numpy.arange(10)
mask = np.ones(len(a), dtype=bool) # all elements included/True.
mask[[7,2,8]] = False # Set unwanted elements to False
print a[mask]
# Gives (removing entries 7, 2 and 8):
[0 1 3 4 5 6 9]
加法(取自@mgilson)。创建的二进制掩码可以很好地用于返回带有a[~mask]
的原始切片,但只有在原始索引经过排序时才相同。
编辑: 下移了,因为我必须意识到此时(2012 年 9 月)我会考虑 np.delete
错误。
您也可以使用np.delete
,尽管掩码功能更强大(并且在未来我认为这应该是一个不错的选择)。但是目前它比上面的要慢,并且会产生带有负索引的意外结果(或给定切片时的步骤)。
print np.delete(a, [7,2,8])
【讨论】:
是的——第二种方法是迄今为止最好的,唯一的纯 numpy 线性方法……回想起来似乎很明显! (请注意,在幕后,numpy.delete
只使用setdiff1d
,而in1d
又使用in1d
。所以它也是n log n。)+1 但你已经有了我的!
@senderle 认真的!这很有趣,也许np.delete
可以使用该执行路径的更改...
@PierreGM,真的,我也从不使用它,但如果你只想要一个没有它的数组副本而不需要其他任何东西,老实说,我认为使用这样的函数没有什么大问题。
@seberg 它存在的事实意味着np.delete
当然对某些人有用,但我一直认为这是一个糟糕的功能,永远不会像人们期望的那样工作(但工作方式与这应该)。当显式的精美索引如此清晰时,为什么还要增加混乱?【参考方案3】:
更像是:
a = numpy.array([1, 2, 3, 4, 5, 6, 7, 4])
exclude_index = numpy.arange(5)
include_index = numpy.setdiff1d(numpy.arange(len(a)), exclude_index)
a[include_index]
# array([6, 7, 4])
# Notice this is a little different from
numpy.setdiff1d(a, a[exclude_index])
# array([6, 7]
【讨论】:
【参考方案4】:我会用布尔掩码来做这件事,但有点不同。这具有在 N 维中工作的好处,具有连续或非索引。内存使用情况取决于是否为掩码数组创建视图或副本,我不确定。
import numpy
a = numpy.arange(10)
sel_id = numpy.arange(5)
mask = numpy.ma.make_mask_none(a.shape)
mask[sel_id] = True
answer = numpy.ma.masked_array(a, mask).compressed()
print answer
# [5 6 7 8 9]
【讨论】:
掩码数组可能是一个非常好的选择。虽然.compressed()
在某种程度上违背了 IMO 屏蔽数组的目的,因为它创建了一个普通的数组副本。【参考方案5】:
此外,如果它们是连续的,请使用 [N:] 语法来选择其余部分。例如, arr[5:] 将选择数组中倒数第 5 个元素。
【讨论】:
【参考方案6】:这是另一种方式,使用 numpy 的 isin()
函数:
import numpy as np
a = np.arange(10)
sel_id = np.arange(5)
a[~np.isin(np.arange(a.size), sel_id)]
解释:
np.arange(a.size)
给出a
的所有索引,即[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
np.isin(np.arange(a.size), sel_id)
在sel_id
和False
中的索引处返回布尔掩码[ True, True, True, True, True, False, False, False, False, False]
和True
。由于我们想要获取不在sel_id
中的索引,我们使用按位非运算符~
来反转布尔掩码。
【讨论】:
【参考方案7】:numpy.setdiff1d(a, a[sel_id])
应该可以解决问题。不知道有没有比这更简洁的东西。
【讨论】:
如果数组中有重复的值,那就不行了。【参考方案8】:假设a
是一维数组,您可以从索引列表中弹出您不想要的项目:
accept = [i for i in range(a.size) if i not in avoid_list]
a[accept]
你也可以尝试使用类似的东西
accept = sorted(set(range(a.size)) - set(indices_to_discard))
a[accept]
这个想法是在你不想要的索引集的互补上使用花哨的索引。
【讨论】:
以上是关于Numpy 索引:返回其余部分的主要内容,如果未能解决你的问题,请参考以下文章
Python 关于数组矩阵变换函数numpy.nonzero(),numpy.multiply()用法