リストの要素間の違いを見つける


113

数値のリストが与えられた場合、すべての(i)番目の要素とその(i+1)番目の要素の違いをどのように見つけますか?

lambda式を使用した方がいいですか、それともリスト内包を使用した方がいいですか?

例えば:

リストを考えるとt=[1,3,6,...]、目標は、リストを見つけることですv=[2,3,...]ので3-1=26-3=3など

回答:


154
>>> t
[1, 3, 6]
>>> [j-i for i, j in zip(t[:-1], t[1:])]  # or use itertools.izip in py2k
[2, 3]

14
絶対的な違いが必要な場合 [abs(j-i) for i,j in zip(t, t[1:])]
Anil

より効率的にしたい場合:(およびのlist(itertools.starmap(operator.sub, zip(t[1:], t)))インポート後)。itertoolsoperator
3

3
実際には単にlist(map(operator.sub, t[1:], t[:-1]))行います。
18

鮮やかさ!私はこの答えが大好きです!
Chayim Friedman、

104

他の答えは正しいですが、数値計算を行っている場合は、numpyを検討することをお勧めします。numpyを使用すると、答えは次のとおりです:

v = numpy.diff(t)

非常に役立ちます!ありがとう!np.diff([2,4,9])次のようになります[2,5]
TravelTrader

これはzipバージョンよりも効率的ですか?
user760900

35

numpyまたはを使用したくない場合zipは、次のソリューションを使用できます。

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

12

を使用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.diffある遅いそれは最初に変換する必要があるためlistndarray。あなたがあれば明らかに起動してndarray、それは次のようになりますずっと速いです。

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

2番目のソリューションでは、無限のイテラブルで動作させるislice(seq, 1, None)代わりにislice(seq, 1, len(seq))
Braham Snyder


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キーワードを使用することもできます。以下の答えを参照してください。これは少しすっきりしていると思います
Adrian Tompkins

4

機能的アプローチ:

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

演算子の方法は素晴らしくエレガントです
bcattle

3

OK。私は適切な解決策を見つけたと思います:

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

2
いいですが、ソートされたリストの場合、v = [x [0] -x [1] for x for zip(t [1:]、t [:-1])]であったはずです!
Amit Karnik 2016年

0

周期的な境界を持つソリューション

数値積分では、リストを周期的な境界条件で差分したい場合があります(最初の要素が最後の要素との差分を計算するためです。この場合、numpy.roll関数が役立ちます:

v-np.roll(v,1)

先頭がゼロのソリューション

別の面倒な解決策(完全を期すため)を使用することです

numpy.ediff1d(v)

これはnumpy.diffとして機能しますが、ベクター上でのみ機能します(入力配列をフラット化します)。結果のベクトルに番号を付加または追加する機能を提供します。これは、気象変数(雨、潜熱など)のフラックスであることが多い累積フィールドを処理する場合に役立ちます。最初のエントリは変更せずに、入力変数と同じ長さの結果のリストが必要になるためです。

それからあなたは書くでしょう

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

もちろん、np.diffコマンドを使用してこれを行うこともできますが、この場合は、シリーズの先頭にprependキーワードを付けてゼロを追加する必要があります。

np.diff(v,prepend=0.0) 

上記のすべてのソリューションは、入力と同じ長さのベクトルを返します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.