Python: n listeden oluşan bir liste oluşturmanın en hızlı yolu


98

Bu yüzden boş listelerin en iyi nasıl oluşturulacağını merak ediyordum:

[[],[],[]...]

Python'un bellekteki listelerle çalışma şekli nedeniyle bu çalışmaz:

[[]]*n

Bu yaratır, [[],[],...]ancak her öğe aynı listedir:

d = [[]]*n
d[0].append(1)
#[[1],[1],...]

Liste anlama gibi bir şey işe yarar:

d = [[] for x in xrange(0,n)]

Ancak bu, döngü için Python sanal makinesini kullanır. Örtülü bir döngü kullanmanın herhangi bir yolu var mı (C ile yazılmasından yararlanarak)?

d = []
map(lambda n: d.append([]),xrange(0,10))

Bu aslında daha yavaştır. :(


1
Bundan önemli ölçüde daha hızlı bir şey olursa şaşırırdım d = [[] for x in xrange(0,n)]. Ya Python'da açıkça döngü yapmalısınız ya da bir Python işlevi / lambda'yı tekrar tekrar çağırmalısınız (bu daha yavaş olmalıdır). Ama yine de birinin yanıldığımı gösteren bir şey göndereceğini umuyorum :).
MAK

1
Bunları ölçtüğünüzde timeitne öğrendiniz?
S.Lott

Bunun map(lambda x: [], xrange(n))bir liste anlayışından daha yavaş olduğunu doğruladım .
Andrew Clark

Yanıtlar:


105

Muhtemelen marjinal olarak daha hızlı olan tek yol

d = [[] for x in xrange(n)]

dır-dir

from itertools import repeat
d = [[] for i in repeat(None, n)]

intHer yinelemede yeni bir nesne oluşturması gerekmez ve makinemde yaklaşık% 15 daha hızlıdır.

Düzenleme : NumPy kullanarak, Python döngüsünden kaçınabilirsiniz.

d = numpy.empty((n, 0)).tolist()

ama bu aslında liste anlayışından 2,5 kat daha yavaştır.


Nasıl olur map(lambda x:[], repeat(None,n))?
PaulMcG

3
@Paul: Lambda ifadesinin işlev çağrısı ek yükü nedeniyle bu yine çok daha yavaş olurdu.
Sven Marnach

Python 3'te aralık farklı olduğu için bunun güncellenmesi gerekmez mi?
beruic

@beruic Sadece ilk kod bloğundaki sorudan kodu alıntılıyorum, bu yüzden bunu değiştirmek gerçekten mantıklı değil.
Sven Marnach

12

Liste anlamaları aslında açık döngüden daha verimli bir şekilde uygulanır ( örneğin fonksiyonlar için disçıktıya bakın ) ve mapyol, her yinelemede önemli bir ek yüke neden olan bir ofaque çağrılabilir nesneyi çağırmak zorundadır.

Ne olursa olsun, [[] for _dummy in xrange(n)]bunu yapmanın doğru yolu budur ve diğer çeşitli yollar arasındaki küçük hız farklarının hiçbiri (eğer varsa) önemli olmamalıdır . Tabii ki, zamanınızın çoğunu bunu yapmaya harcamadığınız sürece - ancak bu durumda, bunun yerine algoritmalarınız üzerinde çalışmalısınız. Bu listeleri ne sıklıkla oluşturuyorsunuz?


5
Lütfen, _değişken adı olarak hayır ! Aksi takdirde güzel cevap :)
Sven Marnach

11
@Sven: Neden olmasın? Yaygın olarak kullanılmayan değişkenler için kullanılır (eğer çağrılmış olsaydı i, ben birinin nerede kullanıldığını arıyordum). Tek tuzak _, REPL'deki son sonucu elde tutmayı gölgelemesidir ... ve bu sadece 2.x'te liste anlayışlarının sızdığı durumdur.

Çok sık değil, bu yüzden devam ettim ve bir liste anlama kullandım. İnsanların söyleyeceklerini görmenin ilginç olacağını düşündüm. PyCon'da Dropbox konuşmasını, bir md5 karmasını saçma bir şekilde güncellemek için for döngüsü yerine itertools.imap kullanımıyla gördüm ve o zamandan beri C döngülerine biraz takıntılıyım.
munchybunch

12
Kullanmamanın en önemli nedeni, insanların kafasını karıştırması ve onlara bunun bir tür özel sözdizimi olduğunu düşündürmesidir. _Etkileşimli yorumlayıcıdaki çatışmaya ek olarak , aynı zamanda genel gettext takma adıyla da çelişir. Değişkenin sahte bir değişken olduğunu netleştirmek istiyorsanız, onu çağırın dummy, değil _.
Sven Marnach

12

Burada biri tatlı ve basit (ve kavramsal), diğeri daha resmi olan ve bir veri setini okuduktan sonra çeşitli durumlarda genişletilebilen iki yöntem vardır.

Yöntem 1: Kavramsal

X2=[]
X1=[1,2,3]
X2.append(X1)
X3=[4,5,6]
X2.append(X3)
X2 thus has [[1,2,3],[4,5,6]] ie a list of lists. 

Yöntem 2: Resmi ve genişletilebilir

Bir listeyi, bir dosyadan okuduğu farklı sayılardan oluşan bir liste olarak saklamanın başka bir zarif yolu. (Buradaki dosya, veri kümesi dizisine sahiptir) Tren, 50 satır ve 20 sütun içeren bir veri kümesidir. yani. [0] treni bana bir csv dosyasının 1. satırını verir, tren [1] bana 2. satırı verir ve bu böyle devam eder. 50 satırlık veri kümesini tek bir liste olarak ayırmakla ilgileniyorum, burada açıklanan değişkenim olan sütun 0 hariç, bu nedenle orijinal tren veri kümesinden kaldırılmalı ve ardından listeden sonra listeyi ölçeklendirmeli - yani bir liste listesi . İşte bunu yapan kod.

Yalnızca açıklayıcı değişkenlerle ilgilendiğim için iç döngüde "1" den okuduğuma dikkat edin. Ve diğer döngüde X1 = [] 'i yeniden başlatıyorum, aksi takdirde X2.append ([0: (len (tren [0]) - 1)]) X1'i defalarca yeniden yazacak - ayrıca bellek açısından daha verimli.

X2=[]
for j in range(0,len(train)):
    X1=[]
    for k in range(1,len(train[0])):
        txt2=train[j][k]
        X1.append(txt2)
    X2.append(X1[0:(len(train[0])-1)])

4

Liste ve liste listesi oluşturmak için aşağıdaki sözdizimini kullanın

     x = [[] for i in range(10)]

bu 1-d liste oluşturacak ve onu başlatmak için [[sayı] içine sayı koyacak ve listenin uzunluğunu aralık içine koyacak (uzunluk)

  • Liste listesi oluşturmak için aşağıdaki sözdizimini kullanın.
    x = [[[0] for i in range(3)] for i in range(10)]

bu, 10 * 3 boyutlu ve 0 değerine sahip listelerin listesini başlatacaktır

  • Öğeye erişmek / değiştirmek için
    x[1][5]=value

2

Bu yüzden en hızlı yolu elde etmek için bazı hız karşılaştırmaları yaptım. Liste anlamaları gerçekten çok hızlı. Yaklaşmanın tek yolu, listenin oluşturulması sırasında bayt kodunun dışlanmasını önlemektir. İlk denemem, prensipte daha hızlı görünen aşağıdaki yöntemdi:

l = [[]]
for _ in range(n): l.extend(map(list,l))

(elbette 2 ** n uzunluğunda bir liste oluşturur) Bu yapı, zamana göre, hem kısa hem de uzun (bir milyon) listeler için liste anlayışından iki kat daha yavaştır.

İkinci girişimim, liste oluşturucuyu benim için çağırmak için starmap kullanmaktı, Liste yapıcısını en yüksek hızda çalıştıran görünen bir yapı var, ancak yine de daha yavaş, ancak yalnızca küçük bir miktar:

from itertools import starmap
l = list(starmap(list,[()]*(1<<n)))

Yeterince ilginç olan, yürütme süresi, yürütme süresi neredeyse tam olarak şunun hızına eşit olduğundan, starmap çözümünü yavaşlatan son liste çağrısı olduğunu gösteriyor:

l = list([] for _ in range(1<<n))

List (()) 'in de bir liste oluşturduğunu fark ettiğimde üçüncü denemem geldi, bu yüzden görünüşte basit olanı denedim:

l = list(map(list, [()]*(1<<n)))

ancak bu, starmap aramasından daha yavaştı.

Sonuç: hız manyakları için: Liste anlayışını kullanın. Gerekirse yalnızca işlevleri çağırın. Yerleşikleri kullanın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.