присоединиться к списку списков в python [дубликат]
этот вопрос уже есть ответ здесь:
является ли короткий синтаксис для объединения списка списков в один список( или итератор) в python?
например,у меня есть список следующим образом, и я хочу перебирать a, b и c.
x = [["a","b"], ["c"]]
лучшее, что я могу придумать, это следующее.
result = []
[ result.extend(el) for el in x]
for el in result:
print el
15 ответов:
Если вы идете только на один уровень глубже, вложенное понимание также будет работать:
>>> 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']
Это известно как сглаживание, и есть много реализаций там:
Как насчет этого, хотя он будет работать только для 1 уровня глубокой вложенности:
>>> x = [["a","b"], ["c"]] >>> for el in sum(x, []): ... print el ... a b cиз этих ссылок, по-видимому, наиболее полной-быстрой-элегантной-etc является реализация следующее:
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)
это работает рекурсивно для бесконечно вложенные элементы:
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']
сравнение производительности:
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 на Windows XP 32-бит, но @temoto в комментариях выше получил
from_iterableбудет быстрее, чемmap+extend, Так что это довольно зависит от платформы и ввода.Держись подальше от
sum(big_list, [])
опоздал на вечеринку, но ...
Я новичок в 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]
то, что вы описываете, известно как уплощение список, и с этим новым знанием вы сможете найти много решений для этого в 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
всегда есть reduce (устарел до 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 >>>к сожалению, оператор plus для конкатенации списков не может использоваться в качестве функции - или повезло, если вы предпочитаете, чтобы лямбды были уродливыми для улучшения видимости.
Если вам нужен список, а не генератор, используйте
list():from itertools import chain x = [["a","b"], ["c"]] y = list(chain(*x))
или рекурсивная операция:
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
к сожалению, 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
у меня была аналогичная проблема, когда мне пришлось создать словарь, который содержал элементы массива и их количество. Ответ релевантен, потому что я сглаживаю список списков, получаю нужные мне элементы, а затем делаю группу и подсчитываю. Я использовал функцию карты Python для создания кортежа элемента, и это count и groupby над массивом. Обратите внимание, что groupby принимает сам элемент массива в качестве keyfunc. Как относительно новый кодер Python, я нахожу его более легким для понимания, будучи И питон тоже.
прежде чем обсуждать код, вот пример данных, которые я должен был сгладить сначала:
{ "_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])во-первых, я бы извлек всю коллекцию "entities", а затем для каждой коллекции entities выполните итерацию по словарю и извлеките атрибут name.
для одноуровневого сглаживания, если вы заботитесь о скорости, это быстрее, чем любой из предыдущих ответов при всех условиях, которые я пробовал. (То есть, если вам нужен результат в виде списка. Если вам нужно только перебирать его на лету, то пример цепи, вероятно, лучше.) Он работает путем предварительного выделения списка конечного размера и копирования частей по срезу (который является копией блока более низкого уровня, чем любой из методов итератора):
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, [])
Comments