Cesta ke kořenům

Moto: panda v koruně pevného stromu

  • Grafy bodů
  • Seznamy
  • Vektory v numpy
  • Grafy funkcí
  • Definice vlastních funkcí
  • Rozlišení dalekohledu

Panda za nás prováděla hodně věcí automaticky -- typické pro Python. Řešení problémů je obvykle představováno kombinací několika základních kamenů. Příkladem budiž kreslení grafů.

Kreslení bodů

Matplotlib.org je grafický modul (sada nástrojů pro kreslení grafů). Zpřístupní se pomocí kouzelného slůvka import:


In [1]:
import matplotlib.pyplot as plt    # plt je vseobecne uzivana zkratka, grafy si kreslime primo do notebookove stranky:
%matplotlib inline

Vykreslení dat do grafu zajistí plot(x,y):


In [2]:
plt.plot(1,2) # bod v x = 1, y = 2


Out[2]:
[<matplotlib.lines.Line2D at 0x7fc7d2d65d30>]

Fakt je tam... jen není vidět.


In [3]:
plt.plot(1,2,'*')    # muj oblibeny symbol je pentagram


Out[3]:
[<matplotlib.lines.Line2D at 0x7fc7d2c92400>]

Žádáme-li víc bodů, musíme je uzavřít do hranatých závorek.


In [4]:
plt.plot([1,2,3],[11,12,13],'+')  # pokud nedefinujeme kreslici symbol, spoji se carou


Out[4]:
[<matplotlib.lines.Line2D at 0x7fc7d2bfcf60>]

In [5]:
plt.plot?

Zjevně bude potřeba upravit měřítka na osách:


In [6]:
plt.xlim(0.9,3.1)
plt.ylim(10,15)
plt.plot([1,2,3],[11,12,13],'*')


Out[6]:
[<matplotlib.lines.Line2D at 0x7fc7d2b8afd0>]

Poznámky:

  • Modul pandas nám zajistil načtení bodů ze souboru. Plot ho umí vykreslit.
  • Důležité: plot(x,y) umí kreslit jen body nikoliv přímo funkce.

Seznam (list)

Chceme-li vykreslit mnoho bodů, je nutné je zapsat do seznamu. Seznam je něco jako pojmenovaný sloupec v tabulkovém procesoru.

Vytváří se výčtem nebo vygenerováním nebo ...:


In [7]:
x = [1,2,3]   # vycet
y = []        # prazdny seznam

Vlastnosti seznamu:

  • Neomezený počet prvků.
  • Prvky lze libovolně přidávat a ubírat.
  • Jednotlivé prvky jsou dostupné jako jmeno[]
  • První prvek má index nula: x[0]
  • python.org

In [8]:
x.append(4) # pridani prvku s hodnotou 4 nakonec
print(x)


[1, 2, 3, 4]

In [9]:
x.pop()  # odebrani posledniho
print(x)


[1, 2, 3]

In [10]:
print(x[2])  # vypsani s indexem 2 -- pozice 3


3

In [11]:
print(len(x))    # zjisteni delky


3

In [12]:
x = [1,2,3]     # prvni sloupecek
y = []   
y.append(1 + x[0]**2)
y.append(1 + x[1]**2)
y.append(1 + x[2]**2)
# kapanek neprehledne

In [13]:
plt.xlim(0.9,3.1)
plt.ylim(0,12)
plt.plot(x,y,'*')    # presna obdoba


Out[13]:
[<matplotlib.lines.Line2D at 0x7fc7d2b9fdd8>]

Pythoní obdoba udělej něco pro několik prvků je

$$y_n = 1 + x_n^2, \quad \mathrm{pro} \ n = 1, N$$


In [14]:
y = [1 + x[n]**2 for n in range(0,len(x))]
print(y)


[2, 5, 10]

In [15]:
plt.xlim(0.9,3.1)
plt.ylim(0,12)
plt.plot(x,y,'*')


Out[15]:
[<matplotlib.lines.Line2D at 0x7fc7d2b059e8>]

Vektory (pole)

Funkci $y = f(x)$ vykreslíme tak, že vygenerujeme sadu (vektor) bodů $\pmb{x} = [x_1, x_2, \ldots ,x_N]$ a vypočteme $y_n = f(x_n)$ pro $n=1,2,\ldots, N$.

Jenže jak vygenerovat vektor?


In [16]:
import numpy as np

numpy definuje operace s vektory, maticemi, umí je násobit, hledat vlastní čísla,...

Definice vektoru pomocí listu (seznamu):


In [17]:
x = np.array([1,2,3,4,5])   # vektor x

$\pmb{y} = f(\pmb{x}) = 1 + \pmb{x}^2$


In [18]:
y = 1 + x**2     # vektor y je vypocitany z vektoru x, prvek po prvku....

In [19]:
plt.plot(x,y,'*')


Out[19]:
[<matplotlib.lines.Line2D at 0x7fc7d2a521d0>]

Vlastnosti vektorů

  • Jsou odvozené z matematických vlastností -- lze je sčítat, násobit (dvěma způsoby), mají velikost,..
  • Počet prvků je neměnný a daný při definici.
  • Možné definice: výčtem, počtem prvků, intervalem $(a .. b)$, odvozenením z listu, ...
  • Prvky jsou přístupné pomocí indexů v hranatých závorkách: x[1]
  • Prvky jsou indexované od nuly: první prvek x[0], druhý x[1], ... x[N-1] -- mimořádně záludné(!)

In [20]:
x = np.zeros(5)   # petiprvkovy vektor plny nul
print(x)


[ 0.  0.  0.  0.  0.]

In [21]:
x = np.linspace(1,5,50)    # vektor s prvky 1 az 5, 50  prvku
print(x)
print(x[0],x[1],x[50-1],(5-1)/49)


[ 1.          1.08163265  1.16326531  1.24489796  1.32653061  1.40816327
  1.48979592  1.57142857  1.65306122  1.73469388  1.81632653  1.89795918
  1.97959184  2.06122449  2.14285714  2.2244898   2.30612245  2.3877551
  2.46938776  2.55102041  2.63265306  2.71428571  2.79591837  2.87755102
  2.95918367  3.04081633  3.12244898  3.20408163  3.28571429  3.36734694
  3.44897959  3.53061224  3.6122449   3.69387755  3.7755102   3.85714286
  3.93877551  4.02040816  4.10204082  4.18367347  4.26530612  4.34693878
  4.42857143  4.51020408  4.59183673  4.67346939  4.75510204  4.83673469
  4.91836735  5.        ]
1.0 1.08163265306 5.0 0.08163265306122448

A právě toto je trik, který potřebujeme na vygenerování $x_n$.


In [22]:
y = 1 + x**2

In [23]:
plt.plot(x,y)


Out[23]:
[<matplotlib.lines.Line2D at 0x7fc7d29b9c18>]

(Už vidíme, proč má funkce plot(x,y) jako default spojení čarami.

Násobení vektorů ve mně vyvolává pnutí:


In [24]:
x = np.array([0,1,0])    # jednotkovy vektor podel y osy
y = np.array([1,0,0])    # jednotkovy vektor podel x osy
s1 = np.inner(x,y)       # inner() je skalarni soucin
s2 = np.cross(x,y)       # cross() je vektorovy soucin
print("Skalární součin ",x,"*",y,"=",s1)
print("Vektorový součin ",x,"x",y,"=",s2)


Skalární součin  [0 1 0] * [1 0 0] = 0
Vektorový součin  [0 1 0] x [1 0 0] = [ 0  0 -1]

Obecné vlastnosti vektorových struktur

  • Sdružení mnoha prvků dohromady (kompaktnejší zápis, skoro neomezené délky)
  • Nad prvky jsou definovány různe operace (+,-,*,x,..).

Kreslení grafů funkcí

Zkusíme několik speciálních funkcí (takových, které se nedají zapsat konečným počtem operací).


In [25]:
import scipy.special as spec

Speciální funkce ve scipy.org. Jde o knihovnu se základními bohatými matematickými funkcemi na numerické integrování, řešení rovnic, ...


In [26]:
x = np.linspace(0,10)
y = np.sin(x)
z = np.exp(-x)
plt.plot(x,y)
plt.plot(x,z)


Out[26]:
[<matplotlib.lines.Line2D at 0x7fc7cd67fb00>]

In [27]:
x = np.linspace(0,30) # bude to zubate
bessel = spec.j1(x)   # funkci je tolik, ze si kazdy muze zvolit svou vlastni...
plt.plot(x,bessel)


Out[27]:
[<matplotlib.lines.Line2D at 0x7fc7ccf13828>]

In [28]:
x = np.linspace(0,30,1000)   # zvolime jemnejsi deleni intervalu
bessel = spec.j1(x)
plt.plot(x,bessel)


Out[28]:
[<matplotlib.lines.Line2D at 0x7fc7ccefc588>]

Definice vlastních funkcí

def jmeno(argumenty):
          prikaz(y)
          return vysledek

In [29]:
# priklad
def fun(x):
    print("Argument:",x)
    return x + 1
x = 3
print("Fun with fun:",fun(x))


Argument: 3
Fun with fun: 4

Fourierova řada

Nyní máme vše připraveno pro kreslení složených funkcí. Například aproximace pily:

$$ f(x) \approx \sum_{k=1}^N \frac{(-1)^k}{k} \sin(2\pi k x)$$ Pila na wiki


In [30]:
def fourier(x,n):
    t = 2*np.pi*x  # skalar
    s = np.array([np.sin(t*k) for k in range(0,n)  if k >0])
    a = np.array([(-1)**k/k for k in range(0,n) if k >0])
    return sum(a*s)

In [31]:
x = np.linspace(0,1.5,1000)
# tady nelze pouzit y = fourier(x), protoze mame jen skalarni argument
y = [fourier(x[i],n=5) for i in range(0,1000)]    # volbou n menime presnost aproximace
plt.plot(x,y)


Out[31]:
[<matplotlib.lines.Line2D at 0x7fc7cce5edd8>]

Rozlišení dalekohledu

wiki.

Rozlišením je konvenčně myšlena poloha prvního minima intensity $$I(x) = I_0 \left( \frac{J_1(x)}{x} \right)^2,$$ kde $$x=\frac{2\pi}{\lambda} R \sin \theta$$.


In [32]:
x = np.linspace(0,20,1000)
y = (spec.j1(x)/x)**2
plt.plot(x,y)


/home/kubaw/.virtualenvs/modern/lib/python3.5/site-packages/ipykernel/__main__.py:2: RuntimeWarning: invalid value encountered in true_divide
  from ipykernel import kernelapp as app
Out[32]:
[<matplotlib.lines.Line2D at 0x7fc7cce4f320>]

Hledání minima může být snadno nahrazeno řešením rovnice $f(x) = J_1(x) / x = 0$


In [33]:
y = spec.j1(x)/x


/home/kubaw/.virtualenvs/modern/lib/python3.5/site-packages/ipykernel/__main__.py:1: RuntimeWarning: invalid value encountered in true_divide
  if __name__ == '__main__':

In [34]:
plt.grid()   # pridani mrizky do obrazku
plt.plot(x,y)


Out[34]:
[<matplotlib.lines.Line2D at 0x7fc7ccf20208>]

In [35]:
plt.xlim(3,5)    # zmensime interval
plt.grid()
plt.plot(x,y)


Out[35]:
[<matplotlib.lines.Line2D at 0x7fc7ccd63a58>]

In [36]:
plt.xlim(3.5,4.0)    # znova zmensime interval
plt.grid()
plt.plot(x,y)


Out[36]:
[<matplotlib.lines.Line2D at 0x7fc7ccd38fd0>]

In [37]:
plt.xlim(3.8,3.9)    # a pokracujem ...
plt.ylim(-0.1,0.1)
plt.grid()
plt.plot(x,y)


Out[37]:
[<matplotlib.lines.Line2D at 0x7fc7cccb5860>]

In [38]:
plt.xlim(3.82,3.85)    # a porad pokracujem ...
plt.ylim(-0.01,0.01)
plt.grid()
plt.plot(x,y)


Out[38]:
[<matplotlib.lines.Line2D at 0x7fc7ccc19710>]

In [39]:
plt.xlim(3.83,3.835)    # porad nekoncime...
plt.ylim(-1e-3,1e-3)
plt.grid()
plt.plot(x,y)


Out[39]:
[<matplotlib.lines.Line2D at 0x7fc7ccc07588>]

In [40]:
plt.xlim(3.831,3.832)    # tak naposledy....
plt.ylim(-1e-4,1e-4)
plt.grid()
plt.plot(x,y)


Out[40]:
[<matplotlib.lines.Line2D at 0x7fc7ccb6f390>]

Výsledek řešení rovnice $f(x) = J_1(x)/x = 0$ je pro $x=3.8317 \pm 0.0001$.


In [41]:
R = 0.1 # [m]
lam = 550e-9  # [m]
rad = 180 / np.pi # radian je asi 57.3 stupne

In [42]:
theta = 3.8317 / (2*np.pi / lam * (R  / 2) ) * rad * 3600

In [43]:
print("Rozlišení dalekohledu o průměru",100*R,"cm ve viditelném světle je",theta,"arcsec.") # vomit


Rozlišení dalekohledu o průměru 10.0 cm ve viditelném světle je 1.3836602000474 arcsec.

In [44]:
print("Rozlišení dalekohledu o průměru {0:.0f} cm ve viditelném světle je {1:.1f} arcsec.".format(100*R,theta))


Rozlišení dalekohledu o průměru 10 cm ve viditelném světle je 1.4 arcsec.

Půlení intervalu

Numerické řešení rovnice $f(x) = 0$ je založeno na počátečním odhadu krajů intervalu $a,b$ pro které musí platit $f(a) \, f(b) < 0$. Ke správnému řešení se propracujeme pomocí půlení intervalu a testování předchozí podmínky. Tedy pro bod v polovině $$x = \frac{a + b}{2}$$ a víme, že kořen je v tom intervalu, pokud $f(a) f(x) < 0$ nebo $f(b) f(x) < 0$.


In [45]:
def f(x):
    return spec.j1(x)/x
a = 3
b = 5
print(f(a)*f(b))
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
b = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
a = x
x = (a + b) / 2
print(x,f(x)*f(a),f(x)*f(b))
#for k in range(0,17):
#    x = (a + b)/2
#    if


-0.00740457608177
4.0 -0.00186604850144 0.00108172082188
3.5 0.00443610298594 -0.000648062078761
3.75 0.000347806919565 -0.000146305120305
3.875 -3.96391159918e-05 7.38588178234e-05
3.8125 1.80230579273e-05 -9.0985315435e-06
3.84375 -2.56276530791e-06 5.63643260261e-06
3.828125 7.66655651515e-07 -4.74934102115e-07
3.8359375 -1.67375962565e-07 5.59501921616e-07
3.83203125 -1.28859876927e-08 1.51804952389e-08
3.830078125 6.4536539218e-08 -5.85326955748e-09
3.8310546875 1.17240321812e-08 -2.34093331043e-09
3.83154296875 1.17329295535e-09 -5.85771160592e-10
3.831787109375 -1.46131203527e-10 2.91558387417e-10
3.8316650390625 7.37202944816e-11 -3.6693117765e-11
3.83172607421875 -9.09174324975e-12 1.80220032026e-11

Potřebujeme nějak učinně opakovat výpočty a rozhodovat se na základě hodnot funkce.

Cykly

Cyklické opakování je možné zařídit prostřednictvím příkazu

for k in [vektor]:
        prikaz(y)
        

In [46]:
# priklad cyklu
for i in range(0,3):
    x = 1 + i**2
    print("#",i," x=",x)


# 0  x= 1
# 1  x= 2
# 2  x= 5

Rozhodovací podmínky

Občas je potřeba se rozhodnout v závislosti na hodnotě nějaké podmínky. To je možné prostřednictvím příkazu:

if podminka:
        prikaz(y), pokud je splnena
     else:
         prikaz(y), pokud neni splnena

In [47]:
# priklad podminky
a = 1
b = 2
if (a > b):
    print("Podmínka splněna:", a > b)
else:
    print("Podmínka nesplněna:",a > b)


Podmínka nesplněna: False

Automatické půlení


In [48]:
a = 3
b = 5
for k in range(0,20):
    x = (a + b) / 2
    if (f(x)*f(a) < 0):
        b = x
    else:
        a = x
    print("#",k," x=",x)


# 0  x= 4.0
# 1  x= 3.5
# 2  x= 3.75
# 3  x= 3.875
# 4  x= 3.8125
# 5  x= 3.84375
# 6  x= 3.828125
# 7  x= 3.8359375
# 8  x= 3.83203125
# 9  x= 3.830078125
# 10  x= 3.8310546875
# 11  x= 3.83154296875
# 12  x= 3.831787109375
# 13  x= 3.8316650390625
# 14  x= 3.83172607421875
# 15  x= 3.831695556640625
# 16  x= 3.8317108154296875
# 17  x= 3.8317031860351562
# 18  x= 3.831707000732422
# 19  x= 3.831705093383789

In [49]:
def pulky(f,a,b):
    for k in range(0,20):
        x = (a + b) / 2
        if (f(x)*f(a) < 0):
            b = x
        else:
            a = x
    return x

In [50]:
print("Funkce na pulky {0:.4f}.".format(pulky(f,3,5)))


Funkce na pulky 3.8317.

Knihovní funkce

Root finding


In [51]:
import scipy.optimize
x = scipy.optimize.brentq(f,3,5)
print("Brent's method for x=",x)


Brent's method for x= 3.8317059702074103

In [ ]: