Soru Python'daki liste listesinden düz bir liste yapmak


Python'daki liste listesinden basit bir liste yapmak için bir kısayol olup olmadığını merak ediyorum.

Bunu bir döngüde yapabilirim, ama belki de biraz "tek liner" var mı? İle denedim azaltmakama bir hata alıyorum.

kod

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

Hata mesajı

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'

2083
2018-06-04 20:30


Menşei


Burada derinlemesine bir tartışma var: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html, keyfi olarak iç içe geçmiş listeler listelerini düzleştirme yöntemlerini tartışmak. İlginç bir okuma! - RichieHindle
Diğer bazı cevaplar daha iyidir, fakat sizin başarısızlığınız nedeni 'uzatma' yönteminin daima Hiçbiri döndürmemesidir. Uzunluk 2 olan bir liste için işe yarayacak, ancak Hiçbiri dönmeyecek. Daha uzun bir liste için, ilk 2 arkı tüketir ve bu da Hiçbiri döndürmez. Daha sonra bu erroya neden olan None.extend (<üçüncü arg>) ile devam eder. - mehtunguh
@ shawn-chin çözümü burada daha pythonic, ama dizisi türü korumak gerekiyorsa, liste listesi yerine bir tuple tuple var, o zaman sen azaltmak gerekir (operator.concat, tuple_of_tuples). Operator.concat'in tuples ile kullanılması, listeyle chain.from_iterables'dan daha hızlı performans gösteriyor. - Meitham
numpy.array ([[1], [2]]) düzleştirici (), tolist (), iç yapıyı kaldırır ve listeyi döndürür [1,2] - user5920660
Şimdi tarafından destekleniyor mpu: import mpu; mpu.datastructures.flatten([1, [2, 3], [4, [5, 6]]]) verir [1, 2, 3, 4, 5, 6] - Martin Thoma


Cevaplar:


flat_list = [item for sublist in l for item in sublist]

hangi demektir ki:

for sublist in l:
    for item in sublist:
        flat_list.append(item)

Şimdiye kadar yayınlanan kısayollardan daha hızlıdır. (l dümdüz listesidir.)

İşte ilgili fonksiyon:

flatten = lambda l: [item for sublist in l for item in sublist]

Kanıt için her zaman olduğu gibi timeit standart kütüphanede modül:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

Açıklama: dayalı kısayollar + (ima edilen kullanım dahil sum) gereklilik O(L**2) L alt listeleri olduğunda - ara sonuç listesi daha uzun sürdüğü için, her adımda yeni bir ara sonuç listesi nesnesi tahsis edilir ve önceki ara sonuçtaki tüm öğeler kopyalanmalıdır (yeni eklenenler de dahil) sonunda). Yani (basitlik ve gerçek genellik kaybı olmadan) her bir öğenin L alt listelerine sahip olduğunuzu söyleyin: ilk I öğeleri ileri-geri L-1 kez, ikinci I öğeleri L-2 kez kopyalanır; toplam kopya sayısı, 1'den 1'e kadar hariç tutulan x için x'in toplamıdır, yani, I * (L**2)/2.

Liste kavraması sadece bir kez bir liste oluşturur ve her bir öğeyi (asıl yerleşim yerinden sonuç listesine) bir defada bir kez kopyalar.


3001
2018-06-04 20:37



Aynı verilerle bir test denedim, itertools.chain.from_iterable : $ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'. Burada gösterilen alternatiflerin en hızlısı olan iç içe geçmiş anlamaya göre iki kat daha hızlı çalışır. - intuited
Anlayacağınız sözdizimini, tam olarak döngüleri için yuvalanmış gibi düşünebileceğinizi anlayana kadar anladım. l listesinde alt liste için: alt öğe için öğe: getiri öğesi - Rob Crowell
@BorisChervenkov: Aramayı tamamladım. list() Yineleyiciyi bir liste haline getirmek için. - intuited
[Ağacın içindeki yaprak için ağaçtaki ağaç yaprağı] anlamak ve uygulamak daha kolay olabilir. - John Mee
@Joel, aslında bugünlerde list(itertools.chain.from_iterable(l)) en iyisidir - diğer yorumlarda ve Shawn'un cevabında görüldüğü gibi. - Alex Martelli


Kullanabilirsiniz itertools.chain():

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))

veya, Python> = 2.6’da, kullanın itertools.chain.from_iterable() listenin paketini açmayı gerektirmeyen:

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))

Bu yaklaşım tartışmalı olarak daha okunabilir [item for sublist in l for item in sublist] ve daha hızlı görünüyor:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

1083
2018-06-04 21:06



@ShawnChin BTW, bu soruyu cevaplarken sahip olduğunuz donanım parçası, şu anki iş istasyonumun yarısı kadar hızlı ve 4 yıldır. - Manuel Gutierrez
@alexandre bkz docs.python.org/2/tutorial/... - Shawn Chin
* yapmak zor bir şey chain liste anlamadan daha basittir. Bu zincirin sadece parametre olarak iletilen yineleyicileri bir araya getirdiğini ve * üst düzey listenin parametrelere genişlemesine neden olduğunu bilmelisiniz. chain tüm yinelenenleri birleştirir, ama daha fazla inmez. Bence bu, bu durumda zincirin kullanımından anlamayı daha anlaşılır kılıyor. - Tim Dierks
@TimDierks: "Bu, Python sözdizimini anlamanızı gerektiriyor" ifadesinin Python'da belirli bir tekniği kullanmayla ilgili bir argüman olduğundan emin değilim. Elbette, karmaşık kullanım karışık olabilir, ancak "uyarılar" operatörü genellikle birçok durumda faydalıdır ve bu özellikle bunu açık bir şekilde kullanmaz; Başlangıçtaki kullanıcılara mutlaka aşikar olmayan tüm dil özelliklerini reddetmek, arkanızdan bir elini bağlamanız anlamına gelir. Aynı zamanda siz de liste anlamalarını ortaya çıkarabilir; diğer kökenden kullanıcılar bir fortekrar tekrar döngü appenddaha açık. - ShadowRanger
ne dersin ['abcde_', ['_abcde', ['e_abcd', ['de_abc', ['cde_ab', ['bcde_a']]]]]] - Aymon Fournier


Yazardan not: Bu verimsiz. Ama eğlenceli, çünkü monadlar harika. Python kodu üretimi için uygun değil.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu sadece, birinci argümanda geçen itebilir unsurları toplar, ikinci argümanı toplamın başlangıç ​​değeri olarak ele alır (eğer verilmezse, 0 bunun yerine kullanılır ve bu durum size bir hata verecektir).

İç içe geçmiş listeleri topladığınız için, aslında [1,3]+[2,4] Sonucunda sum([[1,3],[2,4]],[]), eşittir [1,3,2,4].

Sadece listelerin listelerinde çalıştığını unutmayın. Listeler listeleri listeleri için başka bir çözüme ihtiyacınız olacaktır.


638
2018-06-04 20:35



Bu oldukça düzgün ve zekice ama bunu kullanmazdım çünkü okumak kafa karıştırıcı. - andrewrk
Bu bir Shlemiel, ressamın algoritmasıdır. joelonsoftware.com/articles/fog0000000319.html - gereksiz yere gereksiz ve gereksiz yere çirkin. - Mike Graham
Listelerdeki ekleme işlemi Monoiddüşünmek için en uygun soyutlamalardan biri olan + Genel anlamda operasyon (sadece rakamlarla sınırlı değildir). Bu yüzden bu cevap, listelerin bir monoid olarak (doğru) tedavisi için +1'i hak ediyor. Performansla ilgili olsa da ... - ulidtko
@andrewrk Peki, bazı insanlar bunu yapmanın en temiz yolu olduğunu düşünüyor: youtube.com/watch?v=IOiZatlZtGU neden bu kadar havalı olmadıklarını anlayanlar sadece herkesin bunu yapmasını bekleyene kadar birkaç on yıl beklemelidirler :) Keşfedilen ve icat edilmemiş programlama dillerini (ve soyutlamaları) kullanalım. - jhegedus
Bu, toplamın karesel yönü nedeniyle çok verimsiz bir yoldur. - Jean-François Fabre


En çok önerilen çözümleri test ettim perfplot (Benim bir evcil hayvan projesi, aslında bir sarıcı etrafında timeit) bulundu

list(itertools.chain.from_iterable(a))

En hızlı çözüm olmak için (10'dan fazla liste birleştirildiyse).

enter image description here


Arsa çoğaltmak için kod:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
        numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )

131
2017-07-26 09:38





from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

extend() örneğinizdeki yöntem değiştirir x faydalı bir değer döndürmek yerine reduce() ) Bekler.

Bunu yapmak için daha hızlı bir yol reduce sürüm olurdu

>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

100
2018-06-04 20:35



reduce(operator.add, l) yapmak için doğru yolu olurdu reduce sürümü. Built-ins lambdas daha hızlıdır. - agf
@agf burada nasıl: * timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)  ,017956018447875977   * timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)  ,025218963623046875 - lukmdo
Bu bir Shlemiel ressamın algoritması joelonsoftware.com/articles/fog0000000319.html - Mike Graham
bu sadece için kullanabilirsiniz integers. Ama liste içeriyorsa string? - Freddy
@Reddy: operator.add Fonksiyon hem tamsayı listeleri hem de dizge listeleri için eşit derecede iyi çalışır. - Greg Hewgill


İşte geçerli olan genel bir yaklaşımdır. sayılar, Teller, İç içe listeler ve karışık konteynerler.

kod

from collections import Iterable


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

Not: Python 3'te, yield from flatten(x) yerini alabilir for sub_x in flatten(x): yield sub_x

gösteri

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

Referans

  • Bu çözüm, içindeki bir tarifden değiştirildi Beazley, D. ve B. Jones. Reçete 4.14, Python Yemek Kitabı 3. Baskı, O'Reilly Media Inc. Sebastopol, CA: 2013.
  • Daha önce bulundu SO posta, muhtemelen orijinal gösteri.

55
2017-11-29 04:14



Hemen hemen aynısını yazdım, çünkü çözümünüzü görmedim ... işte burada "tamamlayıcı çoklu listeleri tekrar tekrar düzleştir" ... (+1) - Martin Thoma
@MartinThoma Çok beğeni topladı. FYI, iç içe geçmiş yinelenenlerin düzleştirilmesi sizin için yaygın bir uygulamasa, bunu iyi işleyen bazı üçüncü taraf paketleri vardır. Bu, tekerleği yeniden icat etmekten kurtarabilir. Ben bahsettim more_itertools Bu yazı içinde tartışılan diğerleri arasında. Şerefe. - pylang
Güzel - sadece bir yield from öğrendikten sonra python üzerinde inşaat tipi yield * es2015'te. - Triptych
tarafından değiştir if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)): dizeleri desteklemek için. - Jorge Leitão
Doğru. Orijinal yemek kitabı tarifi aslında dizeleri ve baytları nasıl destekleyeceğini gösterir. Bu desteği yansıtacak şekilde düzenlediyseniz. - pylang


İfademi geri alıyorum. toplam kazanan değil. Liste küçük olduğunda daha hızlı olsa da. Ancak performans daha büyük listelerle önemli ölçüde azalır. 

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

Toplam sürüm hala bir dakikadan fazla sürüyor ve henüz işlenmemiş!

Orta listeler için:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

Küçük listeler ve timeit kullanarak: sayı = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131

31
2018-06-04 20:46



gerçekten minik bir liste için, ör. 3 alt listeyle, belki - ama toplamın performansı O (N ** 2) ile devam ettiğinden, liste kavraması O (N) ile gider, sadece giriş listesini biraz büyütmek bazı şeyleri tersine çevirecektir - gerçekten de LC olacaktır " sonsuz hızla daha hızlı "N kadar sınırda toplamı büyür. Toplamı tasarlamanın ve Python çalışma zamanında ilk uygulamasını yapmaktan sorumluydum ve yine de sayıları toplamaya (gerçekten iyi olan şeyleri) etkili bir şekilde kısıtlamak ve insanlara sunduğu "çekici rahatsızlığı" engellemenin bir yolunu bulmuş olmak isterdim. listeleri "toplamak" isteyen ;-). - Alex Martelli