web-dev-qa-db-fra.com

Convertir Python en tableau NumPy, en remplissant les valeurs manquantes

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)
21
Marco Ancona

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 .

23
ayhan

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 itertools.izip_longest se porte plutôt bien! il n'y a pas de gagnant clair, mais il faudrait le prendre au cas par cas!

15
Divakar

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

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)
2
Kasramvd
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
2
Alexander