2010-01-25 54 views
78

將列表拆分爲的最佳方式大致相等的部分是?例如,如果列表包含7個元素並將其分成2部分,則我們希望在一個部分中獲得3個元素,而另一個元素應該包含4個元素。將列表拆分爲大約相等長度的N個部分

我在尋找類似even_split(L, n)的東西,將L分解爲n部件。

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個塊,而不是3個塊。我可以簡單地轉置(遍歷這個並獲取每列的第一個元素,調用第一部分,然後把第二部分放在第二部分等),但是這會破壞項目的順序。

回答

42

這裏有一個可以工作:

def chunkIt(seq, num): 
    avg = len(seq)/float(num) 
    out = [] 
    last = 0.0 

    while last < len(seq): 
     out.append(seq[int(last):int(last + avg)]) 
     last += avg 

    return out 

測試:

>>> chunkIt(range(10), 3) 
[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] 
>>> chunkIt(range(11), 3) 
[[0, 1, 2], [3, 4, 5, 6], [7, 8, 9, 10]] 
>>> chunkIt(range(12), 3) 
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]] 
+4

你的榜樣將不會爲'''工作>>> chunkIt(range(8),6)'''=> '''[[0],[1],[2,3],[4],[5],[6],[7]]' '' – nopper 2013-10-07 16:01:57

+1

@nopper,我添加了一個「if num == 1:」條件來處理這個邊界情況。 – paulie4 2013-10-10 15:18:02

+6

新訪問者:**請不要使用或註釋此代碼**,它已損壞。例如'chunkIt(range(10),9)'應該返回9個部分,但不是。 – wim 2017-05-16 01:58:52

14

更改代碼以獲得n塊,而不是n大塊:

def chunks(l, n): 
    """ Yield n successive chunks from l. 
    """ 
    newn = int(len(l)/n) 
    for i in xrange(0, n-1): 
     yield l[i*newn:i*newn+newn] 
    yield l[n*newn-newn:] 

l = range(56) 
three_chunks = chunks (l, 3) 
print three_chunks.next() 
print three_chunks.next() 
print three_chunks.next() 

這給:

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

這將爲最終的組分配額外的元素,這是不完美的,但在您的「大致N個相等部分」的規範中:-)因此,我的意思是56個元素會更好(19,19, 18),而這給(18,18,20)。

你可以用下面的代碼更平衡輸出:

#!/usr/bin/python 
def chunks(l, n): 
    """ Yield n successive chunks from l. 
    """ 
    newn = int(1.0 * len(l)/n + 0.5) 
    for i in xrange(0, n-1): 
     yield l[i*newn:i*newn+newn] 
    yield l[n*newn-newn:] 

l = range(56) 
three_chunks = chunks (l, 3) 
print three_chunks.next() 
print three_chunks.next() 
print three_chunks.next() 

,輸出:

[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] 
+0

這給了我一個奇怪的結果。對於p大塊(範圍(54),3):打印len(p)返回18,18,51 ... – user248237dfsf 2010-01-25 03:36:16

+0

固定,這是最終收益率。 – paxdiablo 2010-01-25 03:37:38

+0

另請參見在[鏈接]上的一個孤獨(http://stackoverflow.com/questions/17749743/python-split-a-list-into-x-number-of-chunks?lq=1) – 2013-07-19 16:14:20

3

這是一個增加None使列表長度相等

>>> from itertools import izip_longest 
>>> def chunks(l, n): 
    """ Yield n successive chunks from l. Pads extra spaces with None 
    """ 
    return list(zip(*izip_longest(*[iter(l)]*n))) 

>>> l=range(54) 

>>> chunks(l,3) 
[(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51), (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52), (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53)] 

>>> chunks(l,4) 
[(0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52), (1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53), (2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, None), (3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, None)] 

>>> chunks(l,5) 
[(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50), (1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51), (2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52), (3, 8, 13, 18, 23, 28, 33, 38, 43, 48, 53), (4, 9, 14, 19, 24, 29, 34, 39, 44, 49, None)] 
62

你可以簡單地寫成列表生成器:

def split(a, n): 
    k, m = divmod(len(a), n) 
    return (a[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in xrange(n)) 

例子:

>>> list(split(range(11), 3)) 
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10]] 
+8

這很漂亮。應該有更多的投票... – 2014-09-24 00:57:55

+0

插入'n = min(n,len(a))#不要在第1行創建空桶,以避免在'list(split(range(X,Y )))''where'X abanana 2017-02-23 17:28:37

+0

由於我無法編輯我的評論 - 我應該補充一點,如果列表爲空,那麼我以前的修改可能會通過零錯誤引發除法,因此需要對其進行控制外部或添加到解決方案。 – abanana 2017-02-24 11:50:44

68

只要你不想做傻事像連續大塊:

>>> def chunkify(lst,n): 
...  return [lst[i::n] for i in xrange(n)] 
... 
>>> chunkify(range(13), 3) 
[[0, 3, 6, 9, 12], [1, 4, 7, 10], [2, 5, 8, 11]] 
+3

我不會說連續塊是愚蠢的。也許你想保持塊的排序(即chunk [0] tixxit 2010-01-26 14:39:43

+0

我在開玩笑。但是,如果你真的不在意,這種列表理解的方式很好,簡潔。 – job 2010-01-26 15:49:16

+3

這是使用一個*步長的下標* n * – smci 2014-09-12 03:30:22

2

看一看numpy.split

>>> a = numpy.array([1,2,3,4]) 
>>> numpy.split(a, 2) 
[array([1, 2]), array([3, 4])] 
+5

而numpy.array_split()更加適合,因爲它大致分裂。 – Yariv 2013-03-09 10:38:53

+9

如果數組大小不能被分割數整除,則這不起作用。 – Dan 2013-07-26 02:32:19

+0

這是錯誤的答案,您的解決方案返回榜單列表,而不是列表 – 2018-02-07 18:07:41

-1

另一個方式會是這樣的,這裏的想法是你se石斑魚,但擺脫None。在這種情況下,我們將擁有由列表第一部分中的元素組成的所有'small_parts',以及列表後部分中的'large_parts'。 「較大部分」的長度是len(small_parts)+ 1.我們需要將x看作兩個不同的子部分。

from itertools import izip_longest 

import numpy as np 

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

def another_chunk(x,num): 
    extra_ele = len(x)%num #gives number of parts that will have an extra element 
    small_part = int(np.floor(len(x)/num)) #gives number of elements in a small part 

    new_x = list(grouper(small_part,x[:small_part*(num-extra_ele)])) 
    new_x.extend(list(grouper(small_part+1,x[small_part*(num-extra_ele):]))) 

    return new_x 

我把它設置了退貨的方式元組的列表:

>>> x = range(14) 
>>> another_chunk(x,3) 
[(0, 1, 2, 3), (4, 5, 6, 7, 8), (9, 10, 11, 12, 13)] 
>>> another_chunk(x,4) 
[(0, 1, 2), (3, 4, 5), (6, 7, 8, 9), (10, 11, 12, 13)] 
>>> another_chunk(x,5) 
[(0, 1), (2, 3, 4), (5, 6, 7), (8, 9, 10), (11, 12, 13)] 
>>> 
0

下面是均勻地分佈「剩餘」的元素所有塊中,一次一個,直到有另一種變體沒有剩下。在這個實現中,更大的塊在過程開始時發生。

def chunks(l, k): 
    """ Yield k successive chunks from l.""" 
    if k < 1: 
    yield [] 
    raise StopIteration 
    n = len(l) 
    avg = n/k 
    remainders = n % k 
    start, end = 0, avg 
    while start < n: 
    if remainders > 0: 
     end = end + 1 
     remainders = remainders - 1 
    yield l[start:end] 
    start, end = end, end+avg 

例如,從14個元件的列表生成4組塊:

>>> list(chunks(range(14), 4)) 
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10], [11, 12, 13]] 
>>> map(len, list(chunks(range(14), 4))) 
[4, 4, 3, 3] 
0

同爲job's的答案,但考慮到與列表大小大於chuncks的數量少。

def chunkify(lst,n): 
    [ lst[i::n] for i in xrange(n if n < len(lst) else len(lst)) ] 

如果n(塊的數量)是7和LST(列表劃分)爲[1,2,3]的塊是[[0],[1],[2]]代替[[0],[1],[2],[],[],[],[]]

2

這是我的解決方案:

def chunks(l, amount): 
    if amount < 1: 
     raise ValueError('amount must be positive integer') 
    chunk_len = len(l) // amount 
    leap_parts = len(l) % amount 
    remainder = amount // 2 # make it symmetrical 
    i = 0 
    while i < len(l): 
     remainder += leap_parts 
     end_index = i + chunk_len 
     if remainder >= amount: 
      remainder -= amount 
      end_index += 1 
     yield l[i:end_index] 
     i = end_index 

可生產

>>> list(chunks([1, 2, 3, 4, 5, 6, 7], 3)) 
    [[1, 2], [3, 4, 5], [6, 7]] 
+0

這是我用過的。完美!謝謝 – brunetton 2017-12-26 21:54:41

0

您也可以使用:

split=lambda x,n: x if not x else [x[:n]]+[split([] if not -(len(x)-n) else x[-(len(x)-n):],n)][0] 

split([1,2,3,4,5,6,7,8,9],2) 

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

使用numpy.linspace方法實現。

只需指定要將數組分成的部分數。分區大小几乎相等。

例子:

import numpy as np 
a=np.arange(10) 
print "Input array:",a 
parts=3 
i=np.linspace(np.min(a),np.max(a)+1,parts+1) 
i=np.array(i,dtype='uint16') # Indices should be floats 
split_arr=[] 
for ind in range(i.size-1): 
    split_arr.append(a[i[ind]:i[ind+1]] 
print "Array split in to %d parts : "%(parts),split_arr 

給出:

Input array: [0 1 2 3 4 5 6 7 8 9] 
Array split in to 3 parts : [array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8, 9])] 
-1

使用列表理解:

def divide_list_to_chunks(list_, n): 
    return [list_[start::n] for start in range(n)] 
+0

這並沒有解決甚至使所有塊的問題。 – SuperBiasedMan 2015-11-14 15:25:44

2

如果分割n元素融入大致k塊就可以使n % k塊1周的Elemen比其他塊分配更多的元素。

下面的代碼會給你的長度各塊:

[(n // k) + (1 if i < (n % k) else 0) for i in range(k)] 

例子:n=11, k=3結果[4, 4, 3]

然後,您可以方便地計算出開始indizes各塊:

[i * (n // k) + min(i, n % k) for i in range(k)] 

例如:n=11, k=3 re sults在[0, 4, 8]

使用i+1個塊作爲我們得到的名單l與LEN ni個塊是

l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] 

最後一步創建使用列表中的所有數據塊列表中的邊界理解:

[l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] for i in range(k)] 

實施例:n=11, k=3, l=range(n)結果[range(0, 4), range(4, 8), range(8, 11)]

-1

舍入linspace並將其用作索引比amit12690提出的更容易。

function chunks=chunkit(array,num) 

index = round(linspace(0,size(array,2),num+1)); 

chunks = cell(1,num); 

for x = 1:num 
chunks{x} = array(:,index(x)+1:index(x+1)); 
end 
end 
+0

這是一個很好的Matlab回答... – 2016-08-15 19:55:22

28

這是numpy.array_split存在的理由 *:

>>> L 
[0, 1, 2, 3, 4, 5, 6, 7] 
>>> print(*np.array_split(L, 3)) 
[0 1 2] [3 4 5] [6 7] 
>>> print(*np.array_split(range(10), 4)) 
[0 1 2] [3 4 5] [6 7] [8 9] 

*信貸Zero Piraeus在6室

+3

這應該是頂級答案 – bluesummers 2017-05-10 10:59:49

+0

trèsbien mon cher! – VanillaSpinIce 2017-09-16 21:46:46

2

這裏有一臺發電機,可以處理任何正(整)塊數。如果塊的數量大於輸入列表長度,則某些塊將爲空。該算法在短塊和長塊之間交替而不是分離它們。

我還包括一些測試ragged_chunks函數的代碼。

''' Split a list into "ragged" chunks 

    The size of each chunk is either the floor or ceiling of len(seq)/chunks 

    chunks can be > len(seq), in which case there will be empty chunks 

    Written by PM 2Ring 2017.03.30 
''' 

def ragged_chunks(seq, chunks): 
    size = len(seq) 
    start = 0 
    for i in range(1, chunks + 1): 
     stop = i * size // chunks 
     yield seq[start:stop] 
     start = stop 

# test 

def test_ragged_chunks(maxsize): 
    for size in range(0, maxsize): 
     seq = list(range(size)) 
     for chunks in range(1, size + 1): 
      minwidth = size // chunks 
      #ceiling division 
      maxwidth = -(-size // chunks) 
      a = list(ragged_chunks(seq, chunks)) 
      sizes = [len(u) for u in a] 
      deltas = all(minwidth <= u <= maxwidth for u in sizes) 
      assert all((sum(a, []) == seq, sum(sizes) == size, deltas)) 
    return True 

if test_ragged_chunks(100): 
    print('ok') 

我們可以讓這個更有效的通過出口倍增到range電話,但我覺得以前的版本更易讀(和烘乾機)。

def ragged_chunks(seq, chunks): 
    size = len(seq) 
    start = 0 
    for i in range(size, size * chunks + 1, size): 
     stop = i // chunks 
     yield seq[start:stop] 
     start = stop 
0

這將通過單個表達做分割:

>>> myList = range(18) 
>>> parts = 5 
>>> [myList[(i*len(myList))//parts:((i+1)*len(myList))//parts] for i in range(parts)] 
[[0, 1, 2], [3, 4, 5, 6], [7, 8, 9], [10, 11, 12, 13], [14, 15, 16, 17]] 

在這個例子中的列表具有18的尺寸和被分成5份。部件的尺寸不超過一個元件。

0

我的解決方案,易於理解

def split_list(lst, n): 
    splitted = [] 
    for i in reversed(range(1, n + 1)): 
     split_point = len(lst)//i 
     splitted.append(lst[:split_point]) 
     lst = lst[split_point:] 
    return splitted 

而這個頁面上最短的一行(通過我的女孩寫的)

def split(l, n): 
    return [l[int(i*len(l)/n):int((i+1)*len(l)/n-1)] for i in range(n)] 
相關問題