การค้นหาความแตกต่างระหว่างองค์ประกอบของรายการ


113

เมื่อพิจารณาจากรายการตัวเลขแล้วเราจะพบความแตกต่างระหว่างiองค์ประกอบทุกๆ ( ) -th และ ( i+1) -th ได้อย่างไร

จะดีกว่าถ้าใช้lambdaนิพจน์หรืออาจจะเป็นการเข้าใจรายการ?

ตัวอย่างเช่น:

ได้รับรายการt=[1,3,6,...]เป้าหมายคือการหารายการv=[2,3,...]เพราะ3-1=2, 6-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)))(หลังจากนำเข้าitertoolsและoperator)
blhsing

3
จริงๆแล้วlist(map(operator.sub, t[1:], t[:-1]))จะทำ
blhsing

ยอดเยี่ยม! ฉันชอบคำตอบนี้มาก!
Chayim Friedman

104

คำตอบอื่น ๆ ถูกต้อง แต่ถ้าคุณกำลังทำงานเกี่ยวกับตัวเลขคุณอาจต้องการพิจารณาตัวเลข เมื่อใช้ 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.teeและzipสร้างผลลัพธ์ได้อย่างมีประสิทธิภาพ:

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คือช้าเพราะมีการแปลงแรกไป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))ทำให้มันใช้งานได้กับการวนซ้ำแบบไม่มีที่สิ้นสุด
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]])

FYI: สิ่งนี้ใช้ได้กับรายการเท่านั้น

สำหรับอาร์เรย์ numpy

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

คำตอบที่ดีคุณสามารถใช้คำหลักที่นำหน้าได้ด้วยเพื่อให้แน่ใจว่ามีความยาวเท่ากันโปรดดูคำตอบด้านล่างซึ่งฉันคิดว่าเป็นเพียงเล็กน้อย
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

ตกลง. ฉันคิดว่าฉันพบวิธีแก้ปัญหาที่เหมาะสมแล้ว:

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

2
ใช่ดี แต่ฉันคิดว่ามันควรจะเป็น v = [x [0] -x [1] สำหรับ x ใน zip (t [1:], t [: - 1])] สำหรับรายการที่จัดเรียง!
Amit Karnik

0

วิธีแก้ปัญหาที่มีขอบเขตเป็นระยะ

บางครั้งเมื่อใช้การรวมเชิงตัวเลขคุณจะต้องแตกต่างรายการที่มีเงื่อนไขขอบเขตเป็นระยะ (ดังนั้นองค์ประกอบแรกจะคำนวณความแตกต่างกับค่าสุดท้ายในกรณีนี้ฟังก์ชัน numpy.roll จะมีประโยชน์:

v-np.roll(v,1)

โซลูชันที่มีศูนย์นำหน้า

อีกวิธีหนึ่งที่น่าเบื่อ (เพื่อความสมบูรณ์) คือการใช้

numpy.ediff1d(v)

สิ่งนี้ทำงานเป็น numpy.diff แต่บนเวกเตอร์เท่านั้น (จะทำให้อาร์เรย์อินพุตแบนราบ) มีความสามารถในการเพิ่มหรือต่อท้ายตัวเลขกับเวกเตอร์ที่เป็นผลลัพธ์ สิ่งนี้มีประโยชน์เมื่อจัดการฟิลด์สะสมซึ่งมักจะเป็นกรณีฟลักซ์ในตัวแปรอุตุนิยมวิทยา (เช่นฝนความร้อนแฝง ฯลฯ ) เนื่องจากคุณต้องการให้รายการผลลัพธ์ที่มีความยาวเท่ากับตัวแปรอินพุตโดยที่รายการแรกไม่ถูกแตะต้อง

จากนั้นคุณจะเขียน

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

แน่นอนคุณสามารถทำได้ด้วยคำสั่ง np.diff ในกรณีนี้แม้ว่าคุณจะต้องนำหน้าศูนย์ไปยังชุดข้อมูลด้วยคำหลักที่นำหน้า:

np.diff(v,prepend=0.0) 

วิธีแก้ปัญหาทั้งหมดข้างต้นส่งคืนเวกเตอร์ที่มีความยาวเท่ากับอินพุต


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.