python3 - 파이썬에서 목록의 목록을 플랫 목록으로 만들기




python3 array flatten (20)

나는 파이썬에서리스트의리스트에서 간단한리스트를 만드는 지름길이 있는지 궁금하다.

나는 for 루프에서 그렇게 할 수 있지만, 아마도 멋진 "one-liner"가있을 것인가? 나는 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'

numpy를 사용할 수 있습니다.
flat_list = list(np.concatenate(list_of_list))


가변 길이의 텍스트 기반 목록을 처리 할 때 받아 들인 대답이 나에게 도움이되지 못했습니다. 나에게 도움이되는 대체 접근법이 여기있다.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

작동하지 않는 수락 된 대답 :

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

나에게 맞는 새로운 제안 된 솔루션 :

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

내가 찾은 가장 빠른 해결책 (큰 목록에 대한) 어쨌든 :

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

끝난! 물론 list (l)를 실행하여 목록으로 되돌릴 수 있습니다.


다음은 숫자 , 문자열 , 중첩 목록 및 혼합 컨테이너에 적용되는 일반적인 접근 방식입니다.

암호

from collections import Iterable


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

참고 : Python 3 yield from flatten(x) for sub_x in flatten(x): yield sub_x 대체 할 수 있습니다 for sub_x in flatten(x): yield sub_x

데모

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

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

참고

  • 이 솔루션은 Beazley, D. 및 B. Jones 의 제조법에서 수정되었습니다 . Recipe 4.14, Python Cookbook 3rd Ed., O'Reilly Media Inc. Sebastopol, CA : 2013.
  • 초기 SO 게시물 , 아마도 최초 시위를 발견했습니다.

왜 확장을 사용합니까?

reduce(lambda x, y: x+y, l)

이 잘 작동합니다.


위의 Anil 함수의 나쁜 특징은 사용자가 빈리스트 [] 되도록 항상 두 번째 인수를 수동으로 지정해야한다는 것입니다. 이것은 기본값이되어야합니다. 파이썬 객체가 작동하는 방식 때문에, 이것들은 인자가 아닌 함수 내부에서 설정되어야합니다.

다음은 작동하는 함수입니다.

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

테스트 :

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]

좀 더 깔끔한 화면을 위해 약간의 속도를 포기할 의사가 있다면 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.concatenatenumpy.ravel 문서에서 자세한 내용을 확인할 수 있습니다.


중첩 된 깊이를 모르는 곳에서 데이터 구조를 평탄화하려면 iteration_utilities.deepflatten 1을 사용할 수 있습니다.

>>> 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 으로 캐스팅하거나 명시 적으로 반복해야합니다.

하나의 레벨 만 평평하게하고 각 항목 자체가 iterable 인 경우 iteration_utilities.flatten 을 사용할 수도 있습니다. iteration_utilities.flatten 자체는 itertools.chain.from_iterable 주위의 단순한 래퍼입니다.

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

그냥 몇 가지 타이밍을 추가하려면 (이 답변에 표시된 기능을 포함하지 않았 니코 Schlömer 대답을 기반으로) :

광범위한 범위의 값을 수용 할 수있는 로그 - 로그 플롯입니다. 질적 추론을 위해 : 낮추는 것이 좋습니다.

결과는 iterable이 내부 iterables를 몇 개만 포함하면 sum 가 가장 빠릅니다. 그러나 itertools.chain.from_iterable , iteration_utilities.deepflatten 또는 중첩 된 이해가 itertools.chain.from_iterable 이 가장 itertools.chain.from_iterable 합리적인 성능을 발휘합니다. (이미 Nico Schlömer에 의해 주목 받듯이).

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 면책 조항 : 저는 해당 도서관의 저자입니다.


함수가 작동하지 않는 이유 : 확장은 배열을 내부로 확장하고 반환하지 않습니다. 몇 가지 트릭을 사용하여 람다에서 x를 반환 할 수 있습니다.

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

참고 : extend는 목록에서 +보다 더 효율적입니다.


itertools.chain() 사용할 수 있습니다.

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

또는 Python> = 2.6 이상인 경우 itertools.chain.from_iterable() 을 사용하십시오.이 경우 목록의 압축을 풀 필요가 없습니다.

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

이 방법은 틀림없이 [item for sublist in l for item in sublist] 보다 읽기 쉽고 빠릅니다.

[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 45.2 usec per loop
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[[email protected]]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 522 usec per loop
[[email protected]]$ python --version
Python 2.7.3

perfplot (내 애완 동물 프로젝트, 본질적으로 timeit 대한 래퍼)을 사용하여 제안 된 솔루션을 테스트 perfplot

list(itertools.chain.from_iterable(a))

(10 개 이상의 목록이 연결될 경우) 가장 빠른 솔루션이 될 수 있습니다.

플롯을 재현하는 코드 :

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 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,
    kernels=[
        forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
        numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )

underscore.py 패키지 팬을위한 간단한 코드

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

모든 병목 현상을 해결합니다 (목록 항목이 없거나 복잡한 중첩)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

pip와 함께 underscore.py 를 설치할 수 있습니다.

pip install underscore.py

operator.add 와 혼란이있는 것 같습니다! 두리스트를 함께 추가 할 때, 그 정확한 용어는 concat 이지 추가는 아닙니다. operator.concat 을 사용하면됩니다.

당신이 기능적이라고 생각한다면, 이것만큼 쉽습니다. :

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

나는 내 진술을 되찾았다. 총액이 승자가 아닙니다. 목록이 작을수록 더 빠르지 만. 그러나 큰 목록에서는 성능이 크게 떨어집니다.

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

합계 버전은 1 분 이상 계속 실행 중이며 아직 처리하지 않았습니다!

중간 목록 :

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

참고 : 아래는 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는 스칼라 일뿐입니다. iterable이 아니므로 위의 경로는 여기서 실패합니다.
  • 하나 개의 요소는 '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)가 IterableABC에서 반복 itertools가능한지 확인하고 (2) 요소가 "문자열 유사" 이 아닌지 확인합니다 .


실제 스택 데이터 구조를 사용하여 스택에 대한 재귀 호출을 간단하게 피할 수 있습니다.

alist = [1,[1,2],[1,2,[4,5,6],3, "33"]]
newlist = []

while len(alist) > 0 :
  templist = alist.pop()
  if type(templist) == type(list()) :
    while len(templist) > 0 :
      temp = templist.pop()
      if type(temp) == type(list()) :
        for x in temp :
          templist.append(x)
      else :
        newlist.append(temp)
  else :
    newlist.append(templist)
print(list(reversed(newlist)))

이것은 가장 효율적인 방법은 아니지만 한 줄짜리 (실제로는 두 줄짜리)를 넣는다고 생각했습니다. 두 버전 모두 임의의 계층 구조의 중첩 목록에서 작동하며 언어 기능 (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아래처럼 gloabl을 만들 수 있다고 생각 한다.

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]

지금은 효율성에 대해서는 확신 할 수 없지만.


def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

flat_list = []
for i in list_of_list:
    flat_list+=i

이 코드는 목록 전체를 확장하여 잘 작동합니다. 그것은 비슷하지만 루프 용으로 만 존재합니다. 따라서 루프에 2를 더하는 것보다 복잡성이 적습니다.


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

즉,

for sublist in l:
    for item in sublist:
        flat_list.append(item)

지금까지 게시 된 바로 가기보다 빠릅니다. ( l 은 평평하게하는 목록입니다.)

다음은 해당 함수입니다.

flatten = lambda l: [item for sublist in l for item in sublist]

항상 그렇듯이 표준 라이브러리에서 timeit 모듈을 사용할 수 있습니다.

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

설명 : L 서브리스트가있을 때 + (묵시적 사용을 포함하여)를 기반으로 한 바로 가기는 필요에 따라 L 개의 서브리스트가있을 때 O(L**2) - 중간 결과 목록이 각 단계에서 새로운 중간 결과 목록 객체가 할당되고 이전 중간 결과의 모든 항목을 복사해야합니다 (끝에 추가 된 몇 가지 새로운 항목). 그래서 (단순함을 위해서 그리고 실제로는 일반성을 잃지 않고) 여러분은 각각 I 항목의 L 부분 목록을 가지고 있다고 말합니다. 처음 I 항목은 앞뒤로 L-1 번, 두 번째 I 항목은 L-2 번 등으로 복사됩니다. 총 복사 매수는 1에서 L까지 제외 된 x에 대한 x의 합계, 즉 I * (L**2)/2 입니다.

목록 이해력은 한 번만 한 목록을 생성하고 각 항목을 (원래 거주지에서 결과 목록으로) 정확히 한 번 복사합니다.







flatten