考虑一些载体:
import numpy as np
v = np.arange(10)
假设我们需要找到满足某些条件的最后2个索引。 例如,在Matlab中,它将被写成例如。
find(v <5 , 2,'last')
answer = [ 3 , 4 ] (Note: Matlab indexing from 1)
问题:在Python中最明智的方法是什么?
“Nice”解决方案应该在找到2个所需结果时停止搜索,它不应搜索向量的所有元素。 所以np.where在这个意义上似乎并不“好”。
我们可以使用“for”轻松编写,但有没有其他方法? 我害怕使用“for”因为它可能很慢(至少在Matlab中是如此)。
答案 0 :(得分:1)
即使循环被隐藏,您找到的任何解决方案都会遍历列表。在一个函数内部。 您的问题的解决方案取决于您可以做出的假设,例如列表是排序的? 对于一般情况,我从最后开始迭代循环:
def find(condition, k, v):
indices = []
for i, var in enumerate(reversed(v)):
if condition(var):
indices.append(len(v) - i - 1)
if len(indices) >= k:
break
return indices
然后该条件应作为函数传递,因此您可以使用lambda:
v = range(10)
find(lambda x: x < 5, 3, v)
将输出
[4,3,2]
答案 1 :(得分:1)
此尝试不使用numpy
,并且可能不是非常惯用。
尽管如此,如果我理解正确,zip
,filter
和reversed
都是惰性迭代器,它们只接受它们真正需要的元素。因此,你可以试试这个:
x = list(range(10))
from itertools import islice
res = reversed(list(map(
lambda xi: xi[1],
islice(
filter(
lambda xi: xi[0] < 5,
zip(reversed(x), reversed(range(len(x))))
),
2
)
)))
print(list(res))
输出:
[3, 4]
它的作用(从内到外):
(value, index)
- 对,按islice
map
即使它看起来有点怪异,但它应该都是懒惰的,并在它找到你要找的前两个元素后停止。我没有将它与一个简单的循环进行比较,也许只是使用循环会更简单,更快。
答案 2 :(得分:1)
我不知道&#34;好&#34;短暂的短路解决方案。
最有原则的方法是使用类似Cython
的东西来粗暴地过度简化它为Python添加快速循环。一旦你设置了它就很容易。
如果你不想这样做,你必须雇用一些体操,如:
import numpy as np
def find_last_k(vector, condition, k, minchunk=32):
if k > minchunk:
minchunk = k
l, r = vector.size - minchunk, vector.size
found = []
n_found = 0
while r > 0:
if l <= 0:
l = 0
found.append(l + np.where(condition(vector[l:r]))[0])
n_found += len(found[-1])
if n_found >= k:
break
l, r = 3 * l - 2 * r, l
return np.concatenate(found[::-1])[-k:]
这会尝试平衡循环开销和numpy&#34;不灵活性&#34;通过搜索块,我们以指数方式成长,直到找到足够的命中。
不过很不错。