SORU
6 ŞUBAT 2010, CUMARTESİ


Python: listeler listesinden çıkarma çoğaltır

Python listelerinin bir listesi var:

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

Ve bu öğeleri Kaldır yinelenen istiyorum. Eğer bu set kullanılan edebilirim listeler değil normal bir liste. Ama ne yazık ki liste hashable ve listeleri ayarlama yapamaz. Dizilerini sadece. Dizilerini tüm listeler çevirebilirim o zaman listeler için set ve tekrar kullanın. Ama bu kadar hızlı değil.

Bunu nasıl en verimli şekilde yapılmış olabilir?

Yukarıdaki liste sonucu olmalıdır:

k = [[5, 6, 2], [1, 2], [3], [4]]

Düzeni korumak umurumda değil.

Not: this question benzer ama tam ihtiyacım olan şey. Aradı ama birebir aynısı bulamadı.


Kıyaslama:

import itertools, time


class Timer(object):
    def __init__(self, name=None):
        self.name = name

    def __enter__(self):
        self.tstart = time.time()

    def __exit__(self, type, value, traceback):
        if self.name:
            print '[%s]' % self.name,
        print 'Elapsed: %s' % (time.time() - self.tstart)


k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] * 5
N = 100000

print len(k)

with Timer('set'):
    for i in xrange(N):
        kt = [tuple(i) for i in k]
        skt = set(kt)
        kk = [list(i) for i in skt]


with Timer('sort'):
    for i in xrange(N):
        ks = sorted(k)
        dedup = [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]


with Timer('groupby'):
    for i in xrange(N):
        k = sorted(k)
        dedup = list(k for k, _ in itertools.groupby(k))

with Timer('loop in'):
    for i in xrange(N):
        new_k = []
        for elem in k:
            if elem not in new_k:
                new_k.append(elem)
< . p ^"döngü" (ikinci dereceden yöntem) hızlı, tüm kısa listeler. Uzun listeler için daha hızlı groupby yöntemi dışında herkes. Bu mantıklı mı?

Kısa liste (kod) yineleme: için

[set] Elapsed: 1.3900001049
[sort] Elapsed: 0.891000032425
[groupby] Elapsed: 0.780999898911
[loop in] Elapsed: 0.578000068665

Uzun liste (kod 5 kez yinelenmiş):

[set] Elapsed: 3.68700003624
[sort] Elapsed: 3.43799996376
[groupby] Elapsed: 1.03099989891
[loop in] Elapsed: 1.85900020599

CEVAP
6 ŞUBAT 2010, CUMARTESİ


>>> k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
>>> import itertools
>>> k.sort()
>>> list(k for k,_ in itertools.groupby(k))
[[1, 2], [3], [4], [5, 6, 2]]

itertools genellikle bu tür problemler için en hızlı ve en güçlü çözümler sunariyiyakından tanıdık ile elde değer!-)

Editbir yorumda bahsettiğim gibi, normal iyileştirme çabaları çabaları iyi döner sunduğu çok daha kolay, çünkü bu büyük giriş (büyük-O yaklaşım) üzerinde duruldu. Ama bazen (aslında "trajik bir şekilde önemli sorunları" derin iç döngüler kod iterek sınırları performans Limitleri) bir tuvaletin içine çok daha fazla ayrıntı sağlayan olasılık dağılımları, karar verirken hangi performans ölçütlerini optimize etmek (belki de üst sınır veya 90 persantil değeri daha önemli daha ortalama veya medyan bağlı olarak bir apps), sahne muhtemelen-sezgisel kontroller de start almak için farklı algoritmalar bağlı olarak veri girişi özellikleri, ve benzeri.

"Performans (belirli bir giriş için vs kod kod B) son derece pahalı bu sürecin bir parçasıdır, ve standart kütüphane modülü timeit burada. "nokta dikkatli Ölçümler Ancak, daha kolay bir kabuk isteminde kullanın. Örneğin, burada kısa bir modülü bu sorun için genel bir yaklaşım sergilemek için, nodup.py olarak kaydedin:

import itertools

k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]

def doset(k, map=map, list=list, set=set, tuple=tuple):
  return map(list, set(map(tuple, k)))

def dosort(k, sorted=sorted, xrange=xrange, len=len):
  ks = sorted(k)
  return [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]

def dogroupby(k, sorted=sorted, groupby=itertools.groupby, list=list):
  ks = sorted(k)
  return [i for i, _ in itertools.groupby(ks)]

def donewk(k):
  newk = []
  for i in k:
    if i not in newk:
      newk.append(i)
  return newk

# sanity check that all functions compute the same result and don't alter k
if __name__ == '__main__':
  savek = list(k)
  for f in doset, dosort, dogroupby, donewk:
    resk = f(k)
    assert k == savek
    print 's %s' % (f.__name__, sorted(resk))

Akıl sağlığını kontrol (sadece 20 ** ne zaman yapılır) ve temel kaldırma tekniği (sabit genel adları her işlev için yerel hız için) eşit şeyler koymak unutmayın.

Şimdi küçük bir örnek listesi kontrol edebiliriz:

$ python -mtimeit -s'import nodup' 'nodup.doset(nodup.k)'
100000 loops, best of 3: 11.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort(nodup.k)'
100000 loops, best of 3: 9.68 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby(nodup.k)'
100000 loops, best of 3: 8.74 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.donewk(nodup.k)'
100000 loops, best of 3: 4.44 usec per loop

kuadratik yaklaşım küçük yeterli olduğunu doğrulayan birkaç yinelenen değerleri ile küçük listeler için çekici hale getirmek için sabitler. Yineleme olmadan kısa bir listesi

$ python -mtimeit -s'import nodup' 'nodup.donewk([[i] for i in range(12)])'
10000 loops, best of 3: 25.4 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby([[i] for i in range(12)])'
10000 loops, best of 3: 23.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.doset([[i] for i in range(12)])'
10000 loops, best of 3: 31.3 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort([[i] for i in range(12)])'
10000 loops, best of 3: 25 usec per loop

kuadratik yaklaşım kötü değil, ama sıralama ve groupby olanları iyidir. Vesaire, vesaire.

Eğer (gibi takıntısı performans gösteriyor) bu işlemi bir çekirdek iç döngü sizin bastırıyor--sınırları uygulama, denemeye değer aynı testleri diğer temsilci giriş örnekleri, muhtemelen tespit bazı basit önlem olabilir ayrıca izin almak, bir veya başka bir yaklaşım (ama ölçü olmalı hızlı, elbette).

Ayrıca k -- farklı bir temsili tutulması dikkate değer neden listeleri bir liste yerine ilk etapta dizilerini kümesi olmak zorunda mı? Eğer yinelenen kaldırma iştir sık ve profil gösterileri olacak programın performans düşüklüğü, tutmak bir küme dizilerini her zaman ve listesini almak listelerden sadece eğer ve nerede ihtiyaç, belki de daha hızlı genel olarak, örneğin.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • FrameCityJackal

    FrameCityJac

    4 Aralık 2010
  • AutoStream's Garage419

    AutoStream's

    15 EKİM 2007
  • JamesAtiPhone

    JamesAtiPhon

    16 EYLÜL 2010