如何将任意长度的列表拆分为大小相等的块?
请参阅如果数据结果将直接用于循环,并且不需要存储,则如何以块形式遍历列表。
对于字符串输入的同一问题,请参见每n个字符拆分字符串?。相同的技术通常适用,但也有一些变化。
如何将任意长度的列表拆分为大小相等的块?
请参阅如果数据结果将直接用于循环,并且不需要存储,则如何以块形式遍历列表。
对于字符串输入的同一问题,请参见每n个字符拆分字符串?。相同的技术通常适用,但也有一些变化。
当前回答
一个简单的解决方案
OP已请求“相等大小的块”。我将“等尺寸”理解为“平衡”尺寸:如果尺寸不可能相等(例如,23/5),我们正在寻找尺寸大致相同的物品组。
这里的输入是:
项目列表:input_list(例如,23个数字的列表)要拆分这些项目的组数:n个组(例如5个)
输入:
input_list = list(range(23))
n_groups = 5
连续元素组:
approx_sizes = len(input_list)/n_groups
groups_cont = [input_list[int(i*approx_sizes):int((i+1)*approx_sizes)]
for i in range(n_groups)]
“每N个”元素组:
groups_leap = [input_list[i::n_groups]
for i in range(n_groups)]
后果
print(len(input_list))
print('Contiguous elements lists:')
print(groups_cont)
print('Leap every "N" items lists:')
print(groups_leap)
将输出:23连续元素列表:[[0, 1, 2, 3], [4, 5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16, 17], [18, 19, 20, 21, 22]]跳过每“N”个项目列表:[[0, 5, 10, 15, 20], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18], [4, 9, 14, 19]]
其他回答
上面的答案(由koffein给出)有一个小问题:列表总是被分割成相等数量的分割,而不是每个分区的项目数相等。这是我的版本。“//chs+1”考虑到项目的数量可能不能完全除以分区大小,因此最后一个分区将仅被部分填充。
# Given 'l' is your list
chs = 12 # Your chunksize
partitioned = [ l[i*chs:(i*chs)+chs] for i in range((len(l) // chs)+1) ]
下面我有一个解决方案确实有效,但比这个解决方案更重要的是对其他方法的一些评论。首先,一个好的解决方案不应该要求一个循环按顺序遍历子迭代器。如果我跑
g = paged_iter(list(range(50)), 11))
i0 = next(g)
i1 = next(g)
list(i1)
list(i0)
最后一个命令的适当输出是
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
not
[]
正如这里大多数基于itertools的解决方案所返回的那样。这不仅仅是关于按顺序访问迭代器的常见无聊限制。想象一个消费者试图清理输入不良的数据,该数据颠倒了5的块的适当顺序,即数据看起来像[B5,A5,D5,C5],应该像[A5,B5,C5,D5](其中A5只是五个元素,而不是子列表)。该使用者将查看分组函数的声明行为,并毫不犹豫地编写一个类似
i = 0
out = []
for it in paged_iter(data,5)
if (i % 2 == 0):
swapped = it
else:
out += list(it)
out += list(swapped)
i = i + 1
如果您偷偷摸摸地假设子迭代器总是按顺序完全使用,那么这将产生神秘的错误结果。如果你想交错块中的元素,情况就更糟了。
其次,大量建议的解决方案隐含地依赖于迭代器具有确定性顺序的事实(例如,迭代器没有设置),尽管使用islice的一些解决方案可能还可以,但我对此感到担忧。
第三,itertools-grouper方法有效,但该方法依赖于zip_langest(或zip)函数的内部行为,而这些行为不是其发布行为的一部分。特别是,grouper函数只起作用,因为在zip_langest(i0…In)中,下一个函数总是按next(i0)、next(i 1)、……的顺序调用。。。在重新开始之前。当grouper传递同一迭代器对象的n个副本时,它依赖于此行为。
最后,虽然下面的解决方案可以得到改进,但如果您对上面的假设进行了批评,即子迭代器是按顺序访问的,并且在没有这个假设的情况下被完全阅读,则必须隐式(通过调用链)或显式(通过deques或其他数据结构)为每个子迭代程序存储元素。所以,不要浪费时间(就像我所做的那样),假设人们可以用一些巧妙的技巧来解决这个问题。
def paged_iter(iterat, n):
itr = iter(iterat)
deq = None
try:
while(True):
deq = collections.deque(maxlen=n)
for q in range(n):
deq.append(next(itr))
yield (i for i in deq)
except StopIteration:
yield (i for i in deq)
简单而优雅
L = range(1, 1000)
print [L[x:x+10] for x in xrange(0, len(L), 10)]
或者如果您愿意:
def chunks(L, n): return [L[x: x+n] for x in xrange(0, len(L), n)]
chunks(L, 10)
不调用len(),这对大型列表很有用:
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))))
OR:
def chunks_gen_sentinel(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return iter(imap(tuple, continuous_slices).next,())
OR:
def chunks_gen_filter(n, seq):
continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
return takewhile(bool,imap(tuple, continuous_slices))
[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
要扩展py3中的范围,请执行以下操作
(py3) >>> [list(AA[i:i+SS]) for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]