निरंतर-आकार के टुकड़ों में एक पुनरावृत्ति को कैसे विभाजित किया जाए


85

संभव डुप्लिकेट:
आप पायथन में समान रूप से आकार के विखंडू में एक सूची को कैसे विभाजित करते हैं?

मुझे आश्चर्य है कि मुझे एक "बैच" फ़ंक्शन नहीं मिला जो इनपुट के रूप में एक पुनरावृत्ति ले और पुनरावृत्तियों के पुनरावृत्ति को वापस कर सके।

उदाहरण के लिए:

for i in batch(range(0,10), 1): print i
[0]
[1]
...
[9]

या:

for i in batch(range(0,10), 3): print i
[0,1,2]
[3,4,5]
[6,7,8]
[9]

अब, मैंने लिखा है कि मुझे लगा कि एक बहुत ही सरल जनरेटर था:

def batch(iterable, n = 1):
   current_batch = []
   for item in iterable:
       current_batch.append(item)
       if len(current_batch) == n:
           yield current_batch
           current_batch = []
   if current_batch:
       yield current_batch

लेकिन उपरोक्त मुझे वह नहीं देता जिसकी मैंने उम्मीद की थी:

for x in   batch(range(0,10),3): print x
[0]
[0, 1]
[0, 1, 2]
[3]
[3, 4]
[3, 4, 5]
[6]
[6, 7]
[6, 7, 8]
[9]

इसलिए, मैंने कुछ याद किया है और यह संभवतः अजगर जनरेटर की समझ की पूरी कमी को दर्शाता है। कोई भी मुझे सही दिशा में इंगित करने के लिए परवाह करेगा?

[संपादित करें: मैंने अंततः महसूस किया कि उपरोक्त व्यवहार केवल तब होता है जब मैं इसे अजगर के बजाय इसे अजगर के भीतर चलाता हूं]


अच्छा प्रश्न, अच्छी तरह से लिखा गया है, लेकिन यह पहले से मौजूद है और आपकी समस्या को हल करेगा।
जोश स्मेटन

7
IMO यह वास्तव में डुप्लिकेट नहीं है। अन्य प्रश्न पुनरावृत्तियों के बजाय सूचियों पर ध्यान केंद्रित करते हैं, और उनमें से अधिकांश उत्तरों में लेन () की आवश्यकता होती है जो कि पुनरावृत्तियों के लिए अवांछनीय है। लेकिन एह, वर्तमान में यहाँ स्वीकृत उत्तर को भी लेन () की आवश्यकता है, इसलिए ...
डिक्सेस

7
यह स्पष्ट रूप से एक डुप्लिकेट नहीं है। अन्य प्रश्नोत्तर केवल सूचियों के लिए काम करता है , और यह प्रश्न सभी पुनरावृत्तियों को सामान्य बनाने के बारे में है, जो कि मेरे मन में आया था जब मैं यहाँ आया था।
मार्क ई। हासे

1
@JoshSmeaton @casperOne यह डुप्लिकेट नहीं है और स्वीकृत उत्तर सही नहीं है। लिंक किया हुआ डुप्लिकेट प्रश्न सूची के लिए है और यह चलने के लिए है। सूची लेन () विधि प्रदान करती है, लेकिन चलने योग्य एक लेन () विधि प्रदान नहीं करती है और लेन का उपयोग किए बिना उत्तर अलग होगा () यह सही उत्तर है: batch = (tuple(filterfalse(lambda x: x is None, group)) for group in zip_longest(fillvalue=None, *[iter(iterable)] * n))
त्रिदीप रथ

@TrideepRath हाँ, मैंने फिर से खोलने के लिए मतदान किया है।
जोश स्मेटन

जवाबों:


119

यह शायद अधिक कुशल (तेज) है

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

for x in batch(range(0, 10), 3):
    print x

सूची का उपयोग करके उदाहरण

data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # list of data 

for x in batch(data, 3):
    print(x)

# Output

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

यह नई सूची बनाने से बचता है।


4
रिकॉर्ड के लिए, यह सबसे तेजी से समाधान मैंने पाया है: मेरा 4.5s, तुम्हारा = 0.43s, Donkopotamus = 14.8s =
मैथ्यु

74
आपका बैच वास्तव में एक सूची (len () के साथ) स्वीकार करता है, न कि चलने योग्य (len () के बिना)
tdihp

28
यह तेज है क्योंकि यह समस्या का समाधान नहीं है। रेमंड Hettinger द्वारा समूहक नुस्खा - वर्तमान में इस नीचे - आप एक सामान्य समाधान है कि इनपुट वस्तु की आवश्यकता नहीं है एक है करने के लिए के लिए के लिए क्या देख रहे है लेन विधि।
रॉबर्ट ई मेले

7
आप मिन () का उपयोग क्यों करते हैं? बिना min()कोड पूरी तरह से सही है!
पावेल पैट्रिन

20
Iterables नहीं है len(), अनुक्रम हैंlen()
कोस

60

FWIW, itertools मॉड्यूल में व्यंजनों इस उदाहरण प्रदान करता है:

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(fillvalue=fillvalue, *args)

यह इस तरह काम करता है:

>>> list(grouper(3, range(10)))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]

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

12
@ मैथ्यू के izip_longestसाथ बदलें izip, जो अंतिम प्रविष्टियों को पैड नहीं करेगा, लेकिन जब कुछ तत्व बाहर चलना शुरू करते हैं, तो प्रविष्टियों को काट दें।
GoogieK

3
अजगर 3 में zip_longest / zip होना चाहिए
पीटर गेर्ड्स

5
@GoogieK for x, y in enumerate(grouper(3, xrange(10))): print(x,y)वास्तव में मूल्यों को नहीं भरता है, यह सिर्फ अधूरे खंड को पूरी तरह से गिरा देता है।
kadrach

3
एक लाइनर के रूप में जो अंतिम तत्व को अधूरा छोड़ देता है list(zip(*[iter(iterable)] * n)):। यह मैंने कभी देखा है अजगर कोड का सबसे साफ सा होना चाहिए।
Le Frite

31

जैसा कि दूसरों ने नोट किया है, आपके द्वारा दिया गया कोड वही होता है जो आप चाहते हैं। एक अन्य दृष्टिकोण के लिए itertools.isliceआप निम्नलिखित नुस्खा का एक उदाहरण देख सकते हैं :

from itertools import islice, chain

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

1
@abhilash नहीं ... यह कोड कॉल का उपयोग next()करने के कारण एक StopIterationबार sourceiterसमाप्त हो जाता है, इस प्रकार पुनरावृत्ति को समाप्त करता है। कॉल के बिना nextयह अनिश्चित काल के लिए खाली चलने वालों को वापस करना जारी रखेगा।
donkopotamus

7
मुझे पायथन 3 में उपरोक्त कोड का काम करने के लिए प्रतिस्थापित batchiter.next()करना था next(batchiter)
मार्टिन विबस जूल

2
लिंक किए गए लेख से एक टिप्पणी को इंगित करते हुए: "आपको एक चेतावनी जोड़ना चाहिए कि एक बैच को पूरी तरह से भस्म होना है इससे पहले कि आप अगले एक पर आगे बढ़ सकें।" इस के उत्पादन की तरह कुछ के साथ सेवन किया जाना चाहिए: map(list, batch(xrange(10), 3))। कर: list(batch(xrange(10), 3)अप्रत्याशित परिणाम देगा।
नाथन ब्यूजेंस

2
Py3 पर काम नहीं करता है। .next()बदलना होगा next(..), और list(batch(range(0,10),3))फेंकता हैRuntimeError: generator raised StopIteration
मैथ्यू

1
@ मैथ्यू: बाद वाले मुद्दे को ठीक करने के लिए whileलूप को try:/ में लपेटें except StopIteration: return
शैडो रेंजर

13

मैंने सिर्फ एक जवाब दिया। हालाँकि, अब मुझे लगता है कि सबसे अच्छा समाधान हो सकता है कि कोई नया कार्य न लिखा जाए। अधिक-इटर्टूल में बहुत सारे अतिरिक्त टूल शामिल हैं, और chunkedउनमें से एक है।


यह वास्तव में सबसे अधिक उपयुक्त उत्तर है (भले ही इसके लिए एक और पैकेज की स्थापना की आवश्यकता हो), और वहाँ भी है ichunkedकि पुनरावृत्तियों को जन्म देता है।
vidik13

10

अजीब लगता है, मेरे लिए पायथन 2.x में ठीक काम करता है

>>> def batch(iterable, n = 1):
...    current_batch = []
...    for item in iterable:
...        current_batch.append(item)
...        if len(current_batch) == n:
...            yield current_batch
...            current_batch = []
...    if current_batch:
...        yield current_batch
...
>>> for x in batch(range(0, 10), 3):
...     print x
...
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9]

महान जवाब क्योंकि इसे कुछ भी आयात करने की आवश्यकता नहीं है और पढ़ने के लिए सहज है।
ojunk

8

यह एक बहुत छोटा कोड स्निपेट है जो मैं जानता हूं कि lenपायथन 2 और 3 (मेरी रचना नहीं) के तहत उपयोग और काम नहीं करता है:

def chunks(iterable, size):
    from itertools import chain, islice
    iterator = iter(iterable)
    for first in iterator:
        yield list(chain([first], islice(iterator, size - 1)))

4

पायथन 3.8 के लिए समाधान यदि आप पुनरावृत्तियों के साथ काम कर रहे हैं जो एक lenफ़ंक्शन को परिभाषित नहीं करता है , और थक गया है:

def batcher(iterable, batch_size):
    while batch := list(islice(iterable, batch_size)):
        yield batch

उदाहरण का उपयोग:

def my_gen():
    yield from range(10)
 
for batch in batcher(my_gen(), 3):
    print(batch)

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

बेशक वालरस ऑपरेटर के बिना भी लागू किया जा सकता है।


1
वर्तमान संस्करण में, batcherएक पुनरावृत्ति स्वीकार करता है, न कि एक पुनरावृत्ति। यह एक सूची के साथ एक अनंत लूप में परिणाम होगा, उदाहरण के लिए। लूप iterator = iter(iterable)शुरू करने से पहले शायद एक रेखा होनी चाहिए while
डैनियल पेरेज़

2

यह मैं अपने प्रोजेक्ट में उपयोग करता हूं। यह पुनरावृत्तियों या सूचियों को यथासंभव कुशलता से संभालता है।

def chunker(iterable, size):
    if not hasattr(iterable, "__len__"):
        # generators don't have len, so fall back to slower
        # method that works with generators
        for chunk in chunker_gen(iterable, size):
            yield chunk
        return

    it = iter(iterable)
    for i in range(0, len(iterable), size):
        yield [k for k in islice(it, size)]


def chunker_gen(generator, size):
    iterator = iter(generator)
    for first in iterator:

        def chunk():
            yield first
            for more in islice(iterator, size - 1):
                yield more

        yield [k for k in chunk()]

2
def batch(iterable, n):
    iterable=iter(iterable)
    while True:
        chunk=[]
        for i in range(n):
            try:
                chunk.append(next(iterable))
            except StopIteration:
                yield chunk
                return
        yield chunk

list(batch(range(10), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

अब तक का सर्वश्रेष्ठ उत्तर, हर डेटा संरचना के साथ काम करता है
क्लेमेंट Prévost

1

यह किसी भी चलने के लिए काम करेगा।

from itertools import zip_longest, filterfalse

def batch_iterable(iterable, batch_size=2): 
    args = [iter(iterable)] * batch_size 
    return (tuple(filterfalse(lambda x: x is None, group)) for group in zip_longest(fillvalue=None, *args))

यह इस तरह काम करेगा:

>>>list(batch_iterable(range(0,5)), 2)
[(0, 1), (2, 3), (4,)]

पुनश्च: यह काम नहीं करेगा अगर चलने योग्य कोई भी मूल्य नहीं है।


1

यहाँ reduceफ़ंक्शन का उपयोग करके एक दृष्टिकोण है।

एक लाइन:

from functools import reduce
reduce(lambda cumulator,item: cumulator[-1].append(item) or cumulator if len(cumulator[-1]) < batch_size else cumulator + [[item]], input_array, [[]])

या अधिक पठनीय संस्करण:

from functools import reduce
def batch(input_list, batch_size):
  def reducer(cumulator, item):
    if len(cumulator[-1]) < batch_size:
      cumulator[-1].append(item)
      return cumulator
    else:
      cumulator.append([item])
    return cumulator
  return reduce(reducer, input_list, [[]])

परीक्षा:

>>> batch([1,2,3,4,5,6,7], 3)
[[1, 2, 3], [4, 5, 6], [7]]
>>> batch(a, 8)
[[1, 2, 3, 4, 5, 6, 7]]
>>> batch([1,2,3,None,4], 3)
[[1, 2, 3], [None, 4]]

0

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

def batch(items: Iterable, batch_size: int) -> Iterable[Iterable]:
    # enumerate items and group them by batch index
    enumerated_item_groups = itertools.groupby(enumerate(items), lambda t: t[0] // batch_size)
    # extract items from enumeration tuples
    item_batches = ((t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups)
    return item_batches

अक्सर ऐसा होता है जब आप आंतरिक पुनरावृत्तियों को इकट्ठा करना चाहते हैं, इसलिए यहां अधिक उन्नत संस्करण है।

def batch_advanced(items: Iterable, batch_size: int, batches_mapper: Callable[[Iterable], Any] = None) -> Iterable[Iterable]:
    enumerated_item_groups = itertools.groupby(enumerate(items), lambda t: t[0] // batch_size)
    if batches_mapper:
        item_batches = (batches_mapper(t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups)
    else:
        item_batches = ((t[1] for t in enumerated_items) for key, enumerated_items in enumerated_item_groups)
    return item_batches

उदाहरण:

print(list(batch_advanced([1, 9, 3, 5, 2, 4, 2], 4, tuple)))
# [(1, 9, 3, 5), (2, 4, 2)]
print(list(batch_advanced([1, 9, 3, 5, 2, 4, 2], 4, list)))
# [[1, 9, 3, 5], [2, 4, 2]]

0

संबंधित कार्यक्षमता जिसकी आपको आवश्यकता हो सकती है:

def batch(size, i):
    """ Get the i'th batch of the given size """
    return slice(size* i, size* i + size)

उपयोग:

>>> [1,2,3,4,5,6,7,8,9,10][batch(3, 1)]
>>> [4, 5, 6]

इसे अनुक्रम से i'th बैच मिलता है और यह अन्य डेटा संरचनाओं के साथ-साथ पांडा df.iloc[batch(100,0)]डेटाफ्रेम ( ) या संख्यात्मक सरणी ( array[batch(100,0)]) की तरह काम कर सकता है ।


0
from itertools import *

class SENTINEL: pass

def batch(iterable, n):
    return (tuple(filterfalse(lambda x: x is SENTINEL, group)) for group in zip_longest(fillvalue=SENTINEL, *[iter(iterable)] * n))

print(list(range(10), 3)))
# outputs: [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9,)]
print(list(batch([None]*10, 3)))
# outputs: [(None, None, None), (None, None, None), (None, None, None), (None,)]

0

मैं उपयोग करता हूं

def batchify(arr, batch_size):
  num_batches = math.ceil(len(arr) / batch_size)
  return [arr[i*batch_size:(i+1)*batch_size] for i in range(num_batches)]
  

0

तब तक लेते रहें, जब तक यह खत्म न हो जाए।

def chop(n, iterable):
    iterator = iter(iterable)
    while chunk := list(take(n, iterator)):
        yield chunk


def take(n, iterable):
    iterator = iter(iterable)
    for i in range(n):
        try:
            yield next(iterator)
        except StopIteration:
            return
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.