Apa perbedaan antara chain dan chain.from_iterable di itertools?

Saya tidak dapat menemukan contoh valid di internet di mana saya dapat melihat perbedaan di antara keduanya dan mengapa memilih salah satu dari yang lain.


person user1994660    schedule 21.02.2013    source sumber


Jawaban (6)


Yang pertama membutuhkan 0 atau lebih argumen, masing-masing merupakan iterable, yang kedua mengambil satu argumen yang diharapkan menghasilkan iterable:

from itertools import chain

chain(list1, list2, list3)

iterables = [list1, list2, list3]
chain.from_iterable(iterables)

tetapi iterables dapat berupa iterator apa pun yang menghasilkan iterable:

def gen_iterables():
    for i in range(10):
        yield range(i)

itertools.chain.from_iterable(gen_iterables())

Menggunakan bentuk kedua biasanya merupakan hal yang mudah, namun karena ia mengulang input iterable dengan malas, ini juga merupakan satu-satunya cara Anda dapat merangkai iterator berhingga dalam jumlah tak terbatas:

def gen_iterables():
    while True:
        for i in range(5, 10):
            yield range(i)

chain.from_iterable(gen_iterables())

Contoh di atas akan memberi Anda sebuah iterable yang menghasilkan pola siklus angka yang tidak akan pernah berhenti, namun tidak akan pernah menghabiskan lebih banyak memori daripada yang dibutuhkan oleh satu panggilan range().

person Martijn Pieters    schedule 21.02.2013
comment
saya masih tidak bisa mendapatkannya. dapatkah Anda memberi saya perbedaan keluaran dan kasus penggunaan dalam situasi praktis di mana menggunakan apa - person user1994660; 22.02.2013
comment
@ user1994660: tidak ada perbedaan keluaran. Ini adalah perbedaan input. Itu membuatnya lebih mudah untuk menggunakan input tertentu. - person Martijn Pieters; 22.02.2013
comment
@ user1994660: Saya menggunakan formulir kedua di jawaban ini. - person Martijn Pieters; 22.02.2013
comment
@ user1994660: kasus penggunaan yang bagus untuk bentuk kedua: Idiom Python untuk merangkai (meratakan) iterable tak terbatas dari iterable terbatas? - person Martijn Pieters; 22.02.2013
comment
@ user1994660: Jalankan kode ini: # Return an iterator of iterators def it_it(): return iter( [iter( [11, 22] ), iter( [33, 44] )] ) print( list(itertools.chain.from_iterable(it_it())) ) print( list(itertools.chain(it_it())) ) print( list(itertools.chain(*it_it())) ) Yang pertama adalah yang terbaik; yang kedua tidak mencapai iterator yang disarangkan, ia mengembalikan iterator, bukan angka yang diinginkan; yang ketiga menghasilkan keluaran yang benar TETAPI tidak sepenuhnya malas: * memaksa semua iterator dibuat. Untuk masukan bodoh ini tidak masalah. - person ToolmakerSteve; 20.12.2013
comment
Perhatikan bahwa jika iterable tidak terlalu besar, Anda juga dapat melakukan itertools.chain(*iterables) - person balki; 15.02.2014
comment
@MartijnPieters maukah Anda jika saya mengutip Anda tentang satu-satunya cara Anda dapat merangkai iterator terbatas dalam jumlah tak terbatas? - person Ryan Haining; 09.06.2014
comment
Saya baru-baru ini menambahkan chain.from_iterable ke proyek c++ - person Ryan Haining; 09.06.2014

Saya tidak dapat menemukan contoh yang valid ... di mana saya dapat melihat perbedaan di antara keduanya [chain dan chain.from_iterable] dan mengapa memilih salah satu dari yang lain

Jawaban yang diterima menyeluruh. Bagi mereka yang mencari lamaran cepat, pertimbangkan untuk meratakan beberapa daftar:

list(itertools.chain(["a", "b", "c"], ["d", "e"], ["f"]))
# ['a', 'b', 'c', 'd', 'e', 'f']

Anda mungkin ingin menggunakan kembali daftar ini nanti, jadi Anda membuat daftar yang dapat diubah:

iterable = (["a", "b", "c"], ["d", "e"], ["f"])

Mencoba

Namun, meneruskan iterable ke chain memberikan hasil yang tidak merata:

list(itertools.chain(iterable))
# [['a', 'b', 'c'], ['d', 'e'], ['f']]

Mengapa? Anda memasukkan satu item (tuple). chain membutuhkan setiap daftar secara terpisah.


Solusi

Jika memungkinkan, Anda dapat membongkar iterable:

list(itertools.chain(*iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

list(itertools.chain(*iter(iterable)))
# ['a', 'b', 'c', 'd', 'e', 'f']

Secara umum, gunakan .from_iterable (karena ini juga berfungsi dengan iterator tak terbatas):

list(itertools.chain.from_iterable(iterable))
# ['a', 'b', 'c', 'd', 'e', 'f']

g = itertools.chain.from_iterable(itertools.cycle(iterable))
next(g)
# "a"
person pylang    schedule 17.07.2018

Mereka melakukan hal yang sangat mirip. Untuk sejumlah kecil iterable itertools.chain(*iterables) dan itertools.chain.from_iterable(iterables) melakukan hal yang sama.

Keuntungan utama from_iterables terletak pada kemampuan untuk menangani iterable dalam jumlah besar (berpotensi tak terbatas) karena semuanya tidak perlu tersedia pada saat panggilan.

person BiGYaN    schedule 10.07.2017
comment
Adakah yang tahu jika * operator membongkar iterables dengan malas? - person Rotareti; 25.03.2018
comment
@Rotareti, ya, ia membongkar dengan malas (satu per satu) tetapi dalam kasus ini itertools.chain(*iterables) adalah pemanggilan fungsi. Semua argumen harus ada pada saat panggilan. - person BiGYaN; 27.03.2018
comment
Apakah ini benar? Dari kode CPython sepertinya stackoverflow.com/a/62513808/610569 sama - person alvas; 22.06.2020
comment
@alvas Coba ubah jumlah elemen menjadi sangat besar; dalam kisaran 10_000 hingga 1_000_000 dan Anda akan melihat from_iterables menjadi lebih cepat. - person BiGYaN; 30.06.2020

Cara lain untuk melihatnya:

chain(iterable1, iterable2, iterable3, ...) adalah ketika Anda sudah mengetahui iterable apa yang Anda miliki, sehingga Anda dapat menuliskannya sebagai argumen yang dipisahkan koma.

chain.from_iterable(iterable) adalah ketika iterable Anda (seperti iterable1, iterable2, iterable3) diperoleh dari iterable lain.

person R. Navega    schedule 20.08.2018

Memperluas @martijn-pieters jawaban

Meskipun akses ke item dalam di iterable tetap sama, dan penerapannya bijaksana,

  • itertools_chain_from_iterable (yaitu chain.from_iterable dengan Python) dan
  • chain_new (yaitu chain dengan Python)

dalam implementasi CPython, keduanya merupakan tipe bebek chain_new_internal


Apakah ada manfaat pengoptimalan dari penggunaan chain.from_iterable(x), dengan x merupakan iterable dari iterable; dan tujuan utamanya adalah untuk menggunakan daftar item yang rata?

Kita dapat mencoba membandingkannya dengan:

import random
from itertools import chain
from functools import wraps
from time import time

from tqdm import tqdm

def timing(f):
    @wraps(f)
    def wrap(*args, **kw):
        ts = time()
        result = f(*args, **kw)
        te = time()
        print('func:%r args:[%r, %r] took: %2.4f sec' % (f.__name__, args, kw, te-ts))
        return result
    return wrap

def generate_nm(m, n):
    # Creates m generators of m integers between range 0 to n.
    yield iter(random.sample(range(n), n) for _ in range(m))
    

def chain_star(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain(*x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass

def chain_from_iterable(x):
    # Stores an iterable that will unpack and flatten the list of list.
    chain_x = chain.from_iterable(x)
    # Consumes the items in the flatten iterable.
    for i in chain_x:
        pass


@timing
def versus(f, n, m):
  f(generate_nm(n, m))

P/S: Benchmark berjalan... Menunggu hasilnya.


Hasil

bintang_rantai, m=1000, n=1000

for _ in range(10):
    versus(chain_star, 1000, 1000)

[keluar]:

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6494 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6603 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6367 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6350 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6296 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6399 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6341 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6381 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6343 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 1000, 1000), {}] took: 0.6309 sec

rantai_dari_iterable, m=1000, n=1000

for _ in range(10):
    versus(chain_from_iterable, 1000, 1000)

[keluar]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6416 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6315 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6535 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6334 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6327 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6471 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6426 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6287 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6353 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 1000, 1000), {}] took: 0.6297 sec

bintang_rantai, m=10000, n=1000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2659 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2966 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2953 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3141 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2802 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2799 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2848 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3299 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.2730 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 10000, 1000), {}] took: 6.3052 sec

rantai_dari_iterable, m=10000, n=1000

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3129 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3064 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.3071 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2660 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2837 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2756 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2939 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2715 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 10000, 1000), {}] took: 6.2877 sec

bintang_rantai, m=100000, n=1000

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7874 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3744 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.5584 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.3745 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.7982 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.4054 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6769 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.6476 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 63.7397 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 100000, 1000), {}] took: 62.8980 sec

rantai_dari_iterable, m=100000, n=1000

for _ in range(10):
    versus(chain_from_iterable, 100000, 1000)

[keluar]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7227 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7717 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7159 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7569 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7906 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.6211 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.7294 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8260 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.8356 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 100000, 1000), {}] took: 62.9738 sec

bintang_rantai, m=500000, n=1000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[keluar]:

func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 314.5671 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.9270 sec
func:'versus' args:[(<function chain_star at 0x7f5c7188ef28>, 500000, 1000), {}] took: 313.8992 sec

rantai_dari_iterable, m=500000, n=1000

for _ in range(3):
    versus(chain_from_iterable, 500000, 1000)

[keluar]:

func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8301 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.8104 sec
func:'versus' args:[(<function chain_from_iterable at 0x7f5c7188eb70>, 500000, 1000), {}] took: 313.9440 sec
person alvas    schedule 22.06.2020

Cara lain untuk melihatnya adalah dengan menggunakan chain.from_iterable

ketika Anda memiliki iterable dari iterable seperti iterable bersarang (atau iterbale majemuk) dan menggunakan rantai untuk iterable sederhana

person Chyanit Singh    schedule 20.02.2020