【问题标题】:How to find all occurrences of an element in a list如何在列表中查找所有出现的元素
【发布时间】:2011-09-11 18:12:01
【问题描述】:

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

【问题讨论】:

    标签: python list numpy list-comprehension indices


    【解决方案1】:

    您可以使用列表推导:

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

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

    【讨论】:

      【解决方案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])
      

      对于具有大量元素的列表(数组),这比其他一些解决方案要快得多。

      【讨论】:

      • 这里,values 可以是 NumPy 数组或 Python 列表。
      【解决方案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 个元素的整数列表上测试)。 p>

      注意:基于 Chris_Rands 的评论的注意事项:如果结果足够稀疏,但如果列表中有许多正在被处理的元素的实例,则此解决方案比列表推导式更快搜索(超过约 15% 的列表,在包含 1000 个整数的列表的测试中),列表理解更快。

      【讨论】:

      • 你说这比 list comp 快,你能展示你的时间来证明这一点吗?
      • 这是很久以前的事了,我可能用timeit.timeit 和随机生成的列表。不过,这很重要,我想这可能就是您问的原因。当时我没有想到,但只有在结果足够稀疏的情况下,速度提升才是正确的。我刚刚测试了一个包含要搜索的元素的列表,它比列表理解要慢得多。
      【解决方案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】:
          occurrences = lambda s, lst: (i for i,e in enumerate(lst) if e == s)
          list(occurrences(1, [1,2,3,1])) # = [0, 3]
          

          【讨论】:

            【解决方案7】:

            或者使用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)
            

            符合预期。

            【讨论】:

              【解决方案8】:
              • 有一个answer 使用np.where 来查找单个值的索引,如果包括将列表转换为数组的时间,这并不比列表理解快
              • 导入numpy 并将list 转换为numpy.array 的开销可能使使用numpy 在大多数情况下效率较低。有必要进行仔细的时序分析。
                • 但是,如果需要在list 上执行多个功能/操作,将list 转换为array,然后使用numpy 功能可能是一个更快的选择。李>
              • 此解决方案使用np.wherenp.unique 在列表中查找所有唯一元素 的索引。
                • 在数组上使用np.where(包括将列表转换为数组的时间)比在列表上的列表理解略快,用于查找所有唯一元素的所有索引
                • 这已在具有 4 个唯一值的 2M 元素列表上进行了测试,列表/数组的大小和唯一元素的数量会产生影响。
              • 在阵列上使用numpy 的其他解决方案可以在Get a list of all indices of repeated elements in a numpy array 中找到
              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

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

              找到一个值的索引

              • 在包含 4 个唯一元素的 2M 元素列表中查找单个元素的索引
              # np.where: convert list to array
              %%timeit
              a = np.array(l)
              np.where(a == 's1')
              [out]:
              409 ms ± 41.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
              
              # list-comprehension: on list l
              %timeit [i for i, x in enumerate(l) if x == "s1"]
              [out]:
              201 ms ± 24 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
              
              # filter: on list l
              %timeit list(filter(lambda i: l[i]=="s1", range(len(l))))
              [out]:
              344 ms ± 36.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
              

              查找所有值的索引

              • 在包含 4 个唯一元素的 2M 元素列表中查找所有唯一元素的索引
              # use np.where and np.unique: convert list to array
              %%timeit
              a = np.array(l)
              {v: np.where(a == v)[0].tolist() for v in np.unique(a)}
              [out]:
              682 ms ± 28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
              
              # list comprehension inside dict comprehension: on list l
              %timeit {req_word: [idx for idx, word in enumerate(l) if word == req_word] for req_word in set(l)}
              [out]:
              713 ms ± 16.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
              

              【讨论】:

                【解决方案9】:

                所有出现的另一种解决方案(对不起,如果重复):

                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)
                

                【讨论】:

                  【解决方案10】:

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

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

                  >>> 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
                  

                  【讨论】:

                    【解决方案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】:

                      使用for-loop:

                      • enumeratelist comprehension 的答案更符合pythonic,不一定更快。但是,此答案针对可能不允许使用其中一些 built-in functions 的学生。
                      • 创建一个空列表,indices
                      • 使用for i in range(len(x)): 创建循环,它本质上是遍历索引位置列表[0, 1, 2, 3, ..., len(x)-1]
                      • 在循环中,添加任何i,其中x[i]value 匹配,添加到indices
                      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 函数通过type hints 实现。在这种情况下,列表n 是一堆ints,因此我们搜索value,也定义为int

                      使用while-loop.index

                      • 对于.index,将try-except 用于error handling,因为如果value 不在list 中,则会出现ValueError
                      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]
                      

                      【讨论】:

                      • 您的自定义get_indeices 比正常的列表理解要快一点(~15%)。我正在努力弄清楚。
                      【解决方案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】:

                        如果您使用的是 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)
                        

                        【讨论】:

                          【解决方案15】:

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

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

                          【讨论】:

                            【解决方案16】:

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

                            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() 您只能找到一个索引,尽管您可以自己设置出现次数。

                            【讨论】:

                              【解决方案17】:

                              这是使用np.wherelist_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
                              

                              【讨论】:

                                猜你喜欢
                                • 2012-11-08
                                • 1970-01-01
                                • 2017-10-18
                                • 1970-01-01
                                • 1970-01-01
                                • 2014-07-09
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多