Интересно, есть ли ярлык для создания простого списка из списка списков в Python.
Я могу сделать это в for
цикле, но, может быть, есть какой-нибудь крутой «однострочник»? Я попробовал это с 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'
Ответы:
Учитывая список списков
l
,flat_list = [item for sublist in l for item in sublist]
что значит:
быстрее, чем ярлыки, опубликованные до сих пор. (
l
это список, чтобы сгладить.)Вот соответствующая функция:
В качестве доказательства вы можете использовать
timeit
модуль в стандартной библиотеке:Объяснение: ярлыки, основанные на
+
(включая подразумеваемое использование вsum
), по необходимости,O(L**2)
когда есть L подсписков - поскольку список промежуточных результатов продолжает увеличиваться, на каждом шаге выделяется новый объект списка промежуточных результатов, и все элементы в предыдущем промежуточном результате его необходимо скопировать (а также добавить несколько новых в конце). Итак, для простоты и без фактической потери общности, скажем, у вас есть L подсписков из I элементов каждый: первые I элементы копируются туда и обратно L-1 раз, вторые I элементы L-2 раза и т. Д .; Общее количество копий I раз суммы х для й от 1 до L исключенного, тI * (L**2)/2
.Понимание списка только генерирует один список, один раз, и копирует каждый элемент (из его первоначального места жительства в список результатов) также ровно один раз.
источник
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))'
. Он работает чуть более чем в два раза быстрее, чем понимание вложенного списка, что является самой быстрой из представленных здесь альтернатив.list()
чтобы реализовать итератор в списке.list(itertools.chain.from_iterable(l))
это лучше всего - как отмечено в других комментариях и ответе Шона.Вы можете использовать
itertools.chain()
:Или вы можете использовать,
itertools.chain.from_iterable()
который не требует распаковки списка с*
оператором :источник
*
сложная вещь, которая делаетchain
менее простой, чем понимание списка. Вы должны знать, что цепочка объединяет только итерируемые элементы, передаваемые в качестве параметров, и * заставляет список верхнего уровня расширяться до параметров, поэтомуchain
объединяет все эти итерируемые элементы, но не опускается дальше. Я думаю, что это делает понимание более читабельным, чем использование цепочки в этом случае.for
петлю, которая становитсяappend
все более очевидной.list = [["abc","bcd"],["cde","def"],"efg"]
приведет к выводу["abc", "bcd", "cde", "def", "e", "f", "g"].
*
оператор не может быть использован в Python2Примечание автора : это неэффективно. Но весело, потому что моноиды потрясающие. Это не подходит для производственного кода Python.
Это просто суммирует элементы итерируемого, переданного в первом аргументе, обрабатывая второй аргумент как начальное значение суммы (если не задано,
0
вместо этого используется, и этот случай выдаст вам ошибку).Поскольку вы суммируете вложенные списки, вы фактически получаете
[1,3]+[2,4]
результатsum([[1,3],[2,4]],[])
, который равен[1,3,2,4]
.Обратите внимание, что работает только со списками списков. Для списков списков списков вам понадобится другое решение.
источник
Monoid
, которая является одной из наиболее удобных абстракций для представления+
операции в общем смысле (не ограничиваясь только числами). Так что этот ответ заслуживает от меня +1 за (правильную) обработку списков как моноидов. Спектакль касается, хотя ...Я протестировал большинство предлагаемых решений с помощью perfplot ( мой любимый проект, по сути, обертка вокруг
timeit
), и нашелбыть самым быстрым решением, когда объединяются много небольших списков и несколько длинных списков. (
operator.iadd
одинаково быстро.)Код для воспроизведения сюжета:
источник
extend()
Метод в вашем примере изменяетx
вместо возвращения полезной стоимости (котораяreduce()
ожидает).Более быстрый способ сделать
reduce
версию будетисточник
reduce(operator.add, l)
будет правильным способом сделатьreduce
версию. Встроенные модули работают быстрее, чем лямбды.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)
0.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)
0.025218963623046875integers
. Но что, если список содержитstring
?operator.add
функция работает одинаково хорошо как для списков целых чисел, так и для списков строк.Не изобретайте велосипед, если вы используете Django :
... Панды :
... Itertools :
... Матплотлиб
... Unipath :
... Setuptools :
источник
flatten = itertools.chain.from_iterable
должен быть правильный ответlist_of_menuitems = [1, 2, [3, [4, 5, [6]]]]
это приведет на:[1, 2, 3, 4, 5, 6]
. То, что я скучаю, это ровный уровень.Вот общий подход, который применяется к числам , строкам , вложенным спискам и смешанным контейнерам.
Код
Примечания :
yield from flatten(x)
можно заменитьfor sub_x in flatten(x): yield sub_x
collection.abc
кtyping
модулю.демонстрация
Ссылка
источник
more_itertools
среди других обсуждаемых в этом посте. Приветствия.traverse
также может быть хорошим названием для этого способа дерева, в то время как я бы оставил его менее универсальным для этого ответа, придерживаясь вложенных списков.if hasattr(x, '__iter__')
вместо импорта / проверки,Iterable
и это также исключит строки.Если вы хотите сгладить структуру данных, где вы не знаете, как глубоко она вложена, вы можете использовать 1
iteration_utilities.deepflatten
Это генератор, поэтому вам нужно привести результат к
list
или явно перебрать его.Для того, чтобы сгладить только один уровень , и если каждый из элементов сам по себе итерации вы можете также использовать
iteration_utilities.flatten
который сам по себе является лишь тонкой оболочкой вокругitertools.chain.from_iterable
:Просто добавим немного времени (на основе ответа Нико Шлёмера, который не включает функцию, представленную в этом ответе):
Это логарифмический сюжет для огромного диапазона значений. Для качественного рассуждения: чем ниже, тем лучше.
Результаты показывают , что если итератор содержит лишь несколько внутренних итерируемые тогда
sum
будет быстрым, однако в течение длительных итерируемых только вitertools.chain.from_iterable
,iteration_utilities.deepflatten
или вложенные пониманиях имеют достаточную производительность приitertools.chain.from_iterable
самом быстром (как уже заметил Нико Schlömer).1 Отказ от ответственности: я автор этой библиотеки
источник
sum
больше не работает на произвольных последовательностей , как это начинается с0
, что делаетfunctools.reduce(operator.add, sequences)
замену (не мы рады , что они удаленыreduce
из встроенных команд?). Когда типы известны, это может быть быстрее для использованияtype.__add__
.sum
. Вы случайно не знаете, на каких версиях Python он перестал работать?0
это просто начальное значение по умолчанию, поэтому оно работает, если кто-либо использует аргумент start для запуска с пустым списком ... но он все еще в особых случаях и говорит мне использовать join. Это реализуетfoldl
вместоfoldl1
. Та же проблема всплывает в 2.7.Я забираю свое заявление обратно. сумма не победитель. Хотя это быстрее, когда список невелик. Но производительность значительно ухудшается с большими списками.
Суммарная версия все еще работает более минуты, и она еще не обработана!
Для средних списков:
Используя маленькие списки и timeit: number = 1000000
источник
Там, кажется, путаница с
operator.add
! Когда вы добавляете два списка вместе, правильный термин для этогоconcat
, а не добавить.operator.concat
это то, что вам нужно использовать.Если вы думаете, функционально, это так просто:
Вы видите, что Reduced уважает тип последовательности, поэтому, когда вы предоставляете кортеж, вы возвращаете кортеж. Давайте попробуем со списком:
Ага, вы получите список обратно.
Как насчет производительности ::
from_iterable
довольно быстро! Но это не с чем сравниватьconcat
.источник
list(chain.from_iterable(...))
и 2,5 секунды дляreduce(concat, ...)
. Проблема в том, чтоreduce(concat, ...)
имеет квадратичное время выполнения, тогда какchain
является линейным.Почему вы используете расширение?
Это должно работать нормально.
источник
from functools import reduce
Рассмотрите возможность установки
more_itertools
пакета.Он поставляется с реализацией для
flatten
( источник , из рецептов itertools ):Начиная с версии 2.4, вы можете сгладить более сложные вложенные итерации с помощью
more_itertools.collapse
( источник , предоставленный abarnet).источник
Причина, по которой ваша функция не сработала, заключается в том, что расширение расширяет массив на месте и не возвращает его. Вы все еще можете вернуть x из лямбды, используя что-то вроде этого:
Примечание: расширение более эффективно, чем + в списках.
источник
extend
лучше использовать в качествеnewlist = []
,extend = newlist.extend
,for sublist in l: extend(l)
поскольку он избегает (довольно большой) накладных расходов изlambda
, что поиск атрибутx
, иor
.from functools import reduce
источник
def flatten(l, a=None): if a is None: a = []
[...]Рекурсивная версия
источник
matplotlib.cbook.flatten()
будет работать для вложенных списков, даже если они вложены глубже, чем в примере.Результат:
Это в 18 раз быстрее, чем подчеркивание ._. Flatten:
источник
Принятый ответ не работал для меня при работе с текстовыми списками переменной длины. Вот альтернативный подход, который работал для меня.
Принимается ответ, который не сработал:
Новые предложенные решения , которые сделали работу для меня:
источник
Плохая особенность функции Anil, описанной выше, состоит в том, что она требует, чтобы пользователь всегда вручную указывал второй аргумент, чтобы он был пустым списком
[]
. Вместо этого это должно быть по умолчанию. Из-за того, как работают объекты Python, они должны быть установлены внутри функции, а не в аргументах.Вот рабочая функция:
Тестирование:
источник
Следующее кажется мне самым простым:
источник
Можно также использовать квартиру NumPy :
Редактировать 02.11.2016: Работает только тогда, когда подсписки имеют одинаковые размеры.
источник
Вы можете использовать NumPy:
flat_list = list(np.concatenate(list_of_list))
источник
[1, 2, [3], [[4]], [5, [6]]]
Если вы готовы отказаться от небольшого количества скорости для более чистого вида, то вы можете использовать
numpy.concatenate().tolist()
илиnumpy.concatenate().ravel().tolist()
:Вы можете узнать больше здесь в документах numpy.concatenate и numpy.ravel
источник
[1, 2, [3], [[4]], [5, [6]]]
Самое быстрое решение, которое я нашел (для большого списка в любом случае):
Выполнено! Конечно, вы можете превратить его обратно в список, выполнив list (l)
источник
Простой код для
underscore.py
фанатов пакетовЭто решает все проблемы сглаживания (ни один элемент списка или сложное вложение)
Вы можете установить
underscore.py
с помощью пипаисточник
источник
[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Примечание : ниже применяется к Python 3.3+, потому что он использует
yield_from
.six
это также сторонний пакет, хотя и стабильный. С другой стороны, вы могли бы использоватьsys.version
.В случае
obj = [[1, 2,], [3, 4], [5, 6]]
, все решения здесь хороши, включая понимание списка иitertools.chain.from_iterable
.Однако рассмотрим этот чуть более сложный случай:
Здесь есть несколько проблем:
6
это просто скаляр; это не повторяется, поэтому приведенные выше маршруты потерпят неудачу.'abc'
, является технически итерацией (всеstr
с есть). Однако, читая немного между строк, вы не хотите рассматривать это как таковой - вы хотите рассматривать это как отдельный элемент.[8, [9, 10]]
сам по себе является вложенным итерируемым. Базовое понимание списка иchain.from_iterable
только извлечение "1 уровень вниз".Вы можете исправить это следующим образом:
Здесь вы проверяете, что подэлемент (1) итерируем с
Iterable
ABCitertools
, но также хотите убедиться, что (2) элемент не является «строковым».источник
yield from
наfor
цикл, напримерfor x in flatten(i): yield x
Этот Кодекс также работает хорошо, поскольку он просто расширяет список полностью. Хотя это очень похоже, но есть только один цикл. Так что это сложнее, чем добавление 2 для циклов.
источник
Преимущество этого решения перед большинством других заключается в том, что если у вас есть такой список:
в то время как большинство других решений выдают ошибку, это решение обрабатывает их.
источник
Возможно, это не самый эффективный способ, но я подумал о том, чтобы поставить одну линию (на самом деле две линии). Обе версии будут работать с вложенными списками произвольной иерархии и использовать языковые функции (Python3.5) и рекурсию.
Выход
Это работает в глубине в первую очередь. Рекурсия снижается до тех пор, пока не находит элемент не из списка, затем расширяет локальную переменную
flist
и затем откатывает ее до родителя. Всякий раз, когдаflist
возвращается, он распространяется на родителейflist
в понимании списка. Поэтому в корне возвращается плоский список.Вышеупомянутый создает несколько локальных списков и возвращает их, которые используются для расширения списка родителей. Я думаю, что путь к этому может быть создание gloabl
flist
, как показано ниже.Выход снова
Хотя в настоящее время я не уверен в эффективности.
источник
Еще один необычный подход, который работает для гетеро- и однородных списков целых чисел:
источник
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]
flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
[int(e.strip('[ ]')) for e in str(deep_list).split(',')]
. Но я бы предложил придерживаться предложения Deleet для реальных случаев использования. Он не содержит хакерских преобразований типов, он быстрее и более универсален, потому что он, естественно, также обрабатывает списки со смешанными типами.