如何查找列表中某个元素的所有出现位置

2024-11-22 08:47:00
admin
原创
192
摘要:问题描述:index()将返回列表中某项的第一次出现。是否有一个巧妙的技巧可以返回列表中某个元素的所有索引?解决方案 1:您可以使用列表推导式enumerate:indices = [i for i, x in enumerate(my_list) if x == "whatever"] ...

问题描述:

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


解决方案 1:

您可以使用列表推导式enumerate

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

迭代器为列表中的每个项目enumerate(my_list)生成对。使用循环变量目标将这些对解包到索引和列表项中。我们筛选出所有符合我们标准的项,然后选择这些元素的索引。(index, item)`i, xixxi`

解决方案 2:

虽然这不是一个直接解决列表问题的方法,但numpy对于这类事情确实很有用:

import numpy as np
values = np.array([1,2,3,1,2,4,5,6,3,2,1])
searchval = 3
ii = np.where(values == searchval)[0]

返回:

ii ==>array([2, 8])

与其他一些解决方案相比,这对于具有大量元素的列表(数组)来说可以更快。

解决方案 3:

使用以下解决方案list.index

def indices(lst, element):
    result = []
    offset = -1
    while True:
        try:
            offset = lst.index(element, offset+1)
        except ValueError:
            return result
        result.append(offset)

对于大型列表,它比使用 的列表理解要快得多enumerate如果numpy您已经有数组,它也比解决方案慢得多,否则转换的成本超过了速度增益(在具有 100、1000 和 10000 个元素的整数列表上进行了测试)。

注意:根据 Chris_Rands 的评论,需要注意的是:如果结果足够稀疏,则此解决方案比列表理解更快,但如果列表中有许多被搜索元素的实例(超过列表的~15%,在使用 1000 个整数列表的测试中),则列表理解更快。

解决方案 4:

怎么样:

In [1]: l=[1,2,3,4,3,2,5,6,7]

In [2]: [i for i,val in enumerate(l) if val==3]
Out[2]: [2, 4]

解决方案 5:

more_itertools.locate查找满足条件的所有项目的索引。

from more_itertools import locate


list(locate([0, 1, 1, 0, 1, 0, 0]))
# [1, 2, 4]

list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b'))
# [1, 3]

more_itertools是一个第三方库> pip install more_itertools

解决方案 6:

def occurrences(s, lst):
    return (i for i,e in enumerate(lst) if e == s)

list(occurrences(1, [1,2,3,1])) # = [0, 3]

解决方案 7:

  • 这个答案是while-loop经过测试的最快的实现。

    • 它比下面接受的答案快26% test2()

  • 有一个答案用于np.where查找单个值的索引,如果将列表转换为数组的时间包括在内,它并不比列表理解更快

  • numpy导入并将 a 转换list为 a的开销numpy.array可能使numpy在大多数情况下使用效率较低的选项。需要进行仔细的时间分析。

    • 如果需要对 执行多个函数/操作list,则将 转换listarray,然后使用numpy函数可能会成为更快的选择。

  • 该解决方案使用np.where和来查找列表中
    所有唯一元素np.unique的索引。

    • 在数组上使用np.where(包括将列表转换为数组的时间)比在列表上使用列表理解稍慢,以查找所有唯一元素的所有索引

    • 这已在具有 4 个唯一值的 2M 元素列表上进行了测试,并且列表/数组的大小和唯一元素的数量会产生影响。

  • 在数组上使用的其他解决方案可以在获取 numpy 数组中重复元素的所有索引的列表numpy中找到

  • 经过[python 3.10.4, numpy 1.23.1]测试[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`str`在包含 4 个唯一元素的 2M 元素列表中

# 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)}

def get_indices1(x: list, value: int) -> list:
    indices = list()
    for i in range(len(x)):
        if x[i] == value:
            indices.append(i)
    return indices

def get_indices2(x: list, value: int) -> list:
    indices = list()
    i = 0
    while True:
        try:
            # find an occurrence of value and update i to that index
            i = x.index(value, i)
            # add i to the list
            indices.append(i)
            # advance i by 1
            i += 1
        except ValueError as e:
            break
    return indices

函数调用

%timeit test1()  # list of indices for specified value
%timeit test2()  # list of indices for specified value
%timeit test3()  # list of indices for specified value
%timeit test4()  # dict of indices of all values
%timeit test5()  # dict of indices of all values
%timeit get_indices1(l, 's1')  # list of indices for specified value
%timeit get_indices2(l, 's1')  # list of indices for specified value

结果python 3.12.0

209 ms ± 2.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
78.5 ms ± 733 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
125 ms ± 757 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
340 ms ± 8.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
319 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
74.9 ms ± 1.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
58.2 ms ± 1.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

解决方案 8:

或者使用range(python 3):

l=[i for i in range(len(lst)) if lst[i]=='something...']

对于(python 2):

l=[i for i in xrange(len(lst)) if lst[i]=='something...']

然后(两种情况):

print(l)

和预想的一样。

解决方案 9:

获取列表中一个或多个(相同)项目的所有出现位置

使用 enumerate(alist),当元素 x 等于您要查找的元素时,您可以存储作为列表索引的第一个元素 (n)。

>>> alist = ['foo', 'spam', 'egg', 'foo']
>>> foo_indexes = [n for n,x in enumerate(alist) if x=='foo']
>>> foo_indexes
[0, 3]
>>>

让我们来创建 findindex 函数

此函数以项目和列表作为参数,并返回项目在列表中的位置,就像我们之前看到的一样。

def indexlist(item2find, list_or_string):
  "Returns all indexes of an item in a list or a string"
  return [n for n,item in enumerate(list_or_string) if item==item2find]

print(indexlist("1", "010101010"))

输出


[1, 3, 5, 7]

简单的

for n, i in enumerate([1, 2, 3, 4, 1]):
    if i == 1:
        print(n)

输出:

0
4

解决方案 10:

针对所有情况还有一个解决方案(如果有重复请见谅):

values = [1,2,3,1,2,4,5,6,3,2,1]
map(lambda val: (val, [i for i in xrange(len(values)) if values[i] == val]), values)

解决方案 11:

在 python2 中使用 filter()。

>>> q = ['Yeehaw', 'Yeehaw', 'Googol', 'B9', 'Googol', 'NSM', 'B9', 'NSM', 'Dont Ask', 'Googol']
>>> filter(lambda i: q[i]=="Googol", range(len(q)))
[2, 4, 9]

解决方案 12:

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

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

解决方案 13:

您可以创建一个默认字典

from collections import defaultdict
d1 = defaultdict(int)      # defaults to 0 values for keys
unq = set(lst1)              # lst1 = [1, 2, 2, 3, 4, 1, 2, 7]
for each in unq:
      d1[each] = lst1.count(each)
else:
      print(d1)

解决方案 14:

如果我们事先不知道哪个元素,则基于动态列表理解的解决方案:

lst = ['to', 'be', 'or', 'not', 'to', 'be']
{req_word: [idx for idx, word in enumerate(lst) if word == req_word] for req_word in set(lst)}

结果:

{'be': [1, 5], 'or': [2], 'to': [0, 4], 'not': [3]}

您也可以按照相同的思路考虑所有其他方法,但是index()尽管您可以自己设置出现次数,但您只能找到一个索引。

解决方案 15:

使用for-loop

  • enumerate带有列表推导的答案更符合 Python 风格,但速度不一定更快。不过,这个答案针对的是那些可能不允许使用某些内置函数的学生。

  • 创建一个空列表,indices

  • 使用 创建循环for i in range(len(x)):,本质上是遍历索引位置列表[0, 1, 2, 3, ..., len(x)-1]

  • 在循环中,添加任何ix[i]匹配valueindices

    • x[i] 通过索引访问列表

def get_indices(x: list, value: int) -> list:
    indices = list()
    for i in range(len(x)):
        if x[i] == value:
            indices.append(i)
    return indices

n = [1, 2, 3, -50, -60, 0, 6, 9, -60, -60]
print(get_indices(n, -60))

>>> [4, 8, 9]
  • 函数get_indices是用类型提示实现的。在本例中,列表n是一堆ints,因此我们搜索value,也定义为int


使用while-loop.index

  • 这是该答案中测试过的最快的实现。

    • 它比接受的答案快 26% 。

  • 使用进行错误处理,因为如果不在 中,.index就会发生。try-except`ValueErrorvaluelist`

def get_indices(x: list, value: int) -> list:
    indices = list()
    i = 0
    while True:
        try:
            # find an occurrence of value and update i to that index
            i = x.index(value, i)
            # add i to the list
            indices.append(i)
            # advance i by 1
            i += 1
        except ValueError as e:
            break
    return indices

print(get_indices(n, -60))
>>> [4, 8, 9]

解决方案 16:

如果您使用的是 Python 2,您可以使用以下命令实现相同的功能:

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

my_list您要获取其索引的列表在哪里,以及value搜索的值在哪里。用法:

f(some_list, some_element)

解决方案 17:

创建一个生成器

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

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(indices(input,value));将它们用作字典键:dict(indices(input,value));对它们求和:sum(indices(input,value));在 for 循环中for index_ in indices(input,value):;...等等……而无需创建临时列表/元组或类似内容。

在 for 循环中,当您调用时,您将获得下一个索引,而无需等待先计算所有其他索引。这意味着:如果您出于某种原因退出循环,您将节省查找您不需要的索引所需的时间。

工作原理

  • 调用.index输入iter来查找下一个出现的
    val

  • 使用第二个参数从最后一次发现的事件之后的.index位置开始

  • 收益率指数

  • 重复直到index提高ValueError

其他版本

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

  1. “WhileTrueBreak”:while True:... except ValueError: break。令人惊讶的是,这通常比选项 2 慢一点,而且(IMV)可读性较差

  2. “WhileErrFalse”:使用布尔变量err来标识何时ValueError引发。这通常是最快且比 1更易读的

  3. “RemainingSlice”:使用切片检查 val 是否位于输入的剩余部分:while val in iter[i:]。不出所料,这不能很好地扩展

  4. “LastOccurrence”:首先检查最后一次出现的位置,然后继续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}

解决方案 18:

这是一个非常古老的问题,但我没有在答案中看到这个问题,尽管它被暗示了。

使用可接受的答案enumerate但带有in选项,允许对列表进行字符串搜索,并且如果您选择的话,还可以获得部分命中和不区分大小写的额外好处。

提供索引位置或找到的字符串本身。

给出一个 ISO 国家名称列表,下面给出了其用法的概念

>>> query = "Tre".casefold()
>>> 
>>> print([i for i, x in enumerate(country_list) if query in x.casefold()])
[280, 303, 352, 489]
>>> 
>>> print([x for i, x in enumerate(country_list) if query in x.casefold()])
['Eritrea', 'Spain Extremadura', 'France Centre-Val de Loire', 'Italy Trentino-South Tyrol']
>>> 
>>> country_list[280]
'Eritrea'
>>> 
>>> country_list[489]
'Italy Trentino-South Tyrol'

>>> query = "out".casefold()
>>> print([i for i, x in enumerate(country_list) if query in x.casefold()])
[48, 54, 253, 421, 489, 651, 1075, 1099, 1147, 1240, 1242, 1248, 1306]
>>> print([x for i, x in enumerate(country_list) if query in x.casefold()])
['Australia New South Wales', 'Australia South Australia', 'Djibouti', 'South Georgia and Sandwich Islands', 'Italy Trentino-South Tyrol', 'South Korea', 'South Sudan', 'French Southern Territories', 'United States Outlying Islands', 'United States of America South Carolina', 'United States of America South Dakota', 'United States of America Outlying Islands', 'South Africa']

这是个简单的不区分大小写的部分字符串查找器。

我怀疑它在大规模搜索中效率极低,但对于小规模搜索来说,它很简单、很干净。

解决方案 19:

如果您需要对列表执行多次查找,则编写一个包含每个不同元素的所有位置的字典可能更有效。此时,任何单次查找都是一个常量时间操作。

为此,我们可以:

  • 将索引映射到每个元素上。enumerate可以很容易地提供这一点。

  • 根据元素排序,忽略索引。首先进行排序很重要,这样我们才能...

  • 用于itertools.groupby将那些匹配的元素组合在一起。

  • 遍历该groupby对象来构建字典。

from itertools import groupby
from operator import itemgetter

def build_table(lst):
    snd = itemgetter(1)
    e = enumerate(lst)
    s = sorted(e, key=snd)
    g = groupby(s, key=snd)
    return {
        k: [x[0] for x in v]
        for k, v in g
    }

解决方案 20:

np.where以下是使用与 的时间性能比较list_comprehension。 平均而言似乎np.where更快。

# np.where
start_times = []
end_times = []
for i in range(10000):
    start = time.time()
    start_times.append(start)
    temp_list = np.array([1,2,3,3,5])
    ixs = np.where(temp_list==3)[0].tolist()
    end = time.time()
    end_times.append(end)
print("Took on average {} seconds".format(
    np.mean(end_times)-np.mean(start_times)))
Took on average 3.81469726562e-06 seconds
# list_comprehension
start_times = []
end_times = []
for i in range(10000):
    start = time.time()
    start_times.append(start)
    temp_list = np.array([1,2,3,3,5])
    ixs = [i for i in range(len(temp_list)) if temp_list[i]==3]
    end = time.time()
    end_times.append(end)
print("Took on average {} seconds".format(
    np.mean(end_times)-np.mean(start_times)))
Took on average 4.05311584473e-06 seconds

解决方案 21:

我发现最简单、最快的方法是重新创建一个可以循环的字典,即

l_full_list_w_duplicates=[...]
l_full_list_wo_duplicates=list(set(l_full_list_w_duplicates))

dict_location={}
for i in l_full_list_wo_duplicates:
    dict_location[i]=[]

i_counter=0
for iin l_full_list_w_duplicates:
    dict_location[i]+=[i_counter]
    i_counter+=1

它以线性时间运行,并且可扩展性强

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用