Convierta la secuencia de Python a la matriz NumPy, llenando los valores faltantes
arrays sequence (5)
Aquí hay un enfoque basado en la indexación booleana casi * vectorizado que he usado en varias otras publicaciones:
def boolean_indexing(v):
lens = np.array([len(item) for item in v])
mask = lens[:,None] > np.arange(lens.max())
out = np.zeros(mask.shape,dtype=int)
out[mask] = np.concatenate(v)
return out
Ejecución de la muestra
In [27]: v
Out[27]: [[1], [1, 2], [3, 6, 7, 8, 9], [4]]
In [28]: out
Out[28]:
array([[1, 0, 0, 0, 0],
[1, 2, 0, 0, 0],
[3, 6, 7, 8, 9],
[4, 0, 0, 0, 0]])
* Tenga en cuenta que esto se acuñó como casi vectorizado porque el único bucle realizado aquí es al principio, donde estamos obteniendo las longitudes de los elementos de la lista. Pero esa parte que no sea tan computacionalmente exigente debería tener un efecto mínimo en el tiempo de ejecución total.
Prueba de tiempo de ejecución
En esta sección, estoy cronometrando la
DataFrame-based solution by @Alberto Garcia-Raboso
, la
itertools-based solution by @ayhan
ya que parecen escalar bien y la indexación booleana basada en esta publicación para un conjunto de datos relativamente más grande con tres niveles de tamaño variación a través de los elementos de la lista.
Caso # 1: mayor variación de tamaño
In [44]: v = [[1], [1,2,4,8,4],[6,7,3,6,7,8,9,3,6,4,8,3,2,4,5,6,6,8,7,9,3,6,4]]
In [45]: v = v*1000
In [46]: %timeit pd.DataFrame(v).fillna(0).values.astype(np.int32)
100 loops, best of 3: 9.82 ms per loop
In [47]: %timeit np.array(list(itertools.izip_longest(*v, fillvalue=0))).T
100 loops, best of 3: 5.11 ms per loop
In [48]: %timeit boolean_indexing(v)
100 loops, best of 3: 6.88 ms per loop
Caso # 2: variación de tamaño menor
In [49]: v = [[1], [1,2,4,8,4],[6,7,3,6,7,8]]
In [50]: v = v*1000
In [51]: %timeit pd.DataFrame(v).fillna(0).values.astype(np.int32)
100 loops, best of 3: 3.12 ms per loop
In [52]: %timeit np.array(list(itertools.izip_longest(*v, fillvalue=0))).T
1000 loops, best of 3: 1.55 ms per loop
In [53]: %timeit boolean_indexing(v)
100 loops, best of 3: 5 ms per loop
Caso # 3: mayor número de elementos (100 máx.) Por elemento de lista
In [139]: # Setup inputs
...: N = 10000 # Number of elems in list
...: maxn = 100 # Max. size of a list element
...: lens = np.random.randint(0,maxn,(N))
...: v = [list(np.random.randint(0,9,(L))) for L in lens]
...:
In [140]: %timeit pd.DataFrame(v).fillna(0).values.astype(np.int32)
1 loops, best of 3: 292 ms per loop
In [141]: %timeit np.array(list(itertools.izip_longest(*v, fillvalue=0))).T
1 loops, best of 3: 264 ms per loop
In [142]: %timeit boolean_indexing(v)
10 loops, best of 3: 95.7 ms per loop
Para mí, parece que
no hay un ganador claro, ¡pero tendría que tomarse caso por caso!
itertools.izip_longest
está bastante bien.
La conversión implícita de una secuencia de Python de listas de longitud variable en una matriz NumPy hace que la matriz sea de tipo objeto .
v = [[1], [1, 2]]
np.array(v)
>>> array([[1], [1, 2]], dtype=object)
Intentar forzar otro tipo causará una excepción:
np.array(v, dtype=np.int32)
ValueError: setting an array element with a sequence.
¿Cuál es la forma más eficiente de obtener una matriz densa NumPy de tipo int32, rellenando los valores "faltantes" con un marcador de posición dado?
De mi secuencia de muestra
v
, me gustaría obtener algo como esto, si 0 es el marcador de posición
array([[1, 0], [1, 2]], dtype=int32)
Aquí hay una forma general:
>>> v = [[1], [2, 3, 4], [5, 6], [7, 8, 9, 10], [11, 12]]
>>> max_len = np.argmax(v)
>>> np.hstack(np.insert(v, range(1, len(v)+1),[[0]*(max_len-len(i)) for i in v])).astype(''int32'').reshape(len(v), max_len)
array([[ 1, 0, 0, 0],
[ 2, 3, 4, 0],
[ 5, 6, 0, 0],
[ 7, 8, 9, 10],
[11, 12, 0, 0]], dtype=int32)
Pandas y su
DataFrame
tratan muy bien los datos faltantes.
import numpy as np
import pandas as pd
v = [[1], [1, 2]]
print(pd.DataFrame(v).fillna(0).values.astype(np.int32))
# array([[1, 0],
# [1, 2]], dtype=int32)
Puede usar itertools.zip_longest :
import itertools
np.array(list(itertools.zip_longest(*v, fillvalue=0))).T
Out:
array([[1, 0],
[1, 2]])
Nota: Para Python 2, es itertools.izip_longest .
max_len = max(len(sub_list) for sub_list in v)
result = np.array([sub_list + [0] * (max_len - len(sub_list)) for sub_list in v])
>>> result
array([[1, 0],
[1, 2]])
>>> type(result)
numpy.ndarray