1. La signification des formes dans NumPy
Vous écrivez: «Je sais que c'est littéralement une liste de nombres et une liste de listes où toute la liste ne contient qu'un nombre» mais c'est un peu une façon inutile de penser à ce sujet.
La meilleure façon de penser aux tableaux NumPy est qu'ils se composent de deux parties, un tampon de données qui n'est qu'un bloc d'éléments bruts et une vue qui décrit comment interpréter le tampon de données.
Par exemple, si nous créons un tableau de 12 entiers:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Se a
compose alors d'un tampon de données, arrangé quelque chose comme ceci:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
et une vue qui décrit comment interpréter les données:
>>> a.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)
Ici, la forme (12,)
signifie que le tableau est indexé par un index unique qui va de 0 à 11. Conceptuellement, si nous étiquetons cet index unique i
, le tableau a
ressemble à ceci:
i= 0 1 2 3 4 5 6 7 8 9 10 11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Si nous remodelons un tableau, cela ne change pas le tampon de données. Au lieu de cela, il crée une nouvelle vue qui décrit une manière différente d'interpréter les données. Donc après:
>>> b = a.reshape((3, 4))
le tableau b
a le même tampon de données que a
, mais maintenant il est indexé par deux indices qui vont de 0 à 2 et de 0 à 3 respectivement. Si nous étiquetons les deux indices i
et j
, le tableau b
ressemble à ceci:
i= 0 0 0 0 1 1 1 1 2 2 2 2
j= 0 1 2 3 0 1 2 3 0 1 2 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
ce qui signifie que:
>>> b[2,1]
9
Vous pouvez voir que le deuxième index change rapidement et que le premier index change lentement. Si vous préférez que ce soit l'inverse, vous pouvez spécifier le order
paramètre:
>>> c = a.reshape((3, 4), order='F')
ce qui se traduit par un tableau indexé comme ceci:
i= 0 1 2 0 1 2 0 1 2 0 1 2
j= 0 0 0 1 1 1 2 2 2 3 3 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
ce qui signifie que:
>>> c[2,1]
5
Il doit maintenant être clair ce que signifie pour un tableau d'avoir une forme avec une ou plusieurs dimensions de taille 1. Après:
>>> d = a.reshape((12, 1))
le tableau d
est indexé par deux indices, dont le premier va de 0 à 11, et le deuxième est toujours 0:
i= 0 1 2 3 4 5 6 7 8 9 10 11
j= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
et donc:
>>> d[10,0]
10
Une dimension de longueur 1 est "libre" (dans un certain sens), donc rien ne vous empêche d'aller en ville:
>>> e = a.reshape((1, 2, 1, 6, 1))
donnant un tableau indexé comme ceci:
i= 0 0 0 0 0 0 0 0 0 0 0 0
j= 0 0 0 0 0 0 1 1 1 1 1 1
k= 0 0 0 0 0 0 0 0 0 0 0 0
l= 0 1 2 3 4 5 0 1 2 3 4 5
m= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
et donc:
>>> e[0,1,0,0,0]
6
Consultez la documentation des composants internes NumPy pour plus de détails sur la façon dont les tableaux sont implémentés.
2. Que faire?
Comme il numpy.reshape
suffit de créer une nouvelle vue, vous ne devriez pas avoir peur de l'utiliser chaque fois que nécessaire. C'est le bon outil à utiliser lorsque vous souhaitez indexer un tableau d'une manière différente.
Cependant, dans un calcul long, il est généralement possible d'arranger pour construire des tableaux avec la "bonne" forme en premier lieu, et ainsi minimiser le nombre de remodelages et de transpositions. Mais sans voir le contexte réel qui a conduit à la nécessité d'une refonte, il est difficile de dire ce qui devrait être changé.
L'exemple de votre question est:
numpy.dot(M[:,0], numpy.ones((1, R)))
mais ce n'est pas réaliste. Tout d'abord, cette expression:
M[:,0].sum()
calcule le résultat plus simplement. Deuxièmement, y a-t-il vraiment quelque chose de spécial dans la colonne 0? Peut-être que ce dont vous avez réellement besoin est:
M.sum(axis=0)