Digitalna obrada signala, Vladimir Petrović
Dobrodošli na kurs Digitalna obrada signala na Odseku za elektroniku na Elektrotehničkom fakultetu u Beogradu!
Ovaj notebook je uvod u osnovne programerske koncepte programskog jezika Pajton (Python) koji ćemo koristiti u okviru kursa. Videćemo kako se deklarišu promenljive, liste, nizovi, ali i novi interesantan tip podataka koji zovemo rečnik (dictionary). Zatim, naučićemo sintaksu za konstrukte kontrole toka programa (uslove, petlje) i kako se definišu funkcije. Na kraju ćemo prodiskutovati paradigmu pisanja programa korišćenjem vektorizacije koja je neophodna za efikasne algoritme digitalne obrade signala, digitalne obrade slike i mašinskog učenja. Mnoge stvari ćemo detaljno objasniti tek u toku kursa, na konkretnim primerima.
Okruženje koje vidite se zove Jupyter Lab. Fajlovi sa kojima radimo u ovom okruženju imaju ekstenziju .ipynb što je skraćeno od interactive Python notebook. Ovo okruženje i fajlovi mogućavaju da različiti delovi fajla budu tumačeni na različite načine, pa ćete tako videti delove kao što je ovaj koji su predstavljeni kao tekst i formatirani kao markdown kod ili delove kao što će uslediti koje vidimo kao Pajton kod.
Kreiranje novog fajla, otvaranje već postojećeg i veliki broj opcija vezanih za fajlove dostupan je u padajućem meniju File.
Fajl je izdeljen na ćelije (cells) koje dodajemo klikom na dugmence + u levom kraju tulbara. Ako želimo da ćelije budu namenjene za tekst iz padajućeg menija (jedinog u tulbaru) ćemo odabrati Markdown, a ako želimo da ćelija bude namenjena za programski kod, odabraćemo Code. Ćelija se može premestiti, obrisati, iskopirati kao i bilo koji drugi tekst desnim klikom na nju i odabirom odgovarajuće opcije. Slično kao i u uobičajenim razvojnim okruženjima, neke od opcija su podržane i u tulbaru.
Ćelija se pokreće sa Shift+Enter na tastaturi ili klikom na Play dugme u tulbaru. Tekstualna ćelija može biti u režimu prikaza teksta ili režimu ažuriranja teksta. Duplim klikom na tekst se otvara markdown kod koji se može menjati (slično kao u LaTeX-u). Dodatne opcije pokretanja ćelija su dostupne u padajućem meniju Run.
U pozadini ovog okruženja (backend) se izvršava Pajton kernel na kome se izvršava sav Pajton kod koji je napisan u Code ćelijama. Ukoliko se desi sintaksna greška ili greška u izvršavanju kernel će tu grešku prijaviti, a Jupyter će je samo prikazati. Jupyter se dakle ponaša kao frontend. Za potrebe ovog kursa koristićemo dva kernela: 1) standarndi Python 3 kernel i 2) XPython kernel koji ćemo koristiti samo u slučaju da nam je potrebno naprednije debagovanje koda koje uključuje izvršavanje korak po korak, praćenje vrednosti varijabli i postavljanje tačaka prekida programa (breakpoints). Kernel se bira klikom na opciju u gornjem desnom uglu tulbara. Kernel se može zaustaviti ako smo na primer ušli u neku beskonačnu petlju ili ako je program počeo neočekivano da se ponaša klikom na dugme Stop u tulbaru. U slučaju problema, kernel se može restartovati klikom na dugme Restart u tulbaru. Dodatne opcije vezane za ponašanje kernela su dostupne u padajućem meniju Kernel.
Ne zaboravite da čuvate svoje promene (Ctrl+S ili klikom na disketicu u gornjem levom uglu).
Padajući meni Edit sadrži dodatne komande ažuriranja notebook-a što je uobičajeno u većini razvojnih okruženja.
U padajućem meniju View možemo podesiti koje prozore želimo da vidimo u celom okruženju. Klikom na Show Left Sidebar levi prozor se otvara ili zatvara. On je pre svega značajan zbog navigacije u okviru fajl sistema i rada sa fajlovima. Desni prozor je uglavnom koristan za debagovanje. Statusna linija (Statusbar) je korisna kako bismo videli u kom je stanju kernel i da li se kod ispravno izvršava.
Ostale opcije koje nisu navedene isprobajte sami.
Pajton je programski jezik visokog nivoa opšte namene koga je razvio Gvido van Rosum 1991. godine. Napravljen je sa ciljem da podstiče čitljivost programskog koda. Njegova sintaksa forsira pisanje preglednog koda sa upotrebom "uvlačenja" (identacija - identations). Jezik podržava i paradigmu proceduralnog programiranja (kakav je na primer C) i paradigmu objektno-orijentisanog programiranja (kakvi su na primer C++ ili Java), kao i paradigmu funkcionalnog programiranja (kakav je na primer Scala programski jezik). Na ovom kursu ćemo najčešće koristiti proceduralno programiranje.
Pajton je jezik čiji se programi najčešće interpretiraju, tj. izvršavaju se u programu koji se zove interpreter direktno, bez prethodnog kompajliranja programskog koda. Međutim, postoje ozbiljni sistemi koji su pisani u Pajtonu i kompajlirani za rad na nekom procesoru opšte ili specifične namene. Kod napisan u Pajtonu se za razliku od većine drugih interpreterskih jezika lako može kompajlirati u izvršni fajl i pokrenuti bez interpretera.
Gotovo cela zajednica koja se bavi mašinskim učenjem koristi Pajton kao osnovni alat što je verovatno najviše doprinelo njegovoj popularnosti. Postoji mnogo gotovih paketa za razne primene u inženjerstvu koji se besplatno mogu koristiti i ubrzati razvoj algoritma ili aplikacije. Neke od njih ćemo koristiti i na ovom kursu (na primer: numpy, scipy, matplotlib). Pajton je danas jedan od najpopularnijih i najkorišćenijih programskih jezika na svetu i ne čudi što se sve više zahteva njegovo znanje u industriji.
Ovde ćemo opisati osnovne koncepte programskog jezika Pajton potrebne za početak ovog kursa. Koristimo Pajton 3. Pajton 2 već neko vreme ostaje u senci i konačno ove godine gubi podršku - svi prelaze na pajton 3. Ovaj kurs koristi verziju 3.6.
Promenljive u pajtonu se ne deklarišu ni na koji poseban način, kako smo to navikli u C-u ili C++-u. Dovoljno je samo nekom literalu dodeliti vrednost.
a = 2 # Ovo je komentar koji opisuje varijablu a kojoj dodoeljujemo integer vrednost.
b = 1.3 # float
b += 3 # Operatori mogu kao i u C-u
print(b)
type(b)
4.3
float
c = 1.5 + 0.5j # complex
print(c)
type(c)
(1.5+0.5j)
complex
s = "a ovo je string"
print(s)
ss = 'moze i s jednostrukim navodnicima \n i u novom redu ako koristim \\n'
print(ss)
sss = '''Ili u
vise linija'''
print(sss)
a ovo je string moze i s jednostrukim navodnicima i u novom redu ako koristim \n Ili u vise linija
# Malo lepse da ispisemo b
print(f"Vrednost promenljive b je: {b}, a promenljive a je: {a}")
Vrednost promenljive b je: 4.3, a promenljive a je: 2
isLess = a < b
print(isLess)
if isLess:
print("a je manje od b.") # Obrati paznju! Moram da uvucem liniju. Zar nije lepse?
else:
print("a nije manje od b.")
True a je manje od b.
Liste ne moraju imati elemente koji su istog tipa.
Evo primera jedne liste i raznih manipulacija nad njom.
colors = ['red', 'blue', 'green', 'black', 'white', 'yellow']
type(colors)
list
⚠️ Indeksiranje počinje od 0, kao u C-u, ne od 1 kao u Matlab-u! ⚠️
colors[0]
'red'
print(colors[2])
green
Može i unazad:
colors[-2]
'white'
Ili u grupama, sintaksa je
imeListe[startIndeks:stopIndeks+1:korak]
print(colors[2:4])
['green', 'black']
print(colors[:3])
['red', 'blue', 'green']
print(colors[2:])
['green', 'black', 'white', 'yellow']
evenColors = colors[1::2]
print(evenColors)
['blue', 'black', 'yellow']
print(colors)
reversedColors = colors[::-1]
print(reversedColors)
print(colors[::-1])
['red', 'blue', 'green', 'black', 'white', 'yellow'] ['yellow', 'white', 'black', 'green', 'blue', 'red'] ['yellow', 'white', 'black', 'green', 'blue', 'red']
Dodavanje elemenata u listu i uklanjanje postojećih
colors.append('purple')
colors
['red', 'blue', 'green', 'black', 'white', 'yellow', 'purple']
lastColor = colors.pop()
print(lastColor)
print(colors)
purple ['red', 'blue', 'green', 'black', 'white', 'yellow']
colors.extend(['pink', 'orange'])
print(colors)
['red', 'blue', 'green', 'black', 'white', 'yellow', 'pink', 'orange']
print(evenColors)
doubleColors = evenColors*2 + colors[::-3]
doubleColors
doubleColors += ['gray', 'bubblegum']
print(doubleColors)
['blue', 'black', 'yellow'] ['blue', 'black', 'yellow', 'blue', 'black', 'yellow', 'orange', 'white', 'blue', 'gray', 'bubblegum']
Rečnik je vrlo korisna tabela koja se ponaša slično kao lista, ali u proizvoljnom rasporedu. U rečniku se vrednostima pristupa preko ključeva.
parameters = {'N': 128, 'filterType': 'low-pass', 'isFixedPoint': False}
parameters
{'N': 128, 'filterType': 'low-pass', 'isFixedPoint': False}
parameters['isFixedPoint'] = True
parameters
{'N': 128, 'filterType': 'low-pass', 'isFixedPoint': True}
Možemo prikazati samo ključeve ili samo vrednosti.
print(parameters.keys())
print(parameters.values())
dict_keys(['N', 'filterType', 'isFixedPoint']) dict_values([128, 'low-pass', True])
Ili proveriti da li se neki ključ nalazi u rečniku.
'filterType' in parameters
True
Za dodatne informacije o tipovima i varijablama posetiti link.
Nigde nema ključnih reči begin, end, then, vitičastih zagrada koje definišu delove koda. Uvlačenjem (identacijom) se piše pregledan kod, ali i definiše opseg vidljivosti.
if 2**3 == 8:
print('Obviously!')
print('Next command')
Obviously! Next command
a = 3
if a < 5: # ne zaboravi znak :
print('Less')
elif a == 5:
print('Equal')
else:
print('Greater')
Less
Iteriranje po redu korišćenjem range(N)
funkcije.
for i in range(4):
print(i)
0 1 2 3
Ili ovako:
indices = range(4)
for i in indices:
print(i)
0 1 2 3
Može i da se iterira po vrednostima:
for word in ('cool', 'powerful', 'readable'):
print('Python is %s' % word)
Python is cool Python is powerful Python is readable
Ili malo kompleksnije:
vowels = 'aeiouy'
for i in 'powerful':
if i in vowels:
print(i)
o e u
Ili po rečniku
parameters = {'N': 128, 'filterType': 'low-pass', 'isFixedPoint': False}
for key, val in parameters.items():
print(f'Key: \'{key}\' has value: {val}')
Key: 'N' has value: 128 Key: 'filterType' has value: low-pass Key: 'isFixedPoint' has value: False
z = 1 + 1j
while abs(z) < 100:
z = z**2 + 1
print(z)
(-134+352j)
Može i break
i continue
{:.python}.
for i in range
break
z = 1 + 1j
while abs(z) < 100:
if z.imag < 0:
break
z = z**2 + 1
print(z)
(-11-16j)
a = [1, 0, 2, 4]
for element in a:
if element == 0:
continue
print(1. / element)
1.0 0.5 0.25
Za dodatne informacije o sintaksi i dodatnim opcijama kontrole toka posetiti link.
Funkciju najpre moramo definisati. Ona može imati više ulaznih i više izlaznih argumenata.
import math
def circleAreaPerimeter(r):
A = r**2*math.pi
P = 2*r*math.pi
return A, P
A, P = circleAreaPerimeter(3.2)
print("Area is : %.2f \nPerimeter is : %.2f" %(A, P))
Area is : 32.17 Perimeter is : 20.11
Možemo vratiti više parametara, ali da neke od njih ne koristimo, tj. povratnu vrednost ne moramo dodeliti nekoj promenljivoj.
A, _ = circleAreaPerimeter(3.2)
print(A)
32.169908772759484
Neki parametri fukcije mogu biti podrazumevani.
def rectangleAreaPerimeter(a, b = 2.3):
print(f'a is {a}, b is {b}.')
A = a*b
P = 2*a + 2*b
return A, P
A, P = rectangleAreaPerimeter(3)
print("Area is : %.2f \nPerimeter is : %.2f" %(A, P))
a is 3, b is 2.3. Area is : 6.90 Perimeter is : 10.60
Takođe, parametri funkcije ne moraju da se prosleđuju po redosledu po kome su definisani.
A, P = rectangleAreaPerimeter(b = 3, a = 4)
a is 4, b is 3.
Moguće su još neke opcije kao što je promenljiv broj argumenata funkcije, prosleđivanje argumenata po referenci, korišćenje globalnih promenljivih itd. Za dodatne informacije posetiti link.
NumPy je biblioteka koja predstavlja proširenje Pajton programskog jezika koja dodaje podršku za velike, višedimenzione nizove i matice, zajedno sa veoma opširnim matematičkim aparatom koji radi sa ovim nizovima. U Pajton program se ova biblioteka mora uključiti i to se uobičajno radi na sledeći način:
import numpy as np
Zatim će se sve funkcije iz ovog paketa pozivati sa np.funkcija()
.
Glavni objekat ovog paketa je NumPy array, tj. NumPy niz kome se mogu definisati proizvoljne dimenzije.
a = np.array([0, 1, 2, 3])
a
array([0, 1, 2, 3])
Broj dimenzija niza.
a.ndim
1
Dužina niza i oblik (shape):
print(len(a))
print(a.shape)
4 (4,)
Višedimenzioni nizovi:
b = np.array([[0, 1, 2], [3, 4, 5]]) # 2 x 3 array
b
array([[0, 1, 2], [3, 4, 5]])
b.ndim
2
b.shape
(2, 3)
len(b) # Vraća dužinu niza samo po prvoj dimenziji
2
c = np.array([[[1, 3], [2, 5]], [[3, 6], [4, 7]]])
c
array([[[1, 3], [2, 5]], [[3, 6], [4, 7]]])
c.shape
(2, 2, 2)
Retko se vrendosti upisuju jedna po jedna kao u prethodnim primerima. Zato postoje funkcije koje generišu mnoge često korišćene nizove.
a = np.arange(10) # 0 .. n-1 (!)
print(a)
b = np.arange(1, 10.2, 2.1) # start, end (exclusive), step
print(b)
t = np.arange(0, 10, 0.001)
len(t)
[0 1 2 3 4 5 6 7 8 9] [1. 3.1 5.2 7.3 9.4]
10000
c = np.linspace(0, 1, 6) # start, stop, broj tacaka
print(c)
d = np.linspace(0, 1, 5, endpoint=False)
print(d)
[0. 0.2 0.4 0.6 0.8 1. ] [0. 0.2 0.4 0.6 0.8]
a = np.zeros((3, 3, 3))
print(a)
b = np.zeros(4)
print(b)
[[[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]] [[0. 0. 0.] [0. 0. 0.] [0. 0. 0.]]] [0. 0. 0. 0.]
a = np.ones((3, 3))
print(a)
b = np.ones(3,)
print(b)
[[1. 1. 1.] [1. 1. 1.] [1. 1. 1.]] [1. 1. 1.]
I = np.eye(3)
I
array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
D = np.diag(np.array([1, 2, 3, 4]))
D
array([[1, 0, 0, 0], [0, 2, 0, 0], [0, 0, 3, 0], [0, 0, 0, 4]])
a = np.random.rand(10) # uniformne raspodele u opsegu [0, 1]
print(a)
b = np.random.randn(10) # Gausove raspodele standardne devijacije 1 i srednje vrednosti 0
print(b)
[0.97429476 0.70956798 0.90083729 0.35644578 0.63261704 0.26724759 0.52545188 0.45281079 0.46892374 0.78944932] [-0.13801446 -0.68632746 -0.77031186 0.87929405 -0.92226175 -0.09579874 -0.7724901 -0.44507717 -0.78078794 -0.05224638]
Ako se želi da kod daje iste slučajne brojeve pri svakom pokretanju, onda se mora postaviti seed.
np.random.seed(0)
a = np.random.rand(4)
print(a)
[0.5488135 0.71518937 0.60276338 0.54488318]
Indeksiranje je slično kao kod listi.
a = np.arange(10)
print(a)
print(a[0])
print(a[::-1])
print(a[2:9:3])
print(a[:4])
print(a[4:])
[0 1 2 3 4 5 6 7 8 9] 0 [9 8 7 6 5 4 3 2 1 0] [2 5 8] [0 1 2 3] [4 5 6 7 8 9]
Višedimenzioni nizovi se indeksiraju uređenim parovima.
a = np.diag(np.arange(3))
print(a)
print(a[1, 1])
a[2, 1] = 10
print(a)
[[0 0 0] [0 1 0] [0 0 2]] 1 [[ 0 0 0] [ 0 1 0] [ 0 10 2]]
a[1:3, 1:3]
array([[ 1, 0], [10, 2]])
Logičko indeksiranje:
np.random.seed(3)
a = np.random.randint(0, 21, 15)
print(a)
mask = (a % 3 == 0)
print(mask)
partOfA = a[mask] # or, a[a%3==0]
print(partOfA)
[10 3 8 0 19 10 11 9 10 6 0 20 12 7 14] [False True False True False False False True False True True False True False False] [ 3 0 9 6 0 12]
Dodatno o nizovima na linku.
Operacije nad nizovima se mogu izvršavati element po element, ali je ovo izuzetno nepraktično jer uvek zahteva pisanje velikog broja petlji. Vrlo opširan matematički aparat je podržan u paketu NumPy. Neke od ovih opcija ćemo ovde nabrojati, ali ćemo nastaviti da ih koristimo u toku kursa i na njima ćemo insistirati.
a = np.array([1, 2, 3, 4])
a + 1
array([2, 3, 4, 5])
2**a
array([ 2, 4, 8, 16])
b = np.ones(4) + 1
a*b
array([2., 4., 6., 8.])
Primetimo da izvršavanje raznih operacija nad skalarom i vektorom ili matricom ne rezultira greškom, već se operacija izvršava nad svakim elementom matrice.
Množenje matrica nije množenje pojedinačnih elemenata! Evo primera:
A = np.ones([3, 3])
A*A
array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]])
np.dot(A, A) # Ovako se množe matrice. dot je od "dot product"
array([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]])
⚠️ VAŽNO: Korišćenje ugrađenih operacija direktno nad vektorima utiče na preglednost koda. Međutim, mnogo je bitniji drugi efekat: kod se mnogo brže izvršava. U pozadini, ako procesor treba da izvrši mnogo istih operacija nad gomilom podataka, moguće je da će koristiti takozvane SIMD instrukcije. Naziv je skraćenica od __S__ingle __I__nstruction __M__ultiple __D__ata, što znači da procesor ne mora da gubi vreme na dovlačenje instrukcije iz memorije, njeno dekodovanje i pripremu za izvršavanje za svaki podatak, već to može da uradi jedanput i da iz memorije dovlači samo operande. Dodatno, ovako pisan kod se može kasnije i lakše paralelizovati na više jezgara.
Postupak pisanja koda izbegavanjem petlji i uz pomoć nizova (vektora i matrica) često zovemo vektorizacija (engl. vectorization).
Uporedimo sada vreme izvršavanja ugrađene operacije nad vektorima i korišćenjem petlje.
import numpy as np
import time
length = 1000000
a = np.random.randn(length)
# With loop
t = time.time()
b = np.zeros(length)
for i in range(length):
b[i] = a[i]**5
print(f'Elementwise, time passed: {time.time() - t} seconds.')
# With numpy
t = time.time()
b = a**5
print(f'Builtin NumPy, time passed: {time.time() - t} seconds.')
Elementwise, time passed: 0.5722990036010742 seconds. Builtin NumPy, time passed: 0.07117867469787598 seconds.
Primetite da je vreme izvršavanja za red veličine manji. Evo još par primera množenja vektora i matrica koji će nam dočarati važnost vektorizacije.
import time
length = 2000
x1 = np.random.rand(length)
x2 = np.random.rand(length)
# Skalarni proizvod dva vektora (dot product)
t = time.time()
dot = 0
for i in range(len(x1)):
dot+= x1[i]*x2[i]
print(f'Dot product, time passed: {time.time() - t} seconds.')
# Spoljni proizvod (outer product)
t = time.time()
outer = np.zeros((len(x1),len(x2))) # Create a len(x1) x len(x2) matrix with only zeros
for i in range(len(x1)):
for j in range(len(x2)):
outer[i,j] = x1[i]*x2[j]
print(f'Outer product, time passed: {time.time() - t} seconds.')
### Proizvod matrice i vektora ###
W = np.random.rand(3,len(x1))
t = time.time()
gdot = np.zeros(W.shape[0])
for i in range(W.shape[0]):
for j in range(len(x1)):
gdot[i] += W[i,j]*x1[j]
print(f'General dot product, time passed: {time.time() - t} seconds.')
Dot product, time passed: 0.0009980201721191406 seconds. Outer product, time passed: 1.833996295928955 seconds. General dot product, time passed: 0.0034322738647460938 seconds.
import time
length = 2000
x1 = np.random.rand(length)
x2 = np.random.rand(length)
# Skalarni proizvod dva vektora (dot product)
t = time.time()
dot = np.dot(x1,x2)
print(f'Dot product, time passed: {time.time() - t} seconds.')
# Spoljni proizvod (outer product)
t = time.time()
outer = np.outer(x1,x2)
print(f'Outer product, time passed: {time.time() - t} seconds.')
# Generalni proizvod matrice i vektora
W = np.random.rand(3,len(x1))
t = time.time()
gdot = np.dot(W,x1)
print(f'General dot product, time passed: {time.time() - t} seconds.')
Dot product, time passed: 0.00033092498779296875 seconds. Outer product, time passed: 0.041422128677368164 seconds. General dot product, time passed: 9.250640869140625e-05 seconds.
Da ne zaboravimo, sada smo već dovoljno naučili da otvorimo ugrađeni debager.
Videli smo da sabiranje vektora i skalara daje vektor kod koga je svaki element sabran sa skalarom. Slično je i sa drugim operacijama. Međutim, šta se dešava ako matricu saberemo sa vektorom? Logičan odgovor je da će se javiti greška pošto su dimenzije matrice i vektora različite. Međutim, ovo ne mora uvek da bude slučaj i zapravo je vrlo korisno kad nije.
NumPy će u situacijama kada operandi nisu istih dimenzija transformisati manji tako da njegove dimenzije izjednači sa većim. Ova konverzija se na engleskom zove broadcasting. Najlakše je objasniti na primeru. Evo sličice:
*Sličica preuzeta sa linka
import numpy as np
A = np.tile(np.arange(0, 40, 10), (3, 1)).T
print(f'A: {A}')
b = np.array([0, 1, 2])
print(f'b: {b}')
X = A + b
print(f'A + b: {X}')
A: [[ 0 0 0] [10 10 10] [20 20 20] [30 30 30]] b: [0 1 2] A + b: [[ 0 1 2] [10 11 12] [20 21 22] [30 31 32]]
Treba voditi računa da oblik NumPy niza može biti definisan kao prost niz kome je podrzumevana druga dimenzija 1 ili kao niz kompleksnijeg oblika. Primer:
a = np.arange(0, 40, 10)
a.shape
(4,)
b = np.array([[0, 10, 20, 30]])
b.shape
(1, 4)
bT = b.T #transponovanje
bT.shape
(4, 1)
Konverziju iz jednog u drugi tip možemo uraditi ovako:
a = a[:, np.newaxis] # adds a new axis -> 2D array
a.shape
(4, 1)
A sada će raditi i broadcasting.
b = np.array([0, 1, 2])
X = a + b
X
array([[ 0, 1, 2], [10, 11, 12], [20, 21, 22], [30, 31, 32]])
Nabrojaćemo samo neke. Najčešće rade nad elementima matrice pojedinačno, osim gorenavedene koje su pravljene za matrične operacije.
t = np.arange(0, 10, 0.1)
f = 0.2
print(f'Time:\n{t}')
x = np.sin(2*np.pi*f*t)
print(f'Sine:\n{x}')
y = np.exp(2j*np.pi*f*t)
print(f'Complex exponential:\n{y}')
z = np.sqrt(t)
print(f'Square root:\n{z}')
Time: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2. 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3. 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4. 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5. 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 6. 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7. 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8. 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 9. 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9] Sine: [ 0.00000000e+00 1.25333234e-01 2.48689887e-01 3.68124553e-01 4.81753674e-01 5.87785252e-01 6.84547106e-01 7.70513243e-01 8.44327926e-01 9.04827052e-01 9.51056516e-01 9.82287251e-01 9.98026728e-01 9.98026728e-01 9.82287251e-01 9.51056516e-01 9.04827052e-01 8.44327926e-01 7.70513243e-01 6.84547106e-01 5.87785252e-01 4.81753674e-01 3.68124553e-01 2.48689887e-01 1.25333234e-01 1.22464680e-16 -1.25333234e-01 -2.48689887e-01 -3.68124553e-01 -4.81753674e-01 -5.87785252e-01 -6.84547106e-01 -7.70513243e-01 -8.44327926e-01 -9.04827052e-01 -9.51056516e-01 -9.82287251e-01 -9.98026728e-01 -9.98026728e-01 -9.82287251e-01 -9.51056516e-01 -9.04827052e-01 -8.44327926e-01 -7.70513243e-01 -6.84547106e-01 -5.87785252e-01 -4.81753674e-01 -3.68124553e-01 -2.48689887e-01 -1.25333234e-01 -2.44929360e-16 1.25333234e-01 2.48689887e-01 3.68124553e-01 4.81753674e-01 5.87785252e-01 6.84547106e-01 7.70513243e-01 8.44327926e-01 9.04827052e-01 9.51056516e-01 9.82287251e-01 9.98026728e-01 9.98026728e-01 9.82287251e-01 9.51056516e-01 9.04827052e-01 8.44327926e-01 7.70513243e-01 6.84547106e-01 5.87785252e-01 4.81753674e-01 3.68124553e-01 2.48689887e-01 1.25333234e-01 3.67394040e-16 -1.25333234e-01 -2.48689887e-01 -3.68124553e-01 -4.81753674e-01 -5.87785252e-01 -6.84547106e-01 -7.70513243e-01 -8.44327926e-01 -9.04827052e-01 -9.51056516e-01 -9.82287251e-01 -9.98026728e-01 -9.98026728e-01 -9.82287251e-01 -9.51056516e-01 -9.04827052e-01 -8.44327926e-01 -7.70513243e-01 -6.84547106e-01 -5.87785252e-01 -4.81753674e-01 -3.68124553e-01 -2.48689887e-01 -1.25333234e-01] Complex exponential: [ 1. +0.00000000e+00j 0.9921147 +1.25333234e-01j 0.96858316+2.48689887e-01j 0.92977649+3.68124553e-01j 0.87630668+4.81753674e-01j 0.80901699+5.87785252e-01j 0.72896863+6.84547106e-01j 0.63742399+7.70513243e-01j 0.53582679+8.44327926e-01j 0.42577929+9.04827052e-01j 0.30901699+9.51056516e-01j 0.18738131+9.82287251e-01j 0.06279052+9.98026728e-01j -0.06279052+9.98026728e-01j -0.18738131+9.82287251e-01j -0.30901699+9.51056516e-01j -0.42577929+9.04827052e-01j -0.53582679+8.44327926e-01j -0.63742399+7.70513243e-01j -0.72896863+6.84547106e-01j -0.80901699+5.87785252e-01j -0.87630668+4.81753674e-01j -0.92977649+3.68124553e-01j -0.96858316+2.48689887e-01j -0.9921147 +1.25333234e-01j -1. +1.22464680e-16j -0.9921147 -1.25333234e-01j -0.96858316-2.48689887e-01j -0.92977649-3.68124553e-01j -0.87630668-4.81753674e-01j -0.80901699-5.87785252e-01j -0.72896863-6.84547106e-01j -0.63742399-7.70513243e-01j -0.53582679-8.44327926e-01j -0.42577929-9.04827052e-01j -0.30901699-9.51056516e-01j -0.18738131-9.82287251e-01j -0.06279052-9.98026728e-01j 0.06279052-9.98026728e-01j 0.18738131-9.82287251e-01j 0.30901699-9.51056516e-01j 0.42577929-9.04827052e-01j 0.53582679-8.44327926e-01j 0.63742399-7.70513243e-01j 0.72896863-6.84547106e-01j 0.80901699-5.87785252e-01j 0.87630668-4.81753674e-01j 0.92977649-3.68124553e-01j 0.96858316-2.48689887e-01j 0.9921147 -1.25333234e-01j 1. -2.44929360e-16j 0.9921147 +1.25333234e-01j 0.96858316+2.48689887e-01j 0.92977649+3.68124553e-01j 0.87630668+4.81753674e-01j 0.80901699+5.87785252e-01j 0.72896863+6.84547106e-01j 0.63742399+7.70513243e-01j 0.53582679+8.44327926e-01j 0.42577929+9.04827052e-01j 0.30901699+9.51056516e-01j 0.18738131+9.82287251e-01j 0.06279052+9.98026728e-01j -0.06279052+9.98026728e-01j -0.18738131+9.82287251e-01j -0.30901699+9.51056516e-01j -0.42577929+9.04827052e-01j -0.53582679+8.44327926e-01j -0.63742399+7.70513243e-01j -0.72896863+6.84547106e-01j -0.80901699+5.87785252e-01j -0.87630668+4.81753674e-01j -0.92977649+3.68124553e-01j -0.96858316+2.48689887e-01j -0.9921147 +1.25333234e-01j -1. +3.67394040e-16j -0.9921147 -1.25333234e-01j -0.96858316-2.48689887e-01j -0.92977649-3.68124553e-01j -0.87630668-4.81753674e-01j -0.80901699-5.87785252e-01j -0.72896863-6.84547106e-01j -0.63742399-7.70513243e-01j -0.53582679-8.44327926e-01j -0.42577929-9.04827052e-01j -0.30901699-9.51056516e-01j -0.18738131-9.82287251e-01j -0.06279052-9.98026728e-01j 0.06279052-9.98026728e-01j 0.18738131-9.82287251e-01j 0.30901699-9.51056516e-01j 0.42577929-9.04827052e-01j 0.53582679-8.44327926e-01j 0.63742399-7.70513243e-01j 0.72896863-6.84547106e-01j 0.80901699-5.87785252e-01j 0.87630668-4.81753674e-01j 0.92977649-3.68124553e-01j 0.96858316-2.48689887e-01j 0.9921147 -1.25333234e-01j] Square root: [0. 0.31622777 0.4472136 0.54772256 0.63245553 0.70710678 0.77459667 0.83666003 0.89442719 0.9486833 1. 1.04880885 1.09544512 1.14017543 1.18321596 1.22474487 1.26491106 1.30384048 1.34164079 1.37840488 1.41421356 1.44913767 1.4832397 1.51657509 1.54919334 1.58113883 1.61245155 1.64316767 1.67332005 1.70293864 1.73205081 1.76068169 1.78885438 1.81659021 1.84390889 1.87082869 1.8973666 1.92353841 1.94935887 1.97484177 2. 2.02484567 2.04939015 2.07364414 2.0976177 2.12132034 2.14476106 2.16794834 2.19089023 2.21359436 2.23606798 2.25831796 2.28035085 2.30217289 2.32379001 2.34520788 2.36643191 2.38746728 2.40831892 2.42899156 2.44948974 2.46981781 2.48997992 2.50998008 2.52982213 2.54950976 2.56904652 2.58843582 2.60768096 2.62678511 2.64575131 2.66458252 2.68328157 2.70185122 2.7202941 2.73861279 2.75680975 2.77488739 2.79284801 2.81069386 2.82842712 2.84604989 2.86356421 2.88097206 2.89827535 2.91547595 2.93257566 2.94957624 2.96647939 2.98328678 3. 3.01662063 3.03315018 3.04959014 3.06594194 3.082207 3.09838668 3.1144823 3.13049517 3.14642654]
Još mnogo detalja o NumPy paketu i manimulacijama sa vektorima i matricama možete pronaći na linku. Neke od tamo pomenutih stvari ćemo možda koristiti kasnije, ali ovo je uvod. Dosta za sada. :)
Složićemo se da je vizuelizacija vrednosti prostim ispisom na izlaz blago rečeno nepraktična. Zato ćemo često koristiti grafike. Za potrebe crtanja (plotovanja) grafika, koristićemo paket matplotlib i njegov deo pyplot.
Kao i NumPy, pyplot se mora uključiti u kod. Konvencija je da se to uradi ovako:
import matplotlib.pyplot as plt
⚠️ VAŽNO: Biblioteka matplotlib neće prikazivati grafike ako se koristi kernel XPython koji je pogodan za debagovanje. Zbog toga XPython treba koristiti samo u slučaju potrebe naprednijeg debagovanja. U nekoj od narednih verzija ovog kernela se očekuje ispravka ovog problema.
Da vidimo kako će izgledati podaci sada:
plt.plot(t, x)
plt.show()
Ovaj grafik je nejasan. Šta je na x-osi, a šta na y-osi? Može lepše.
fig, ax = plt.subplots()
ax.plot(t, x)
ax.set(xlabel='Time (s)', ylabel='x(t)',
title='Just a simple siusoid')
ax.grid() # turn on the grid
plt.show()
Može i više gradika na jednoj slici:
fig, ax = plt.subplots()
ax.plot(t, y.real, label='Real part')
ax.plot(t, y.imag, label='Imaginary part')
ax.set(xlabel='Time (s)', ylabel='y(t)',
title='Complex exponential')
ax.grid() # turn on the grid
ax.legend()
plt.show()
fig, ax = plt.subplots()
ax.plot(y.real, y.imag)
ax.set(xlabel='real(y(t))', ylabel='imag(y(t))',
title='Complex exponential',
aspect='equal')
plt.show
<function matplotlib.pyplot.show(close=None, block=None)>
Ili u dikretnim tačkama:
x = np.linspace(0.1, 2 * np.pi, 41)
y = np.exp(np.sin(x))
plt.stem(x, y, use_line_collection=True)
plt.show()
Još mnogo lepih primera možemo naći na linku.
Nekad je važno da različite deove grafika možemo da uvećamo. Za to nam je potreban interaktivni prozor. Za tu svrhu ćemo koristiti matplotlib-widgets ekstenziju. Da bi se ona aktivirala potrebno je paket matplotlib uključiti na sledeći način:
%matplotlib widget
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plt.stem(x, y, use_line_collection=True);
Važno je da se ovakav pristup koristi samo sa Python 3 kernelom, XPython će izbacivati greške. Dodatno, widget nije vidljiv u eksportovanim html ili pdf fajlovima, pa u tom slučaju ne treba koristiti interaktivne prozore. Poništavanje opcije za interaktivne prozore se postiže komandom %matplotlib inline
.
To je to što se uvoda tiče. Mnoge stvari će postati jasnije na primerima. Do kuckanja!