6.2. Tablica ndarray¶
ndarray to typ przechowujący dane liczbowe w numpy. Jest dwiema rzeczami w jednym: pojedynczym upakowanym blokiem danych oraz małym deskryptorem przed tym blokiem, który mówi, jak go odczytać.
6.2.1. Wnętrze pudełka¶
Blok danych przechowuje każdy element tablicy jeden za drugim, bez niczego dodatkowego pomiędzy nimi. Każdy element zajmuje tę samą liczbę bajtów – jeden dla tablicy wartości uint8, dwa dla uint16, cztery dla float. 256-elementowa tablica uint8 to dokładnie 256 bajtów danych; te same 256 liczb w pythonowej list zajmuje kilobajt – jedno 32-bitowe gniazdo na element, niezależnie od tego, jak mało bitów wartość faktycznie potrzebuje.
Deskryptor zapisuje, co blok oznacza. Pięć wartości wystarczy, aby opisać dowolną prostokątną tablicę, bez względu na liczbę wymiarów:
dtype– typ elementu. Rozstrzyga, ile bajtów zajmuje każdy element i jaki zakres wartości może przechowywać (omówione na stronie Dtypes).itemsize– szerokość jednego elementu w bajtach, wyprowadzona z dtype.ndim– liczba wymiarów (1 dla wektora, 2 dla macierzy, 3 dla bryły, do 4).shape– rozmiar wzdłuż każdego wymiaru jako krotka.strides– sposób przechodzenia przez blok danych, aby przejść każdą oś. Omówione na stronie Kształt i kroki.
To wszystko. Każda szybka ścieżka w numpy – arytmetyka, redukcje, rozgłaszanie, wycinanie – działa bezpośrednio na podstawie tych pięciu wartości plus wskaźnika do danych, bez narzutu Pythona na element.
6.2.2. Co daje ten projekt¶
Z „upakowanego bloku + małego deskryptora” wynikają trzy właściwości, które definiują zachowanie reszty tego rozdziału.
Matematyka element po elemencie działa jako pojedyncze wywołanie. a + b między dwiema tablicami o zgodnym kształcie dodaje oba bufory i zapisuje trzeci, wszystko w obrębie jednego wywołania biblioteki. np.sin(a) robi to samo dla sinusa każdego elementu. Operatory arytmetyczne, porównania i bitowe działają w ten sposób.
Spojrzenie na te same dane w inny sposób jest darmowe. Zapytanie o podobszar tablicy lub o te same dane ułożone w innym kształcie nie przemieszcza żadnych bajtów. Operacja zwraca nowy deskryptor wskazujący na ten sam blok danych. Wynik nazywany jest widokiem – drugim oknem na ten sam bufor bazowy. Zapis przez widok zapisuje do źródła.
Mieszane kształty wciąż działają. Krótsza tablica wobec dłuższej, wiersz wobec macierzy, kolumna wobec wiersza – numpy wyrównuje je przez rozgłaszanie, niewielki zestaw reguł, które rozstrzygają, która krótka oś rozciąga się, aby dopasować się do długiej. Rozciąganie jest wirtualne; żadne dane nie są powielane.
6.2.3. Co kosztuje ten projekt¶
Z tego samego projektu wynikają dwa ograniczenia.
Każdy element ma ten sam typ. Lista może przechowywać int obok str obok listy trzech kolejnych wartości int; ndarray nie może. Dtype jest ustalany w czasie konstrukcji. Strona Dtypes omawia niewielki zestaw typów, które obsługuje numpy, oraz reguły wynikające z ustalenia jednego z nich.
Powiększanie tablicy nie jest darmowe. Lista przechowuje zapasowe gniazda na końcu i tanio obsługuje .append. ndarray ma dokładnie taki rozmiar, jaki musi mieć; dołączanie oznaczałoby zaalokowanie nowego, większego bufora i skopiowanie do niego starej zawartości. Celowo nie ma metody append(). Właściwym wzorcem na kamerze jest wstępne zaalokowanie miejsca docelowego w jego ostatecznym rozmiarze i wypełnienie go; strona Wydajność omawia tę technikę.
Z upakowanym, typowanym buforem na dane, małym deskryptorem na metadane oraz trzema gwarancjami zachowania (szybka matematyka element po elemencie, niekopiujące alternatywne widoki tych samych danych oraz kształty, które się rozgłaszają), ndarray jest fundamentem, na którym opiera się reszta rozdziału. Jak tablica faktycznie powstaje – z literału, z wstępnie wypełnionej alokacji, z bufora urządzenia peryferyjnego – to kolejne praktyczne pytanie.