【问题标题】:Python: Finding differences between elements of a listPython:查找列表元素之间的差异
【发布时间】:2011-01-24 22:38:12
【问题描述】:

给定一个数字列表,如何找出第 (i) 个元素与其第 (i+1) 个元素之间的差异?

使用lambda 表达式或列表推导式更好吗?

例如:

给定一个列表t=[1,3,6,...],目标是找到一个列表v=[2,3,...],因为3-1=26-3=3

【问题讨论】:

    标签: python list


    【解决方案1】:

    Python 3.10 开始,使用新的pairwise 函数可以在元素对之间滑动,从而在滚动对上进行映射:

    from itertools import pairwise
    
    [y-x for (x, y) in pairwise([1, 3, 6, 7])]
    # [2, 3, 1]
    

    中间结果是:

    pairwise([1, 3, 6, 7])
    # [(1, 3), (3, 6), (6, 7)]
    

    【讨论】:

    • 大声笑,多年来我的常用功能中都有类似的东西。我将我的实现称为“chunker”,但pairwise 的声音很好听。非常有用。
    【解决方案2】:

    您还可以使用

    将差异转换为易于阅读的转换矩阵
    v = t.reshape((c,r)).T - t.T
    

    其中c = 列表中的项目数,r = 1,因为列表基本上是一个向量或一维数组。

    【讨论】:

      【解决方案3】:

      好的。我想我找到了正确的解决方案:

      v = [x[0]-x[1] for x in zip(t[1:],t[:-1])]
      

      【讨论】:

      • 你很好,但我认为它应该是 v = [x[0]-x[1] for x in zip(t[1:], t[:-1])]对于排序列表!
      【解决方案4】:

      我怀疑这就是 numpy diff 命令所做的事情,但为了完整起见,您可以简单地区分子向量:

      from numpy import array as a
      a(x[1:])-a(x[:-1])
      

      此外,我想将这些解决方案添加到问题的概括中:

      具有周期性边界的解

      有时在数值积分中,您可能希望使用周期性边界条件来区分列表(因此第一个元素计算与最后一个元素的差异。在这种情况下,numpy.roll 函数很有帮助:

      v-np.roll(v,1)
      

      零前缀的解决方案

      另一个numpy 解决方案(只是为了完整性)是使用

      numpy.ediff1d(v)
      

      这与 numpy.diff 一样,但仅适用于向量(它会使输入数组变平)。它提供了向结果向量添加或附加数字的能力。这在处理通常是气象变量(例如雨、潜热等)中的通量的累积字段时很有用,因为您想要一个与输入变量长度相同的结果列表,而第一个条目保持不变。

      然后你会写

      np.ediff1d(v,to_begin=v[0])
      

      当然,您也可以使用 np.diff 命令执行此操作,但在这种情况下,您需要使用 prepend 关键字将零添加到系列中:

      np.diff(v,prepend=0.0) 
      

      上述所有解决方案都返回一个与输入长度相同的向量。

      【讨论】:

        【解决方案5】:

        我建议使用

        v = np.diff(t)
        

        这简单易读。

        但如果您希望v 的长度与t 相同,那么

        v = np.diff([t[0]] + t) # for python 3.x
        

        v = np.diff(t + [t[-1]])
        

        仅供参考:这仅适用于列表。

        对于 numpy 数组

        v = np.diff(np.append(t[0], t))
        

        【讨论】:

        • 不错的答案,您也可以使用 prepend 关键字来确保相同的长度,请参阅下面的答案,我认为这有点整洁
        【解决方案6】:

        使用 Python 3.8+ 中可用的 := walrus 运算符:

        >>> t = [1, 3, 6]
        >>> prev = t[0]; [-prev + (prev := x) for x in t[1:]]
        [2, 3]
        

        【讨论】:

          【解决方案7】:

          如果您不想使用numpyzip,可以使用以下解决方案:

          >>> t = [1, 3, 6]
          >>> v = [t[i+1]-t[i] for i in range(len(t)-1)]
          >>> v
          [2, 3]
          

          【讨论】:

            【解决方案8】:

            您可以使用itertools.teezip 来高效地构建结果:

            from itertools import tee
            # python2 only:
            #from itertools import izip as zip
            
            def differences(seq):
                iterable, copied = tee(seq)
                next(copied)
                for x, y in zip(iterable, copied):
                    yield y - x
            

            或者改用itertools.islice

            from itertools import islice
            
            def differences(seq):
                nexts = islice(seq, 1, None)
                for x, y in zip(seq, nexts):
                    yield y - x
            

            您也可以避免使用itertools 模块:

            def differences(seq):
                iterable = iter(seq)
                prev = next(iterable)
                for element in iterable:
                    yield element - prev
                    prev = element
            

            如果您不需要存储所有结果并支持无限迭代,所有这些解决方案都可以在恒定空间中工作。


            以下是解决方案的一些微基准:

            In [12]: L = range(10**6)
            
            In [13]: from collections import deque
            In [15]: %timeit deque(differences_tee(L), maxlen=0)
            10 loops, best of 3: 122 ms per loop
            
            In [16]: %timeit deque(differences_islice(L), maxlen=0)
            10 loops, best of 3: 127 ms per loop
            
            In [17]: %timeit deque(differences_no_it(L), maxlen=0)
            10 loops, best of 3: 89.9 ms per loop
            

            以及其他建议的解决方案:

            In [18]: %timeit [x[1] - x[0] for x in zip(L[1:], L)]
            10 loops, best of 3: 163 ms per loop
            
            In [19]: %timeit [L[i+1]-L[i] for i in range(len(L)-1)]
            1 loops, best of 3: 395 ms per loop
            
            In [20]: import numpy as np
            
            In [21]: %timeit np.diff(L)
            1 loops, best of 3: 479 ms per loop
            
            In [35]: %%timeit
                ...: res = []
                ...: for i in range(len(L) - 1):
                ...:     res.append(L[i+1] - L[i])
                ...: 
            1 loops, best of 3: 234 ms per loop
            

            注意:

            • zip(L[1:], L) 等价于 zip(L[1:], L[:-1]),因为 zip 已经在最短的输入处终止,但它避免了 L 的整个副本。
            • 通过索引访问单个元素非常很慢,因为每个索引访问都是 python 中的方法调用
            • numpy.diffslow 因为它必须首先将 list 转换为 ndarray。显然,如果您开始使用ndarray,它会快得多

              In [22]: arr = np.array(L)
              
              In [23]: %timeit np.diff(arr)
              100 loops, best of 3: 3.02 ms per loop
              

            【讨论】:

            • 在第二个解决方案中,islice(seq, 1, None) 而不是 islice(seq, 1, len(seq)) 使其适用于无限迭代
            【解决方案9】:

            函数式方法:

            >>> import operator
            >>> a = [1,3,5,7,11,13,17,21]
            >>> map(operator.sub, a[1:], a[:-1])
            [2, 2, 2, 4, 2, 4, 4]
            

            使用生成器:

            >>> import operator, itertools
            >>> g1,g2 = itertools.tee((x*x for x in xrange(5)),2)
            >>> list(itertools.imap(operator.sub, itertools.islice(g1,1,None), g2))
            [1, 3, 5, 7]
            

            使用索引:

            >>> [a[i+1]-a[i] for i in xrange(len(a)-1)]
            [2, 2, 2, 4, 2, 4, 4]
            

            【讨论】:

              【解决方案10】:

              我的方式

              >>>v = [1,2,3,4,5]
              >>>[v[i] - v[i-1] for i, value in enumerate(v[1:], 1)]
              [1, 1, 1, 1]
              

              【讨论】:

              【解决方案11】:

              其他答案是正确的,但如果您正在做数值工作,您可能需要考虑 numpy.使用numpy,答案是:

              v = numpy.diff(t)
              

              【讨论】:

              • 非常有帮助!谢谢! np.diff([2,4,9]) 将是 [2,5]
              • 这会比zip 版本更高效吗?
              【解决方案12】:
              >>> t
              [1, 3, 6]
              >>> [j-i for i, j in zip(t[:-1], t[1:])]  # or use itertools.izip in py2k
              [2, 3]
              

              【讨论】:

              • 如果您需要绝对差异,[abs(j-i) for i,j in zip(t, t[1:])]
              • 如果你想提高效率:list(itertools.starmap(operator.sub, zip(t[1:], t)))(在导入itertoolsoperator之后)。
              • 其实只要list(map(operator.sub, t[1:], t[:-1]))就行了。
              猜你喜欢
              • 1970-01-01
              • 2013-02-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-01-18
              • 2014-10-20
              • 1970-01-01
              相关资源
              最近更新 更多