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



14 Answers

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

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

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

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

मैं 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 पुस्तकालय के निर्माता हूँ




a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]



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

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

जैसे।

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



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




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



सरल अभी तक सुरुचिपूर्ण

l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]

या यदि आप पसंद करते हैं:

chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)



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

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



इस बिंदु पर, मुझे लगता है कि हमें एक रिकर्सिव जनरेटर की आवश्यकता है, बस मामले में ...

पायथन 2 में:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

पायथन 3 में:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

इसके अलावा, भारी विदेशी आक्रमण के मामले में, एक सजाया गया रिकर्सिव जनरेटर आसान हो सकता है:

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e



कोड:

def split_list(the_list, chunk_size):
    result_list = []
    while the_list:
        result_list.append(the_list[:chunk_size])
        the_list = the_list[chunk_size:]
    return result_list

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

print split_list(a_list, 3)

परिणाम:

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



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

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



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

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

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

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




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

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

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




मुझे पता है कि यह पुराना है लेकिन मुझे नहीं पता कि कोई भी numpy.array_split उल्लेख नहीं numpy.array_split :

lst = range(50)
In [26]: np.array_split(lst,5)
Out[26]: 
[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
 array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
 array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
 array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]



Related