Python: cara tercepat untuk membuat daftar n daftar

97

Jadi saya bertanya-tanya bagaimana cara terbaik membuat daftar daftar kosong:

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

Karena cara Python bekerja dengan daftar di memori, ini tidak berfungsi:

[[]]*n

Ini memang membuat [[],[],...]tetapi setiap elemen adalah daftar yang sama:

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

Sesuatu seperti pemahaman daftar berfungsi:

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

Tapi ini menggunakan VM Python untuk perulangan. Apakah ada cara untuk menggunakan loop tersirat (memanfaatkannya karena ditulis dalam C)?

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

Ini sebenarnya lebih lambat. :(

munchybunch
sumber
1
Saya akan terkejut jika ada sesuatu yang jauh lebih cepat dari d = [[] for x in xrange(0,n)]. Anda harus melakukan loop secara eksplisit dengan Python atau memanggil fungsi Python / lambda berulang kali (yang seharusnya lebih lambat). Tapi tetap berharap seseorang akan memposting sesuatu yang menunjukkan bahwa saya salah :).
MAK
1
Ketika Anda mengukurnya dengan timeit, apa yang Anda pelajari?
S. Lott
Saya baru saja mengonfirmasi bahwa map(lambda x: [], xrange(n))itu lebih lambat dari pemahaman daftar.
Andrew Clark

Jawaban:

105

Mungkin satu-satunya cara yang sedikit lebih cepat dari

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

adalah

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

Itu tidak harus membuat intobjek baru di setiap iterasi dan sekitar 15% lebih cepat di mesin saya.

Edit : Dengan NumPy, Anda dapat menghindari loop Python menggunakan

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

tetapi ini sebenarnya 2,5 kali lebih lambat daripada pemahaman daftar.

Sven Marnach
sumber
Bagaimana dengan map(lambda x:[], repeat(None,n))?
PaulMcG
3
@Paul: Itu akan menjadi jauh lebih lambat lagi karena overhead pemanggilan fungsi ekspresi lambda.
Sven Marnach
Bukankah seharusnya ini diperbarui sekarang karena kisarannya berbeda dengan Python 3?
beruic
@beruic Saya hanya mengutip kode dari pertanyaan di blok kode pertama, jadi tidak masuk akal untuk mengubahnya.
Sven Marnach
12

Daftar comprehensions sebenarnya dilaksanakan lebih efisien daripada looping eksplisit (lihat yang diskeluaran misalnya fungsi ) dan mapcara memiliki untuk memohon sebuah objek callable ophaque pada setiap iterasi, yang menimbulkan biaya overhead overhead yang cukup.

Apapun, [[] for _dummy in xrange(n)]adalah cara yang tepat untuk melakukannya dan tidak ada yang kecil (jika ada sama sekali) perbedaan kecepatan antara berbagai cara lainnya harus peduli. Kecuali tentu saja Anda menghabiskan sebagian besar waktu Anda melakukan ini - tetapi dalam kasus itu, Anda harus mengerjakan algoritme Anda sebagai gantinya. Seberapa sering Anda membuat daftar ini?


sumber
5
Tolong, tidak _sebagai nama variabel! Jika tidak, jawaban yang bagus :)
Sven Marnach
11
@ Sven: Mengapa tidak? Ini biasanya digunakan untuk variabel yang tidak digunakan (jika dipanggil i, saya akan mencari di mana itu digunakan). Satu-satunya kesalahan adalah bahwa hal itu membayangi _hasil terakhir di REPL ... dan itu hanya kasus di 2.x di mana pemahaman daftar bocor.
Tidak terlalu sering, itulah sebabnya saya melanjutkan dan menggunakan pemahaman daftar. Saya pikir akan menarik untuk melihat apa yang orang katakan. Saya melihat Dropbox berbicara di PyCon dengan penggunaan itertools.imap alih-alih for loop untuk memperbarui hash md5 berkali-kali, dan saya sudah agak terobsesi dengan loop C sejak saat itu.
munchybunch
12
Alasan terpenting untuk tidak menggunakannya adalah karena hal itu cenderung membingungkan orang, membuat mereka berpikir ini semacam sintaks khusus. Dan selain konflik dengan _interpreter interaktif, itu juga bertentangan dengan alias gettext umum. Jika Anda ingin menjelaskan bahwa variabel tersebut adalah variabel dummy, sebut saja dummy, bukan _.
Sven Marnach
12

Berikut dua metode, yang satu manis dan sederhana (dan konseptual), yang lain lebih formal dan dapat diperluas dalam berbagai situasi, setelah membaca kumpulan data.

Metode 1: Konseptual

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. 

Metode 2: Formal dan dapat diperluas

Cara elegan lain untuk menyimpan daftar sebagai daftar daftar nomor yang berbeda - yang dibaca dari sebuah file. (File di sini memiliki rangkaian dataset) Train adalah kumpulan data dengan katakanlah 50 baris dan 20 kolom. yaitu. Train [0] memberi saya baris pertama dari file csv, train [1] memberi saya baris ke-2 dan seterusnya. Saya tertarik untuk memisahkan dataset dengan 50 baris sebagai satu daftar, kecuali kolom 0, yang merupakan variabel yang saya jelaskan di sini, jadi harus dihapus dari dataset orignal train, dan kemudian menskalakan daftar demi daftar- yaitu daftar daftar . Inilah kode yang melakukan itu.

Perhatikan bahwa saya membaca dari "1" di loop dalam karena saya hanya tertarik pada variabel penjelas. Dan saya menginisialisasi ulang X1 = [] di loop lain, jika tidak, X2.append ([0: (len (train [0]) - 1)]) akan menulis ulang X1 berulang kali - selain itu lebih hemat memori.

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)])
ekta
sumber
4

Untuk membuat daftar dan daftar gunakan sintaks di bawah ini

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

ini akan membuat daftar 1-d dan untuk memulainya masukkan nomor [[nomor] dan set panjang daftar masukkan panjang dalam kisaran (panjang)

  • Untuk membuat daftar daftar gunakan sintaks di bawah ini.
    x = [[[0] for i in range(3)] for i in range(10)]

ini akan menginisialisasi daftar daftar dengan 10 * 3 dimensi dan dengan nilai 0

  • Untuk mengakses / memanipulasi elemen
    x[1][5]=value
waqas ahmed
sumber
2

Jadi saya melakukan beberapa perbandingan kecepatan untuk mendapatkan cara tercepat. Pemahaman daftar memang sangat cepat. Satu-satunya cara untuk mendekat adalah dengan menghindari bytecode keluar selama pembuatan daftar. Upaya pertama saya adalah metode berikut, yang pada prinsipnya tampak lebih cepat:

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

(menghasilkan daftar dengan panjang 2 ** n, tentu saja) Konstruksi ini dua kali lebih lambat dari pemahaman daftar, menurut timeit, untuk daftar pendek dan panjang (satu juta).

Upaya kedua saya adalah menggunakan starmap untuk memanggil konstruktor daftar untuk saya, Ada satu konstruksi, yang tampaknya menjalankan konstruktor daftar dengan kecepatan tinggi, tetapi masih lebih lambat, tetapi hanya dalam jumlah kecil:

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

Cukup menarik waktu eksekusi menunjukkan bahwa itu adalah panggilan daftar terakhir yang membuat solusi starmap lambat, karena waktu eksekusinya hampir persis sama dengan kecepatan:

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

Upaya ketiga saya datang ketika saya menyadari bahwa list (()) juga menghasilkan daftar, jadi saya mencoba yang benar-benar sederhana:

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

tapi ini lebih lambat dari panggilan starmap.

Kesimpulan: untuk maniak kecepatan: Gunakan pemahaman daftar. Hanya panggil fungsi, jika perlu. Gunakan bawaan.

Jurjen Bos
sumber