list Pythonでリストのリストから平らなリストを作る





15 Answers

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()を使用して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]よりも読みやすくなり、より速く表示されます。

[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ 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
[me@home]$ python --version
Python 2.7.3
python list multidimensional-array flatten

私は、Pythonのリストのリストから単純なリストを作るためのショートカットがあるのだろうかと思います。

私はforループでそれを行うことができますが、おそらくクールな "one-liner"がありますか? 私はそれを減らしてみましたが、エラーが発生します。

コード

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'



私は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'
    )



数字文字列ネストされたリスト、および混合されたコンテナに適用される一般的なアプローチです。

コード

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のレシピから修正されています。 レシピ4.14、Python Cookbook第3版、O'Reilly Media Inc. Sebastopol、CA:2013。
  • 以前のSO投稿 、おそらく元のデモンストレーションを見つけました。



なぜあなたは拡張を使用していますか?

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

これは正常に動作するはずです。




operator.add混乱しているようです。 2つのリストを一緒に追加すると、その正しい言葉はconcatであり、addではありません。 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はかなり高速です! しかし、それはコンカットで減らすための比較ではありません。

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop



あなたの関数がうまくいかなかった理由:extendは配列をインプレースで拡張し、それを返さない。 いくつかのトリックを使ってラムダからxを返すこともできます:

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

注意:extendはリスト上で+よりも効率的です。




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

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]



NumPyのflat使うこともできます:

import numpy as np
list(np.array(l).flat)

編集11/02/2016:サブリストの寸法が同じ場合にのみ機能します。




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をインストールすることができunderscore.py

pip install underscore.py



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



よりnumpy.concatenate().tolist()ためにnumpy.concatenate().tolist()のスピードをあきらめたい場合は、 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ドキュメントをnumpy.concatenateしてnumpy.ravel




異種および同種の整数リストのために働く別の珍しいアプローチ:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]



私は最近、サブリストに文字列と数値データが混在している状況に直面しました。

test = ['591212948',
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'],
['Jon'],
['Doe'],
['fl'],
92001,
555555555,
'hello',
['hello2', 'a'],
'b',
['hello33', ['z', 'w'], 'b']]

flat_list = [item for sublist in test for item in sublist]ようなメソッドはflat_list = [item for sublist in test for item in sublist]ませんでした。 だから、私は1つ以上のレベルのサブリストに対して以下の解決策を考え出した

def concatList(data):
    results = []
    for rec in data:
        if type(rec) == list:
            results += rec
            results = concatList(results)
        else:
            results.append(rec)
    return results

そしてその結果

In [38]: concatList(test)
Out[38]:
 Out[60]:
['591212948',
'special',
'assoc',
'of',
'Chicago',
'Jon',
'Doe',
'Jon',
'Doe',
'fl',
92001,
555555555,
'hello',
'hello2',
'a',
'b',
'hello33',
'z',
'w',
'b']



これは最も効率的な方法ではないかもしれませんが、私は1ライナー(実際には2ライナー)を入れると考えました。どちらのバージョンも任意の階層ネストされたリストで動作し、言語機能(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以下のように、落ち着きを作成するかもしれないと思う。

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]

現時点では効率についてはわかりませんが。




reducefrom functoolsaddoperatorをリスト上で使用する単純な再帰的メソッド:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

関数flattenlstパラメータとして取り込まれます。それは、すべての要素をループlst到達整数は(も変更できるようになるまでintにはfloatstr最も外側の再帰の戻り値に追加され、他のデータ型のため、など)。

再帰は、forループやモナドのようなメソッドと異なり、リストの深さに制限されない一般的な解決策です。たとえば、深度5のリストは、次のようにフラット化できますl

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]



Related