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


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
Question

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



more_itertoolsパッケージのインストールを検討してmore_itertools

> pip install more_itertools

これは、 flattenitertoolsレシピからのsource )用の実装と一緒に出荷されます

import more_itertools

# Using flatten()
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

バージョン2.4では、 more_itertools.collapsesource 、abarnetによって提供)をmore_itertools.collapse 、より複雑でネストされたiterableをフラット化できます。

# Using collapse()
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]       # given example 
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]   # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]



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

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



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

def unusual_flatten(some_list: list) -> list:
    cleaned_list = str(some_list).replace(("["), "").replace("]", "").split(",")
    return [int(item) for item in cleaned_list]

サンプルリストへの適用...

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]

unusual_flatten(l)

結果:

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




ネストされている深さを知らないところでデータ構造を平坦化したい場合は、 iteration_utilities.deepflatten使用することができ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にキャストするか、明示的に反復処理する必要があります。

1つのレベルだけを平坦化するために、各アイテムがそれ自身iterableである場合、それ自体itertools.chain.from_iterable周りの薄いラッパーであるiteration_utilities.flattenを使うこともできiteration_utilities.flatten

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

ちょうどいくつかのタイミングを追加する(この答えに表示された関数を含まないNicoSchlömerの答えに基づいて):

スパンされた膨大な範囲の値に対応するためのログログプロットです。 定性的推論の場合:低い方が良い。

結果は、iterableがいくつかの内部iterableしか含んでいない場合、 sumは最も速くなりますが、長いiterableの場合は、 itertools.chain.from_iterableiteration_utilities.deepflattenまたは入れ子にされた理解のみがitertools.chain.from_iterableが妥当なパフォーマンス(すでにNicoSchlö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免責事項:私はその図書館の著者です




次は私には一番シンプルだと思う:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]



from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

あなたの例のextend()メソッドは、有用な値( reduce()期待する)を返す代わりに、 xを変更します。

reduceバージョンを行うより速い方法は、

>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]



私が見つけた最速の解決策(とにかく大きなリストのために):

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

完了! もちろん、リスト(l)を実行することでリストに戻すことができます。




あなたはこれを試すことができます:

weird_list=[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
nice_list = [int(e) for e in str(a) if e not in "[] ,"]



:以下はyield_fromを使用しているため、Python 3.3以降に適用されます。

obj = [[1, 2,], [3, 4], [5, 6]]場合、ここではすべての解がリスト内包とitertools.chain.from_iterableを含めて良好itertools.chain.from_iterable

しかし、このやや複雑なケースを考えてみましょう。

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

ここにいくつかの問題があります:

  • 1つの要素、 6は単なるスカラーです。 iterableではないので、上記のルートはここで失敗します。
  • 1つの要素'abc' 技術的に反復可能です(すべてのstrがあります)。 しかし、行間を少しずつ読んで、それをそのまま扱うことは望ましくない - それを単一の要素として扱いたい。
  • 最後の要素[8, [9, 10]]は、それ自体がネストされた繰り返し可能です。 基本的なリストの理解とchain.from_iterableは、「1つのレベルを下に」抽出するだけです。

We can remedy this as follows:

>>> import sys
>>> from collections import Iterator

>>> py2 = sys.version_info[0] == 2

>>> if py2:
...     str_types = basestring, unicode, str
... else:
...    str_types = str, bytes


>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, str_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Here, we check that the sub-element (1) is iterable with Iterable , an ABC from itertools , but also want to ensure that (2) the element is not "string-like." The first few lines ensure compatability between Python 2 & 3.

This competes with or is a hair faster than matplotlib.cbook.flatten :

>>> from matplotlib.cbook import flatten as m_flatten

%timeit flatten(obj*100)
1.39 µs ± 21.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit m_flatten(obj*100)
1.43 µs ± 17.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)



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]



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

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

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




リスト、数値、文字列、その他の混合コンテナタイプのネストリストに適用される一般的なアプローチです。

from collections import Iterable


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

list(flatten(l))                               # list of lists
#[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

このソリューションは、サブジェネレータからアイテムを抽出するPython 3の強力なyield fromキーワードを使用します。 このソリューションは文字列には適用されません。 UPDATE:文字列をサポートするようになりました。

REF: Beazley、D.およびB.Jonesから修正された解 レシピ4.14、Python Cookbook第3版、O'Reilly Media Inc. Sebastopol、CA:2013。




かなり単純に実際のスタックデータ構造を使って、スタックへの再帰呼び出しを避けることができます。

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



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




Links