Как сделать плоский список из списка списков

avatar
Emma
4 июня 2009 в 20:30
2912560
35
4202

Есть ли ярлык для создания простого списка из списка списков в Python?

Я могу сделать это в цикле for, но есть ли какой-нибудь классный "однострочник"?

Я пробовал это с functools.reduce():

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

Но я получаю эту ошибку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Источник
RichieHindle
4 июня 2009 в 20:41
33

Подробно это обсуждается здесь: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html, обсуждая несколько методов сглаживания произвольно вложенных списков. Интересное чтение!

mehtunguh
11 июня 2013 в 21:48
9

Некоторые другие ответы лучше, но причина вашей неудачи в том, что метод «extend» всегда возвращает None. Для списка длиной 2 он будет работать, но не вернет None. Для более длинного списка он будет использовать первые 2 аргумента, которые вернут None. Затем он переходит к None.extend (<третий аргумент>), что вызывает эту ошибку

Meitham
6 октября 2014 в 21:46
0

Решение @ shawn-chin здесь более питоническое, но если вам нужно сохранить тип последовательности, скажем, у вас есть кортеж кортежей, а не список списков, тогда вы должны использовать reduce (operator.concat, tuple_of_tuples). Использование operator.concat с кортежами, кажется, работает быстрее, чем chain.from_iterables со списком.

Golden Lion
18 декабря 2020 в 16:48
0

coderhelper.com/questions/50259290/… (в этой статье объясняется разница между np.flatten () и tf.flatten () при использовании (статического и динамического) ndarray.

gnoodle
20 июня 2021 в 23:10
0

Является ли ваш список полностью двухуровневым? под этим я подразумеваю, является ли каждое целое число в списке, которое находится в другом списке, ровно двумя уровнями? (в отличие от некоторых целых чисел во вложенном списке и некоторых целых чисел непосредственно в первом списке). Тот факт, что у вас есть [7] в вашем списке (вместо 7), означает, что да, это совершенно двухуровневый список. Так разве не сработает такая простая вещь, как l2=[], за которой следует [l2.extend(i) for i in l]?

Ответы (35)

avatar
Alex Martelli
4 июня 2009 в 20:37
5967

Дан список списков t,

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

, что означает:

flat_list = []
for sublist in t:
    for item in sublist:
        flat_list.append(item)

быстрее, чем ранее опубликованные ярлыки. (t - это список для сглаживания.)

Вот соответствующая функция:

def flatten(t):
    return [item for sublist in t for item in sublist]

В качестве доказательства можно использовать модуль timeit в стандартной библиотеке:

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

Объяснение: ярлыки на основе + (включая подразумеваемое использование в sum) по необходимости являются O(T**2) при наличии T подсписок - поскольку промежуточный список результатов становится длиннее, на каждом этапе a назначается новый объект списка промежуточных результатов, и все элементы в предыдущем промежуточном результате должны быть скопированы (а также несколько новых, добавленных в конце). Итак, для простоты и без фактической потери общности предположим, что у вас есть T подсписок по k элементов в каждом: первые k элементов копируются туда и обратно T-1 раз, вторые k элементов T-2 раз и так далее; общее количество копий в k раз больше суммы x для x от 1 до T без учета, то есть k * (T**2)/2.

Составление списка просто генерирует один список, один раз, и копирует каждый элемент (из исходного места жительства в список результатов) также ровно один раз.

intuited
15 октября 2010 в 01:21
555

Я попробовал провести тест с теми же данными, используя 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))'. Он работает чуть более чем в два раза быстрее, чем понимание вложенных списков, что является самой быстрой из представленных здесь альтернатив.

Rob Crowell
27 июля 2011 в 16:43
316

Мне было трудно понять синтаксис, пока я не понял, что вы можете думать о нем точно так же, как о вложенных циклах for. для подсписка в l: для элемента в подсписке: элемент доходности

John Mee
29 августа 2013 в 01:38
198

[лист за деревом в лесу за листом в дереве], возможно, будет легче понять и применить.

Sнаđошƒаӽ
12 июля 2021 в 17:19
0

@RobCrowell То же самое. Мне кажется, что список не читается правильно , что-то не так - я всегда, кажется, ошибаюсь и заканчиваю поиском в Google. Для меня это правильно [leaf for leaf in tree for tree in forest]. Желаю, чтобы это было так. Я уверен, что мне здесь не хватает грамматики, и я был бы признателен, если бы кто-нибудь мог указать на это.

Michael Higgins
11 августа 2021 в 05:33
0

Я почти уверен, что просматривал это уже 20 раз. Кто-нибудь бьет это?

avatar
MasterOne Piece
3 мая 2022 в 12:21
-1
list(numpy.concatenate(regular_list).flat)
avatar
Zubin
30 апреля 2022 в 22:01
-1
import os
eval(os.popen("ruby -e 'puts [[1, 2, 3], [4, 5, 6]].flatten.inspect'").read())
[1, 2, 3, 4, 5, 6]
BrokenBenchmark
1 мая 2022 в 00:27
0

Не могли бы вы дать объяснение вашего кода, например. использование eval(), os.popen() и т. д.?

avatar
Jayesh Chandrapal
29 апреля 2022 в 21:59
0
def flatten_array(arr):
  result = []
  for item in arr:
    if isinstance(item, list):
      for num in item:
        result.append(num)
    else:
      result.append(item)
  return result

print(flatten_array([1, 2, [3, 4, 5], 6, [7, 8], 9]))
// output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
avatar
BhushanD
2 апреля 2022 в 15:31
0

Учитывая, что список состоит только из целых чисел:

import re
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(map(int,re.sub('(\[|\])','',str(l)).split(',')))
avatar
Vincent Aranega
14 марта 2022 в 13:06
0

Не интернет-пользователь, но, увидев все ответы здесь, я думаю, что в этом длинном списке пропущено некоторое сопоставление с образцом, так что вот оно :)

Эти два метода, вероятно, неэффективны, но в любом случае их легко читать (по крайней мере, мне, возможно, я избалован функциональным программированием):

def flat(x):
    match x:
        case []:
            return []
        case [[*sublist], *r]:
            return [*sublist, *flat(r)]

Вторая версия рассматривает списки списков списков... независимо от вложенности:

def flat(x):
    match x:
        case []:
            return []
        case [[*sublist], *r]:
            return [*flat(sublist), *flat(r)]
        case [h, *r]:
            return [h, *flat(r)]
avatar
SorousH Bakhtiary
11 января 2022 в 15:56
0

Если я хочу добавить что-то к замечательным предыдущим ответам, вот моя рекурсивная функция flatten, которая может сглаживать не только вложенные списки, но и любой заданный контейнер или вообще любой объект, который может выбрасывать элементы. Это также работает для любой глубины вложенности, и это ленивый итератор, который выдает элементы в соответствии с запросом:

def flatten(iterable):
    # These types won't considered a sequence or generally a container
    exclude = str, bytes

    for i in iterable:
        try:
            if isinstance(i, exclude):
                raise TypeError
            iter(i)
        except TypeError:
            yield i
        else:
            yield from flatten(i)

Таким образом, вы можете исключать типы, которые вы не хотите сглаживать, например str или что-то еще.

Идея заключается в том, что если объект может пройти iter(), он готов выдавать элементы. Таким образом, итерируемый объект может иметь даже выражения генератора в качестве элемента.

Кто-то может возразить, почему вы написали это в общем, когда ОП не просил? хорошо, ты прав. Я просто почувствовал, что это может кому-то помочь (как и мне).

Тестовые случаи:

lst1 = [1, {3}, (1, 6), [[3, 8]], [[[5]]], 9, ((((2,),),),)]
lst2 = ['3', B'A', [[[(i ** 2 for i in range(3))]]], range(3)]

print(list(flatten(lst1)))
print(list(flatten(lst2)))

выход:

[1, 3, 1, 6, 3, 8, 5, 9, 2]
['3', b'A', 0, 1, 4, 0, 1, 2]
avatar
Tuhin Paul
8 января 2022 в 07:55
0

Используйте два for в понимании списка:

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flat_l = [e for v in l for e in v]
print(flat_l)
avatar
dtlam26
9 декабря 2021 в 09:15
9

Согласно вашему списку [[1, 2, 3], [4, 5, 6], [7], [8, 9]], который является 1 уровнем списка, мы можем просто использовать sum(list,[]) без использования каких-либо библиотек

sum([[1, 2, 3], [4, 5, 6], [7], [8, 9]],[])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Uriya Harpeness
9 декабря 2021 в 09:24
1

Этот ответ уже есть в этом вопросе: coderhelper.com/a/952946/14273548

Arel
29 декабря 2021 в 20:29
0

Аккуратный! Хотя другой ответ здесь, coderhelper.com/a/952946/14273548, объясняет причины, по которым следует избегать этого решения , как правило, (это неэффективно и сбивает с толку).

avatar
Manlai
9 декабря 2021 в 06:10
0

нерекурсивная функция для сглаживания списков списков любой глубины:

def flatten_list(list1):
    out = []
    inside = list1
    while inside:
        x = inside.pop(0)
        if isinstance(x, list):
            inside[0:0] = x
        else:
            out.append(x)
    return out

l = [[[1,2],3,[4,[[5,6],7],[8]]],[9,10,11]]
flatten_list(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
avatar
Alon Gouldman
7 октября 2021 в 17:38
2

Мне нужно решение, которое может работать с множественным вложением (например, [[1], [[[2]], [3]]], [1, 2, 3]), но при этом не будет рекурсивным (у меня был большой уровень рекурсии, и я получил ошибку рекурсии.

Вот что я придумал:

def _flatten(l) -> Iterator[Any]:
    stack = l.copy()
    while stack:
        item = stack.pop()
        if isinstance(item, list):
            stack.extend(item)
        else:
            yield item


def flatten(l) -> Iterator[Any]:
    return reversed(list(_flatten(l)))

и тесты:

@pytest.mark.parametrize('input_list, expected_output', [
    ([1, 2, 3], [1, 2, 3]),
    ([[1], 2, 3], [1, 2, 3]),
    ([[1], [2], 3], [1, 2, 3]),
    ([[1], [2], [3]], [1, 2, 3]),
    ([[1], [[2]], [3]], [1, 2, 3]),
    ([[1], [[[2]], [3]]], [1, 2, 3]),
])
def test_flatten(input_list, expected_output):
    assert list(flatten(input_list)) == expected_output
avatar
rafael de jesus silva monteiro
7 октября 2021 в 16:02
0

Вы можете использовать следующее:

def flatlst(lista):
    listaplana = []
    for k in lista: listaplana = listaplana + k
    return listaplana
Vimanyu
26 ноября 2021 в 01:35
0

+ оператор каждый раз создает новый список. Лучше использовать += или .extend().

avatar
Mehmet Burak Sayıcı
6 ноября 2020 в 17:04
-3
np.hstack(listoflist).tolist()
Donald Duck
6 ноября 2020 в 19:31
0

Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность. Вы можете прочитать Как ответить на и отредактировать свой ответ, чтобы улучшить его.

avatar
mmj
8 августа 2020 в 14:52
7

Существует несколько ответов с такой же схемой рекурсивного добавления, как показано ниже, но ни один из них не использует try, что делает решение более надежным и Pythonic .

def flatten(itr):
    for x in itr:
        try:
            yield from flatten(x)
        except TypeError:
            yield x

Использование : это генератор, вы обычно хотите заключить его в повторяющийся построитель, например list() или tuple(), или использовать его в цикле for 32703306751675.

Преимущества этого решения:

  • работает с любыми итерациями (даже с будущими!)
  • работает с любой комбинацией и глубиной вложенности
  • работает также, если верхний уровень содержит голые элементы
  • нет зависимостей
  • эффективный (вы можете частично сгладить вложенную итерацию, не тратя время на оставшуюся часть, которая вам не нужна)
  • универсальный (вы можете использовать его для создания итерации по вашему выбору или в цикле)

Примечание. поскольку ВСЕ итерации сглаживаются, строки разбиваются на последовательности отдельных символов. Если вам не нравится / не требуется такое поведение, вы можете использовать следующую версию, которая отфильтровывает повторяющиеся элементы сглаживания, такие как строки и байты:

def flatten(itr):
    if type(itr) in (str,bytes):
        yield itr
    else:
        for x in itr:
            try:
                yield from flatten(x)
            except TypeError:
                yield x

juanpa.arrivillaga
31 июля 2021 в 18:53
0

зачем вам использовать кортеж? теперь ваше решение неэффективно.

juanpa.arrivillaga
31 июля 2021 в 18:53
0

И с любой последовательностью sum((flatten(e) for e in itr), tuple()) очень неэффективно,

mmj
1 августа 2021 в 17:37
0

@ juanpa.arrivillaga Ваш комментарий заставил меня задуматься об улучшении моего ответа, и я думаю, что нашел лучший вариант, что вы думаете?

avatar
kederrac
25 января 2020 в 21:08
8

вы можете использовать метод list extend, он самый быстрый:

flat_list = []
for sublist in l:
    flat_list.extend(sublist)

производительность:

import functools
import itertools
import numpy
import operator
import perfplot



def functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, a, [])


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


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


def extend(a):
    n = []

    list(map(n.extend, a))

    return n 


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        functools_reduce_iconcat, extend,itertools_chain, numpy_flat
        ],
    n_range=[2**k for k in range(16)],
    xlabel='num lists',
    )

вывод: enter image description here

avatar
Max Malysh
26 июля 2019 в 18:34
307

Не изобретайте велосипед

Если вы используете -

... Django :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... Панды :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... Itertools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

...Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... Инструменты настройки :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
geckos
26 августа 2019 в 00:57
14

flatten = itertools.chain.from_iterable должно быть правильным ответом

imjoseangel
20 февраля 2020 в 10:14
5

Мне нравится решение Pandas. Если у вас есть что-то вроде: list_of_menuitems = [1, 2, [3, [4, 5, [6]]]], это приведет к: [1, 2, 3, 4, 5, 6]. Чего мне не хватает, так это плоского уровня.

Stef
5 июля 2021 в 15:32
1

теперь есть и more_itertools.flatten

avatar
phoxis
16 мая 2018 в 09:41
4

Возможно, это не самый эффективный способ, но я решил использовать однострочник (на самом деле двухстрочный). Обе версии будут работать с вложенными списками произвольной иерархии и использовать языковые функции (Python3.5) и рекурсию.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Вывод:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Это работает сначала глубоко. Рекурсия продолжается до тех пор, пока не найдет элемент, не входящий в список, затем расширяет локальную переменную flist и затем откатывает ее до родительского элемента. Каждый раз, когда возвращается flist, он расширяется до родительского flist в понимании списка. Поэтому в корне возвращается плоский список.

Вышеупомянутый создает несколько локальных списков и возвращает их, которые используются для расширения родительского списка. Я думаю, что для этого можно создать глобальный объект flist, как показано ниже.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Вывод снова

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Хотя на данный момент я не уверен в эффективности.

Maciek
9 апреля 2020 в 18:31
1

Почему расширение ([l]) вместо добавления (l)?

avatar
Brad Solomon
1 февраля 2018 в 18:33
5

Примечание : приведенное ниже относится к Python 3.3+, поскольку в нем используется yield_from. six также является сторонним пакетом, но работает стабильно. В качестве альтернативы вы можете использовать sys.version.


В случае obj = [[1, 2,], [3, 4], [5, 6]] все решения здесь хороши, включая понимание списка и itertools.chain.from_iterable.

Однако рассмотрим этот немного более сложный случай:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Здесь несколько проблем:

  • Один элемент, 6, является просто скаляром; он не повторяется, поэтому приведенные выше маршруты здесь не пройдут.
  • Один элемент, 'abc', , технически повторяемый (все str). Однако, немного читая между строк, вы не хотите рассматривать его как таковой - вы хотите рассматривать его как отдельный элемент.
  • Последний элемент, [8, [9, 10]], сам по себе является вложенным итеративным. Базовое понимание списка и chain.from_iterable извлекают только «1 уровень ниже».

Вы можете исправить это следующим образом:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Здесь вы проверяете, что подэлемент (1) повторяется с помощью Iterable, ABC от itertools, но также хотите убедиться, что (2) элемент не "строковый".

pylang
19 июня 2018 в 19:06
1

Если вас все еще интересует совместимость с Python 2, замените yield from на цикл for, например for x in flatten(i): yield x

avatar
EL_DON
1 февраля 2018 в 18:22
19

matplotlib.cbook.flatten() будет работать для вложенных списков, даже если они имеют более глубокую вложенность, чем в примере.

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print(list(matplotlib.cbook.flatten(l2)))

Результат:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Это в 18 раз быстрее, чем подчеркивание ._. Flatten:

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636
abdul
8 сентября 2021 в 14:33
0

Я думаю, что это самый быстрый из всех функций

avatar
tharndt
9 января 2018 в 14:34
2

Еще один необычный подход, который работает для гетеро- и однородных списков целых чисел:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]
Darkonaut
10 января 2018 в 22:03
0

Это просто более сложный и немного более медленный способ по сравнению с тем, что уже было опубликовано ранее. Вчера я заново изобрел его предложение, так что в наши дни этот подход кажется довольно популярным;)

tharndt
11 января 2018 в 08:17
0

Не совсем: wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10] >> nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]

tharndt
11 января 2018 в 08:32
0

мой код как один лайнер будет: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]

Darkonaut
11 января 2018 в 16:31
1

Вы действительно правы, +1, предложение 3000 не будет работать с многозначными числами, я тоже не проверял это раньше, хотя это должно быть очевидно. Вы можете упростить свой код и написать [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Но я бы посоветовал придерживаться предложения Делита в реальных случаях использования. Он не содержит хитрых преобразований типов, он быстрее и универсальнее, потому что, естественно, также обрабатывает списки со смешанными типами.

tharndt
13 января 2018 в 08:02
0

Спасибо! Конечно, это должно было быть смешно. Я уже видел предложение Делита в книге по питону.

Darkonaut
13 января 2018 в 16:04
0

Вы можете сказать нам, какую книгу? Я много думал об этом, потому что это так эффективно и красиво. В целом неизбежно достигнет предела рекурсии, но для подобных случаев с небольшим количеством рекурсий это кажется идеальным.

tharndt
15 января 2018 в 08:18
2

К сожалению нет. Но недавно я видел этот код здесь: Практическое руководство по Python 6.1.2

avatar
englealuze
8 августа 2017 в 14:59
5
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
EL_DON
22 апреля 2019 в 21:34
0

Ошибка для python2.7 для примера вложенного списка в вопросе: [[1, 2, 3], [4, 5, 6], [7], [8, 9]]

avatar
Nico Schlömer
26 июля 2017 в 09:38
664

Я протестировал большинство предлагаемых решений с помощью perfplot (мой любимый проект, по сути, обертка вокруг timeit) и обнаружил

import functools
import operator
functools.reduce(operator.iconcat, a, [])

, чтобы быть самым быстрым решением как при объединении большого количества небольших списков, так и нескольких длинных списков. (operator.iadd одинаково быстро.)

enter image description here

enter image description here


Код для воспроизведения сюжета:

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 functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, 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,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)
Sara
20 января 2019 в 13:57
37

Для огромных вложенных списков 'list (numpy.array (a) .flat)' является самой быстрой среди всех вышеперечисленных функций.

Justas
14 октября 2019 в 08:05
0

Пробовал использовать регулярное выражение: 'list (map (int, re.findall (r "[\ w] +", str (a))))'. Скорость немного ниже, чем у numpy_concatenate

Leo
30 апреля 2020 в 00:31
0

Есть ли способ сделать трехмерный график? количество массивов по среднему размеру массива?

Boris
14 ноября 2020 в 06:05
0

@Sara, пожалуйста, определите "огромный"?

mirekphd
5 декабря 2020 в 11:08
0

Пробовал numpy_flat на тестовом примере из кода Россетты (ссылка) и получил VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray

avatar
devil in the detail
5 июля 2017 в 05:14
27

Самым простым мне кажется следующее:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print(np.concatenate(l))
[1 2 3 4 5 6 7 8 9]
kylebebak
3 октября 2021 в 01:01
1

OP не упоминает, что они хотят использовать numpy. У Python есть хорошие способы сделать это, не полагаясь на библиотеку.

avatar
pylang
2 декабря 2016 в 18:35
36

Рассмотрите возможность установки пакета more_itertools.

> pip install more_itertools

Он поставляется с реализацией для flatten (источник, из рецептов itertools): <8580165858

import more_itertools


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

Примечание: как указано в документах, flatten требуется список списков. См. Ниже сведения о сглаживании более нестандартных входных данных.


Начиная с версии 2.4, вы можете сглаживать более сложные вложенные итерации с помощью more_itertools.collapse (исходный код, предоставленный abarnet).

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

viddik13
5 марта 2020 в 15:53
0

Если вы можете позволить себе добавить пакет в свой проект - этот ответ лучше всего

Sajad.sni
8 сентября 2020 в 08:32
0

он терпит неудачу, когда все элементы не указаны в списке. (например, lst = [1, [2,3]]). конечно, целое число не повторяется.

viddik13
30 октября 2020 в 02:05
0

также имейте в виду, что список строк будет сведен к списку символов

avatar
pylang
29 ноября 2016 в 04:14
136

Вот общий подход, который применяется к числам, , строкам , вложенным спискам и смешанным контейнерам <340472770> Это может сгладить как простые, так и сложные контейнеры (см. Также Демонстрация ).

Код

from typing import Iterable 
#from collections import Iterable                            # < py38


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

Примечания :

Демо

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

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

Ссылка

  • Этот раствор является модифицированным по рецепту в Бизли, Д. и Б. Джонс. Рецепт 4.14, Поваренная книга Python, 3-е изд. O'Reilly Media Inc. Севастополь, Калифорния: 2013.
  • Нашел более ранний пост SO, возможно, оригинальную демонстрацию.
Martin Thoma
25 марта 2017 в 15:32
6

Я просто написал примерно то же самое, потому что я не видел вашего решения ... вот что я искал "рекурсивно сгладить полные несколько списков" ... (+1)

pylang
25 марта 2017 в 17:51
3

@MartinThoma Очень признателен. К вашему сведению, если сглаживание вложенных итераций является для вас обычной практикой, есть некоторые сторонние пакеты, которые справляются с этим хорошо. Это может спасти от изобретения велосипеда. Я упомянул more_itertools среди других, обсуждаемых в этом посте. Ваше здоровье.

Wolf
15 июня 2017 в 10:22
0

Возможно, traverse также могло бы быть хорошим названием для этого способа дерева, тогда как я бы оставил его менее универсальным для этого ответа, придерживаясь вложенных списков.

Ryan Allen
30 апреля 2018 в 16:46
0

Вы можете проверить if hasattr(x, '__iter__') вместо импорта / проверки на Iterable, и это также исключит строки.

sunnyX
12 июня 2019 в 21:35
0

приведенный выше код, похоже, не работает, если один из вложенных списков имеет список строк. [1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] выводят: - [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5]

pylang
12 июня 2019 в 22:52
0

@sunnyX Кажется, работает, когда я пробую ваш ввод, даже с глубоко вложенным списком строк, например. list(flatten([["a", "b", ["c", "d", ["e", "f", ["g"]]]]])) -> ['a', 'b', 'c', 'd', 'e', 'f', 'g']. Какую версию Python вы используете?

sunnyX
13 июня 2019 в 13:53
0

@pylang Python 3.6.2

WestCoastProjects
16 июня 2021 в 19:21
0

Строки - это PITA: они действуют как списки, когда мы этого НЕ хотим. так что да, этот код нужен

avatar
MSeifert
26 ноября 2016 в 00:20
65

Если вы хотите сгладить структуру данных, где вы не знаете, насколько глубоко она вложена, вы можете использовать iteration_utilities.deepflatten1

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

Это генератор, поэтому вам нужно привести результат к list или явно перебрать его.


Чтобы сгладить только один уровень, и если каждый из элементов сам по себе итеративен, вы также можете использовать iteration_utilities.flatten, который сам по себе является просто тонкой оболочкой вокруг itertools.chain.from_iterable: <76129579 >

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Просто чтобы добавить некоторые тайминги (на основе ответа Нико Шлёмера, который не включает функцию, представленную в этом ответе):

Enter image description here

Это диаграмма лог-журнал, позволяющая учесть огромный диапазон охватываемых значений. Из качественных соображений: чем ниже, тем лучше.

Результаты показывают, что если итерируемый объект содержит только несколько внутренних итераций, то sum будет самым быстрым, однако для длинных итераций приемлемую производительность имеют только itertools.chain.from_iterable, iteration_utilities.deepflatten или вложенное понимание, при этом itertools.chain.from_iterable является самый быстрый (как уже заметил Нико Шлёмер).

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

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

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1 Отказ от ответственности: я являюсь автором этой библиотеки

avatar
mkultra
27 октября 2016 в 03:24
7

Если вы готовы отказаться от небольшой скорости ради более чистого вида, вы можете использовать numpy.concatenate().tolist() или numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

Вы можете узнать больше здесь, в документации, numpy.concatenate и numpy.ravel.

EL_DON
22 апреля 2019 в 21:39
1

Не работает для неравномерно вложенных списков, например [1, 2, [3], [[4]], [5, [6]]]

juanpa.arrivillaga
31 июля 2021 в 18:54
0

@EL_DON, конечно, вопрос не об этом, есть еще один вопрос, связанный с этим случаем.

EL_DON
2 августа 2021 в 19:53
0

@ juanpa.arrivillaga, однако, это простое и естественное продолжение вопроса. Ответы, позволяющие справиться с большей глубиной вложенности, с большей вероятностью будут полезны тем, кто найдет этот вопрос.

avatar
Meitham
14 сентября 2016 в 15:09
43

Похоже, есть путаница с operator.add! Когда вы складываете два списка вместе, правильный термин для этого - concat, а не складывать. operator.concat - это то, что вам нужно использовать.

Если вы думаете о функциональности, это очень просто:

>>> from functools import reduce
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

Вы видите, что reduce учитывает тип последовательности, поэтому, когда вы предоставляете кортеж, вы получаете обратно кортеж. Попробуем со списком:

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

Ага, вы вернули список.

Как насчет производительности:

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable довольно быстро! Но это не сравнение, чтобы уменьшить с concat.

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
Mr_and_Mrs_D
28 мая 2017 в 13:20
1

Хм, чтобы быть честным, второй пример тоже должен быть списком (или первым кортежем?)

kaya3
18 декабря 2019 в 20:38
3

Использование таких небольших входных данных - не лучшее сравнение. Для 1000 последовательностей длиной 1000 я получаю 0,037 секунды для list(chain.from_iterable(...)) и 2,5 секунды для reduce(concat, ...). Проблема в том, что reduce(concat, ...) имеет квадратичное время выполнения, тогда как chain линейно.

avatar
mdh
17 июля 2016 в 12:57
15

Также можно использовать плоский NumPy:

import numpy as np
list(np.array(l).flat)

Работает, только если подсписки имеют одинаковые размеры.

avatar
Shawn Chin
4 июня 2009 в 21:06
1888

Вы можете использовать itertools.chain():

import itertools

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

Или вы можете использовать itertools.chain.from_iterable(), который не требует распаковки списка с помощью * оператора:

merged = list(itertools.chain.from_iterable(list2d))
Tim Dierks
3 сентября 2014 в 14:13
16

* - это хитрая вещь, которая делает chain менее простым, чем понимание списка. Вы должны знать, что цепочка объединяет только итерации, переданные в качестве параметров, а * заставляет список верхнего уровня расширяться до параметров, поэтому chain объединяет все эти итерации, но не спускается дальше. Я думаю, что это делает понимание более читаемым, чем использование цепочки в данном случае.

ShadowRanger
12 ноября 2015 в 20:26
91

@TimDierks: Я не уверен, что «это требует понимания синтаксиса Python» - это аргумент против использования данной техники в Python. Конечно, сложное использование может сбить с толку, но оператор splat обычно полезен во многих обстоятельствах, и это не делает его особенно непонятным; отказ от всех языковых функций, которые не обязательно очевидны для начинающих пользователей, означает, что вы связываете одну руку за спиной. Можно также выбросить понимание списка, пока вы занимаетесь этим; пользователи из других источников найдут for цикл, который неоднократно append казался более очевидным.

avatar
Igor Krivokon
4 июня 2009 в 20:47
30

Причина, по которой ваша функция не работает, заключается в том, что extend расширяет массив на месте и не возвращает его. Вы все еще можете вернуть x из лямбда, используя что-то вроде этого:

reduce(lambda x,y: x.extend(y) or x, l)

Примечание: расширение более эффективно, чем + в списках.

agf
24 сентября 2011 в 10:12
8

extend лучше использовать как newlist = [], extend = newlist.extend, for sublist in l: extend(l), поскольку он позволяет избежать (довольно больших) накладных расходов на lambda, поиск атрибутов на x и or.

Markus Dutschke
2 июля 2019 в 12:24
0

для python 3 добавьте from functools import reduce

avatar
Nadia Alramli
4 июня 2009 в 20:46
45

Беру свое заявление обратно. сумма не является победителем. Хотя быстрее, когда список невелик. Но производительность значительно снижается при увеличении списков.

>>> 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

Суммарная версия все еще выполняется более минуты и еще не обработана!

Для средних списков:

>>> 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

Использование небольших списков и timeit: number = 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
Alex Martelli
4 июня 2009 в 21:07
25

для действительно небольшого списка, например один с 3 подсписками, возможно, - но поскольку производительность sum идет с O (N ** 2), в то время как понимание списка идет с O (N), простое увеличение входного списка немного изменит ситуацию - действительно, LC будет " бесконечно быстрее », чем сумма на пределе по мере роста N. Я отвечал за разработку суммы и ее первую реализацию в среде выполнения Python, и мне все еще жаль, что я не нашел способ эффективно ограничить ее суммированием чисел (в чем он действительно хорош) и заблокировать "привлекательные неудобства", которые он предлагает людям. кто хочет "суммировать" списки ;-).

avatar
Andrea Ambu
4 июня 2009 в 20:38
41

Вам не нужно расширять . Это должно работать нормально:

reduce(lambda x, y: x+y, l)
andorov
19 января 2017 в 18:15
10

для python3 from functools import reduce

Mr_and_Mrs_D
29 мая 2017 в 12:04
0

Извините, это очень медленно, посмотрите остальные ответы

Asfand Qazi
7 сентября 2018 в 13:36
1

Это, безусловно, самое простое для понимания, но короткое решение, которое работает на Python 2 и 3. Я понимаю, что многие люди, занимающиеся Python, занимаются обработкой данных, где требуется обработать огромные объемы данных, и поэтому очень заботятся о скорости, но когда вы пишут сценарий оболочки и имеют всего несколько десятков элементов в нескольких подсписках, тогда это идеально.

avatar
Triptych
4 июня 2009 в 20:35
1083

Примечание автора : Это неэффективно. Но весело, потому что моноиды потрясающие. Это не подходит для производственного кода Python.

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

Это просто суммирует элементы итерации, переданные в первом аргументе, обрабатывая второй аргумент как начальное значение суммы (если не указано, вместо него используется 0, и в этом случае будет выдана ошибка).

Поскольку вы суммируете вложенные списки, вы фактически получаете [1,3]+[2,4] как результат sum([[1,3],[2,4]],[]), что равно [1,3,2,4].

Обратите внимание, что работает только со списками списков. Для списков списков списков вам понадобится другое решение.

andrewrk
15 июня 2010 в 18:55
119

это довольно аккуратно и умно, но я бы не стал его использовать, потому что его сложно читать.

Mike Graham
25 апреля 2012 в 18:24
94

Это алгоритм художника Шлемиэля joelonsoftware.com/articles/fog0000000319.html - излишне неэффективный, а также излишне уродливый.

ulidtko
3 декабря 2014 в 10:35
48

Операция добавления к спискам образует Monoid , что является одной из самых удобных абстракций для понимания операции + в общем смысле (не ограничиваясь только числами). . Так что этот ответ заслуживает от меня +1 за (правильное) рассмотрение списков как моноида. Производительность вызывает беспокойство ...

jhegedus
5 октября 2015 в 08:51
9

@andrewrk Что ж, некоторые люди думают, что это самый чистый способ сделать это: youtube.com/watch?v=IOiZatlZtGU тем, кто не понимает, почему это круто, просто нужно подождать несколько десятилетий, пока все так делают :) давайте использовать языки программирования (и абстракции), которые открыты, а не изобретены, Monoid обнаружен.

Jean-François Fabre♦
31 июля 2017 в 18:04
13

это очень неэффективный способ из-за квадратичного аспекта суммы.

ds4940
4 января 2018 в 16:46
4

В этой статье объясняется математика неэффективности mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python

isaaclw
8 февраля 2018 в 20:04
1

Кажется, это не работает в python 2.7, если я не делаю что-то не так. Когда я имею дело со строками: TypeError: can only concatenate list (not "str") to list Когда я меняю его на ints, я получаю ту же ошибку, но s/str/int/. Я предполагал, что «сумма» - стандартный оператор ...?

naught101
20 февраля 2018 в 01:28
1

Так ... тогда это отлично подходит для коротких случаев?

whackamadoodle3000
18 июля 2018 в 12:12
0

@isaaclw затем поместите '' вместо []

Jean-François Fabre♦
25 августа 2018 в 13:44
3

@ ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 no, sum защищен от суммирования строк. Вы должны использовать str.join

mortonjt
29 октября 2018 в 22:22
0

Обратите внимание, что это работает не во всех ситуациях. Возможно, вам придется выполнить sum (l + [[]]) вместо

Boris
24 марта 2020 в 18:15
1

@jhegedus «давайте использовать языки программирования, которые были открыты, а не изобретены», и все же вы здесь, используете Python. Люди предпочитают вещи, предназначенные для людей.

Triptych
25 марта 2020 в 18:10
0

@ Борис, это была не его цитата.

Boris
25 марта 2020 в 19:57
0

@Triptych, как это повлияет на то, что я сказал?

Tushar Sadhwani
28 апреля 2020 в 20:11
1

Обновите это: начиная с Python 3.8, теперь вы можете использовать sum(l, start=[]), что намного понятнее.

Nephanth
19 июня 2020 в 09:56
6

Как функциональный программист, я не понимаю, почему вы сочли это уродливым или непонятным: в Python суммирование двух списков объединяет их. Итак, я считаю, что использование суммы в списке списков является наиболее интуитивно понятным подходом к объединению всех элементов указанного списка ... (хотя это бессмысленно)

avatar
Greg Hewgill
4 июня 2009 в 20:35
231
>>> from functools import reduce
>>> 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() в вашем примере изменяет x вместо того, чтобы возвращать полезное значение (которое ожидает functools.reduce()).

Более быстрый способ сделать версию reduce -

>>> 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]
agf
24 сентября 2011 в 10:04
32

reduce(operator.add, l) будет правильным способом сделать версию reduce. Встроенные функции быстрее, чем лямбды.