Python původně asi vůbec neměl ambice stát se linguou francou vědecké komunity, nicméně už od počátku spoustu lidí zaujal, a tak asi není divu, že již v roce 1995 začala skupina zájemců pracovat na podpoře pro maticové výpočty.
Byť výsledek jejich práce se nikdy součástí oficiální distribuce Python'u nestal, asi nejen díky zapojení Guida van Rossuma v začátku práce na tomto balíku do Python'u postupně probublala podpora pro jeho snadné nasazení (především v podobě rozšířené výřezové notace).
Chcete-li dnes v Python'u používat vícerozměrná pole (a to ne nutně pouze číselná), je instalace knihovny NumPy víceméně automatickou volbou.
Hned na začátek této úvodní přednášky bohužel jedno důležité upozornění:
Narozdíl od Python'u neprošel NumPy žádným „vyčištěním starého balastu“, takže podle toho po (kumulovaných) dvaceti letech vývoje bohužel vypadá…
Není proto výjimkou – spíše pravidlem :-( – mít na stejnou funkcionalitu více různých metod, a to dokonce i ve stejném modulu. Někdy jsou úplně stejné, jindy dávají stejné výsledky jen pro trochu jiné volby parametrů a podobně. Kolega to vyjádřil lapidárně: „NumPy vypadá, jako kdyby do něj každý dopsal to, co zrovna potřeboval, aniž by se předtím pořádně podíval, jestli v něm náhodou už něco podobného není.“
V podstatě bych řekl, že NumPy vypadá tak trochu jako Python řady 2.x, možná dokonce ještě o něco hůř. Jelikož nemá moc použitelných alternativ, musíme s tím přežít.
Druhou důležitou poznámkou je, že Numpy je vyvíjen s plánovaným odstavováním starší funkcionality verzi od verze. Což by nevadilo, pokud by se při tom kód postupně nestával zpětně nekompatibilní. Anebo ani to by možná tak nevadilo, kdybyste vždy pohledem do kódu dokázali z příkazu import numpy
vydedukovat, jakou verzi měl zrovna autor na mysli. Což ale samozřejmě nejde…
A do třetice všeho zlého: S výše uvedenými dvěma body přímo souvisí další dvě věci, a to dokumentace a tutoriály.
Že dokumentace vypadá všelijak, ne všechno je dopsané a už vůbec ne zhyperlinkované, že je občas dost těžké přijít na to, která z popisované funkcionality je ta „správná“ pro budoucí použití, je poměrně nešťastné, ale jakž takž pochopitelné. Že ale tutoriály roztroušené po webu jsou prostě napsané pro v té době aktuální verzi Numpy a že si jejich autor s sebou „vláčí“ svoje preference (obzvláště na pytel v případě zlozvyků z Python'u 2.x), je už doslova na zabití.
Základním přínosem knihovny NumPy je velmi silná podpora vícerozměrných polí, a to včetně nejrůznějších operací nad nimi.
S tím souvisí, že pole je možno zavést mnoha různými způsoby, od klasického pythoního „seznamu seznamů“ (resp. n-tic)..
..přes nejrůznější zkratky u „klasické“ matice..
..až po specifické metody:
PS: A to jsem ještě nezmínil třebas maticové pohledy nad daty ze souborového systému a jiné. Nebo to, že prvky polí mohou být i velmi komplikované složené typy a ne jen pouze čísla.
Unární a binární operace nad poli jsou v základu prováděny „po složkách“.
Zatímco násobení skalárem je celkem běžné..
a = numpy.array([[1, 2, 3], [4, 5, 6]])
.
..a stejně tak sčítání..
..už takové násobení při letmé znalosti lineární algebry hlavu zamotá (kdo slyšel o Hadamardově součinu matic?):
NumPy nepřekvapivě obsahuje metody, kterými (většinou) docílíte toho, co zamýšlíte, ale ne vždy jsou tam nebo takové, jak byste možná čekali. (Pokud za sebou nemáte MATLAB, protože spousta věcí je mu prý podobná.)
Metody operující nad poli se dají rozdělit přibližně na dvě skupiny:
Ty druhé ale mohou operovat třebas jen po řádcích i jinak, záleží na konkrétní metodě a jejím účelu (a vizi autora kódu).
Důležitým předpokladem pro správnou práci s knihovnou NumPy je pochopení principu broadcastingu. Jde v něm o to, že knihovní metody dokážou za jistých – přesně definovaných! – podmínek zpracovat požadavky na operace mezi polemi, jejichž rozměry si z hlediska dané operace jinak nemusí vůbec vyhovovat.
Trochu zjednodušeně se dá říct, že pokud je možné jedno (nebo i obě!) pole „protáhnout“ z jedničky na vyhovující rozměr, bude tak (fiktivně) provedeno jeho nakopírováním do chybějících rozměrů a výsledek bude spočítán klasicky složku po složce, jak by se stalo v případě, kdy by obě měla vyhovující rozměr hned na začátku.
Spoustu operací dokáže broadcasting velmi elegantně zapsat a díky vnitřní optimalizaci i rychle vyřešit. Pro velmi velké vstupy však už nemusí být rychlejší a vůbec méně náročnější než třebas méně přehledná smyčka nad menšími výseky dat.
Jak bylo vidět už na předchozích příkladech, v duchu pythoní tradice dokáží metody Numpy zpracovat vstupy v nejrůznějším provedení.
Ještě pohodlnější je, že dokonce i běžné binární operátory dokážou podobným způsobem přizpůsobit jeden ze svých vstupů, pokud ten druhý je numpyovské pole. K poli tak můžete třebas přičítat nejen skalár (tedy pythonovské číslo), ale též i vektor v podobě pythonovského seznamu a podobně:
Abychom se hned na začátku vyhnuli nedorozumění, dvourozměrná pole nejsou maticemi v matematickém smyslu slova! Ovšem knihovna NumPy obsahuje i tyto „matematické matice“, jako podtřídu obecných vícerozměrných polí.
Zatímco u vícerozměrných polí všechny běžné matematické (binární) operace fungují po složkách a na maticové operace je třeba používat speciální metody (nebo v případě Python'u 3.5 a výše pro násobení operátor @
), ve specifickém případě skutečných matic (tedy specielně označených 2D-polí) fungují „maticově“, tedy tak, jak je člověk zvyklý z lineární algebry:
Možná nejen proto je používání typu matrix vřele nedoporučováno.
Pokud vám zrovna stačí pole jednorozměrné a nepotřebujete žádné dodatečné operace nad jeho prvky, rychlejší a podstatně méně „těžkotonážní“ variantou je modul array
, který je součástí distribuce Python'u.
Každý z modulů má i tak ovšem svá specifika, takže si musíte volbu důkladně rozmyslet: