python - into - split array in n parts
Dividir una lista en N partes de aproximadamente la misma longitud (19)
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]]
¿Cuál es la mejor manera de dividir una lista en partes aproximadamente iguales? Por ejemplo, si la lista tiene 7 elementos y se 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)
que divide 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 trozos de 3, en lugar de 3 trozos. Podría simplemente transponer (iterar sobre esto y tomar el primer elemento de cada columna, llamar a esa parte uno, luego tomar el segundo y ponerlo en la segunda parte, etc.), pero eso destruye el orden de los elementos.
Aquí hay otra variante que distribuye los elementos "restantes" de manera uniforme entre todos los fragmentos, uno a la vez hasta que no quede ninguno. En esta implementación, los trozos más grandes se producen al comienzo 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, genera 4 fragmentos 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]
Aquí hay un generador que puede manejar cualquier número positivo (entero) de trozos. Si la cantidad de trozos es mayor que la longitud de la lista de entrada, algunos trozos estarán vacíos. Este algoritmo alterna entre fragmentos cortos y largos en lugar de segregarlos.
También ragged_chunks
un 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 hacerlo 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
Aquí hay uno que agrega None
para hacer que las listas tengan 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)]
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]]
Cambiar 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 está dentro de tu especificación de "aproximadamente N partes iguales" :-) Con eso, me refiero a 56 elementos sería mejor como (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()
qué 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]
Eche un vistazo a numpy.split :
>>> a = numpy.array([1,2,3,4])
>>> numpy.split(a, 2)
[array([1, 2]), array([3, 4])]
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
Esto hará la división con 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.
Implementación usando el método numpy.linspace.
Solo especifique la cantidad de partes en las que desea dividir la matriz. Las divisiones serán 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])]
Lo mismo que job''s respuesta job''s , pero tiene en cuenta las listas con un tamaño menor que el número de trozos.
def chunkify(lst,n):
[ lst[i::n] for i in xrange(n if n < len(lst) else len(lst)) ]
si n (número de fragmentos) es 7 y lst (la lista para dividir) es [1, 2, 3], los fragmentos son [[0], [1], [2]] en lugar de [[0], [1 ], [2], [], [], [], []]
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 delineador más corto en esta página (escrito por mi niña)
def split(l, n):
return [l[int(i*len(l)/n):int((i+1)*len(l)/n-1)] for i in range(n)]
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]]
Otra forma sería algo como esto, la idea aquí es usar mero, pero deshacerse de None
. En este caso, tendremos todas las ''small_parts'' formadas a partir de los elementos de la primera parte de la lista, y ''larger_parts'' de la parte posterior de la lista. La longitud de las "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 configuro 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)]
>>>
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]]
Redondear el linspace y usarlo como índice es una solución más fácil que lo que amit12690 propone.
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
Si divide n
elementos en aproximadamente k
trozos, puede hacer n % k
trozos 1 elemento más grande que los otros trozos para distribuir los elementos adicionales.
El siguiente código le dará la longitud de los fragmentos:
[(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 indices 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 trozo i+1
como el límite obtenemos que el primer 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 listas:
[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)
resulta en [range(0, 4), range(4, 8), range(8, 11)]
También puedes 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]]
Usando la comprensión de la lista:
def divide_list_to_chunks(list_, n):
return [list_[start::n] for start in range(n)]