Список списков списков в python

Является ли короткий синтаксис для объединения списка списков в один список (или итератор) в python?

Например, у меня есть список следующим образом, и я хочу перебирать значения a, b и c.

x = [["a","b"], ["c"]]

Лучшее, что я могу придумать, заключается в следующем.

result = []
[ result.extend(el) for el in x] 

for el in result:
  print el

Ответ 1

import itertools
a = [['a','b'], ['c']]
print(list(itertools.chain.from_iterable(a)))

Ответ 2

Если вы продвигаетесь только на один уровень, вложенное понимание также будет работать:

>>> x = [["a","b"], ["c"]]
>>> [inner
...     for outer in x
...         for inner in outer]
['a', 'b', 'c']

В одной строке это будет:

>>> [j for i in x for j in i]
['a', 'b', 'c']

Ответ 3

x = [["a","b"], ["c"]]

result = sum(x, [])

Ответ 4

Это известно как сплющивание, и там есть много реализаций:

Как насчет этого, хотя он будет работать только для 1 уровня глубокого вложения:

>>> x = [["a","b"], ["c"]]
>>> for el in sum(x, []):
...     print el
...
a
b
c

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

def flatten(l, ltypes=(list, tuple)):
    ltype = type(l)
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        i += 1
    return ltype(l)

Ответ 5

l = []
map(l.extend, list_of_lists)

кратчайшее!

Ответ 6

Это работает рекурсивно для бесконечно вложенных элементов:

def iterFlatten(root):
    if isinstance(root, (list, tuple)):
        for element in root:
            for e in iterFlatten(element):
                yield e
    else:
        yield root

Результат:

>>> b = [["a", ("b", "c")], "d"]
>>> list(iterFlatten(b))
['a', 'b', 'c', 'd']

Ответ 7

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

import itertools
import timeit
big_list = [[0]*1000 for i in range(1000)]
timeit.repeat(lambda: list(itertools.chain.from_iterable(big_list)), number=100)
timeit.repeat(lambda: list(itertools.chain(*big_list)), number=100)
timeit.repeat(lambda: (lambda b: map(b.extend, big_list))([]), number=100)
timeit.repeat(lambda: [el for list_ in big_list for el in list_], number=100)
[100*x for x in timeit.repeat(lambda: sum(big_list, []), number=1)]

Производство:

>>> import itertools
>>> import timeit
>>> big_list = [[0]*1000 for i in range(1000)]
>>> timeit.repeat(lambda: list(itertools.chain.from_iterable(big_list)), number=100)
[3.016212113769325, 3.0148865239060227, 3.0126415732791028]
>>> timeit.repeat(lambda: list(itertools.chain(*big_list)), number=100)
[3.019953987082083, 3.528754223385439, 3.02181439266457]
>>> timeit.repeat(lambda: (lambda b: map(b.extend, big_list))([]), number=100)
[1.812084445152557, 1.7702404451095965, 1.7722977998725362]
>>> timeit.repeat(lambda: [el for list_ in big_list for el in list_], number=100)
[5.409658160700605, 5.477502077679354, 5.444318360412744]
>>> [100*x for x in timeit.repeat(lambda: sum(big_list, []), number=1)]
[399.27587954973444, 400.9240571138051, 403.7521153804846]

Это с Python 2.7.1 на 32-разрядной Windows XP, но @temoto в комментариях выше получил from_iterable быстрее, чем map+extend, поэтому он довольно зависим от платформы и ввода.

Держитесь подальше от sum(big_list, [])

Ответ 8

Поздно к вечеринке, но...

Я новичок в python и исхожу из фона lisp. Это то, что я придумал (проверьте имена var для lulz):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

Кажется, нужно работать. Тест:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

возвращает:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]

Ответ 9

Если вам нужен список, а не генератор, используйте list():

from itertools import chain
x = [["a","b"], ["c"]]
y = list(chain(*x))

Ответ 10

То, что вы описываете, называется списком выравнивания, и благодаря этим новым знаниям вы сможете найти множество решений для этого в Google (нет встроенного метода сглаживания), Вот один из них: http://www.daniel-lemire.com/blog/archives/2006/05/10/flattening-lists-in-python/:

def flatten(x):
    flat = True
    ans = []
    for i in x:
        if ( i.__class__ is list):
            ans = flatten(i)
        else:
            ans.append(i)
    return ans

Ответ 11

Всегда уменьшаются (устаревшие до functools):

>>> x = [ [ 'a', 'b'], ['c'] ]
>>> for el in reduce(lambda a,b: a+b, x, []):
...  print el
...
__main__:1: DeprecationWarning: reduce() not supported in 3.x; use functools.reduce()
a
b
c
>>> import functools
>>> for el in functools.reduce(lambda a,b: a+b, x, []):
...   print el
...
a
b
c
>>>

К сожалению, оператор плюса для конкатенации списков не может использоваться как функция - или повезло, если вы предпочитаете, чтобы лямбды были уродливыми для улучшения видимости.

Ответ 12

Или рекурсивная операция:

def flatten(input):
    ret = []
    if not isinstance(input, (list, tuple)):
        return [input]
    for i in input:
        if isinstance(i, (list, tuple)):
            ret.extend(flatten(i))
        else:
            ret.append(i)
    return ret

Ответ 13

К сожалению, у Python нет простого способа сгладить списки. Попробуйте следующее:

def flatten(some_list):
    for element in some_list:
        if type(element) in (tuple, list):
            for item in flatten(element):
                yield item
        else:
            yield element

Что будет рекурсивно сгладить список; вы можете сделать

result = []
[ result.extend(el) for el in x] 

for el in flatten(result):
      print el

Ответ 14

Для одноуровневого сглаживания, если вам нужна скорость, это быстрее, чем любой из предыдущих ответов при любых условиях, которые я пробовал. (То есть, если вам нужен результат в виде списка. Если вам нужно только перебирать его на лету, то пример цепи, вероятно, лучше.) Он работает, предварительно распределяя список конечного размера и копируя части в по срезу (который является копией блока нижнего уровня, чем любой из методов итератора):

def join(a):
    """Joins a sequence of sequences into a single sequence.  (One-level flattening.)
    E.g., join([(1,2,3), [4, 5], [6, (7, 8, 9), 10]]) = [1,2,3,4,5,6,(7,8,9),10]
    This is very efficient, especially when the subsequences are long.
    """
    n = sum([len(b) for b in a])
    l = [None]*n
    i = 0
    for b in a:
        j = i+len(b)
        l[i:j] = b
        i = j
    return l

Отсортированный список времени с комментариями:

[(0.5391559600830078, 'flatten4b'), # join() above. 
(0.5400412082672119, 'flatten4c'), # Same, with sum(len(b) for b in a) 
(0.5419249534606934, 'flatten4a'), # Similar, using zip() 
(0.7351131439208984, 'flatten1b'), # list(itertools.chain.from_iterable(a)) 
(0.7472689151763916, 'flatten1'), # list(itertools.chain(*a)) 
(1.5468521118164062, 'flatten3'), # [i for j in a for i in j] 
(26.696547985076904, 'flatten2')] # sum(a, [])

Ответ 15

У меня была аналогичная проблема, когда мне приходилось создавать словарь, содержащий элементы массива и их счет. Ответ важен, потому что я сглаживаю список списков, получаю нужные мне элементы, а затем делаю группу и подсчитываю. Я использовал функцию отображения Python для создания кортежа элемента, и он подсчитывает и группирует по массиву. Обратите внимание, что groupby принимает элемент массива как keyfunc. Как относительно новый кодер Python, я считаю это более понятным для меня, будучи также Pythonic.

Прежде чем обсуждать код, вот пример данных, которые мне пришлось сначала сгладить:

{ "_id" : ObjectId("4fe3a90783157d765d000011"), "status" : [ "opencalais" ],
  "content_length" : 688, "open_calais_extract" : { "entities" : [
  {"type" :"Person","name" : "Iman Samdura","rel_score" : 0.223 }, 
  {"type" : "Company",  "name" : "Associated Press",    "rel_score" : 0.321 },          
  {"type" : "Country",  "name" : "Indonesia",   "rel_score" : 0.321 }, ... ]},
  "title" : "Indonesia Police Arrest Bali Bomb Planner", "time" : "06:42  ET",         
  "filename" : "021121bn.01", "month" : "November", "utctime" : 1037836800,
  "date" : "November 21, 2002", "news_type" : "bn", "day" : "21" }

Это результат запроса от Mongo. Код ниже выравнивает набор таких списков.

def flatten_list(items):
  return sorted([entity['name'] for entity in [entities for sublist in  
   [item['open_calais_extract']['entities'] for item in items] 
   for entities in sublist])

Сначала я извлечу всю коллекцию "сущностей", а затем для каждой коллекции сущностей, итерацию по словарю и извлечение атрибута имени.