python - आप सूची को समान आकार के टुकड़ों में कैसे विभाजित करते हैं?




list split (20)

मेरे पास मनमानी लंबाई की एक सूची है, और मुझे इसे बराबर आकार के हिस्सों में विभाजित करने और उस पर काम करने की आवश्यकता है। ऐसा करने के कुछ स्पष्ट तरीके हैं, जैसे काउंटर और दो सूचियां रखना, और जब दूसरी सूची भर जाती है, तो इसे पहली सूची में जोड़ें और डेटा के अगले दौर के लिए दूसरी सूची खाली करें, लेकिन यह संभावित रूप से बेहद महंगा है।

मैं सोच रहा था कि किसी के पास किसी भी लंबाई की सूचियों के लिए इसका अच्छा समाधान था, उदाहरण के लिए जनरेटर का उपयोग करना।

मैं itertools में कुछ उपयोगी खोज रहा था लेकिन मुझे कुछ भी स्पष्ट रूप से उपयोगी नहीं मिला। हालांकि, इसे याद किया हो सकता है।

संबंधित प्रश्न: भाग में एक सूची में फिर से शुरू करने के लिए सबसे अधिक "पायथनिक" तरीका क्या है?


यहां अन्य उत्तरों की आलोचना:

इनमें से कोई भी जवाब समान रूप से आकार के टुकड़े नहीं हैं, वे सभी अंत में एक रनट खंड छोड़ देते हैं, इसलिए वे पूरी तरह से संतुलित नहीं होते हैं। यदि आप काम को वितरित करने के लिए इन कार्यों का उपयोग कर रहे थे, तो आपने दूसरों के सामने अच्छी तरह से परिष्कृत होने की संभावना बनाई है, इसलिए यह कुछ भी करने के आसपास बैठेगा जबकि अन्य कड़ी मेहनत कर रहे थे।

उदाहरण के लिए, वर्तमान शीर्ष उत्तर के साथ समाप्त होता है:

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]

मैं बस अंत में उस दौड़ से नफरत करता हूँ!

अन्य, list(grouper(3, xrange(7))) तरह list(grouper(3, xrange(7))) , और chunk(xrange(7), 3) दोनों वापसी: [(0, 1, 2), (3, 4, 5), (6, None, None)]None सिर्फ पैडिंग नहीं है, बल्कि मेरी राय में सुरुचिपूर्ण है। वे समान रूप से reering नहीं कर रहे हैं।

हम इन्हें बेहतर क्यों नहीं विभाजित कर सकते हैं?

मेरा समाधान

यहां एक संतुलित समाधान है, जिसे मैंने उत्पादन में उपयोग किए गए फ़ंक्शन से अनुकूलित किया है ( range साथ xrange को प्रतिस्थापित करने के लिए पायथन 3 में नोट):

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 

और मैंने एक जेनरेटर बनाया जो वही करता है यदि आप इसे एक सूची में डालते हैं:

def iter_baskets_from(items, maxbaskets=3):
    '''generates evenly balanced baskets from indexable iterable'''
    item_count = len(items)
    baskets = min(item_count, maxbaskets)
    for x_i in xrange(baskets):
        yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]

और आखिरकार, चूंकि मैं देखता हूं कि उपर्युक्त सभी कार्य तत्वों को एक संगत क्रम में लौटाते हैं (जैसा कि उन्हें दिया गया था):

def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
    '''
    generates balanced baskets from iterable, contiguous contents
    provide item_count if providing a iterator that doesn't support len()
    '''
    item_count = item_count or len(items)
    baskets = min(item_count, maxbaskets)
    items = iter(items)
    floor = item_count // baskets 
    ceiling = floor + 1
    stepdown = item_count % baskets
    for x_i in xrange(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in xrange(length)]

उत्पादन

उन्हें जांचने के लिए:

print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))

कौन सा प्रिंट आउट करता है:

[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]

ध्यान दें कि संगत जनरेटर अन्य लंबाई के समान लंबाई पैटर्न में भाग प्रदान करता है, लेकिन आइटम सभी क्रम में होते हैं, और वे समान रूप से विभाजित होते हैं क्योंकि कोई अलग तत्वों की सूची विभाजित कर सकता है।


आप utilspie लाइब्रेरी के get_chunks फ़ंक्शन का भी उपयोग कर सकते हैं:

>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]

आप utilspie माध्यम से utilspie स्थापित कर सकते हैं:

sudo pip install utilspie

अस्वीकरण: मैं utilspie पुस्तकालय के निर्माता हूँ


उदाहरण के लिए यदि आपके पास 3 का खंड आकार था, तो आप यह कर सकते थे:

zip(*[iterable[i::3] for i in range(3)]) 

स्रोत: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/

मैं इसका उपयोग तब करूंगा जब मेरा हिस्सा आकार निश्चित संख्या है, मैं टाइप कर सकता हूं, उदाहरण के लिए '3', और कभी नहीं बदलेगा।


एक और अधिक स्पष्ट संस्करण।

def chunkList(initialList, chunkSize):
    """
    This function chunks a list into sub lists 
    that have a length equals to chunkSize.

    Example:
    lst = [3, 4, 9, 7, 1, 1, 2, 3]
    print(chunkList(lst, 3)) 
    returns
    [[3, 4, 9], [7, 1, 1], [2, 3]]
    """
    finalList = []
    for i in range(0, len(initialList), chunkSize):
        finalList.append(initialList[i:i+chunkSize])
    return finalList

जनरेटर अभिव्यक्ति:

def chunks(seq, n):
    return (seq[i:i+n] for i in xrange(0, len(seq), n))

जैसे।

print list(chunks(range(1, 1000), 10))

मुझे आश्चर्य है कि किसी ने भी इसे दो-तर्क फ़ॉर्म का उपयोग करने का विचार नहीं किया है:

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

डेमो:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

यह किसी भी पुनरावर्तनीय के साथ काम करता है और उत्पादन आलसी उत्पादन करता है। यह इटरेटर की बजाय tuples देता है, लेकिन मुझे लगता है कि यह एक निश्चित लालित्य है। यह पैड भी नहीं है; यदि आप पैडिंग चाहते हैं, तो उपर्युक्त पर एक साधारण भिन्नता पर्याप्त होगी:

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: tuple(islice(it, size)), (padval,) * size)

डेमो:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

izip_longest समाधान की तरह, उपरोक्त हमेशा पैड। जहां तक ​​मुझे पता है, वैकल्पिक रूप से पैड के लिए कोई भी दो या दो-लाइन इटारेटोल्स नुस्खा नहीं है। उपर्युक्त दो दृष्टिकोणों को जोड़कर, यह बहुत करीब आता है:

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

डेमो:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

मेरा मानना ​​है कि यह सबसे छोटा चंकर प्रस्तावित है जो वैकल्पिक पैडिंग प्रदान करता है।


मुझे पायथन और जेएफएसबेस्टियन द्वारा प्रस्तावित पायथन डॉक का संस्करण बहुत पसंद है, लेकिन इसमें दो कमियां हैं:

  • यह बहुत स्पष्ट नहीं है
  • मैं आमतौर पर अंतिम खंड में एक भरना मूल्य नहीं चाहता

मैं इसे अपने कोड में बहुत कुछ उपयोग कर रहा हूं:

from itertools import islice

def chunks(n, iterable):
    iterable = iter(iterable)
    while True:
        yield tuple(islice(iterable, n)) or iterable.next()

अद्यतन: एक आलसी भाग संस्करण:

from itertools import chain, islice

def chunks(n, iterable):
   iterable = iter(iterable)
   while True:
       yield chain([next(iterable)], islice(iterable, n-1))

मैं विभिन्न दृष्टिकोणों के प्रदर्शन के बारे में उत्सुक था और यहां यह है:

पायथन 3.5.1 पर परीक्षण किया

import time
batch_size = 7
arr_len = 298937

#---------slice-------------

print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
    if not arr:
        break

    tmp = arr[0:batch_size]
    arr = arr[batch_size:-1]
print(time.time() - start)

#-----------index-----------

print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
    tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)

#----------batches 1------------

def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx:min(ndx + n, l)]

print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#----------batches 2------------

from itertools import islice, chain

def batch(iterable, size):
    sourceiter = iter(iterable)
    while True:
        batchiter = islice(sourceiter, size)
        yield chain([next(batchiter)], batchiter)


print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#---------chunks-------------
def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
    tmp = x
print(time.time() - start)

#-----------grouper-----------

from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(iterable, n, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
    tmp = x
print(time.time() - start)

परिणाम:

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844

यदि आप कुछ सुपर सरल चाहते हैं:

def chunks(l, n):
    n = max(1, n)
    return (l[i:i+n] for i in xrange(0, len(l), n))

यदि आप सूची का आकार जानते हैं:

def SplitList(list, chunk_size):
    return [list[offs:offs+chunk_size] for offs in range(0, len(list), chunk_size)]

यदि आप नहीं (एक पुनरावर्तक):

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

बाद के मामले में, यदि आप यह सुनिश्चित कर सकते हैं कि अनुक्रम में हमेशा दिए गए आकार की कुल संख्या (यानी कोई अपूर्ण अंतिम खंड नहीं है) तो इसे एक और अधिक सुंदर तरीके से दोहराया जा सकता है।


यहां एक जनरेटर है जो मनमाने ढंग से पुनरावृत्तियों पर काम करता है:

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

उदाहरण:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

लेन () जो कॉलिंग के बिना बड़ी सूचियों के लिए अच्छा है:

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]

और यह पुनरावृत्तियों के लिए है:

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))

उपर्युक्त का कार्यात्मक स्वाद:

def isplitter2(l, n):
    return takewhile(bool,
                     (tuple(islice(start, n))
                            for start in repeat(iter(l))))

या:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(tuple, continuous_slices).next,())

या:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(tuple, continuous_slices))

सीधे (पुराने) पायथन दस्तावेज (itertools के लिए व्यंजनों) से:

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

वर्तमान संस्करण, जैसा कि जेएफएसबेस्टियन द्वारा सुझाया गया है:

#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

मुझे लगता है कि गिडो का टाइम मशीन काम करता है-काम करेगा-काम करेगा-फिर से काम कर रहा था।

ये समाधान काम करते हैं क्योंकि [iter(iterable)]*n (या पिछले संस्करण में समतुल्य) एक पुनरावर्तक बनाता है, सूची में बार बार बार-बार। izip_longest तब प्रभावी रूप से "प्रत्येक" इटरेटर के राउंड-रॉबिन का प्रदर्शन करता है; क्योंकि यह वही इटरेटर है, यह प्रत्येक ऐसे कॉल द्वारा उन्नत होता है, जिसके परिणामस्वरूप प्रत्येक ऐसे ज़िप-राउंडोबिन n वस्तुओं के एक ट्यूपल उत्पन्न करते हैं।


हे, एक लाइन संस्करण

In [48]: chunk = lambda ulist, step:  map(lambda i: ulist[i:i+step],  xrange(0, len(ulist), step))

In [49]: chunk(range(1,100), 10)
Out[49]: 
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
 [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
 [31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
 [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
 [51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
 [61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
 [71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
 [81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
 [91, 92, 93, 94, 95, 96, 97, 98, 99]]


toolz लाइब्रेरी में इसके लिए partition कार्य है:

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]

See this reference

>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>> 

Python3


[AA[i:i+SS] for i in range(len(AA))[::SS]]

जहां एए सरणी है, एसएस खंड का आकार है। उदाहरण के लिए:

>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3

def chunk(input, size):
    return map(None, *([iter(input)] * size))

def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'




chunks