[Python] पायथन में सूचियों की सूची से एक फ्लैट सूची बनाना


Answers

आप itertools.chain() उपयोग कर सकते हैं:

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

या, पायथन> = 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

मुझे आश्चर्य है कि पाइथन में सूचियों की सूची से एक साधारण सूची बनाने के लिए शॉर्टकट है या नहीं।

मैं इसे लूप में कर सकता हूं, लेकिन हो सकता है कि कुछ ठंडा "एक-लाइनर" हो? मैंने इसे कम करने की कोशिश की, लेकिन मुझे एक त्रुटि मिली।

कोड

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'



मैं हाल ही में एक ऐसी स्थिति में आया जहां मेरे पास उपन्यासकारों में तारों और संख्यात्मक डेटा का मिश्रण था

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] की तरह काम नहीं किया है। तो, मैं 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']



आप इसे आजमा सकते हैं:

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



more_itertools पैकेज को स्थापित करने पर विचार करें।

> pip install more_itertools

यह flatten के लिए एक कार्यान्वयन के साथ जहाज ( source , itertools व्यंजनों से ):

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.collapse ( source , abarnet द्वारा योगदान) के साथ more_itertools.collapse कर सकते हैं।

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



मेरे लिए सबसे सरल लग रहा है:

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



आप वास्तविक स्टैक डेटा संरचना का उपयोग करके स्टैक पर रिकर्सिव कॉल से बच सकते हैं।

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().ravel().tolist() numpy.concatenate().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

आप docs numpy.concatenate और numpy.ravel में यहां और अधिक जानकारी प्राप्त कर सकते हैं




यहां एक सामान्य दृष्टिकोण है जो सूचियों, संख्याओं, तारों और अन्य मिश्रित कंटेनर प्रकारों की नेस्टेड सूचियों पर लागू होता है।

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

यह समाधान कीवर्ड yield from पाइथन 3 की शक्तिशाली yield from को नियोजित करता है, जो उप-जेनरेटर से आइटम निकालता है। नोट, यह समाधान तारों पर लागू नहीं होता है। अद्यतन: अब स्ट्रिंग का समर्थन करता है।

आरईएफ: बीज़ले, डी और बी जोन्स से समाधान संशोधित पकाने की विधि 4.14, पायथन कुकबुक तीसरी एड।, ओ रेली मीडिया इंक सेबस्तोपोल, सीए: 2013।




नोट : नीचे पायथन 3.3+ पर लागू होता है क्योंकि यह yield_from का उपयोग करता है।

obj = [[1, 2,], [3, 4], [5, 6]] , यहां सभी समाधान अच्छे हैं, जिसमें सूची समझ और itertools.chain.from_iterable

हालांकि, इस थोड़ा अधिक जटिल मामले पर विचार करें:

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

यहां कई समस्याएं हैं:

  • एक तत्व, 6 , सिर्फ एक स्केलर है; यह पुनरावर्तनीय नहीं है, इसलिए उपर्युक्त मार्ग यहां असफल हो जाएंगे।
  • One element, 'abc' , is technically iterable (all str s are). However, reading between the lines a bit, we don't want to treat it as such--we want to treat it as a single element.
  • The final element, [8, [9, 10]] is itself a nested iterable. Basic list comprehension and chain.from_iterable only extract "1 level down."

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)



यदि आप डेटा-स्ट्रक्चर को फ़्लैट करना चाहते हैं, जहां आप नहीं जानते कि यह कितना गहरा है, तो आप 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 डालना होगा या स्पष्ट रूप से इसे फिर से चालू करना होगा।

केवल एक स्तर को फ़्लैट करने के लिए और यदि प्रत्येक आइटम स्वयं ही itertools.chain.from_iterable : itertools.chain.from_iterable तो आप 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]

बस कुछ समय जोड़ने के लिए (निको श्लोमर उत्तर के आधार पर जिसमें इस उत्तर में प्रस्तुत समारोह शामिल नहीं था):

यह स्पैन किए गए मूल्यों की विशाल श्रृंखला के लिए समायोजित करने के लिए लॉग-लॉग प्लॉट है। गुणात्मक तर्क के लिए: निचला बेहतर है।

परिणाम दिखाते हैं कि यदि पुनरावर्तनीय में केवल कुछ आंतरिक पुनरावृत्तियों होते हैं तो sum सबसे तेज़ होगा, हालांकि लंबे समय तक केवल itertools.chain.from_iterable , iteration_utilities.deepflatten या नेस्टेड समझ के साथ itertools.chain.from_iterable साथ उचित प्रदर्शन होता है (जैसा कि पहले से निको श्लोमर द्वारा देखा गया है)।

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 अस्वीकरण: मैं उस पुस्तकालय का लेखक हूं




एक और असामान्य दृष्टिकोण जो हेटरो- और पूर्णांक की सजातीय सूचियों के लिए काम करता है:

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]




आप विस्तार का उपयोग क्यों करते हैं?

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

यह ठीक काम करना चाहिए।




मैंने पाया सबसे तेज़ समाधान (वैसे भी बड़ी सूची के लिए):

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

किया हुआ! आप सूची को निष्पादित करके इसे वापस सूची में बदल सकते हैं (एल)




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]



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() विधि एक उपयोगी मान लौटने के बजाय x को संशोधित करती है (जो reduce() है)।

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]



Links