Dividir una lista en N partes de aproximadamente la misma longitud

¿Cuál es la mejor manera de dividir una lista en partes aproximadamente iguales? Por ejemplo, si la lista tiene 7 elementos y está dividida en 2 partes, queremos obtener 3 elementos en una parte, y la otra debe tener 4 elementos.

Estoy buscando algo como even_split(L, n) que rompe L en n partes.

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

El código anterior da los trozos de 3, en lugar de 3 trozos. Simplemente podría transponer (iterar sobre esto y tomar el primer elemento de cada columna, llamar a esa primera parte, luego tomar la segunda y colocarla en la segunda parte, etc.), pero eso destruye el orden de los artículos.

Aquí hay uno que podría funcionar:

 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 

Pruebas:

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

Puedes escribirlo simplemente como un generador de listas:

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

Ejemplo:

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

Mientras no quieras nada tonto como trozos continuos:

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

Esta es la razón de ser de 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] 

* crédito a Zero Piraeus en la habitación 6

Cambiando el código para producir n trozos en lugar de trozos de 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() 

lo que da:

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

Esto asignará los elementos adicionales al grupo final, que no es perfecto pero dentro de su especificación de “aproximadamente N partes iguales” 🙂 Con eso, quiero decir que 56 elementos serían mejores que (19,19,18) mientras que esto da (18,18,20).

Puede obtener la salida más equilibrada con el siguiente código:

 #!/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() 

que produce:

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

Si divide n elementos en aproximadamente k trozos, puede hacer que n % k trozos 1 elemento sea más grande que los otros trozos para distribuir los elementos adicionales.

El siguiente código le dará la longitud de los trozos:

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

Ejemplo: n=11, k=3 resultados en [4, 4, 3]

A continuación, puede calcular fácilmente los índices de inicio para los trozos:

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

Ejemplo: n=11, k=3 resultados en [0, 4, 8]

Usando el i+1 th chunk como el límite obtenemos que el i th trozo de la lista l con len n es

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

Como paso final, cree una lista de todos los fragmentos utilizando la comprensión de lista:

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

Ejemplo: n=11, k=3, l=range(n) da como resultado [range(0, 4), range(4, 8), range(8, 11)]

Aquí hay uno que agrega None para hacer las listas de igual longitud.

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

Ver more_itertools.divide :

 n = 2 [list(x) for x in mit.divide(n, range(5, 11))] # [[5, 6, 7], [8, 9, 10]] [list(x) for x in mit.divide(n, range(5, 12))] # [[5, 6, 7, 8], [9, 10, 11]] 

Instalar via > pip install more_itertools .

Echa un vistazo a numpy.split :

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

Implementación utilizando el método numpy.linspace.

Solo especifique el número de partes en las que desea que se divida la matriz. Las divisiones serán de un tamaño casi igual.

Ejemplo:

 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 

Da :

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

Aquí está mi solución:

 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 

Produce

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

Aquí hay un generador que puede manejar cualquier número positivo (entero) de trozos. Si el número de trozos es mayor que la longitud de la lista de entrada, algunos trozos estarán vacíos. Este algoritmo alterna entre partes cortas y largas en lugar de segregarlas.

También he incluido algún código para probar la función 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') 

Podemos hacer esto un poco más eficiente exportando la multiplicación a la llamada de range , pero creo que la versión anterior es más legible (y DRYer).

 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 

Esto hará la división por una sola expresión:

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

La lista en este ejemplo tiene el tamaño 18 y está dividida en 5 partes. El tamaño de las partes difiere en no más de un elemento.

Digamos que quieres dividir en 5 partes:

 p1, p2, p3, p4, p5 = np.split(df, 5) 

Usando la lista de comprensión:

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

Mi solución, fácil de entender.

 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 

Y el más breve de una sola línea en esta página (escrito por mi chica)

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

Otra forma sería algo como esto, la idea aquí es usar mero, pero deshacerse de None . En este caso, tendremos todos los ‘small_parts’ formados a partir de elementos en la primera parte de la lista, y ‘larger_parts’ de la parte posterior de la lista. La longitud de ‘partes más grandes’ es len (small_parts) + 1. Necesitamos considerar x como dos subpartes diferentes.

 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 

La forma en que lo tengo configurado devuelve una lista de tuplas:

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

Aquí hay otra variante que distribuye los elementos “restantes” uniformemente entre todos los trozos, uno a la vez hasta que no queda ninguno. En esta implementación, los trozos más grandes ocurren al principio del proceso.

 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 

Por ejemplo, genere 4 fragmentos a partir de una lista de 14 elementos:

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

Lo mismo que la respuesta del trabajo , pero tiene en cuenta las listas con un tamaño más pequeño que el número de chuncks.

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

si n (número de trozos) es 7 y lst (la lista para dividir) es [1, 2, 3] los trozos son [[0], [1], [2]] en lugar de [[0], [1 ], [2], [], [], [], []]

También podrías usar:

 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]] 
 #!/usr/bin/python first_names = ['Steve', 'Jane', 'Sara', 'Mary','Jack','Bob', 'Bily', 'Boni', 'Chris','Sori', 'Will', 'Won','Li'] def chunks(l, n): for i in range(0, len(l), n): # Create an index range for l of n items: yield l[i:i+n] result = list(chunks(first_names, 5)) print result 

Seleccionado de este enlace , y esto fue lo que me ayudó. Tenía una lista predefinida.

Yo mismo he escrito código en este caso:

 def chunk_ports(port_start, port_end, portions): if port_end < port_start: return None total = port_end - port_start + 1 fractions = int(math.floor(float(total) / portions)) results = [] # No enough to chuck. if fractions < 1: return None # Reverse, so any additional items would be in the first range. _e = port_end for i in range(portions, 0, -1): print "i", i if i == 1: _s = port_start else: _s = _e - fractions + 1 results.append((_s, _e)) _e = _s - 1 results.reverse() return results 

divide_ports (1, 10, 9) regresaría

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

Redondear el linspace y usarlo como un índice es una solución más fácil que lo que propone 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