La conversion implicite d'une séquence Python de listes de longueur variable en un tableau NumPy fait que le tableau est de type objet.
v = [[1], [1, 2]]
np.array(v)
>>> array([[1], [1, 2]], dtype=object)
Essayer de forcer un autre type provoquera une exception:
np.array(v, dtype=np.int32)
ValueError: setting an array element with a sequence.
Quelle est la façon la plus efficace d'obtenir un tableau NumPy dense de type int32, en remplissant les valeurs "manquantes" avec un espace réservé donné?
De ma séquence d'échantillons v
, je voudrais obtenir quelque chose comme ça, si 0 est l'espace réservé
array([[1, 0], [1, 2]], dtype=int32)
Vous pouvez utiliser itertools.Zip_longest :
import itertools
np.array(list(itertools.Zip_longest(*v, fillvalue=0))).T
Out:
array([[1, 0],
[1, 2]])
Remarque: Pour Python 2, c'est itertools.izip_longest .
Voici une approche basée sur l'indexation booléenne presque * vectorisée que j'ai utilisée dans plusieurs autres articles -
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
Exécution d'échantillons
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]])
* Veuillez noter que cela a été considéré comme presque vectorisé car le seul bouclage effectué ici est au début, où nous obtenons les longueurs des éléments de la liste. Mais cette partie qui n'est pas si exigeante en termes de calcul devrait avoir un effet minimal sur le temps d'exécution total.
Test d'exécution
Dans cette section, je chronomètre DataFrame-based solution by @Alberto Garcia-Raboso
, itertools-based solution by @ayhan
car ils semblent bien évoluer et l'indexation booléenne est basée sur ce post pour un ensemble de données relativement plus large avec trois niveaux de variation de taille entre les éléments de la liste.
Cas n ° 1: variation de taille plus importante
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
Cas n ° 2: variation de taille moindre
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
Cas n ° 3: plus grand nombre d'éléments (100 max) par élément de liste
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
Pour moi, il semble il n'y a pas de gagnant clair, mais il faudrait le prendre au cas par cas!itertools.izip_longest
se porte plutôt bien!
Les pandas et leurs DataFrame
- s traitent à merveille des données manquantes.
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)
Voici une manière générale:
>>> 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)
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