1. Znaczenie kształtów w NumPy
Piszesz: „Wiem dosłownie jest to lista liczb i lista list, gdzie cała lista zawiera tylko liczbę”, ale to trochę niepomocny sposób, aby o tym myśleć.
Najlepszym sposobem myślenia o tablicach NumPy jest to, że składają się one z dwóch części, bufora danych, który jest tylko blokiem surowych elementów oraz widoku opisującego sposób interpretacji bufora danych.
Na przykład, jeśli utworzymy tablicę 12 liczb całkowitych:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Następnie a
składa się z bufora danych, ułożonego mniej więcej tak:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
oraz widok opisujący sposób interpretacji danych:
>>> 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,)
Tutaj kształt (12,)
oznacza, że tablica jest indeksowana przez pojedynczy indeks, który biegnie od 0 do 11. Koncepcyjnie, jeśli oznaczymy ten pojedynczy indeks i
, tablica a
wygląda następująco:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Jeśli przekształcimy tablicę, nie zmieni to bufora danych. Zamiast tego tworzy nowy widok, który opisuje inny sposób interpretacji danych. Więc później:
>>> b = a.reshape((3, 4))
tablica b
ma taki sam bufor danych jak a
, ale teraz jest indeksowana przez dwa indeksy, które działają odpowiednio od 0 do 2 i od 0 do 3. Jeśli oznaczymy dwa indeksy i
i j
, tablica b
wygląda następująco:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
co oznacza że:
>>> b[2,1]
9
Widać, że drugi indeks zmienia się szybko, a pierwszy indeks zmienia się powoli. Jeśli wolisz, aby było odwrotnie, możesz określić order
parametr:
>>> c = a.reshape((3, 4), order='F')
co powoduje tablicę indeksowaną w ten sposób:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
co oznacza że:
>>> c[2,1]
5
Teraz powinno być jasne, co to znaczy, że tablica ma kształt o co najmniej jednym wymiarze wielkości 1. Po:
>>> d = a.reshape((12, 1))
tablica d
jest indeksowana przez dwa indeksy, z których pierwszy biegnie od 0 do 11, a drugi indeks zawsze wynosi 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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
a więc:
>>> d[10,0]
10
Wymiar długości 1 jest „wolny” (w pewnym sensie), więc nic nie powstrzymuje cię przed pójściem do miasta:
>>> e = a.reshape((1, 2, 1, 6, 1))
podając tablicę indeksowaną w ten sposób:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
a więc:
>>> e[0,1,0,0,0]
6
Zobacz dokumentację takie wewnętrzne NumPy aby uzyskać więcej informacji o tym, jak tablice zostały zaimplementowane.
2. Co robić?
Ponieważ numpy.reshape
po prostu tworzy nowy widok, nie powinieneś się bać korzystania z niego w razie potrzeby. To właściwe narzędzie do użycia, gdy chcesz zindeksować tablicę w inny sposób.
Jednak w długim obliczeniu zwykle możliwe jest ustawienie konstruowania tablic o „właściwym” kształcie, a tym samym zminimalizowanie liczby przekształceń i transpozycji. Ale nie widząc faktycznego kontekstu, który doprowadził do konieczności przekształcenia, trudno powiedzieć, co należy zmienić.
Przykład w twoim pytaniu to:
numpy.dot(M[:,0], numpy.ones((1, R)))
ale to nie jest realistyczne. Po pierwsze, to wyrażenie:
M[:,0].sum()
oblicza wynik prościej. Po drugie, czy naprawdę jest coś specjalnego w kolumnie 0? Być może tak naprawdę potrzebujesz:
M.sum(axis=0)