python - كيف تقسم قائمة إلى قطع بحجم متساو؟




list split (20)

لدي قائمة بطول تعسفي ، ولست بحاجة إلى تقسيمها إلى أجزاء بحجم متساو والعمل عليها. هناك بعض الطرق الواضحة للقيام بذلك ، مثل الاحتفاظ بعنصر وقائمتين ، وعندما تملأ القائمة الثانية ، أضفها إلى القائمة الأولى وأفرغ القائمة الثانية لجولة البيانات التالية ، لكن هذا قد يكون مكلفًا للغاية.

كنت أتساءل إذا كان أي شخص لديه حل جيد لهذا للقوائم من أي طول ، على سبيل المثال استخدام المولدات.

كنت أبحث عن شيء مفيد في itertools لكن لم أتمكن من العثور على أي شيء مفيد بشكل واضح. لقد افتقدته ، على الرغم من.

سؤال ذو صلة: ما هي الطريقة الأكثر "pythonic" لتكرار أكثر من قائمة في قطع؟


نقد إجابات أخرى هنا:

لا شيء من هذه الإجابات هي قطع متساوية الحجم ، فكلها تترك قطعة صلبة في النهاية ، لذلك فهي غير متوازنة تمامًا. إذا كنت تستخدم هذه الوظائف لتوزيع العمل ، فقد قمت ببناء احتمال الانتهاء بشكل جيد قبل الآخرين ، لذلك كان يجلس حول عدم القيام بأي شيء في حين أن الآخرين استمروا في العمل بجد.

على سبيل المثال ، تنتهي الإجابة الأعلى الحالية بـ:

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

أنا فقط أكره أن runt في النهاية!

غيرها ، مثل list(grouper(3, xrange(7))) ، والجزء chunk(xrange(7), 3) حد سواء العودة: [(0, 1, 2), (3, 4, 5), (6, None, None)] . None هو مجرد الحشو، وليس بالأحرى inelegant في رأيي. انهم ليسوا بالتساوي التغلب على iterables.

لماذا لا يمكننا تقسيمها بشكل أفضل؟

الحل الخاص بي

إليك حل متوازن ، تم تكييفه من وظيفة قمت باستخدامها في الإنتاج (ملاحظة في Python 3 لاستبدال xrange range ):

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

لاحظ أن المولد المترابط يوفر قطعًا في نفس أنماط الطول كالنقطتين الأخريين ، ولكن العناصر كلها في ترتيب ، وهي مقسمة بالتساوي كما قد يقسم أحد قائمة العناصر المنفصلة.


أعلم أن هذا شيء قديم لكنني لا أفهم لماذا لم يذكر أحد 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])]

إذا كان لديك حجم مكون من 3 على سبيل المثال ، فيمكنك القيام بما يلي:

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

source: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/

سأستخدم هذا عندما يكون حجم المقطع الثابت ثابتًا يمكنني كتابة ، على سبيل المثال "3" ، ولن يتغير أبدًا.


إذا كنت تريد شيء بسيط للغاية:

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

الشفرة:

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

النظر في استخدام القطع matplotlib.cbook

فمثلا:

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s

تحتوي مكتبة toolz على وظيفة partition لهذا:

from toolz.itertoolz.core import partition

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

تعبير المولد:

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

على سبيل المثال.

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

دون استدعاء len () وهو أمر جيد للقوائم الكبيرة:

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

وهذا بالنسبة لـ iterables:

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

عند هذه النقطة ، أعتقد أننا بحاجة إلى مولد تكراري ، فقط في حالة ...

في الثعبان 2:

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

في python 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

لقد رأيت إجابة Python-ish الأكثر روعة في نسخة duplicate من هذا السؤال:

from itertools import zip_longest

a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

يمكنك إنشاء n-tuple لأي ن. إذا كان a = range(1, 15) ، فستكون النتيجة كما يلي:

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]

إذا كانت القائمة مقسمة بالتساوي ، فيمكنك استبدال zip_longest zip ، وإلا zip_longest الثلاثية (13, 14, None) . بايثون 3 يستخدم أعلاه. بالنسبة إلى Python 2 ، استخدم izip_longest .


مباشرة من وثائق بيثون (القديمة) (وصفات ل 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)

الإصدار الحالي ، كما اقترحه JFSebastian:

#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 (أو ما يعادله في الإصدار الأقدم) ينشئ مكررًا واحدًا ، تكرار n مرة في القائمة. ثم تقوم بشكل فعال بأداء izip_longest دائري لكل "مكرر". نظرًا لأن هذا هو نفس المكرّر ، فإنه يتم تقديمه بواسطة كل مكالمة من هذا النوع ، مما يؤدي إلى إنشاء مثل هذا الرمز البريدي - roundrobin الذي ينشئ مجموعة واحدة من العناصر n .


هنا مولد الذي يعطي القطع التي تريدها:

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
import pprint
pprint.pprint(list(chunks(range(10, 75), 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]]

إذا كنت تستخدم Python 2 ، يجب عليك استخدام xrange() بدلاً من range() :

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i + n]

كما يمكنك ببساطة استخدام فهم القائمة بدلاً من كتابة دالة. بيثون 3:

[l[i:i + n] for i in range(0, len(l), n)]

الإصدار Python 2:

[l[i:i + n] for i in xrange(0, len(l), n)]

هنا هو مولد يعمل على التكرار التعسفي:

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

يمكنك أيضا استخدام وظيفة utilspie من مكتبة utilspie النحو التالي:

>>> 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 عبر النقطة:

sudo pip install utilspie

إخلاء المسؤولية: أنا منشئ مكتبة utilspie .



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 عبارة عن مصفوفة ، يكون 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