【问题标题】:Indices of matching elements given two lists of which one has redundant entries给定两个列表的匹配元素索引,其中一个列表具有冗余条目
【发布时间】:2015-07-13 17:41:59
【问题描述】:

我有两个列表,aba 包含我想知道 b 中匹配元素索引的元素。在b 中,每个元素都是唯一的,这与a 不同。

a = [1993, 1993, 1994, 1995, 1996, 1996, 1998, 2003, 2005, 2005]
b = [1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014]

使用来自Finding the indices of matching elements in list in Python的解决方案:

matching = [match for match, element in enumerate(b) if element in a]

matching 不过只是[27, 28, 29, 30, 32, 37, 39],但我希望它是[27, 27, 28, 29, 30, 30, 32, 37, 39, 39]

【问题讨论】:

    标签: python list indexing


    【解决方案1】:
    >>> a = [1993, 1993, 1994, 1995, 1996, 1996, 1998, 2003, 2005, 2005]
    >>> b = [1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014]
    >>> [b.index(x) for x in a]
    [27, 27, 28, 29, 30, 30, 32, 37, 39, 39]
    

    【讨论】:

    • 在这种情况下有效,但如果 a 中的任何值不在 b 中,则会引发 ValueError。 [b.index(x) for x in a if b.count(x)] 会更安全...
    • 当然,这完全取决于您是否希望它失败。
    【解决方案2】:

    怎么样

    print [b.index(i) for i in a if i in b]
    

    【讨论】:

      【解决方案3】:

      这是对 Padraic Cunningham 的 suggestion to use sets 的扩展。相反,如果将要索引的列表转换为字典,则可以实现 O(1) 查找,用于 O(n) 预处理:

      a = [1993, 1993, 1994, 1995, 1996, 1996, 1998, 2003, 2005, 2005]
      b = [1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014]
      
      d = {value: index for index, value in enumerate(b)}
      print([d[x] for x in a])
      
      
      
      >>> timeit("[bisect_left(b, x) for x in a]", "from __main__ import a, b; from bisect import bisect_left")
      3.513558427607279
      >>> timeit("[b.index(x) for x in a]", "from __main__ import a, b")
      8.010070997323822
      >>> timeit("d = {value: index for index, value in enumerate(b)}; [d[x] for x in a]", "from __main__ import a, b")
      5.5277420695707065
      >>> timeit("[d[x] for x in a]", "from __main__ import a, b, ;d = {value : index for index, value in enumerate(b)}")
      1.1214096146165389
      

      因此,如果您不考虑预处理,那么在实际处理中您的速度几乎是使用 b.index 的 8 倍 - 如果您使用大量列表 a 而不是更少的 b,这会更好s。如果你只做一次,使用bisect_left 会更快,并且可以保证b 是单调递增的。

      【讨论】:

        【解决方案4】:

        如果你有很大的列表,将 b 设置为集合会更有效:

        st = set(b)
        print([b.index(x) for x in a if x in st])
        

        由于您的数据已排序并假设 a 中的所有元素都在 b 中,您还可以使用 bisect,因此每个索引查找都是 O(log n):

        a = [1993, 1993, 1994, 1995, 1996, 1996, 1998, 2003, 2005, 2005]
        b = [1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014]
        
        
        from bisect import bisect_left
        print [bisect_left(b, x) for x in a]
        [27, 27, 28, 29, 30, 30, 32, 37, 39, 39]
        

        在小型数据集上,它的运行速度是索引的两倍:

        In [22]: timeit [bisect_left(b, x) for x in a]
        100000 loops, best of 3: 4.2 µs per loop
        
        In [23]: timeit [b.index(x) for x in a]
        100000 loops, best of 3: 8.84 µs per loop
        

        另一种选择是使用 dict 来存储索引,这意味着代码将在线性时间内运行,一次通过 a,一次通过 b:

        # store all indexes as values and years as keys
        indexes = {k: i for i, k in enumerate(b)}
        # one pass over a accessing each index in constant time
        print [indexes[x] for x in a]
        [27, 27, 28, 29, 30, 30, 32, 37, 39, 39]
        

        即使在小输入集上也比索引更有效,并且随着 a 的增长会更有效:

        In [34]: %%timeit                                                            
        indexes = {k: i for i, k in enumerate(b)}
        [indexes[x] for x in a]
           ....: 
        100000 loops, best of 3: 7.54 µs per loop
        
        In [39]: b = list(range(1966,2100))
        In [40]: samp = list(range(1966,2100))
        In [41]: a = [choice(samp) for _ in range(100)]
        
        In [42]: timeit [b.index(x) for x in a 
        10000 loops, best of 3: 154 µs per loop   
        In [43]: %%timeit                      
        indexes = {k: i for i, k in enumerate(b)}
        [indexes[x] for x in a]
           ....: 
        10000 loops, best of 3: 22.5 µs per loop
        

        【讨论】:

        • 现在您只需查找两次,一次在一组中,一次在列表中。只有当它不在列表中时,这才是一种改进。
        • @LennartRegebro。因此,对于许多未出现在 b 中的值,0(1) 查找比线性查找要慢吗?您认为如果 b 中有 1000000 个元素,使用列表会更好吗?
        • 不,但如果它在列表中,OP 的问题假定,那么你根本不需要 if 测试。如果它大部分都在列表中,那么捕获错误会更好。
        • @LennartRegebro。好吧,我添加了一个比索引更有效的 bisect 方法
        • 是的,如果我们知道 b 是有序的,那会加快速度。
        猜你喜欢
        • 2012-09-21
        • 2021-05-19
        • 2018-02-25
        • 1970-01-01
        • 1970-01-01
        • 2016-10-14
        • 2018-08-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多