Je veux créer un tableau numpy dans lequel chaque élément doit être une liste, donc plus tard je pourrai ajouter de nouveaux éléments à chacun.
J'ai déjà regardé sur google et ici sur le débordement de la pile, mais il ne semble nulle part être trouvé.
Le problème principal est que numpy suppose que votre liste doit devenir un tableau, mais ce n'est pas ce que je recherche.
Comme vous l'avez découvert, np.array
essaie de créer un tableau 2D lorsque quelque chose comme
A = np.array([[1,2],[3,4]],dtype=object)
Vous avez appliqué quelques astuces pour contourner ce comportement par défaut.
L'une consiste à rendre les sous-listes de longueur variable. Il ne peut pas créer un tableau 2D à partir de ceux-ci, il a donc recours au tableau d'objets:
In [43]: A=np.array([[1,2],[],[1,2,3,4]])
In [44]: A
Out[44]: array([[1, 2], [], [1, 2, 3, 4]], dtype=object)
Et vous pouvez ensuite ajouter des valeurs à chacune de ces listes:
In [45]: for i in A: i.append(34)
In [46]: A
Out[46]: array([[1, 2, 34], [34], [1, 2, 3, 4, 34]], dtype=object)
np.empty
crée également un tableau d'objets:
In [47]: A=np.empty((3,),dtype=object)
In [48]: A
Out[48]: array([None, None, None], dtype=object)
Mais vous devez alors faire attention à la façon dont vous changez les éléments en listes. np.fill
est tentant, mais a des problèmes:
In [49]: A.fill([])
In [50]: A
Out[50]: array([[], [], []], dtype=object)
In [51]: for i in A: i.append(34)
In [52]: A
Out[52]: array([[34, 34, 34], [34, 34, 34], [34, 34, 34]], dtype=object)
Il s'avère que fill
met la même liste dans tous les emplacements, donc en modifier un modifie tous les autres. Vous pouvez obtenir le même problème avec une liste de listes:
In [53]: B=[[]]*3
In [54]: B
Out[54]: [[], [], []]
In [55]: for i in B: i.append(34)
In [56]: B
Out[56]: [[34, 34, 34], [34, 34, 34], [34, 34, 34]]
La bonne façon d'initialiser le empty
A
est avec une itération, par ex.
In [65]: A=np.empty((3,),dtype=object)
In [66]: for i,v in enumerate(A): A[i]=[v,i]
In [67]: A
Out[67]: array([[None, 0], [None, 1], [None, 2]], dtype=object)
In [68]: for v in A: v.append(34)
In [69]: A
Out[69]: array([[None, 0, 34], [None, 1, 34], [None, 2, 34]], dtype=object)
La question et les commentaires ne permettent pas de savoir si vous souhaitez ajouter des listes ou des listes au tableau. Je viens de montrer en annexe aux listes.
Il y a un np.append
, dont les nouveaux utilisateurs abusent souvent. Il ne remplace pas l'ajout de liste. C'est une interface pour np.concatenate
. Ce n'est pas une opération sur place; il renvoie un nouveau tableau.
Définir une liste à ajouter peut également être délicat:
In [72]: np.append(A,[[1,23]])
Out[72]: array([[None, 0, 34], [None, 1, 34], [None, 2, 34], 1, 23], dtype=object)
Vous devez construire un autre tableau d'objets pour concaténer à l'original, par exemple.
In [76]: np.append(A,np.empty((1,),dtype=object))
Out[76]: array([[None, 0, 34], [None, 1, 34], [None, 2, 34], None], dtype=object)
Dans tout cela, un tableau de listes est plus difficile à construire qu'une liste de listes, et pas plus facile, ni plus rapide, à manipuler. Vous devez en faire un tableau 2D de listes pour en tirer un avantage.
In [78]: A[:,None]
Out[78]:
array([[[None, 0, 34]],
[[None, 1, 34]],
[[None, 2, 34]]], dtype=object)
Vous pouvez remodeler, transposer, etc. un tableau d'objets, où la création et la manipulation d'une liste de listes de listes devient plus compliquée.
In [79]: A[:,None].tolist()
Out[79]: [[[None, 0, 34]], [[None, 1, 34]], [[None, 2, 34]]]
===
Comme indiqué dans https://stackoverflow.com/a/57364472/901925 , np.frompyfunc
est un bon outil pour créer un tableau d'objets.
np.frompyfunc(list, 0, 1)(np.empty((3,2), dtype=object))
Si vous avez vraiment besoin d'un tableau de listes 1-d, vous devrez envelopper vos listes dans votre propre classe car numpy essaiera toujours de convertir vos listes en tableaux à l'intérieur d'un tableau (ce qui est plus efficace mais nécessite évidemment des éléments de taille constante) , par exemple via
class mylist:
def __init__(self, l):
self.l=l
def __repr__(self):
return repr(self.l)
def append(self, x):
self.l.append(x)
et puis vous pouvez changer n'importe quel élément sans changer la dimension des autres
>>> x = mylist([1,2,3])
>>> y = mylist([1,2,3])
>>> import numpy as np
>>> data = np.array([x,y])
>>> data
array([[1,2,3], [1,2,3]], dtype=object)
>>> data[0].append(2)
>>> data
array([[1,2,3,2], [1,2,3]], dtype=object)
Comme suggéré par ALi_m
il existe en fait un moyen de forcer numpy à créer simplement un tableau 1-d pour les références, puis à les alimenter avec des listes réelles
>>> data = np.empty(2, dtype=np.object)
>>> data[:] = [1, 2, 3], [1, 2, 3]
>>> data
array([[1, 2, 3], [1, 2, 3]], dtype=object)
>>> data[0].append(4)
>>> data
array([[1, 2, 3, 4], [1, 2, 3]], dtype=object)