Dividir una lista en N partes de aproximadamente la misma longitud
Pregunta
¿Cuál es la mejor manera de dividir una lista en apenas ¿a partes iguales?Por ejemplo, si la lista tiene 7 elementos y la divide 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)
eso se 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 proporciona fragmentos de 3, en lugar de 3 fragmentos.Podría simplemente transponer (iterar sobre esto y tomar el primer elemento de cada columna, llamar a esa parte uno, luego tomar la segunda y ponerlo en la parte dos, etc.), pero eso destruye el orden de los elementos.
Solución
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]]
Otros consejos
Puede escribirlo de manera bastante simple como un generador de lista:
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 que no desea que 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]]
Este es el 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]
* de crédito a cero Pireo en la habitación 6
Cambiar el código a trozos n
rendimiento 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 para el grupo final que no es perfecto, pero bien dentro de su especificación de las "partes aproximadamente iguales N" :-) Por eso, quiero decir 56 elementos serían mejor como (19,19,18) mientras que esto da (18,18,20).
Se 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 salidas:
[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 se divide elementos n
en trozos más o menos k
puede hacer trozos n % k
1 elemento más grandes que los otros trozos de 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: resultados n=11, k=3
en [4, 4, 3]
A continuación, se puede calcular fácilmente los Indizes de inicio de los trozos:
[i * (n // k) + min(i, n % k) for i in range(k)]
Ejemplo: resultados n=11, k=3
en [0, 4, 8]
Uso del trozo i+1
th como el límite obtenemos que el trozo de i
th 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 crear una lista de todos los trozos usando listas por comprensión:
[l[i * (n // k) + min(i, n % k):(i+1) * (n // k) + min(i+1, n % k)] for i in range(k)]
Ejemplo: resultados n=11, k=3, l=range(n)
en [range(0, 4), range(4, 8), range(8, 11)]
Aquí es uno que añade None
para hacer la lista de la misma 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)]
Esto va a hacer 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 se divide en 5 partes. El tamaño de las piezas difiere en no más de un elemento.
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 través > pip install more_itertools
.
Para consultar 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.
Sólo especifique el número de partes desea que la matriz que se divide en divisiones to.The será de 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 (número entero) de trozos. Si el número de fragmentos es mayor que la longitud de la lista de entrada de algunos trozos estará vacía. Este algoritmo se alterna entre trozos cortos y largos en lugar de la segregación de los mismos.
También he incluido algo de 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 este ligeramente más eficiente mediante la exportación de la multiplicación en la llamada range
, pero creo que la versión anterior es más fácil de leer (y secadora).
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
Uso de lista por 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 más corto 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)]
decir que desea dividir en 5 partes:
p1, p2, p3, p4, p5 = np.split(df, 5)
Otra forma sería algo como esto, la idea aquí es utilizar mero, pero deshacerse de None
. En este caso tendremos todos small_parts '' formados a partir de elementos en la primera parte de la lista, y '' larger_parts desde la parte posterior de la lista. Longitud de 'partes más grandes' es len (small_parts) + 1. Debemos considerar X como dos diferentes sub-partes.
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 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)]
>>>
Esta es otra variante que se propaga a los "restantes" elementos de manera uniforme entre todos los trozos, uno a la vez hasta que no quede ninguno. En esta implementación, los trozos más grandes se producen al comenzar el 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, generar 4 trozos 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]
Al igual que la respuesta de , pero toma en cuenta las listas con el tamaño más pequeño que el número de los desconchados.
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 de 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 es posible 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
enlace , y esto fue lo que me ayudó. Tenía una lista predefinida.
He código escrito en este caso a mí mismo:
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) devolvería
[(1, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10)]
este código funciona para mí (compatible con Python3):
def chunkify(tab, num):
return [tab[i*num: i*num+num] for i in range(len(tab)//num+(1 if len(tab)%num else 0))]
ejemplo (para matriz de bytes tipo, pero funciona para listas también):
b = bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08')
>>> chunkify(b,3)
[bytearray(b'\x01\x02\x03'), bytearray(b'\x04\x05\x06'), bytearray(b'\x07\x08')]
>>> chunkify(b,4)
[bytearray(b'\x01\x02\x03\x04'), bytearray(b'\x05\x06\x07\x08')]
Éste proporciona trozos de longitud <= n,> = 0
def
chunkify(lst, n):
num_chunks = int(math.ceil(len(lst) / float(n))) if n < len(lst) else 1
return [lst[n*i:n*(i+1)] for i in range(num_chunks)]
por ejemplo
>>> chunkify(range(11), 3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
>>> chunkify(range(11), 8)
[[0, 1, 2, 3, 4, 5, 6, 7], [8, 9, 10]]
He intentado mayor parte de las soluciones, pero no lo hicieron el trabajo para mi caso, así que hago una nueva función que el trabajo para la mayoría de los casos y para cualquier tipo de matriz:
import math
def chunkIt(seq, num):
seqLen = len(seq)
total_chunks = math.ceil(seqLen / num)
items_per_chunk = num
out = []
last = 0
while last < seqLen:
out.append(seq[last:(last + items_per_chunk)])
last += items_per_chunk
return out
def evenly(l, n):
len_ = len(l)
split_size = len_ // n
split_size = n if not split_size else split_size
offsets = [i for i in range(0, len_, split_size)]
return [l[offset:offset + split_size] for offset in offsets]
Ejemplo:
l = [a for a in range(97)]
debe consistir en 10 partes, cada uno tiene 9 elementos, excepto el último.
Salida:
[[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, 75, 76, 77, 78, 79, 80],
[81, 82, 83, 84, 85, 86, 87, 88, 89],
[90, 91, 92, 93, 94, 95, 96]]
Para completar la linspace y que sirva como un índice es una solución más fácil de 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