<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>NumPy – operace s polemi</title>
  <date>2017-04-23</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
  <!--
  <style>
    /* CSSka */
  </style>
  <use-math/>
  -->
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->

<!--
    Nápověda:
        np.info(np.arange)
        np.lookfor('create array')
    
    Kapitola:
        http://www.scipy-lectures.org/intro/numpy/operations.html
    Konkrétně:
        http://www.scipy-lectures.org/intro/numpy/operations.html#elementwise-operations
        http://www.scipy-lectures.org/intro/numpy/operations.html#sorting-data
        http://www.scipy-lectures.org/intro/numpy/operations.html#basic-reductions
    
    Další:
        https://docs.scipy.org/doc/numpy-1.14.2/user/
        https://docs.scipy.org/doc/numpy-1.14.2/user/quickstart.html
        https://docs.scipy.org/doc/numpy-1.14.2/reference/routines.html
        https://docs.scipy.org/doc/numpy-1.14.2/reference/generated/numpy.concatenate.html
    A ještě:
        https://engineering.ucsb.edu/~shell/che210d/numpy.pdf
-->


<slide title="Úvod">

    <p>
        Operace s <strong>(vícerozměrnými) poli</strong> (tedy objekty typu <strong><code>ndarray</code></strong>) v <em>NumPy</em> jsou velmi důležité a také velmi rozmanité. Ruku v ruce s výřezy máme k dispozici velmi početnou armádu metod, které slouží k úpravě již existujících polí/matic a výběru nějakých jejich podmnožin.
    </p>
    <p>
        V dalším si ukážeme pár základních příkladů na práci s numpyovskými polemi, ale jako vždy – <a href="https://docs.scipy.org/doc/numpy-1.14.2/reference/routines.html" class="external">knihovna <em>Numpy</em> je příliš rozsáhlá</a>, abychom zde probrali všechno, a navíc v žádném případě nemůžu tvrdit, že bych ji vůbec nějak ovládal…
    </p>
    <p>
        PS: V příkladech budu používat modul <em>numpy</em> naimportovaný podle zvyku jako:
        <example lang="python">
            import numpy as np
        </example>
    </p>

</slide>
<slide title="I. Binární (a unární) operace mezi poli">

    <p>
        Jak <a href="overview.xml?slajd=4">už jsem zmiňoval</a> v přehledové přednášce, unární a <strong>binární operace nad poli jsou v základu prováděny „po složkách“</strong>:
    </p>
    <example lang="python">
        # základní pole
        >>> a = np.array([[1, 2, 3], [4, 5, 6]])
        
        # mínus pole
        >>> -a
        array([[-1, -2, -3],
               [-4, -5, -6]])
        
        # násobení pole skalárem
        >>> 3 * a
        array([[ 3,  6,  9],
               [12, 15, 18]])
        
        # sčítání polí
        >>> a + a
        array([[ 2,  4,  6],
               [ 8, 10, 12]])
    </example>
    <p>
        Zatímco u násobení pole skalárem a sčítání či odčítání polí (stejného tvaru) to nikoho nepřekvapí, takové násobení už nemá s maticovým vůbec nic společného a musíte na to při práci pamatovat:
    </p>
    <example lang="python">
        >>> a * a
        array([[ 1,  4,  9],
               [16, 25, 36]])
    </example>

</slide>
<slide title="Další příklady se skaláry">

    <p>
        Se skaláry se dá nepřekvapivě „vyřádit“ mnohem více:
    </p>
    <example lang="python">
        >>> a = np.arange(6).reshape(2, 3)
        >>> a
        array([[0, 1, 2],
               [3, 4, 5]])
        
        >>> a + 1
        array([[1, 2, 3],
               [4, 5, 6]])
        
        >>> 2**a
        array([[ 1,  2,  4],
               [ 8, 16, 32]])
        
        >>> 2**(a + 1)
        array([[ 2,  4,  8],
               [16, 32, 64]])
        
        >>> 2**a - 1
        array([[ 0,  1,  3],
               [ 7, 15, 31]])
    </example>

</slide>
<slide title="Složitější typy">

    <p>
        <em>Numpy</em> ale dokáže i zkonvertovat (a případně též <a href="broadcasting.xml">zbroadcastovat</a>) i složitější pythoní typy, třebas n-tice a seznamy:
    </p>
    <example lang="python">
        >>> a = np.arange(6).reshape(2, 3)
        >>> a
        array([[0, 1, 2],
               [3, 4, 5]])
        
        # Přičtení seznamu zprava:
        >>> a + [1, 1, 1]
        array([[1, 2, 3],
               [4, 5, 6]])
        
        # Násobení n-ticí zleva:
        >>> (2,) * a
        array([[ 0,  2,  4],
               [ 6,  8, 10]])
    </example>

</slide>
<slide title="II. Logické operace">

    <p>
        U logických operací je třeba dát pozor, zda požadujeme jako výsledek:
    </p>
    <ul>
        <li>
            <a href="?slajd=6">pole</a>, jehož prvky budou příslušným logickým složením prvků vstupních polí (tedy v podstatě binární operace nad prvky pole);
        </li>
        <li>
            <a href="?slajd=7">logickou hodnotu</a>, která je výsledkem porovnání vstupních polí jako celku.
        </li>
    </ul>
    <note>
        Následující příklady podle <a href="http://www.scipy-lectures.org/intro/numpy/operations.html" class="external">dokumentace ke SciPy</a>.
    </note>

</slide>
<slide title="Po prvcích">

    <p>
        K dispozici máme celou armádu pythoních <a href="/materialy/python/types/boolean.xml?slajd=3">porovnávacích operátorů</a> plus spoustu dedikovaných logických funkcí:
    </p>
    <example lang="python">
        >>> a = np.array([1, 1, 0, 0], dtype=bool)
        >>> b = np.array([1, 0, 1, 0], dtype=bool)
        
        # operátor
        >>> a == b
        array([ True, False, False,  True])
        
        # logická funkce
        >>> np.logical_or(a, b)
        array([ True,  True,  True, False])
    </example>

</slide>
<slide title="Po polích">

    <p>
        Většina <a href="https://docs.scipy.org/doc/numpy-1.14.2/reference/routines.logic.html" class="external">dostupných logických funkcí</a> ale operuje nad poli jako celkem:
    </p>
    <example lang="python">
        >>> a = np.array([1, 2, 0, 4])
        >>> b = np.array([1, 2, 3, 4])
        
        # srovnání polí
        >>> np.array_equal(a, b)
        False
        
        # redukce pole podle pravdivostní hodnoty prvků
        >>> np.all(a)
        False
        >>> np.any(a)
        True
    </example>

</slide>
<slide title="Příklad">

    <p>
        Operace se dají dohromady poskládat do výrazně složitějších výroků:
    </p>
    <example lang="python">
        >>> a = np.array([1, 2, 3, 2])
        >>> b = np.array([2, 2, 3, 2])
        >>> c = np.array([6, 4, 4, 5])
        >>> ((a &lt;= b) &amp; (b &lt;= c)).all()
        True
        
        # Protože:
        >>> a &lt;= b
        array([ True,  True,  True,  True])
        >>> b &lt;= c
        array([ True,  True,  True,  True])
        >>> (a &lt;= b) &amp; (b &lt;= c)
        array([ True,  True,  True,  True])
    </example>

</slide>
<slide title="III. Matematické funkce">

    <p>
        <em>Numpy</em> obsahuje kromě základních matematických konstant..
    </p>
    <example lang="python">
        >>> np.pi
        3.141592653589793
        >>> np.e
        2.718281828459045
    </example>
    <p>
        ..i <a href="https://docs.scipy.org/doc/numpy-1.14.2/reference/routines.math.html" class="external">spoustu matematických metod</a>. Mezi ty patří jak klasické funkce (trigonometrické funkce, transcendetní funkce…), tak také armáda funkcí specializovaných na konkrétní výpočetní oblast (lineární algebra, Fourierova transformace…).
    </p>
    <note>
        V rámci <em>Numpy</em> se jim říká <strong>univerzální funkce</strong> (tzv. <a href="https://docs.scipy.org/doc/numpy-1.14.2/reference/ufuncs.html" class="external"><code>ufunc</code></a>) a jedná se o – typicky vektorizované – funkce operující nad poli metodou „prvek po prvku“ a splňující jisté základní požadavky (jako třebas <a href="broadcasting.xml">broadcasting</a> a <em>typovou konverzi</em> v závislosti na vstupech a výstupech).
    </note>

</slide>
<slide title="Příklady">

    <example lang="python">
        # „běžná“ matematika
        >>> x = np.array([0, np.pi / 2, np.pi])
        >>> x
        array([0.        , 1.57079633, 3.14159265])
        
        >>> np.sin(x)
        array([0.0000000e+00, 1.0000000e+00, 1.2246468e-16])
        
        >>> np.log(x)
        __main__:1: RuntimeWarning: divide by zero encountered in log
        array([      -inf, 0.45158271, 1.14472989])
        >>> np.log(0)
        -inf
    </example>
    <br/>
    <example lang="python">
        # lineární algebra
        >>> np.linalg.inv([[1, 1], [1, 0]])
        array([[ 0.,  1.],
               [ 1., -1.]])
    </example>
    <br/>
    <example lang="python">
        # komplexní čísla
        >>> np.conjugate(1+2j)
        (1-2j)
    </example>
    <br/>
    <example lang="python">
        # mnohočleny
        >>> from numpy.polynomial import Polynomial as P
        
        # (x-1)(x-2)(x-3)
        >>> p = P.fromroots([1, 2, 3])
        # x^3 - 6x^2 + 11x - 6 (ale v opačném pořadí!)
        >>> p
        Polynomial([-6., 11., -6.,  1.], domain=[-1.,  1.], window=[-1.,  1.])
   </example>

</slide>
<slide title="IV. Další operace">

    <p>
        Už jsem zmiňoval, že <a href="https://docs.scipy.org/doc/numpy-1.14.2/reference/routines.html" class="external">knihovna <em>Numpy</em> je obrovská</a> (a navíc často pěkně zmatená), ale přesto si ještě pár dalších základních operací ukážeme.
    </p>
    <p>
        Především půjde o další <em>redukující operace</em>, které převádí vstupní pole na jeden výstupní prvek (jako jsou sumace, extrémy či základní statistické ukazatele).
    </p>

</slide>
<slide title="Sumace – „sum()“">

    <p>
        Při <strong>redukci sčítáním</strong> si především můžete vybrat, zda sečtete všechny prvky dohromady, nebo zda provedete sčítání prvků pouze po vybraných osách:
    </p>
    <example lang="python">
        >>> x = np.array([[1,2], [3,4]])
        >>> x
        array([[1, 2],
               [3, 4]])
        
        # celkové sčítání
        >>> np.sum(x)
        10
        
        # sčítání po osách
        >>> np.sum(x, axis=0)
        array([4, 6])
        >>> np.sum(x, axis=1)
        array([3, 7])
    </example>

</slide>
<slide title="Sumace – „cumsum()“">

    <p>
        Za zmínku stojí též funkce <em>cumsum()</em> pro kumulativní sčítání nejen ve vybrané ose:
    </p>
    <example lang="python">
        >>> x = np.arange(12).reshape(3,4)
        >>> x
        array([[ 0,  1,  2,  3],
               [ 4,  5,  6,  7],
               [ 8,  9, 10, 11]])
        
        # přes osy
        >>> x.cumsum(axis=0)
        array([[ 0,  1,  2,  3],
               [ 4,  6,  8, 10],
               [12, 15, 18, 21]])
        >>> x.cumsum(axis=1)
        array([[ 0,  1,  3,  6],
               [ 4,  9, 15, 22],
               [ 8, 17, 27, 38]])
        
        # PS: výchozí chování
        >>> x.cumsum()
        array([ 0,  1,  3,  6, 10, 15, 21, 28, 36, 45, 55, 66])
    </example>

</slide>
<slide title="Extrémy – „min()“, „max()“">

    <p>
        Stejně jako u <a href="?slajd=12">redukce sčítáním</a>, <strong>hledání extrémů</strong> může probíhat přes celé pole nebo jen ve vybraných osách:
    </p>
    <example lang="python">
        >>> x = np.array([[1,2], [3,4]])
        >>> x
        array([[1, 2],
               [3, 4]])
        
        # maximum přes celé pole a jeho index
        >>> np.max(x)
        4
        >>> np.argmax(x)
        3
        
        # maximum po osách
        >>> np.max(x, axis=0)
        array([3, 4])
        >>> np.max(x, axis=1)
        array([2, 4])
    </example>
    <note>
        A podobně samozřejmě i pro <em>min()</em> a <em>argmin()</em>.
    </note>

</slide>
<slide title="Řazení – „sort()“, „argsort()“">

    <p>
        Prvky v polích je samozřejmě též možno i řadit, a to buď klasicky podle jejich hodnot..
    </p>
    <example lang="python">
        >>> a = np.array([[4, 3, 5], [1, 2, 1]])
        >>> a
        array([[4, 3, 5],
               [1, 2, 1]])

        # výchozí řazení po řádcích
        >>> np.sort(a)
        array([[3, 4, 5],
               [1, 1, 2]])

        # vnucené řazení podle jiné osy (zde po sloupcích)
        >>> np.sort(a, axis=0)
        array([[1, 2, 1],
               [4, 3, 5]])
    </example>
    <note>
        Také můžete zavolat metodu <em>sort()</em> přímo na poli, ale pak proběhne řazení v místě (tj. ono pole bude mít seřazené prvky a originál ztratíte).
    </note>
    <p>
        ..nebo kapku neklasicky pomocí jejich indexů (ať už je získáte odkudkoli; zde z toho samého pole):
    </p>
    <example lang="python">
        # vstupní pole k seřazení
        >>> a = np.array([4, 3, 1, 2])
        
        # indexy seřazených prvků
        >>> j = np.argsort(a)
        >>> j
        array([2, 3, 1, 0])
        
        # výpis seřazených prvků podle jejich indexů
        >>> a[j]
        array([1, 2, 3, 4])
    </example>

</slide>
<slide title="Statistika">

    <p>
        Statistika je samozřejmě velmi rozsáhlá oblast, tak jen drobná ochutnávka:
    </p>
    <example lang="python">
        >>> x = np.array([1, 2, 3, 1])
        
        # průměr
        >>> x.mean()
        1.75
        
        # medián
        >>> np.median(x)
        1.5
        
        # standardní deviace
        >>> x.std()
        0.82915619758885
    </example>
    <p>
        Za zapamatování stojí především „klasická“ numpyovská nekonzistence ve volání dostupných metod…
    </p>

</slide>
<slide title="V. Komplexnější příklad">

    <p>
        Pro zajímavost komplexnější příklad z dokumentace – výpočet Mandelbrotovy množiny ne úplně triviálním postupem:
    </p>
    <example lang="python" src="_files/mandelbrot/mandelbrot.py" />
    <img src="_files/mandelbrot/mandelbrot.png" width="640" height="480" alt="Mandelbrotova množina" />

</slide>


</lecture>
