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 acompose 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 aressemble à 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 ba 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 iet j, le tableau bressemble à 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 orderparamè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 dest 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.reshapesuffit 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)