Поиск индексов совпадающих элементов в списке в Python

У меня длинный список чисел с плавающей запятой от 1 до 5, называемый "средний", и я хочу вернуть список индексов для элементов, которые меньше или больше b

def find(lst,a,b):
    result = []
    for x in lst:
        if x<a or x>b:
            i = lst.index(x)
            result.append(i)
    return result

matches = find(average,2,4)

Но удивительно, что выход для "спичек" имеет много повторений в нем, например [2, 2, 10, 2, 2, 2, 19, 2, 10, 2, 2, 42, 2, 2, 10, 2, 2, 2, 10, 2, 2,...].

Почему это происходит?

Ответ 1

Вы используете .index() который найдет только первое вхождение вашего значения в списке. Поэтому, если у вас есть значение 1.0 в индексе 2 и в индексе 9, то .index(1.0) всегда будет возвращать 2, независимо от того, сколько раз 1.0 появляется в списке.

Используйте enumerate() для добавления индексов в ваш цикл:

def find(lst, a, b):
    result = []
    for i, x in enumerate(lst):
        if x<a or x>b:
            result.append(i)
    return result

Вы можете разбить это на понимание списка:

def find(lst, a, b):
    return [i for i, x in enumerate(lst) if x<a or x>b]

Ответ 2

Это довольно тяжелая зависимость, но если вы делаете много такого, вам следует подумать об использовании numpy.

In [56]: import random, numpy

In [57]: lst = numpy.array([random.uniform(0, 5) for _ in xrange(1000)]) # example list

In [58]: a, b = 1, 3

In [59]: numpy.flatnonzero((lst > a) & (lst < b))[:10]
Out[59]: array([ 0, 12, 13, 15, 18, 19, 23, 24, 26, 29])

В ответ на вопрос Seanny123 я использовал этот временной код:

import numpy, timeit, random

a, b = 1, 3

lst = numpy.array([random.uniform(0, 5) for _ in xrange(1000)])

def numpy_way():
    numpy.flatnonzero((lst > 1) & (lst < 3))[:10]

def list_comprehension():
    [e for e in lst if 1 < e < 3][:10]

print timeit.timeit(numpy_way)
print timeit.timeit(list_comprehension)

Версия numpy более чем в 60 раз быстрее.

Ответ 3

>>> average =  [1,3,2,1,1,0,24,23,7,2,727,2,7,68,7,83,2]
>>> matches = [i for i in range(0,len(average)) if average[i]<2 or average[i]>4]
>>> matches
[0, 3, 4, 5, 6, 7, 8, 10, 12, 13, 14, 15]