Python 是否可以在numpy数组切片运算符中组合逻辑条件和限制条件
我有下面的代码,它正是我想要的,但是它太慢了,因为它涉及到一个不必要的具体化步骤:Python 是否可以在numpy数组切片运算符中组合逻辑条件和限制条件,python,arrays,numpy,slice,Python,Arrays,Numpy,Slice,我有下面的代码,它正是我想要的,但是它太慢了,因为它涉及到一个不必要的具体化步骤: ### init a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]]) ### condition 1) element 0 has to be larger than 1 ### condition 2) limit the output to 2 elements b = a[a[:,0] > 1][:2] 问题是,当我有一个大数组时,速度非常慢(考虑
### init
a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
### condition 1) element 0 has to be larger than 1
### condition 2) limit the output to 2 elements
b = a[a[:,0] > 1][:2]
问题是,当我有一个大数组时,速度非常慢(考虑到我只想用条件2切掉一小块)。这很容易做到,但我还没有找到一种方法把它放进一行
因此,是否有一种简洁的方法可以在一行中有效地实现这一点?大概是这样的:
b = a[a[:,0] > 1 and :2]
a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
# Check your condition
mask = a[:, 0] > 1
# Copy those rows the array that satisfy the condition
temp = a[mask]
# Take first two rows of temp
b = temp[:2]
谢谢大家! 您可能可以稍微加快这段代码的速度,您当前的代码是这样工作的:
b = a[a[:,0] > 1 and :2]
a = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
# Check your condition
mask = a[:, 0] > 1
# Copy those rows the array that satisfy the condition
temp = a[mask]
# Take first two rows of temp
b = temp[:2]
我怀疑最昂贵的操作是中间的复制操作,你可以通过这样做来避免它:
mask = a[:, 0] > 1
# Find the first two True values in mask
where = np.where(mask)[0][:2]
# Only copy the rows you really want
b = a[where]
也许有一种更有效的方法可以找到前两个真值,我没有考虑太多,但关键是先找到您想要的值,然后只复制这些行。我想不出一个更快速的解决方案,但您可以使用
numba
:
from numba import autojit
def filtfunc(a):
idx = []
for ii in range(a.shape[0]):
if (a[ii, 0] > 1):
idx.append(ii)
if (len(idx) == 2):
break
return a[idx]
jit_filter = autojit(filtfunc)
以下是另外两个建议的解决方案供参考:
def marco_filter(a):
return a[a[:,0] > 1][:2]
def rico_filter(a):
mask = a[:, 0] > 1
where = np.where(mask)[0][:2]
return a[where]
一些时间安排:
%%timeit a = np.random.random_integers(1, 12, (1000,1000))
marco_filter(a)
# 100 loops, best of 3: 11.6 ms per loop
%%timeit a = np.random.random_integers(1, 12, (1000,1000))
rico_filter(a)
# 10000 loops, best of 3: 44.8 µs per loop
%%timeit a = np.random.random_integers(1, 12, (1000,1000))
jit_filter(a)
# 10000 loops, best of 3: 30.7 µs per loop
如果
n=2
与a.shape[0]
相比非常小,那么使用这个小函数可能是值得的。其基本思想是计算一个掩码,该掩码刚好足够大,可以给出所需的最终行数。在这里,我迭代地进行。通常迭代速度很慢,但如果迭代次数足够少,在其他地方节省的时间可能是值得的
def mask(a):
return a[:,0]>1
def paul_filter1(a,n):
# incremental w/ sum
j = a.shape[0]
for i in xrange(n,j+1):
am = mask(a[:i,:])
if np.sum(am)>=n:
j = i
break
return a[am,:]
请注意,遮罩am
可能比其正在处理的尺寸短。它有效地用False
填充其余部分。我还没有检查这是否有文件记录
在这个小例子中,foo
比a[a[:,0]>1,:][:2,:]
慢3倍
但是对于更大的数组,比如说a2=np.tile(a,[1000,1])
,使用foo
的时间保持不变,但是“蛮力”不断变慢,因为它必须将掩码应用于更多行。当然,这些计时确实取决于所需行在a
中的位置。如果foo
必须使用几乎所有的行,就不会有任何节省
编辑
解决Bi Rico对重复的np.sum
(即使是快速编译的代码)的担忧,我们可以增量地构建,其中
:
def paul_filter3(a,n):
# incremental adding index
j = a.shape[0]
am = mask(a[:n,:])
am = np.where(am)[0].tolist()
if len(am)<n:
for i in xrange(n,j):
if mask(a[[i],:]):
am.append(i)
if len(am)>=n:
break
am = np.array(am)
return a[am,:]
使用1000x1000
随机整数数组(1:12)进行测试,时间为(使用20而不是2,调整掩码使更多行为假)
In [172]: timeit paul_filter4(a,20)
1000 loops, best of 3: 690 us per loop
In [173]: timeit paul_filter3(a,20)
1000 loops, best of 3: 1.22 ms per loop
In [175]: timeit paul_filter1(a,20)
1000 loops, best of 3: 994 us per loop
In [176]: timeit rico_filter(a,20)
1000 loops, best of 3: 668 us per loop
In [177]: timeit marco_filter(a,20)
10 loops, best of 3: 21 ms per loop
rico\u过滤器
使用其中
最快,但我的选择使用cumsum
并不落后。3个增量过滤器的速度相似,大约是快速过滤器的一半
在生成和测试的a
中,大多数行都是True
。这与marco的
担忧一致,即极限条件是逻辑条件的一小部分。在这些条件下,Bi Rico担心paul_filter1
可能爆炸是不现实的
如果更改测试参数,则必须测试a
的所有行(a[:,0]>11
),
然后,使用
where
和cumsum
的过滤器与原始过滤器一样长。增量过滤器的速度较慢,是原来的15倍或更多。但我第一次尝试使用np.sum
时,速度最快。如果是代码本身还是numpy背后的数学问题,你应该解释问题是什么。这个问题是更适合codereview.stackexchange我认为…但我不认为您可以…为什么不简单地颠倒索引顺序?通过执行a[:,:2][a[:,0]>1]
您将不会“物化”大数组,但只有一个包含您要查找的元素。不幸的是,这不起作用,因为您可能会在第二步中删除元素。如果有n
元素,我需要n
元素。现在似乎不太可能有一种超干净的方法来完成此操作。事实上,短路find
for numpy还不存在;根据对的第一个回答,这是2.0版本的一个功能要求。如果他们甚至没有,我不确定有没有一种好的方法可以不使用普通python或编写自己的扩展来实现这一点。是的,我有类似的方法,但希望有一种更优雅的方法。嗯,看看ali_m的时间安排s、 这是一个令人印象深刻的有效解决方案!虽然您的基本想法在这里是正确的,但通过使用am=am[:i,0]>1;如果np.sum(am)=n…
您已将其转化为O(n**2)方法。即使n很小,如果a[:,0],也可能会爆炸
有很多小值。你可以通过做类似于@ali\m的回答来解决这个问题。关键是要避免O(n)循环中的操作,如sum
。我尝试了一些替代的增量函数,这些函数在循环中不使用sum
,并且没有任何加速。np.sum
是编译代码,所以即使它是O(n)它不会降低循环的速度。如果增量方法最终测试了大多数行,那么它的速度就会变慢。