## Uvod v `numpy`: tabele, indeksiranje, operacije

Bistvo tegale bo spoznavanje modula `numpy` prek nalog iz Advent of Code 2021. Letos je bilo precej nalog, ki se jih je elegantno rešilo s tem modulom. Izbral sem jih in jih uredil glede na to, kaj iz `numpy`-ja potrebujemo. Pred vsako nalogo bom razložil nekaj stvari, ki nam pridejo prav. Potem je ideja, da rešitev nalogo. Potem pa pogledate mojo rešitev. Po tem je vedno koristno, če še sami (recimo po spominu, brez škiljenja) rešite nalogo z enakim trikom, če ste jo prej kako drugače (sploh, če je bil tisti "kako drugače" nepotrebno zapleten).

Modula numpy navadno ne uvozimo z `import numpy`, temveč z

    import numpy as np

Tako počnejo praktično vsi. To pa zato, ker njegove funkcije kličemo res velikokrat in se zelo pozna, ali moramo pisati, na primer, `numpy.sum` ali `np.sum`. Sploh, če imamo `numpy.sum(numpy.any(~numpy.any(x, axis=1)))`.

`numpy` je modul za delo s tabelami. (Včasih se mi bo zareklo in bom namesto "tabela" rekel matrika. To ni najbolj lepo, ni pa vedno narobe.) Te so lahko poljubno velike, poljubno dimenzionalne in vsebujejo poljubne reči. Za razliko od Pythonovih seznamov `numpy`-jeve tabele vedno vsebujejo elemente istega tipa - same `int`-e, same `float`-e, same `bool`-e...

Tabelo najprejprosteje pripravimo s funkcijo `numpy.array`, ki ji kot argument podamo običajni Pythonov seznam. Če vsebuje same `int`-e, bo to tabela `int`-ov, če bo vmes kakšen `float`, bo tabela `float`-ov, če bodo sami `bool`-i, bo tabela `bool`-ov. Da se še kaj drugega, vendar nas za zdaj ne zanima.

    a = np.array([5, 8, 3, 1, 10, 5])

    a

    array([ 5,  8,  3,  1, 10,  5])

Tale `a` se obnaša nekoliko podobno kot Pythonov seznam. Lahko ga indeksiramo - z leve, z desne in z vsakršnimi rezinami.

    a[2]

    3

    a[-3]

    1

    a[2:5]

    array([ 3,  1, 10])

Tako kot v Pythonovih seznamih lahko vse elemente razen prvega dobimo z

    a[1:]

    array([ 8,  3,  1, 10,  5])

in vse razen zadnjega z

    a[:-1]

    array([ 5,  8,  3,  1, 10])

Od Pythonovih seznamov pa se razlikuje po tem, da računske operacije nad njim delujejo "po elementih".

    a

    array([ 5,  8,  3,  1, 10,  5])

    a + 10

    array([15, 18, 13, 11, 20, 15])

    a * 7

    array([35, 56, 21,  7, 70, 35])

    a ** 2

    array([ 25,  64,   9,   1, 100,  25])

In celo

    2 ** a

    array([  32,  256,    8,    2, 1024,   32])

Še bolj pomembno: tudi računske operacije med dvema tabelama se odvijajo po elementih.

    b = np.array([4, 1, 12, 2, 1, -5])

    a

    array([ 5,  8,  3,  1, 10,  5])

    b

    array([ 4,  1, 12,  2,  1, -5])

    a - b

    array([ 1,  7, -9, -1,  9, 10])

    a * b

    array([ 20,   8,  36,   2,  10, -25])

Za tole je pomembno, da sta tabeli enako veliki. Če ne bi bili, bi se numpy pač pritožil.

Podobno se vedejo operacije, kot so `<` in `==`.

    b == 1

    array([False,  True, False, False,  True, False])

    a > b

    array([ True,  True, False, False,  True,  True])

Kako ugotoviti, koliko `1` je v tabeli `b`? (Dve sta.) Tabele nimajo metode `count`. Je ne potrebujejo. Naredimo tako:

    sum(b == 1)

    2

Vendar tega ne počnemo - nikoli. `numpy` ima svoj `sum`, `np.sum`, ki je veliko hitrejši od Pythonovega. Pa še nekatere superpowers ima. Poklicati moramo torej

    np.sum(b == 1)

    2

Rezultat je isti in tudi pri hitrosti se zdaj seveda še ne pozna. Nekoč pa se bo, zato se naučite delati tako.

Še eno vprašanje, preden se lotimo naloge: kako bi ugotovili, koliko elementov `a` je večjih od istoležnih elementov `b`?

    a > b

    array([ True,  True, False, False,  True,  True])

    np.sum(a > b)

    4

## Naloga

Zdaj znate dovolj za elegantno reševanje prve naloge, Sonar Sweep.

Lahko jo rešitev najprej v čistem Pythonu, vendar jo rešite lepo - do zaporednih elementov pridite z `zip`-om in rezinami in tako naprej. Potem razmislite, kako se to pove v `numpy`-ju. Če izvzamemo pripravo podatkov, se pravi, ko so podatki enkrat že v tabeli, bi moral biti prvi del zlahko rešljiv z enim samim klicem ... drugi del pa tudi, če se znajdete. Če ne, bo pa kakšna vrstica več.