Объединение внутренних списков или целых чисел [дубликаты]

Я чувствую, что упускаю что-то очевидное, но это так... Я хотел бы перейти от:

lst = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]

to:

output = [0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]

Я могу сделать это с помощью цикла for, например:

output = []
for l in lst:
    if hasattr(l, '__iter__'):
        output.extend(l)
    else:
        output.append(l)

Может быть, цикл for в порядке, но кажется, что должен быть более элегантный способ сделать это... Попытка сделать это с помощью numpy кажется еще более запутанной, потому что рваные массивы не так легко обрабатываются... так что вы можете' т (например):

output = np.asanyarray(lst).flatten().tolist()

Заранее спасибо.

Обновление:

Вот мое сравнение двух методов, предоставленных @T.J и @Ashwini - спасибо обоим!

In [5]: %paste
from itertools import chain
from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
    for x in lis:
        if isinstance(x, Iterable) and not isinstance(x, basestring):
            yield x
        else:
            yield [x]

%timeit list(chain.from_iterable(solve(lis)))

%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
100000 loops, best of 3: 10.1 us per loop
100000 loops, best of 3: 8.12 us per loop

Обновление 2:

...
lis = lis *10**5
%timeit list(chain.from_iterable(solve(lis)))

%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
1 loops, best of 3: 699 ms per loop
1 loops, best of 3: 698 ms per loop

person blazetopher    schedule 21.06.2013    source источник
comment
... или для нерегулярных списков попробуйте это.   -  person Aya    schedule 21.06.2013
comment
Да, конечно - извиняюсь за дубликат, не подумал искать "сглаживание" :/   -  person blazetopher    schedule 22.06.2013
comment
Вероятно, потому что (на Python 2.x) вы можете просто сделать from compiler.ast import flatten; flatten([[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]) -> [0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]   -  person Aya    schedule 22.06.2013
comment
Измените размер списка перед синхронизацией решений: lis = lis *10**5, itertools.chain быстрее, чем обычное понимание списка для списка большего размера, для небольших списков время не имеет значения.   -  person Ashwini Chaudhary    schedule 22.06.2013
comment
Очень хороший момент - похоже, что lis=lis *10**5 в значительной степени является переломным моментом между двумя методологиями.   -  person blazetopher    schedule 22.06.2013
comment
@blazetopher ну, это странно, itertools.chain быстрее работает и для умеренного количества элементов. И на самом деле это предпочтительнее обычного понимания списка для выравнивания списка. см.: stackoverflow.com/a/408281/846892   -  person Ashwini Chaudhary    schedule 23.06.2013


Ответы (3)


Вот довольно простой подход, использующий понимание списка:

>>> data = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
>>> [a for x in data for a in (x if isinstance(x, list) else [x])]
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]

Вот сравнение времени, похоже, что моя версия немного быстрее (обратите внимание, что я изменил свой код, чтобы использовать collections.Iterable, чтобы убедиться, что сравнение справедливо):

In [9]: %timeit list(chain.from_iterable(solve(data)))
100000 loops, best of 3: 9.22 us per loop

In [10]: %timeit [a for x in data for a in (x if isinstance(x, Iterable) else [x])]
100000 loops, best of 3: 6.45 us per loop
person Andrew Clark    schedule 21.06.2013
comment
Спасибо! Я сам был очень близок к этому (не опубликовал), но не понял правильно логическую часть. Знаете ли вы, является ли это более/менее эффективным, чем ответ @Ashwini? - person blazetopher; 22.06.2013
comment
@blazetopher Только что добавил сравнение времени. - person Andrew Clark; 22.06.2013
comment
Спасибо за обновление! - person blazetopher; 22.06.2013
comment
Не используйте input в качестве имени переменной. - person Ashwini Chaudhary; 22.06.2013
comment
@AshwiniChaudhary Хороший вопрос, спасибо! - person Andrew Clark; 22.06.2013
comment
@AshwiniChaudhary Согласен, глупая оплошность с моей стороны - person blazetopher; 22.06.2013

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

>>> from itertools import chain
>>> from collections import Iterable
>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
    for x in lis:
        if isinstance(x, Iterable) and not isinstance(x, basestring):
            yield x
        else:
            yield [x]
...             

>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]

Отлично работает и для строк:

>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], "234"]
>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, '234']

Сравнение времени:

>>> lis = lis *(10**4)
#modified version of FJ's answer that works for strings as well
>>> %timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
10 loops, best of 3: 110 ms per loop

>>> %timeit list(chain.from_iterable(solve(lis)))
1 loops, best of 3: 98.3 ms per loop
person Ashwini Chaudhary    schedule 21.06.2013
comment
Спасибо, это хорошее решение. Любая идея, если это более/менее эффективно, чем ответ @ F.J? - person blazetopher; 22.06.2013
comment
Решение @blazetopher FJ будет быстрее, поскольку он использует понимание списка, я использовал функцию, чтобы сделать решение более читабельным. И мое решение также обрабатывает случай, если в данных присутствуют строки. - person Ashwini Chaudhary; 22.06.2013
comment
преимущества обоих методов! Спасибо еще раз! - person blazetopher; 22.06.2013
comment
@blazetopher взгляните на мое обновленное решение. - person Ashwini Chaudhary; 22.06.2013
comment
Интересно, что ваши результаты timeit отличаются от результатов @TJ, я редактирую свой пост, чтобы показать свое сравнение - person blazetopher; 22.06.2013
comment
@blazetopher обратите внимание, что я также изменил решение FJ для обработки строк. - person Ashwini Chaudhary; 22.06.2013
comment
@AshwaniChaudhary - да, спасибо. Когда я запускаю сравнение timeit, понимание выходит для меня немного быстрее по какой-то причине. - person blazetopher; 22.06.2013

Вы можете использовать генератор, чтобы список состоял только из итераций

((i if isinstance(i,Iterable) else [i]) 

А затем используйте любой из следующих методов, чтобы объединить их в один список:

не могли бы вы попробовать sum

from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
sum(((i if isinstance(i,Iterable) else [i]) for i in lis), [])

Вы должны указать начальное значение для суммы [], чтобы начать с суммы

или itertools.chain() но в этом случае Вы должны распаковать Ваш список верхнего уровня в набор списков

import itertools
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
list(itertools.chain(*((i if isinstance(i,Iterable) else [i]) for i in lis)))

или просто список понимания

[a for x in input for a in (x if isinstance(x, Iterable) else [x])]

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

person oleg    schedule 21.06.2013
comment
Ваши методы будут работать только в том случае, если все элементы lis являются итерируемыми. - person Ashwini Chaudhary; 22.06.2013
comment
Вы правы, я должен позаботиться об этом - person oleg; 22.06.2013