খণ্ডে একটি তালিকা পুনরাবৃত্তি সবচেয়ে "অজগর" উপায় কি?


487

আমার কাছে পাইথন স্ক্রিপ্ট রয়েছে যা পূর্ণসংখ্যার একটি তালিকা ইনপুট হিসাবে নেয়, যা আমার একবারে চারটি পূর্ণসংখ্যার সাথে কাজ করা দরকার। দুর্ভাগ্যক্রমে, আমার ইনপুটটির নিয়ন্ত্রণ নেই, বা আমি এটি ফোর-এলিমেন্ট টিউপসগুলির তালিকা হিসাবে দিয়েছি। বর্তমানে, আমি এটির মাধ্যমে পুনরাবৃত্তি করছি:

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

এটি দেখতে অনেকটা "সি-থিঙ্ক" এর মতো লাগে, যা এই পরিস্থিতিটি মোকাবেলার আরও একটি অজগর উপায় রয়েছে বলে আমার সন্দেহ হয়। পুনরাবৃত্তি করার পরে তালিকাটি বাতিল করা হয়েছে, সুতরাং এটি সংরক্ষণ করার প্রয়োজন নেই। এরকম কিছু আরও ভাল হতে পারে?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

এখনও ঠিক "অনুভূতি" দেয় না, যদিও। : - /

সম্পর্কিত প্রশ্ন: আপনি কীভাবে পাইথনে সমান আকারের অংশগুলিতে একটি তালিকা ভাগ করবেন?


3
তালিকার আকার চারটির একাধিক না হলে আপনার কোড কাজ করবে না।
পেড্রো হেনরিক্স

5
আমি তালিকাটি প্রসারিত করছি () যাতে এটির দৈর্ঘ্য চারটির একাধিক হওয়ার আগে এটি আরও দীর্ঘ হয়।
বেন ফাঁকা

4
@ ΤΖΩΤΖΙΟΥ - প্রশ্নগুলি খুব অনুরূপ, তবে বেশ নকল নয়। এটি "আকারের যে কোনও সংখ্যায় বিভক্ত" বনাম "" কোনও আকারের এন অংশগুলিতে বিভক্ত "। :-)
বেন ফাঁকা 18


উত্তর:


339

পাইথনের ইটারটোল ডক্সের রেসিপি বিভাগ থেকে পরিবর্তিত :

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

উদাহরণ
সিউডোকোডে উদাহরণটি অবিচ্ছিন্ন রাখতে।

grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'

দ্রষ্টব্য: পাইথন 2 এর izip_longestপরিবর্তে ব্যবহার করুন zip_longest


67
অবশেষে অজগর সেশনে এটির সাথে চারপাশে খেলার সুযোগ পেল। যারা আমার মতই বিভ্রান্ত তাদের জন্য, এটি একই পুনরাবৃত্তিকে একাধিক বার izip_longest খাওয়ানো হচ্ছে যার ফলে এটি পৃথক সিকোয়েন্স থেকে ডোরাকাটা মানগুলির চেয়ে একই ক্রমের ক্রমাগত মানগুলি গ্রাস করে। আমি এটা ভালোবাসি!
বেন ফাঁকা

6
ফিলভ্যালু ফিরিয়ে আউট করার সেরা উপায় কী? ([আইটেম আইটেমের আইটেমটি যদি আইটেমটি ভলভ্যালু না হয়) গ্রেপারে আইটেমগুলির জন্য (পুনরাবৃত্তিযোগ্য))?

14
আমি সন্দেহ করি যে 256k আকারের খণ্ডগুলির জন্য এই গ্রেপার রেসিপিটির পারফরম্যান্স খুব খারাপ izip_longestহবে , কারণ 256k আর্গুমেন্ট খাওয়ানো হবে।
অ্যানাটোলি টেকটোনিক

13
বেশ কয়েকটি জায়গায় কমেন্টাররা বলে "আমি যখন শেষ পর্যন্ত কাজ করেছিলাম কীভাবে এটি কাজ করে ...." সম্ভবত কিছুটা ব্যাখ্যা প্রয়োজন explanation বিশেষত পুনরাবৃত্তকারী দিকগুলির তালিকা।
লন্ডনব্রব

6
এটি ব্যবহার করার কোনও উপায় কি তবে Noneশেষ অংশটি পূরণ না করে ?
সিএমসিডিগ্রাগনকাই

420
def chunker(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)

সহজ। সহজ। ফাস্ট। যে কোনও অনুক্রমের সাথে কাজ করে:

text = "I am a very, very helpful text"

for group in chunker(text, 7):
   print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'

print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']

for group in chunker(animals, 3):
    print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']

16
@ কার্লোস ক্র্যাসোবনের সংস্করণ কোনও পুনরাবৃত্তির জন্য কাজ করে (উপরের কোড হিসাবে সিকোয়েন্স নয়); এটি সংক্ষিপ্ত এবং সম্ভবত ঠিক তত দ্রুত বা আরও দ্রুত। যদিও এটি itertoolsমডিউলের সাথে অপরিচিত লোকদের জন্য কিছুটা অস্পষ্ট (অস্পষ্ট) হতে পারে ।
jfs

1
একমত। এটি সর্বাধিক জেনেরিক এবং পাইথোনিক উপায়। স্পষ্ট এবং সংক্ষিপ্ত. (এবং অ্যাপ ইঞ্জিনে কাজ করে)
ম্যাট উইলিয়ামসন

3
দ্রষ্টব্য যে chunkera generatorreturn [...]তালিকাটি পেতে রিটার্নটি প্রতিস্থাপন করুন:
আতঙ্ক

11
একটি ফাংশন ভবন লেখার এবং তারপর একটি জেনারেটর ফিরে পরিবর্তে, এছাড়াও আপনি সরাসরি একটি জেনারেটর লিখতে পারে ব্যবহার yield: for pos in xrange(0, len(seq), size): yield seq[pos:pos + size]। আমি নিশ্চিত নই যে অভ্যন্তরীণভাবে এটি কোনও প্রাসঙ্গিক দিক থেকে অন্যরকমভাবে পরিচালনা করা হবে তবে এটি এমনকি একটি সামান্য বিট আরও পরিষ্কার হতে পারে।
আলফ

3
দ্রষ্টব্য এটি কেবল এমন ক্রমগুলির জন্য কাজ করে যা সূচি অনুসারে আইটেমগুলিতে অ্যাক্সেস সমর্থন করে এবং জেনেরিক পুনরাবৃত্তকারীদের পক্ষে কাজ করবে না, কারণ তারা __getitem__পদ্ধতি সমর্থন করে না ।
অ্যাপোলোভ

135

আমি একজন ভক্ত

chunk_size= 4
for i in range(0, len(ints), chunk_size):
    chunk = ints[i:i+chunk_size]
    # process chunk of size <= chunk_size

যদি লেন (ইনস) চঙ্কসাইজের একাধিক না হয় তবে এটি কীভাবে আচরণ করবে?
PlsWork

3
@ অন্নোভাপুরেতে chunkসর্বশেষ ব্যাচের উপাদানগুলির জন্য 1, 2 বা 3 উপাদান থাকবে। স্লাইস সূচকগুলি কেন সীমার বাইরে থাকতে পারে সে সম্পর্কে এই প্রশ্নটি দেখুন ।
বরিস

22
import itertools
def chunks(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
    foo += x1 + x2 + x3 + x4

for chunk in chunks(ints,4):
    foo += sum(chunk)

অন্য উপায়:

import itertools
def chunks2(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
    foo += x1 + x2 + x3 + x4

2
জেনারেটর ব্যবহারের জন্য +1, সমস্ত প্রস্তাবিত সমাধানগুলির মধ্যে সর্বাধিক "পাইথোনিক" এর মতো seams
সের্গেই গোলোভচেঙ্কো

7
এটি এত সহজ কোনও কিছুর জন্য বরং দীর্ঘ এবং আনাড়ি, যা মোটেই পাইথোনিক নয়। আমি এস লট এর সংস্করণ পছন্দ করি
zenazn

4
@ জেনাজন: এটি জেনারেটরের উদাহরণগুলিতে কাজ করবে, কাটছে না
জানুস ট্রয়লসেন

জেনারেটর এবং অন্যান্য স্লাইজেবল পুনরাবৃত্তকারীগুলির সাথে সঠিকভাবে কাজ করার পাশাপাশি, চূড়ান্ত অংশটি এর চেয়ে কম হলে প্রথম সমাধানের জন্য "ফিলার" মানের প্রয়োজন হয় না size, যা কখনও কখনও কাম্য হয়।
ড্যানো

1
জেনারেটরের জন্যও +1। অন্যান্য সমাধানগুলির জন্য একটি lenকল প্রয়োজন এবং তাই অন্য জেনারেটরের উপর কাজ করবেন না।
কুয়াদিউ


11

এই সমস্যার আদর্শ সমাধান পুনরাবৃত্তকারীগুলির সাথে কাজ করে (কেবল সিকোয়েন্স নয়)। এটিও দ্রুত হওয়া উচিত।

এটি হ'ল এটির সমাধানগুলির জন্য ডকুমেন্টেশন দ্বারা প্রদত্ত সমাধান:

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

%timeitআমার ম্যাক বুক এয়ারে আইপথন ব্যবহার করে , আমি প্রতি লুপটিতে 47.5 পাই get

যাইহোক, ফলাফলটি এমনকি মাপের গোষ্ঠীগুলির জন্য প্যাডড হওয়ায় এটি আমার পক্ষে সত্যিই কার্যকর হয় না। প্যাডিং ছাড়াই একটি সমাধান কিছুটা জটিল। সবচেয়ে নিষ্পাপ সমাধান হতে পারে:

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

সরল, তবে বেশ ধীর: প্রতি লুপে 693 us

isliceঅভ্যন্তরীণ লুপের জন্য আমি সবচেয়ে ভাল সমাধানটি নিয়ে আসতে পারি :

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

একই ডেটাসেটের সাহায্যে আমি প্রতি লুপ 305 পাই।

এর চেয়ে দ্রুত কোনও খাঁটি সমাধান পেতে অক্ষম, আমি একটি গুরুত্বপূর্ণ ক্যাভিয়েট সহ নিম্নলিখিত সমাধানটি সরবরাহ করি: আপনার ইনপুট ডেটাতে যদি এর উদাহরণ filldataথাকে তবে আপনি ভুল উত্তর পেতে পারেন।

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

আমি এই উত্তরটি সত্যই পছন্দ করি না, তবে এটি উল্লেখযোগ্যভাবে দ্রুত। আমাদের লুপ প্রতি 124


আপনি সি স্তরে এটা সরিয়ে ~ 10-15% এর রেসিপি # 3 জন্য রানটাইম কমে যায় (বাদ itertoolsআমদানির; mapPy3 হতে হবে mapবা imap): def grouper(n, it): return takewhile(bool, map(tuple, starmap(islice, repeat((iter(it), n)))))। আপনার চূড়ান্ত ফাংশনটি প্রেরণেল ব্যবহার করে কম ভঙ্গুর করা যায়: fillvalueযুক্তি থেকে মুক্তি পান ; একটি প্রথম লাইন যুক্ত করুন fillvalue = object(), তারপরে ifচেকটি if i[-1] is fillvalue:এবং যে লাইনে এটি নিয়ন্ত্রণ করে তাতে পরিবর্তন করুন yield tuple(v for v in i if v is not fillvalue)। গ্যারান্টিসের কোনও মানই iterableফিলার মানটির জন্য ভুল হতে পারে না।
শেডোএ্যাঞ্জার

বিটিডাব্লু, # 4 এ বড় থাম্বস আপ। আমি এখন অবধি যা পোস্ট করা হয়েছে তার চেয়ে ভাল উত্তর (পারফরম্যান্স-ওয়াইজ) হিসাবে আমার # 3 এর অপ্টিমাইজেশন পোস্ট করতে যাচ্ছিলাম, তবে এটি নির্ভরযোগ্য করার লক্ষ্যে, স্থিতিস্থাপক # 4 ওভারটিমাইজড # 3 দ্বিগুণ তত দ্রুত রান করতে সক্ষম হয়েছে; পাইথন স্তরের লুপগুলি (এবং কোনও তাত্ত্বিক অ্যালগোরিদমিক পার্থক্য AFAICT) জয়ের জন্য কোনও সমাধান আশা করিনি। আমি ধরে নিয়েছি / isliceঅবজেক্ট অবজেক্টস ব্যয় করার কারণে # 3 হেরে গেছে ( nতুলনামূলকভাবে বড় হলে # 3 টি জয় , উদাহরণস্বরূপ গ্রুপগুলির সংখ্যা কম, তবে এটি একটি অস্বাভাবিক ক্ষেত্রে উপযুক্ত হবে), তবে আমি এটি আশা করি না যে এটি যথেষ্ট হবে চরম।
শেডোএ্যাঞ্জার

# 4 এর জন্য, শর্তাধীন প্রথম শাখাটি কেবল সর্বশেষ পুনরাবৃত্তির (চূড়ান্ত টিপল) নেওয়া হয়। সব আবার চূড়ান্ত tuple reconstituting পরিবর্তে থেকে অবাঞ্ছিত প্যাডিং বন্ধ ফালি যে উপরের এবং ব্যবহারের মূল iterable দৈর্ঘ্য মডিউল ক্যাশে izip_longestচূড়ান্ত tuple করুন: yield i[:modulo]। এছাড়াও, জন্য argsপরিবর্তনশীল, এটা পরিবর্তে tuple তালিকার: args = (iter(iterable),) * n। আরও কয়েকটি ঘড়ির চক্র বন্ধ করে দেয়। সর্বশেষে, আমরা যদি ভরাটটি উপেক্ষা করে ধরে নিই Noneতবে শর্তসাপেক্ষ if None in iআরও বেশি ঘড়ির চক্রের হয়ে উঠতে পারে।
কুম্বা

1
@ কুম্বা: আপনার প্রথম পরামর্শটি ধরে নিয়েছে ইনপুটটির দৈর্ঘ্য জানা আছে। যদি এটি কোনও পুনরুক্তি / জেনারেটর, জ্ঞাত দৈর্ঘ্যের সংগ্রহ নয়, তবে ক্যাশে করার কিছুই নেই। যাইহোক যাইহোক এই ধরনের অপ্টিমাইজেশন ব্যবহার করার কোনও সত্য কারণ নেই; আপনি অস্বাভাবিক কেসটিকে (শেষটি yield) অপ্টিমাইজ করছেন , যখন সাধারণ কেস প্রভাবিত হয় না।
শ্যাডোর্যাঞ্জার

10

আমার একটি সমাধান দরকার যা সেট এবং জেনারেটরগুলির সাথেও কাজ করবে। আমি খুব ছোট এবং সুন্দর কিছু নিয়ে আসতে পারিনি, তবে এটি কমপক্ষে বেশ পাঠযোগ্য read

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

তালিকা:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

সেট করুন:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

জেনারেটর:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

8

অন্যান্য প্রস্তাবগুলির মতো, তবে একেবারে অভিন্ন নয়, আমি এটি এইভাবে করতে পছন্দ করি, কারণ এটি সহজ এবং পড়া সহজ:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

এইভাবে আপনি শেষ আংশিক খণ্ডটি পাবেন না। যদি আপনি (9, None, None, None)শেষ অংশ হিসাবে পেতে চান তবে কেবল izip_longestএখান থেকে ব্যবহার করুন itertools


এর মাধ্যমে উন্নতি করা যেতে পারেzip(*([it]*4))
জিন-ফ্রান্সোইস ফ্যাব্রে

@ জিন-ফ্রেঞ্চোইস ফ্যাব্রে: পঠনযোগ্যতার দৃষ্টিকোণ থেকে আমি এটিকে উন্নতি হিসাবে দেখছি না। এবং এটি প্রান্তিক ধীর। আপনি যদি গল্ফ করছেন তবে এটি উন্নতি নয়, যা আমি নই।
ক্রিশ

না আমি গল্ফ করছি না, তবে যদি আপনার 10 টি যুক্তি থাকে? আমি এই নির্মাণটি কিছু অফিসিয়াল পৃষ্ঠায় পড়েছি b তবে অবশ্যই আমি এখনই এটি খুঁজে পাচ্ছি না :)
জিন-ফ্রানসোয়া ফ্যাব্রে

@ জিন-ফরাসোইস ফ্যাব্রে: আমার যদি 10 টি যুক্তি বা পরিবর্তনশীল সংখ্যক যুক্তি থাকে তবে এটি একটি বিকল্প তবে আমি লিখব: জিপ (* (এটি)) * 10)
ক্রিস

অধিকার! এটাই আমি পড়েছি আমি যে তালিকা তৈরি করেছি তা নয় :)
জিন-ফ্রান্সোয়েস ফ্যাব্রে

8

আপনার যদি কোনও বাহ্যিক প্যাকেজ ব্যবহার করতে আপত্তি না থাকে তবে আপনি 1iteration_utilities.grouper থেকে ব্যবহার করতে পারেন । এটি সমস্ত পুনরাবৃত্তিকে সমর্থন করে (কেবল ক্রম নয়):iteration_utilties

from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

যা প্রিন্ট করে:

(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

দৈর্ঘ্যটি গ্রুপাইজের একাধিক না হলেও এটি শেষ (সম্পূর্ণ অসম্পূর্ণ গ্রুপ) বা কাটা কাটা (অসম্পূর্ণ শেষ গ্রুপটিকে ত্যাগ করে) সর্বশেষকে সমর্থন করে:

from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

benchmarks

আমি উল্লিখিত কয়েকটি পদ্ধতির রান-টাইমের তুলনা করার সিদ্ধান্তও নিয়েছি। এটি বিভিন্ন মাপের তালিকার ভিত্তিতে "10" উপাদানগুলির গ্রুপে লগ-লগ প্লট করে group গুণগত ফলাফলের জন্য: নিম্নের অর্থ দ্রুত:

এখানে চিত্র বর্ণনা লিখুন

কমপক্ষে এই বেঞ্চমার্কে সেরা iteration_utilities.grouperঅভিনয় করে। অনুসরণ ক্রেজ এর পদ্ধতির দ্বারা ।

মাপদণ্ডটি তৈরি করা হয়েছিল 1 দিয়ে । এই বেঞ্চমার্কটি চালানোর জন্য ব্যবহৃত কোডটি ছিল:simple_benchmark

import iteration_utilities
import itertools
from itertools import zip_longest

def consume_all(it):
    return iteration_utilities.consume(it, None)

import simple_benchmark
b = simple_benchmark.BenchmarkBuilder()

@b.add_function()
def grouper(l, n):
    return consume_all(iteration_utilities.grouper(l, n))

def Craz_inner(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

@b.add_function()
def Craz(iterable, n, fillvalue=None):
    return consume_all(Craz_inner(iterable, n, fillvalue))

def nosklo_inner(seq, size):
    return (seq[pos:pos + size] for pos in range(0, len(seq), size))

@b.add_function()
def nosklo(seq, size):
    return consume_all(nosklo_inner(seq, size))

def SLott_inner(ints, chunk_size):
    for i in range(0, len(ints), chunk_size):
        yield ints[i:i+chunk_size]

@b.add_function()
def SLott(ints, chunk_size):
    return consume_all(SLott_inner(ints, chunk_size))

def MarkusJarderot1_inner(iterable,size):
    it = iter(iterable)
    chunk = tuple(itertools.islice(it,size))
    while chunk:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot1(iterable,size):
    return consume_all(MarkusJarderot1_inner(iterable,size))

def MarkusJarderot2_inner(iterable,size,filler=None):
    it = itertools.chain(iterable,itertools.repeat(filler,size-1))
    chunk = tuple(itertools.islice(it,size))
    while len(chunk) == size:
        yield chunk
        chunk = tuple(itertools.islice(it,size))

@b.add_function()
def MarkusJarderot2(iterable,size):
    return consume_all(MarkusJarderot2_inner(iterable,size))

@b.add_arguments()
def argument_provider():
    for exp in range(2, 20):
        size = 2**exp
        yield size, simple_benchmark.MultiArgument([[0] * size, 10])

r = b.run()

1 অস্বীকৃতি: আমি গ্রন্থাগারগুলির লেখক iteration_utilitiesএবং simple_benchmark


7

যেহেতু কেউ এখনও এটি উল্লেখ করেনি এখানে একটি zip()সমাধান রয়েছে:

>>> def chunker(iterable, chunksize):
...     return zip(*[iter(iterable)]*chunksize)

এটি কেবল তখনই কাজ করে যদি আপনার সিকোয়েন্সের দৈর্ঘ্যটি সর্বদা খণ্ড আকারের দ্বারা বিভাজ্য হয় বা আপনি যদি এটি অনুসরণ না করে তবে পিছনের অংশটির যত্ন নেই।

উদাহরণ:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

বা তালিকার পরিবর্তে একটি পুনরাবৃত্তির ফিরিয়ে দিতে itertools.izip ব্যবহার করে :

>>> from itertools import izip
>>> def chunker(iterable, chunksize):
...     return izip(*[iter(iterable)]*chunksize)

@ ΤΖΩΤΖΙΟΥ এর উত্তর ব্যবহার করে প্যাডিং স্থির করা যেতে পারে :

>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
...     it   = chain(iterable, repeat(fillvalue, chunksize-1))
...     args = [it] * chunksize
...     return izip(*args)

5

জিপ () এর পরিবর্তে মানচিত্র () ব্যবহার করে জেএফ সেবাস্তিয়ান এর উত্তরে প্যাডিংয়ের সমস্যাটি সমাধান করা হয়েছে:

>>> def chunker(iterable, chunksize):
...   return map(None,*[iter(iterable)]*chunksize)

উদাহরণ:

>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]

2
এটি itertools.izip_longest(পাই 2) / itertools.zip_longest(পাই 3) দিয়ে আরও ভালভাবে পরিচালনা করা হয় ; এই ব্যবহারটি mapদ্বিগুণ-অবমানিত, এবং পাই 3 এ উপলব্ধ নয় (আপনি Noneম্যাপার ফাংশন হিসাবে পাস করতে পারবেন না , এবং যখন সংক্ষিপ্ততম পুনরাবৃত্তি শেষ হয়ে যায়, এটি দীর্ঘায়িত হয় না; এটি প্যাড করে না) stop
শ্যাডোএ্যাঞ্জার

4

আর একটি পদ্ধতির দ্বি-যুক্তি ফর্মটি ব্যবহার করা হবে iter:

from itertools import islice

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

প্যাডিং ব্যবহার করতে এটি সহজেই রূপান্তর করা যেতে পারে (এটি মার্কাস জার্ডেরোটের উত্তরের অনুরূপ ):

from itertools import islice, chain, repeat

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

এগুলি এমনকি padচ্ছিক প্যাডিংয়ের জন্য সংযুক্ত করা যেতে পারে:

_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

1
আপনার প্যাডিং বাদ দেওয়ার বিকল্প রয়েছে বলে মনে করা যায়!
n611x007

3

যদি তালিকাটি বড় হয় তবে এটি করার সর্বাধিক কার্য সম্পাদন করার উপায় হ'ল জেনারেটর ব্যবহার করা:

def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield tuple(result)
            result = []
    if len(result) > 0:
        yield tuple(result)

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

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)

(আমি মনে করি যে মিজার্ডএক্স এর এরটুলস পরামর্শটি কার্যত এটির সমতুল্য))
রবার্ট রসনি

1
(প্রকৃতপক্ষে, প্রতিচ্ছবিতে, আমি না it itertools.islice একটি পুনরুক্তি ফেরত দেয় তবে এটি কোনও বিদ্যমান ব্যবহার করে না))
রবার্ট রসনি

এটা চমৎকার এবং সহজ, কিন্তু এমনকি রূপান্তর ছাড়া কোনো কারণে 4-7 বার গৃহীত গ্রাউপার পদ্ধতি তুলনায় ধীর উপর tuple করার iterable = range(100000000)& chunksize10000. পর্যন্ত
Valentas

তবে, সাধারণভাবে আমি এই পদ্ধতির সুপারিশ করব, কারণ সর্বশেষ আইটেমটির জন্য পরীক্ষা করা ধীর ডকস.পাইথন.আরথ
ib /

3

সামান্য ফাংশন এবং জিনিসগুলি ব্যবহার সত্যিই আমার কাছে আবেদন করে না; আমি কেবল স্লাইস ব্যবহার করতে পছন্দ করি:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...

অনির্দিষ্টকালের স্রোতের জন্য সুন্দর তবে কোনটিই ভাল নয় যা জানা নেই len। আপনি itertools.repeatবা দিয়ে একটি পরীক্ষা করতে পারেন itertools.cycle
n611x007

1
এছাড়াও, জেনারেটর এক্সপ্রেশনটি ব্যবহারের পরিবর্তে শারীরিকভাবে একটি তালিকা তৈরি করতে [...for...] তালিকার বোধগম্যতা ব্যবহার করার কারণে মেমরিটি খায় কারণ কেবলমাত্র পরবর্তী উপাদানটির যত্ন নেওয়া হবে এবং মেমরি (...for...)
ছাড়িয়ে যাবে

2

একটি তালিকার সমস্ত রূপান্তর এড়াতে import itertoolsএবং:

>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

উত্পাদন:

... 
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>> 

আমি চেক করেছি groupbyএবং এটি তালিকাতে বা ব্যবহারে রূপান্তরিত হয় না lenতাই আমি (মনে করি) এটি প্রতিটি মানের রেজোলিউশনটি বাস্তবে ব্যবহার না হওয়া পর্যন্ত বিলম্ব করবে। দু: খজনকভাবে উপলভ্য উত্তরগুলির মধ্যে (এই সময়ে) কোনও এই প্রকরণটি দেয় বলে মনে হয় নি।

স্পষ্টতই যদি আপনার প্রতিটি আইটেমকে ঘরের নীড়ের নীচে জি লুপের জন্য হ্যান্ডেল করতে হয়:

for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

এতে আমার নির্দিষ্ট আগ্রহটি ছিল জিমেইল এপিআই-তে 1000 অবধি ব্যাচগুলিতে পরিবর্তন জমা দেওয়ার জন্য একটি জেনারেটর গ্রহণ করা প্রয়োজন:

    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)

আপনি যে তালিকাটিকে বেছে নিচ্ছেন সেটি যদি আরোহণের পূর্ণসংখ্যার ক্রম ছাড়া অন্য কিছু হয়?
পলমিসজি

@ পলম্যাকগুইয়ার গ্রুপপ্লে দেখুন ; অর্ডার বর্ণনা করার জন্য একটি ফাংশন দেওয়া হয়েছে তবে পুনরাবৃত্ত উপাদানগুলির কিছু হতে পারে, তাই না?
জন মে

1
হ্যাঁ, আমি গ্রুপবাইয়ের সাথে পরিচিত। তবে বার্তাগুলি যদি "ABCDEFG" অক্ষরগুলি থাকে, তবে groupby(messages, lambda x: x/3)আপনাকে একটি টাইপ-এয়ারার দিত (কোনও স্ট্রিংকে কোনও int দ্বারা ভাগ করার চেষ্টা করার জন্য), 3-বর্ণের গ্রুপিং নয় not এখন আপনি যদি groupby(enumerate(messages), lambda x: x[0]/3)কিছু আছে। তবে আপনি আপনার পোস্টে এটি বলেন নি।
পলমিসজি

2

NumPy দিয়ে এটি সহজ:

ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

আউটপুট:

1 2
3 4
5 6
7 8

2
def chunker(iterable, n):
    """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
    """
    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(next(it))
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __name__ == '__main__':
    import doctest

    doctest.testmod()

2

আমি যদি কিছু মিস না করি তবে জেনারেটর এক্সপ্রেশন সহ নিম্নলিখিত সহজ সমাধানের কথা উল্লেখ করা হয়নি। এটি ধরে নিয়েছে যে আকার এবং অংশ উভয়ই জানা গেছে (যা প্রায়শই এমন হয়), এবং কোনও প্যাডিং প্রয়োজন হয় না:

def chunks(it, n, m):
    """Make an iterator over m first chunks of size n.
    """
    it = iter(it)
    # Chunks are presented as tuples.
    return (tuple(next(it) for _ in range(n)) for _ in range(m))

1

আপনার দ্বিতীয় পদ্ধতিতে, আমি এটি করে 4 টি পরবর্তী গ্রুপে এগিয়ে যাব:

ints = ints[4:]

তবে, আমি কোনও পারফরম্যান্স পরিমাপ করি নি তাই কোনটি বেশি দক্ষ হতে পারে তা আমি জানি না।

এই কথাটি বলে, আমি সাধারণত প্রথম পদ্ধতিটি বেছে নেব। এটি সুন্দর নয়, তবে এটি প্রায়শই বাইরের বিশ্বের সাথে ইন্টারফেস করার একটি পরিণতি।


1

তবুও অন্য উত্তর, এর সুবিধাগুলি হ'ল:

1) সহজেই বোধগম্য
2) কোনও পুনরাবৃত্তির উপর ভিত্তি করে কাজ করে, কেবল সিক্যুয়েন্স নয় (উপরের উত্তরগুলির কয়েকটি ফাইলহ্যান্ডলে দম বন্ধ হয়ে যাবে)
3) একবারে স্মৃতিতে অংশ লোড করে না
4) রেফারেন্সের খণ্ড-দীর্ঘ তালিকা তৈরি করে না মেমরিতে একই পুনরাবৃত্তকারী
5) তালিকার শেষে ভ্যালু মানের কোনও প্যাডিং নেই

বলা হচ্ছে, আমি এটির সময় নিই নি তাই এটি আরও চতুর পদ্ধতির তুলনায় ধীর হতে পারে এবং ব্যবহারের ক্ষেত্রে কিছু সুবিধা অপ্রাসঙ্গিক হতে পারে।

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h 

আপডেট:
অভ্যন্তরীণ এবং বাহ্যিক লুপগুলি একই পুনরুক্তিকারীর কাছ থেকে মানগুলি টানছে এই কারণে বেশ কয়েকটি ত্রুটি:
1) বহিরাগত লুপে প্রত্যাশার মতো কাজ করে না - এটি একটি অংশ বাদ দেওয়ার পরিবর্তে কেবল পরবর্তী আইটেমটিতে চালিয়ে যায় । তবে এটি কোনও সমস্যার মতো বলে মনে হচ্ছে না কারণ বাইরের লুপে পরীক্ষা করার মতো কিছুই নেই।
2) বিরতি অভ্যন্তরীণ লুপে প্রত্যাশার মতো কাজ করে না - পুনরুক্তিকরণের পরবর্তী আইটেমটির সাথে নিয়ন্ত্রণ আবার অভ্যন্তরীণ লুপে বাতাসে প্রবেশ করবে। পুরো খণ্ডগুলি এড়াতে, হয় অভ্যন্তর পুনরাবৃত্তকারী (উপরের উপরে) একটি টিপলে মুড়ে রাখুন, উদাহরণস্বরূপ for c in tuple(ii), অথবা একটি পতাকা নির্ধারণ করুন এবং পুনরুক্তিটি নির্গমন করুন।


1
def group_by(iterable, size):
    """Group an iterable into lists that don't exceed the size given.

    >>> group_by([1,2,3,4,5], 2)
    [[1, 2], [3, 4], [5]]

    """
    sublist = []

    for index, item in enumerate(iterable):
        if index > 0 and index % size == 0:
            yield sublist
            sublist = []

        sublist.append(item)

    if sublist:
        yield sublist

+1 এটি প্যাডিং বাদ দেয়; পুলিশের এবং bcoughlan 's খুব অনুরূপ
n611x007

1

আপনি মজাদার লাইব্রেরি থেকে পার্টিশন বা অংশগুলি ব্যবহার করতে পারেন :

from funcy import partition

for a, b, c, d in partition(4, ints):
    foo += a * b * c * d

এই ফাংশনগুলির পুনরাবৃত্ত সংস্করণও রয়েছে ipartitionএবং ichunksযা এই ক্ষেত্রে আরও দক্ষ হবে।

আপনি তাদের বাস্তবায়নেও উঁকি দিতে পারেন ।


1

সমাধান সম্পর্কে J.F. Sebastian এখানে দিয়েছেন :

def chunker(iterable, chunksize):
    return zip(*[iter(iterable)]*chunksize)

এটি চালাক, তবে এর একটি অসুবিধা রয়েছে - সর্বদা ফিরতি ফিরুন। পরিবর্তে স্ট্রিং কিভাবে?
অবশ্যই লিখতে পারেন''.join(chunker(...)) , তবে অস্থায়ী tuple যাইহোক নির্মিত হয়।

আপনি নিজের লেখার মাধ্যমে অস্থায়ী টুপল থেকে মুক্তি পেতে পারেন zip:

class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_zip(*iterables, reductor=tuple):
    iterators = tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

তারপর

def chunker(data, size, reductor=tuple):
    return custom_zip(*[iter(data)]*size, reductor=reductor)

ব্যবহারের উদাহরণ:

>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'

2
আপনার উত্তর পরিবর্তন করার জন্য কোনও সমালোচনা বোঝানো হয়নি, বরং একটি মন্তব্য: কোড একটি দায়বদ্ধতা। আপনি যত বেশি কোড লিখবেন আপনি তত বেশি জায়গা লিখবেন বাগগুলি আড়াল করার জন্য। এই দৃষ্টিকোণ থেকে, zipবিদ্যমানটি ব্যবহার না করে পুনর্লিখনগুলি সেরা ধারণা বলে মনে হয় না।
আলফে

1

আমি এই পদ্ধতির পছন্দ। এটি সহজ এবং যাদুকরী নয় এবং সমস্ত পুনরাবৃত্ত প্রকারের সমর্থন করে এবং আমদানির প্রয়োজন হয় না।

def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
    chunk = tuple(next(it) for _ in range(chunk_size))
    if not chunk:
        break
    yield chunk

1

আমি কখনই আমার খণ্ডগুলিকে প্যাড করতে চাই না, সুতরাং সেই প্রয়োজনীয়তা অপরিহার্য। আমি দেখতে পেয়েছি যে কোনও পুনরাবৃত্তির উপর কাজ করার ক্ষমতাও প্রয়োজন। এটি দেওয়া, আমি গৃহীত উত্তরটি https://stackoverflow.com/a/434411/1074659 এ প্রসারিত করার সিদ্ধান্ত নিয়েছি ।

প্যাডিং মানগুলি তুলনা এবং ফিল্টার করার প্রয়োজনের কারণে প্যাডিং না চাইলে পারফরম্যান্সটি এই পদ্ধতির ক্ষেত্রে সামান্য আঘাত নেয়। তবে বড় আকারের আকারগুলির জন্য, এই ইউটিলিটিটি খুব পারফরম্যান্ট।

#!/usr/bin/env python3
from itertools import zip_longest


_UNDEFINED = object()


def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
    """
    Collect data into chunks and optionally pad it.

    Performance worsens as `chunksize` approaches 1.

    Inspired by:
        https://docs.python.org/3/library/itertools.html#itertools-recipes

    """
    args = [iter(iterable)] * chunksize
    chunks = zip_longest(*args, fillvalue=fillvalue)
    yield from (
        filter(lambda val: val is not _UNDEFINED, chunk)
        if chunk[-1] is _UNDEFINED
        else chunk
        for chunk in chunks
    ) if fillvalue is _UNDEFINED else chunks

1

এখানে আমদানি ছাড়াই এমন এক চুনকার যা জেনারেটরকে সমর্থন করে:

def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = tuple(next(it) for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

ব্যবহারের উদাহরণ:

>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]

1

পাইথন ৩.৮ এর সাহায্যে আপনি ওয়ালরাস অপারেটর এবং ব্যবহার করতে পারেন itertools.islice

from itertools import islice

list_ = [i for i in range(10, 100)]

def chunker(it, size):
    iterator = iter(it)
    while chunk := list(islice(iterator, size)):
        print(chunk)
In [2]: chunker(list_, 10)                                                         
[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]

0

এটি করার কোনও দুর্দান্ত উপায় বলে মনে হয় না। এখানে এমন একটি পৃষ্ঠা রয়েছে যার মধ্যে বেশ কয়েকটি পদ্ধতি রয়েছে:

def split_seq(seq, size):
    newseq = []
    splitsize = 1.0/size*len(seq)
    for i in range(size):
        newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
    return newseq

0

তালিকাগুলি যদি একই আকারের হয় তবে আপনি এগুলি 4-টিপলগুলির তালিকায় একত্রিত করতে পারেন zip()। উদাহরণ স্বরূপ:

# Four lists of four elements each.

l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)

for i1, i2, i3, i4 in zip(l1, l2, l3, l4):
    ...

zip()ফাংশনটি যা উত্পাদন করে তা এখানে :

>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]

যদি তালিকাগুলি বড় হয় এবং আপনি এগুলি কোনও বৃহত তালিকায় একত্রিত করতে না চান তবে ব্যবহার করুন itertools.izip(), যা তালিকার পরিবর্তে পুনরুক্তি উত্পাদন করে।

from itertools import izip

for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
    ...

0

ওয়ান-লাইনার, xআকারের খণ্ডগুলির একটি তালিকাতে পুনরাবৃত্তি করতে অ্যাডহক সমাধান 4-

for a, b, c, d in zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.