SciPy je nadgradnja NumPy paketa, i sadrži veliki broj numeričkih algoritama za cijeli niz područja. Ovdje su pobrojana neka nama zanimljivija:
Za zadnja dva područja postoje i napredniji paketi. Za slike smo već npr. koristili scikit-image), a za statistiku ćemo korititi pandas paket.
SciPy paket učitavamo pomoću scipy
modula.
from scipy import *
Narvno, možemo učitati i samo podpaket koji nas zanima, u ovom slučaju za linearnu algebru.
import scipy.linalg as la
Kao primjer pogledajmo Besselove funkcije:
# jn, yn: Besselove funkcije prvog i drugog reda s realnim stupnjem
# jn_zeros, yn_zeros: računaju pripadne nultočke
from scipy.special import jn, yn, jn_zeros, yn_zeros
n = 0 # stupanj
x = 0.0
print ("J_{}({}) = {:f}".format(n, x, jn(n, x)))
x = 1.0
print ("Y_{}({}) = {:f}".format(n, x, yn(n, x)))
J_0(0.0) = 1.000000 Y_0(1.0) = 0.088257
from pylab import *
%matplotlib inline
x = linspace(0, 10, 100)
fig, ax = subplots()
for n in range(4):
ax.plot(x, jn(n, x), label=r"$J_%d(x)$" % n)
ax.legend();
n = 0 # stupanj
m = 4 # broj nultočaka za izračunati
jn_zeros(n, m)
array([ 2.40482556, 5.52007811, 8.65372791, 11.79153444])
from scipy.integrate import quad, dblquad, tplquad
quad
funkcija se koriste za numeričku integracije (quad ... jer se na engleskom taj proces zove kvadratura). dblquad
služi za dvostruke, a tplquad
za trostruke integrale.
Jednostavan primjer, računamo $$\begin{equation*} \int_0^1 x\, \mathrm{d}x \end{equation*}$$
def f(x):
return x
x_donje = 0
x_gornje = 1
rez, abserr = quad(f, x_donje, x_gornje)
print ("Rezultat = {}, apsolutna greška = {}".format(rez,abserr))
Rezultat = 0.5, apsolutna greška = 5.551115123125783e-15
Ove funkcije imaju puno opcionalnih argumenata. Ako želimo funkciji koju integriramo proslijediti dodatne parametre (vidi ovdje), možemo korisiti varijablu args
.
def integrand(x, n):
"""
Besselova funkcija prvog tipa stupnja n.
"""
return jn(n, x)
x_d = 0
x_g = 10
rez, abserr = quad(integrand, x_d, x_g, args=(3,))
print (rez, abserr)
0.7366751370811073 9.389126882496403e-13
Korištenje anonimnih funkcija:
rez, abserr = quad(lambda x: exp(-x ** 2), -Inf, Inf)
print ("numerički = {}, {}".format(rez, abserr))
egzaktno = sqrt(pi)
print ("egzaktno = {}".format(egzaktno))
numerički = 1.7724538509055159, 1.4202636780944923e-08 egzaktno = 1.7724538509055159
U više dimenzija:
\begin{equation*} \int_a^b \int_{g(x)}^{h(x)} f(x,y)\,\mathrm{d}y\mathrm{d}x \end{equation*}def integrand(x, y):
return exp(-x**2-y**2)
x_d = 0
x_g = 10
y_d = 0
y_g = 10
# ovdje je a = x_d, b = x_g, g(x) = y_d, h(x) = y_g
# g(x) i h(x) trebaju biti funkcije!
rez, abserr = dblquad(integrand, x_d, x_g, lambda x : y_d, lambda x: y_g)
print (rez, abserr)
0.7853981633974476 1.3753098510218528e-08
SciPy nudi dvije mogućnosti rješavanja ODJ: Funkciju odeint
i klasu ode
. Mi ćemo prikazati odeint
.
from scipy.integrate import odeint, ode
Sustav ODJ zapisujemo kao:
$y' = f(y, t)$
gdje je
$y = [y_1(t), y_2(t), ..., y_n(t)]$,
Još trebamo i početne uvjete $y(0)$.
Ovo je sintaksa:
y_t = odeint(f, y_0, t)
t
je niz vremena za koje želimo riješiti ODJy_t
je niz s jednim retkom za svaki trenutak iz t
, a stupci daje rješenje y_i(t)
u tom trenutkuOpis problema: http://en.wikipedia.org/wiki/Double_pendulum
from IPython.display import Image
Image(url='http://upload.wikimedia.org/wikipedia/commons/c/c9/Double-compound-pendulum-dimensioned.svg')
Ovo su jednadže s wiki stranice:
${\dot \theta_1} = \frac{6}{m\ell^2} \frac{ 2 p_{\theta_1} - 3 \cos(\theta_1-\theta_2) p_{\theta_2}}{16 - 9 \cos^2(\theta_1-\theta_2)}$
${\dot \theta_2} = \frac{6}{m\ell^2} \frac{ 8 p_{\theta_2} - 3 \cos(\theta_1-\theta_2) p_{\theta_1}}{16 - 9 \cos^2(\theta_1-\theta_2)}.$
${\dot p_{\theta_1}} = -\frac{1}{2} m \ell^2 \left [ {\dot \theta_1} {\dot \theta_2} \sin (\theta_1-\theta_2) + 3 \frac{g}{\ell} \sin \theta_1 \right ]$
${\dot p_{\theta_2}} = -\frac{1}{2} m \ell^2 \left [ -{\dot \theta_1} {\dot \theta_2} \sin (\theta_1-\theta_2) + \frac{g}{\ell} \sin \theta_2 \right]$
Definiramo:
$x = [\theta_1, \theta_2, p_{\theta_1}, p_{\theta_2}]$
g = 9.82
L = 0.5
m = 0.1
def dx(x, t):
"""
Desna strana ODJ
"""
x1, x2, x3, x4 = x[0], x[1], x[2], x[3]
dx1 = 6.0/(m*L**2) * (2 * x3 - 3 * cos(x1-x2) * x4)/(16 - 9 * cos(x1-x2)**2)
dx2 = 6.0/(m*L**2) * (8 * x4 - 3 * cos(x1-x2) * x3)/(16 - 9 * cos(x1-x2)**2)
dx3 = -0.5 * m * L**2 * ( dx1 * dx2 * sin(x1-x2) + 3 * (g/L) * sin(x1))
dx4 = -0.5 * m * L**2 * (-dx1 * dx2 * sin(x1-x2) + (g/L) * sin(x2))
return [dx1, dx2, dx3, dx4]
# početni uvjet
x0 = [pi/4, pi/2, 0, 0]
# niz vremena
t = linspace(0, 10, 250)
# rješenje ODJ
x = odeint(dx, x0, t)
# nacrtajmo rješenje
# crtamo kuteve
fig, axes = subplots(1,2, figsize=(12,4))
axes[0].plot(t, x[:, 0], 'r', label="theta1")
axes[0].plot(t, x[:, 1], 'b', label="theta2")
x1 = + L * sin(x[:, 0])
y1 = - L * cos(x[:, 0])
x2 = x1 + L * sin(x[:, 1])
y2 = y1 - L * cos(x[:, 1])
axes[1].plot(x1, y1, 'r', label="njihalo1")
axes[1].plot(x2, y2, 'b', label="njihalo2")
axes[1].set_ylim([-1, 0])
axes[1].set_xlim([1, -1]);
Jednostavna animacija, kasnije ćemo vidjeti kako možemo napravit bolju animaciju.
from IPython.display import display,clear_output
import time
fig, ax = subplots(figsize=(4,4))
for t_idx, tt in enumerate(t[:200]):
x1 = + L * sin(x[t_idx, 0])
y1 = - L * cos(x[t_idx, 0])
x2 = x1 + L * sin(x[t_idx, 1])
y2 = y1 - L * cos(x[t_idx, 1])
ax.cla()
ax.plot([0, x1], [0, y1], 'r.-')
ax.plot([x1, x2], [y1, y2], 'b.-')
ax.set_ylim([-1.5, 0.5])
ax.set_xlim([1, -1])
display(fig)
clear_output(wait=True)
time.sleep(0.03)
Opis problema možete pročitati ovdje: http://en.wikipedia.org/wiki/Damping
Jednadžba je
\begin{equation} \frac{\mathrm{d}^2x}{\mathrm{d}t^2} + 2\zeta\omega_0\frac{\mathrm{d}x}{\mathrm{d}t} + \omega^2_0 x = 0 \end{equation}Definiramo $p = \frac{\mathrm{d}x}{\mathrm{d}t}$:
\begin{equation} \frac{\mathrm{d}p}{\mathrm{d}t} = - 2\zeta\omega_0 p - \omega^2_0 x \end{equation}\begin{equation} \frac{\mathrm{d}x}{\mathrm{d}t} = p \end{equation}def dy(y, t, zeta, w0):
"""
Desna strana ODJ za harmonički oscilator
"""
x, p = y[0], y[1]
dx = p
dp = -2 * zeta * w0 * p - w0**2 * x
return [dx, dp]
# početno stanje:
y0 = [1.0, 0.0]
# vremena, frekvencija
t = linspace(0, 10, 1000)
w0 = 2*pi*1.0
# rješavamo ODJ za tri vrste prigušenja
y1 = odeint(dy, y0, t, args=(0.0, w0)) # negušeno
y2 = odeint(dy, y0, t, args=(0.2, w0)) # podgušeno
y3 = odeint(dy, y0, t, args=(1.0, w0)) # kritičko gušenje
y4 = odeint(dy, y0, t, args=(5.0, w0)) # pregušeno
fig, ax = subplots()
ax.plot(t, y1[:,0], 'k', label="negušeno", linewidth=0.25)
ax.plot(t, y2[:,0], 'r', label="podgušeno")
ax.plot(t, y3[:,0], 'b', label="kritičko gušenje")
ax.plot(t, y4[:,0], 'g', label="pregušeno")
ax.legend();
Paket je fftpack
:
from scipy.fftpack import *
Primjenimo Fourierovu transformaciju na prethodni primjer harmoničkog oscilatora.
N = len(t)
dt = t[1]-t[0]
# y2 je rješenje podgušenog harmoničkog oscilatora
F = fft(y2[:,0])
# izračunajmo frekvencije
w = fftfreq(N, dt)
fig, ax = subplots(figsize=(9,3))
ax.plot(w, abs(F));
Kako je signal realan, spektar je simetričan. Stoga nam je dosta nacrtati pozitivne frekvencije.
indeksi = where(w > 0)
w_pos = w[indeksi]
F_pos = F[indeksi]
fig, ax = subplots(figsize=(9,3))
ax.plot(w_pos, abs(F_pos))
ax.set_xlim(0, 5);
Detaljna dokumentacija: http://docs.scipy.org/doc/scipy/reference/linalg.html
Nećemo prolaziti kroz sve funkcije.
$A x = b$
A = array([[1,2,-1], [4,5,6], [7,8,9]])
b = array([1,2,3])
x = solve(A, b)
x
array([-0.33333333, 0.66666667, 0. ])
# provjera
dot(A, x) - b
array([ 0.00000000e+00, -2.22044605e-16, 0.00000000e+00])
$A X = B$
A = rand(3,3)
B = rand(3,3)
X = solve(A, B)
X
array([[ 0.5199847 , 1.33508075, 0.98631147], [-0.85868255, -2.24599046, 0.53620857], [ 0.79516259, 0.18409863, -0.79258861]])
# provjera
norm(dot(A, X) - B)
4.1910000110727263e-16
evals = eigvals(A)
evals
array([ 1.16326497+0.j , -0.12471967+0.21365995j, -0.12471967-0.21365995j])
evals, evecs = eig(A)
evals
array([ 1.16326497+0.j , -0.12471967+0.21365995j, -0.12471967-0.21365995j])
evecs
array([[ 0.57615104+0.j , 0.31100815-0.18792585j, 0.31100815+0.18792585j], [ 0.55783043+0.j , -0.76806662+0.j , -0.76806662-0.j ], [ 0.59739031+0.j , -0.28786918+0.44177235j, -0.28786918-0.44177235j]])
Svojstveni vektori su stupci u evecs
:
n = 1
norm(dot(A, evecs[:,n]) - evals[n] * evecs[:,n])
6.6772208449746893e-16
To nije sve, postoje i specijalizirane funkcije, kao npr. eigh
za hermitske matrice
# inverz
inv(A)
array([[ 0.18224249, 2.32018496, -1.51321686], [-0.76564105, -4.50039798, 5.74351864], [ 2.03427742, -2.36102176, 1.10236963]])
# determinanta
det(A)
0.07119829516433468
# razne norme
norm(A, ord=2), norm(A, ord=Inf)
(1.4186446775313915, 1.2152976994987217)
Više informacija na http://en.wikipedia.org/wiki/Sparse_matrix
Postoji više formata rijetkih matrica, mi nećemo ulaziti u detalje.
from scipy.sparse import *
# gusta matrica
M = array([[1,0,0,0], [0,3,0,0], [0,1,1,0], [1,0,0,1]]); M
array([[1, 0, 0, 0], [0, 3, 0, 0], [0, 1, 1, 0], [1, 0, 0, 1]])
# pretvorimo je u rijetku matricu
A = csr_matrix(M); A
<4x4 sparse matrix of type '<class 'numpy.int64'>' with 6 stored elements in Compressed Sparse Row format>
# vratimo natrag
A.todense()
matrix([[1, 0, 0, 0], [0, 3, 0, 0], [0, 1, 1, 0], [1, 0, 0, 1]], dtype=int64)
Pametniji način kreiranja rijetke matrice.
A = lil_matrix((4,4)) # prazna 4x4 rijetka matrica
A[0,0] = 1
A[1,1] = 3
A[2,2] = A[2,1] = 1
A[3,3] = A[3,0] = 1
A
<4x4 sparse matrix of type '<class 'numpy.float64'>' with 6 stored elements in LInked List format>
A.todense()
matrix([[ 1., 0., 0., 0.], [ 0., 3., 0., 0.], [ 0., 1., 1., 0.], [ 1., 0., 0., 1.]])
# konvertiranje
A = csr_matrix(A); A
<4x4 sparse matrix of type '<class 'numpy.float64'>' with 6 stored elements in Compressed Sparse Row format>
A = csc_matrix(A); A
<4x4 sparse matrix of type '<class 'numpy.float64'>' with 6 stored elements in Compressed Sparse Column format>
A.todense()
matrix([[ 1., 0., 0., 0.], [ 0., 3., 0., 0.], [ 0., 1., 1., 0.], [ 1., 0., 0., 1.]])
(A * A).todense()
matrix([[ 1., 0., 0., 0.], [ 0., 9., 0., 0.], [ 0., 4., 1., 0.], [ 2., 0., 0., 1.]])
(A @ A).todense()
matrix([[ 1., 0., 0., 0.], [ 0., 9., 0., 0.], [ 0., 4., 1., 0.], [ 2., 0., 0., 1.]])
dot(A,A)
<4x4 sparse matrix of type '<class 'numpy.float64'>' with 6 stored elements in Compressed Sparse Column format>
v = array([1,2,3,4])[:,newaxis]; v
array([[1], [2], [3], [4]])
Vektor v
smo mogli konstruirati i drugačije (vidjeli smo primjere u predavanju o NumPy-ju), no uvijek trebamo doći do dvodimenzionalnog niza. Za razliku od MATLAB-a u kojemu su svi nizovi 2D, u NumPy-ju 1D niz nije isto što i matrica $n\times 1$ ili $1\times n$.
Npr. jedna mogućnost je
v = array([[1,2,3,4]]).T
# rijetka matrica puta vektor
A * v
array([[ 1.], [ 6.], [ 5.], [ 5.]])
A.todense() * v
matrix([[ 1.], [ 6.], [ 5.], [ 5.]])
Više na http://scipy-lectures.github.com/advanced/mathematical_optimization/index.html
Modul je optimize
:
from scipy import optimize
def f(x):
return 4*x**3 + (x-2)**2 + x**4
fig, ax = subplots()
x = linspace(-5, 3, 100)
ax.plot(x, f(x));
x_min = optimize.fmin_bfgs(f, -2)
x_min
Optimization terminated successfully. Current function value: -3.506641 Iterations: 5 Function evaluations: 24 Gradient evaluations: 8
array([-2.67298151])
optimize.fmin_bfgs(f, 0.5)
Optimization terminated successfully. Current function value: 2.804988 Iterations: 3 Function evaluations: 15 Gradient evaluations: 5
array([ 0.46961745])
optimize.brent(f)
0.46961743402759754
optimize.fminbound(f, -4, 2)
-2.6729822917513886
Problem oblika $f(x) = 0$ se rješava fsolve
funkcijom.
omega_c = 3.0
def f(omega):
return tan(2*pi*omega) - omega_c/omega
import numpy as np
np.seterr(divide='ignore')
fig, ax = subplots(figsize=(10,4))
x = linspace(0, 3, 1000)
y = f(x)
maska = where(abs(y) > 50)
x[maska] = y[maska] = NaN # da se riješimo asimptote
ax.plot(x, y)
ax.plot([0, 3], [0, 0], 'k')
ax.set_ylim(-5,5);
optimize.fsolve(f, 0.1)
array([ 0.23743014])
optimize.fsolve(f, 0.6)
array([ 0.71286972])
optimize.fsolve(f, 1.1)
array([ 1.18990285])
Funkcija interp1d
, za dane nizove $x$ i $y$ koordinata vraća objekt koji se ponaša kao funkcija.
from scipy.interpolate import *
n = arange(0, 10)
x = linspace(0, 9, 100)
y_meas = sin(n) + 0.1 * randn(len(n)) # ubacujemo malo šuma
y_real = sin(x)
linear_interpolation = interp1d(n, y_meas)
y_interp1 = linear_interpolation(x)
cubic_interpolation = interp1d(n, y_meas, kind='cubic')
y_interp2 = cubic_interpolation(x)
fig, ax = subplots(figsize=(10,4))
ax.plot(n, y_meas, 'bs', label='podaci sa šumom')
ax.plot(x, y_real, 'k', lw=2, label='originalna funkcija')
ax.plot(x, y_interp1, 'r', label='linearna interpolacija')
ax.plot(x, y_interp2, 'g', label='kubična interpolacija')
ax.legend(loc=3);
Više na http://docs.scipy.org/doc/scipy/reference/stats.html.
Mi ćemo kasnije raditi s moćnijim paketom pandas
.
from scipy import stats
# slučajna varijabla s Poissionovom distribucijom
X = stats.poisson(3.5)
n = arange(0,15)
fig, axes = subplots(2,1, sharex=True)
# kumulativna distribucija (CDF)
axes[0].step(n, X.cdf(n))
# histogram 1000 slučajnih realizacija od X
axes[1].hist(X.rvs(size=1000));
# normalna distribucija
Y = stats.norm()
x = linspace(-5,5,100)
fig, axes = subplots(3,1, sharex=True)
# PDF
axes[0].plot(x, Y.pdf(x))
# CDF
axes[1].plot(x, Y.cdf(x));
# histogram
axes[2].hist(Y.rvs(size=1000), bins=50);
Osnovna statistika:
X.mean(), X.std(), X.var()
(3.5, 1.8708286933869707, 3.5)
Y.mean(), Y.std(), Y.var()
(0.0, 1.0, 1.0)
from verzije import *
from IPython.display import HTML
HTML(print_sysinfo()+info_packages('numpy,scipy,matplotlib'))
Python verzija | 3.5.3 |
kompajler | GCC 4.8.2 20140120 (Red Hat 4.8.2-15) |
sustav | Linux |
broj CPU-a | 8 |
interpreter | 64bit |
numpy verzija | 1.11.3 |
scipy verzija | 0.19.0 |
matplotlib verzija | 2.0.0 |
lil_matrix
, konvertirajte je u CSR
format i riješite $A x = b$ za neki $b$.Za početnu točku uzmite $x_0 =(1,5,5,1)$.
Slika moonlanding.png je puna šuma. Zadaća je očistiti sliku koristeći Fourierovu transformaciju.
pylab.imread()
.scipy.fftpack
i nacrtajte spektar slike.