Index()将给出列表中第一个出现的项。是否有一个巧妙的技巧可以返回一个元素列表中的所有索引?


当前回答

There’s an answer using np.where to find the indices of a single value, which is not faster than a list-comprehension, if the time to convert a list to an array is included The overhead of importing numpy and converting a list to a numpy.array probably makes using numpy a less efficient option for most circumstances. A careful timing analysis would be necessary. In cases where multiple functions/operations will need to be performed on the list, converting the list to an array, and then using numpy functions will likely be a faster option. This solution uses np.where and np.unique to find the indices of all unique elements in a list. Using np.where on an array (including the time to convert the list to an array) is slightly slower than a list-comprehension on a list, for finding all indices of all unique elements. This has been tested on an 2M element list with 4 unique values, and the size of the list/array and number of unique elements will have an impact. Other solutions using numpy on an array can be found in Get a list of all indices of repeated elements in a numpy array Tested in [python 3.10.4, numpy 1.23.1] and [python 3.11.0, numpy 1.23.4]

import numpy as np
import random  # to create test list

# create sample list
random.seed(365)
l = [random.choice(['s1', 's2', 's3', 's4']) for _ in range(20)]

# convert the list to an array for use with these numpy methods
a = np.array(l)

# create a dict of each unique entry and the associated indices
idx = {v: np.where(a == v)[0].tolist() for v in np.unique(a)}

# print(idx)
{'s1': [7, 9, 10, 11, 17],
 's2': [1, 3, 6, 8, 14, 18, 19],
 's3': [0, 2, 13, 16],
 's4': [4, 5, 12, 15]}

%timeit在2M元素列表中,有4个唯一的str元素

# create 2M element list
random.seed(365)
l = [random.choice(['s1', 's2', 's3', 's4']) for _ in range(2000000)]

功能

def test1():
    # np.where: convert list to array and find indices of a single element
    a = np.array(l)
    return np.where(a == 's1')
    

def test2():
    # list-comprehension: on list l and find indices of a single element
    return [i for i, x in enumerate(l) if x == "s1"]


def test3():
    # filter: on list l and find indices of a single element
    return list(filter(lambda i: l[i]=="s1", range(len(l))))


def test4():
    # use np.where and np.unique to find indices of all unique elements: convert list to array
    a = np.array(l)
    return {v: np.where(a == v)[0].tolist() for v in np.unique(a)}


def test5():
    # list comprehension inside dict comprehension: on list l and find indices of all unique elements
    return {req_word: [idx for idx, word in enumerate(l) if word == req_word] for req_word in set(l)}

函数调用

%timeit test1()
%timeit test2()
%timeit test3()
%timeit test4()
%timeit test5()

python 3.10.4

214 ms ± 19.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
85.1 ms ± 1.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
146 ms ± 1.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
365 ms ± 11.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
360 ms ± 5.82 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

结果python 3.11.0

209 ms ± 15.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
70.4 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
132 ms ± 4.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
371 ms ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
314 ms ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

其他回答

创建一个生成器

生成器速度很快,占用的内存很小。它们可以让你灵活地使用结果。

def indices(iter, val):
    """Generator: Returns all indices of val in iter
    Raises a ValueError if no val does not occur in iter
    Passes on the AttributeError if iter does not have an index method (e.g. is a set)
    """
    i = -1
    NotFound = False
    while not NotFound:
        try:
            i = iter.index(val, i+1)
        except ValueError:
            NotFound = True
        else:
            yield i
    if i == -1:
        raise ValueError("No occurrences of {v} in {i}".format(v = val, i = iter))

上面的代码可以用来创建一个索引列表:list(indexes (input,value));使用它们作为字典键:dict(索引(输入,值));求和:Sum (indexes (input,value));在for循环中index_ In indexes (input,value):;等……无需创建临时列表/元组或类似的。

在for循环中,当你调用下一个索引时,你将得到它,而不需要等待所有其他索引先计算出来。这意味着:如果出于某种原因跳出循环,就可以节省查找根本不需要的索引所需的时间。

它是如何工作的

在输入iter上调用.index来查找 瓦尔 使用第二个参数.index从该点开始 在最后发现的事件之后 收益率指数 重复操作,直到index引发ValueError

选择版本

我尝试了四种不同的流量控制版本;两个EAFP(使用try - except)和两个TBYL(在while语句中使用逻辑测试):

"WhileTrueBreak": while True: ... except ValueError: break. Surprisingly, this was usually a touch slower than option 2 and (IMV) less readable "WhileErrFalse": Using a bool variable err to identify when a ValueError is raised. This is generally the fastest and more readable than 1 "RemainingSlice": Check whether val is in the remaining part of the input using slicing: while val in iter[i:]. Unsurprisingly, this does not scale well "LastOccurrence": Check first where the last occurrence is, keep going while i < last

1、2和4之间的整体表现差异可以忽略不计,所以这取决于个人风格和偏好。鉴于.index使用ValueError来让你知道它没有找到任何东西,而不是例如返回None, eafp方法似乎适合我。

下面是4个代码变体和timeit(以毫秒为单位)对于不同长度的输入和稀疏匹配的结果

@version("WhileTrueBreak", versions)
def indices2(iter, val):
    i = -1
    while True:
        try:
            i = iter.index(val, i+1)
        except ValueError:
            break
        else:
            yield i

@version("WhileErrFalse", versions)
def indices5(iter, val):
    i = -1
    err = False
    while not err:
        try:
            i = iter.index(val, i+1)
        except ValueError:
            err = True
        else:
            yield i

@version("RemainingSlice", versions)
def indices1(iter, val):
    i = 0
    while val in iter[i:]:
        i = iter.index(val, i)
        yield i
        i += 1

@version("LastOccurrence", versions)
def indices4(iter,val):
    i = 0
    last = len(iter) - tuple(reversed(iter)).index(val)
    while i < last:
        i = iter.index(val, i)
        yield i
        i += 1
Length: 100, Ocurrences: 4.0%
{'WhileTrueBreak': 0.0074799987487494946, 'WhileErrFalse': 0.006440002471208572, 'RemainingSlice': 0.01221001148223877, 'LastOccurrence': 0.00801000278443098}
Length: 1000, Ocurrences: 1.2%
{'WhileTrueBreak': 0.03101000329479575, 'WhileErrFalse': 0.0278000021353364, 'RemainingSlice': 0.08278000168502331, 'LastOccurrence': 0.03986000083386898}
Length: 10000, Ocurrences: 2.05%
{'WhileTrueBreak': 0.18062000162899494, 'WhileErrFalse': 0.1810499932616949, 'RemainingSlice': 2.9145700042136014, 'LastOccurrence': 0.2049500006251037}
Length: 100000, Ocurrences: 1.977%
{'WhileTrueBreak': 1.9361200043931603, 'WhileErrFalse': 1.7280600033700466, 'RemainingSlice': 254.4725100044161, 'LastOccurrence': 1.9101499929092824}
Length: 100000, Ocurrences: 9.873%
{'WhileTrueBreak': 2.832529996521771, 'WhileErrFalse': 2.9984100023284554, 'RemainingSlice': 1132.4922299943864, 'LastOccurrence': 2.6660699979402125}
Length: 100000, Ocurrences: 25.058%
{'WhileTrueBreak': 5.119729996658862, 'WhileErrFalse': 5.2082200068980455, 'RemainingSlice': 2443.0577100021765, 'LastOccurrence': 4.75954000139609}
Length: 100000, Ocurrences: 49.698%
{'WhileTrueBreak': 9.372120001353323, 'WhileErrFalse': 8.447749994229525, 'RemainingSlice': 5042.717969999649, 'LastOccurrence': 8.050809998530895}

如果你使用的是Python 2,你可以用这个实现相同的功能:

f = lambda my_list, value:filter(lambda x: my_list[x] == value, range(len(my_list)))

其中my_list是要获取索引的列表,value是要搜索的值。用法:

f(some_list, some_element)

你可以使用枚举的列表推导式:

indices = [i for i, x in enumerate(my_list) if x == "whatever"]

迭代器enumerate(my_list)为列表中的每一项生成对(index, item)。使用i, x作为循环变量目标,将这些对解包到索引i和列表项x中。我们向下筛选到所有符合条件的x,并选择这些元素的索引i。

如果你需要搜索所有元素在某些索引之间的位置,你可以声明它们:

[i for i,x in enumerate([1,2,3,2]) if x==2 & 2<= i <=3] # -> [3]

There’s an answer using np.where to find the indices of a single value, which is not faster than a list-comprehension, if the time to convert a list to an array is included The overhead of importing numpy and converting a list to a numpy.array probably makes using numpy a less efficient option for most circumstances. A careful timing analysis would be necessary. In cases where multiple functions/operations will need to be performed on the list, converting the list to an array, and then using numpy functions will likely be a faster option. This solution uses np.where and np.unique to find the indices of all unique elements in a list. Using np.where on an array (including the time to convert the list to an array) is slightly slower than a list-comprehension on a list, for finding all indices of all unique elements. This has been tested on an 2M element list with 4 unique values, and the size of the list/array and number of unique elements will have an impact. Other solutions using numpy on an array can be found in Get a list of all indices of repeated elements in a numpy array Tested in [python 3.10.4, numpy 1.23.1] and [python 3.11.0, numpy 1.23.4]

import numpy as np
import random  # to create test list

# create sample list
random.seed(365)
l = [random.choice(['s1', 's2', 's3', 's4']) for _ in range(20)]

# convert the list to an array for use with these numpy methods
a = np.array(l)

# create a dict of each unique entry and the associated indices
idx = {v: np.where(a == v)[0].tolist() for v in np.unique(a)}

# print(idx)
{'s1': [7, 9, 10, 11, 17],
 's2': [1, 3, 6, 8, 14, 18, 19],
 's3': [0, 2, 13, 16],
 's4': [4, 5, 12, 15]}

%timeit在2M元素列表中,有4个唯一的str元素

# create 2M element list
random.seed(365)
l = [random.choice(['s1', 's2', 's3', 's4']) for _ in range(2000000)]

功能

def test1():
    # np.where: convert list to array and find indices of a single element
    a = np.array(l)
    return np.where(a == 's1')
    

def test2():
    # list-comprehension: on list l and find indices of a single element
    return [i for i, x in enumerate(l) if x == "s1"]


def test3():
    # filter: on list l and find indices of a single element
    return list(filter(lambda i: l[i]=="s1", range(len(l))))


def test4():
    # use np.where and np.unique to find indices of all unique elements: convert list to array
    a = np.array(l)
    return {v: np.where(a == v)[0].tolist() for v in np.unique(a)}


def test5():
    # list comprehension inside dict comprehension: on list l and find indices of all unique elements
    return {req_word: [idx for idx, word in enumerate(l) if word == req_word] for req_word in set(l)}

函数调用

%timeit test1()
%timeit test2()
%timeit test3()
%timeit test4()
%timeit test5()

python 3.10.4

214 ms ± 19.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
85.1 ms ± 1.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
146 ms ± 1.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
365 ms ± 11.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
360 ms ± 5.82 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

结果python 3.11.0

209 ms ± 15.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
70.4 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
132 ms ± 4.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
371 ms ± 20.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
314 ms ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)