Modul itertools adalah kumpulan fungsi pustaka standar yang membangun iterator dari iterable lain, semuanya ditulis dalam C dan semuanya hemat memori karena mengalirkan elemen satu per satu. Fungsi-fungsinya menjadi bahan penyusun untuk kebutuhan yang lebih spesifik daripada zip dan enumerate, mulai dari memotong stream tak terbatas, menyambung beberapa sumber, mengelompokkan data, sampai menyusun pasangan. Topik ini membahas count, islice, chain, groupby, dan combinations beserta kebiasaan pemakaiannya.
count dan islice: stream tak terbatas berjendela#
Fungsi count(start, step) menghasilkan angka berkelanjutan tanpa akhir, sehingga perannya seperti range yang tidak pernah selesai. Karena perulangan dan fungsi konsumsi menuntut sumber yang berhenti, fungsi islice(iterator, awal, akhir, langkah) memotong iterator apa pun menjadi jendela tertentu dan berhenti persis pada batasnya. Dua fungsi ini hampir selalu tampil bersama, yaitu count sebagai sumber dan islice sebagai pembatasnya.
from itertools import count, islice
print(list(islice(count(10, 5), 4)))
print(list(islice(count(), 3, 8, 2)))[10, 15, 20, 25]
[3, 5, 7]Pemanggilan pertama mengambil empat angka berurutan dengan selisih lima, sedangkan yang kedua memotong mulai indeks 3 sampai sebelum 8 dengan langkah dua. islice juga bekerja atas iterator biasa tanpa menggandakan datanya, sebagaimana contoh berikut atas list kuadrat, dan hasilnya tetap berupa iterator yang dikonsumsi lewat list() untuk diperiksa.
from itertools import islice
data = [n * n for n in range(10)]
print(list(islice(data, 2, 7, 2)))[4, 16, 36]Hasilnya elemen pada indeks 2, 4, dan 6, dan tidak ada list baru yang dibangun untuk keseluruhan data selain hasilnya. Keunggulan ini nyata ketika jendelanya jauh di tengah stream yang besar, karena islice melewatkan elemen awal tanpa menyalinnya. Batas yang diberikan wajib ada ketika sumbernya tak terbatas, sebab islice hanyalah jendela dan tidak pernah menghentikan sumbernya sendiri.
chain menyambung beberapa urutan#
Fungsi chain(*iterables) mengalirkan elemen dari beberapa iterable secara berurutan seolah-olah semuanya satu urutan tunggal. Alternatif gabungan lewat operator + menuntut kedua sumber berupa list dan menyalin seluruh isinya, sedangkan chain membaca keduanya tanpa menyalin, sehingga aman untuk sumber besar maupun iterator dari generator. Varian chain.from_iterable menerima satu iterable yang isinya beberapa iterable, cocok ketika jumlah sumbernya baru diketahui saat berjalan.
from itertools import chain
piket_senin = ["Ali", "Budi"]
piket_selasa = ["Citra", "Dewi"]
print(list(chain(piket_senin, piket_selasa)))
print(list(chain.from_iterable([[1, 2], [3], [4, 5]])))['Ali', 'Budi', 'Citra', 'Dewi']
[1, 2, 3, 4, 5]Kedua bentuk menghasilkan urutan tunggal, dan perbedaannya hanya pada cara sumbernya diberikan. Seluruh fungsi modul ini bersifat lazy, sehingga chain membaca sumber-sumbernya hanya sejauh yang dikonsumsi, dan sumber kedua bahkan belum disentuh selama konsumen masih membaca sumber pertama. Hal ini berbeda dari penggabungan list_a + list_b yang menyalin seluruh elemen sebelum satu elemen pun dibaca. Pola yang lazim adalah meratakan baris-baris dari berkas atau daftar bersarang menjadi satu aliran, lalu mengalirkannya ke sum, max, atau perulangan lain tanpa membangun gabungan yang utuh di memori.
groupby mengelompokkan elemen berdampingan#
Fungsi groupby(iterable, key) menghasilkan pasangan berupa nilai kunci dan iterator anggota kelompoknya, tetapi hanya untuk elemen yang berdampingan dan kuncinya sama. Perilaku ini berbeda dari pengelompokan pada dictionary yang menyatukan elemen yang tersebar, sehingga aturan pemakaiannya tegas, yaitu urutkan data dengan kunci yang sama terlebih dahulu agar seluruh anggota satu kelompok benar-benar berdampingan.
from itertools import groupby
kata = ["apel", "anggur", "jeruk", "kiwi", "kelapa", "mangga"]
urut = sorted(kata, key=lambda k: k[0])
for huruf, kelompok in groupby(urut, key=lambda k: k[0]):
print(huruf, list(kelompok))a ['apel', 'anggur']
j ['jeruk']
k ['kiwi', 'kelapa']
m ['mangga']Pola pemanfaatannya lazim untuk data yang memang tersusun menurut waktunya, seperti baris log yang dikelompokkan per sesi atau per jam. Nilai kunci yang dihasilkan berasal dari argumen key, sedangkan iterator kelompoknya berisi elemen asli dalam urutan kemunculannya, bukan hasil pengurutan ulang. Apabila kelompok perlu ditampung untuk dipakai lebih dari sekali, kumpulkan setiap kelompok menjadi list pada putaran pertama.
Batas perilakunya terlihat pada data yang belum diurutkan. Kelompok pada data berikut terpecah karena anggota dengan kunci yang sama tidak berdampingan, dan hasilnya berupa tiga kelompok alih-alih dua.
from itertools import groupby
for nilai, kelompok in groupby([1, 1, 2, 1, 1]):
print(nilai, len(list(kelompok)))1 2
2 1
1 2combinations menyusun pasangan#
Fungsi combinations(iterable, r) menghasilkan tuple berisi elemen dari sebuah iterable tanpa memperhatikan urutan pilihannya, sedangkan permutations(iterable, r) menganggap urutan pilihan yang berbeda sebagai hasil yang berbeda. Keduanya mengalirkan hasil satu per satu tanpa menyusun seluruh kemungkinan di memori terlebih dahulu, sehingga jumlah kombinasi yang besar tetap dapat diproses per bagian.
from itertools import combinations, permutations
tim = ["Ali", "Budi", "Citra"]
print(list(combinations(tim, 2)))
print(list(permutations(tim, 2)))[('Ali', 'Budi'), ('Ali', 'Citra'), ('Budi', 'Citra')]
[('Ali', 'Budi'), ('Ali', 'Citra'), ('Budi', 'Ali'), ('Budi', 'Citra'), ('Citra', 'Ali'), ('Citra', 'Budi')]Tiga anggota tim menghasilkan tiga pasangan bermain melalui combinations, sedangkan permutations menghasilkan enam urutan karena posisinya diperhitungkan. Kombinasi cocok untuk pasangan permainan, pemilihan sampel, dan gabungan pengaturan yang urutannya tidak membedakan hasil, sedangkan permutasi dipakai ketika susunannya sendiri bermakna. Banyaknya kombinasi mengikuti notasi binomial , yang tumbuh cepat untuk yang besar, sehingga fungsi ini biasa dipakai bersama islice atau perulangan yang berhenti lebih awal ketika seluruh kemungkinannya tidak diperlukan.
Kesalahan umum#
Kesalahan pertama adalah mengiterasi groupby tanpa mengurutkan data terlebih dahulu, dan contoh pemecahannya sudah diperlihatkan pada bagian sebelumnya. Perbaikannya selalu sama, yaitu sorted(data, key=...) dengan kunci yang persis sama dengan kunci groupby, karena dua kunci yang berbeda adalah dua sumber kegagalan yang sulit dilacak.
Kesalahan kedua adalah menganggap iterator itertools sebagai list yang dapat dibaca ulang. Semua fungsi modul ini menghasilkan iterator sekali pakai, sehingga pemakaian kedua menghasilkan urutan kosong tanpa pesan apa pun.
from itertools import count, islice
jendela = islice(count(), 3)
print(list(jendela))
print(list(jendela))[0, 1, 2]
[]Perbaikannya adalah menyimpan hasilnya sebagai list bila data perlu dibaca berkali-kali, atau menyusun ulang rantai iteratornya setiap kali. Kebiasaan yang aman adalah memperlakukan seluruh keluaran modul ini sebagai aliran sekali lewat, lalu memilih list hanya pada titik akhir ketika data benar-benar harus ditampung.
Ringkasan#
countmenghasilkan stream angka tanpa akhir, danislicememberi jendela berhenti atas iterator apa pun tanpa menyalin data.chainmenyambung beberapa iterable menjadi satu aliran, dengan varianfrom_iterableuntuk sumber yang jumlahnya dinamis.groupbymengelompokkan elemen yang berdampingan menurut kunci, sehingga data wajib diurutkan dengan kunci yang sama terlebih dahulu.combinationsdanpermutationsmenyusun pilihan tanpa urutan dan dengan urutan, mengalirkan hasilnya satu per satu.
Referensi#
- itertools — Python Software Foundation — Referensi resmi seluruh fungsi itertools beserta resep pemakaiannya.
- Functional Programming HOWTO — Python Software Foundation — Panduan resmi yang membahas itertools dalam kerangka pemrosesan aliran data.