import math
import sympy
import scipy
import control
import numpy as np
from scipy import signal
from matplotlib import pyplot as plt
Welcome to Signals and systems!
In this simple tutorial, we will learn about python3's basic commands and methods that we will use them for Signal processing, Dynamic systems and control theory.
Consider that this tutorial uses Python 3.7.0
This tutorial has been created by $\href{https://hnxj.github.io}{Hamed \: Nejat}$ for Signals and systems course of $\href{https://ee.sharif.edu}{Electrical \: Engineering \: Department \: of \: Sharif \: University \: of \: Technology}$, with major help of official documents of Numpy, Scipy, Matplotlib, Control and Sympy. special thanks to all other python developers, this tutorial is free to use.
Anaconda is easy to use, hence pure python is also suitable for this course
Windows (x64/x86) (~490MB) : https://repo.anaconda.com/archive/Anaconda3-2019.07-Windows-x86_64.exe
for MacOS (~650MB) : https://repo.anaconda.com/archive/Anaconda3-2019.07-MacOSX-x86_64.pkg
for Linux (~510MB) : https://repo.anaconda.com/archive/Anaconda3-2019.07-Linux-x86_64.sh
Make sure that JupyterLab is installed by this command on Anaconda Prompt :
In anaconda prompt, write:
-->conda create -n env_name python=3.7
Then everytime you want to code for this course, write:
-->conda activate evn_name
And when you dont want to use this env:
-->conda deactivate env_name
for anaconda users:
-->conda install jupyter
for main python users:
-->pip install
Open Anaconda prompt, one of the ways is to search "Anaconda prompt" in windows search.
After opening of Anaconda Prompt, Enter:
-->jupyter lab
Then, you are in jupyter environment. click on file tab, then click on new notebook.
On cmd/terminal, install spyder using this commands:
-->conda install spyder
To run:
-->spyder
For more questions about tutorial class or homeworks:
contact us : hamednejat7@gmail.com
print("You will never walk alone") # Raw string
print("There is" + " a " + "GoldenSky :)" # Multi strings
" Walk on", "trough the rain") # multi line
a = int(input("Enter int number"))
b = float(input("enter float"))
s = str(input("Enter string"))
print(a, b, s)
print(6) # Integers
print(-7) # Integers
print(18.0) # Floats
print("18.0 " + str(-32.4353)) # Numbers to string
print(float("18.34") + 32.4353) # Strings to number
print(float("-18.34") + 32.4353) # Signed strings to number, Sign is included in
# number while it was converted from string to number!
print("First number: {}, Second number: {} and so on ...".format(4.01, 3.98))
print ('%.2f'%(11.1725612)) # 2-digit precision, rounded down because 11.1725612 < 11.175
print ('%.2f'%(11.1775612)) # 2-digit presicion rounded up because 11.1775612 > 11.175
print('%.5f'%(float("18.34") + 32.4353)) # Strings to number, 5-digit precision
print('%.3f'%(float("-18.34") + 32.4353)) # Signed strings to number, 3-digit precision
print('%f %d %s'%(11.13, 11.13, 11.13)) # printing float, integer and string in same command.
print('%f'%(1323.12312312312)) # NOTE!: default float precision is 6-digits
a = 4 # integer
b = -5.12 # float
c = "Estereeng" # string
# char is almost same with str in python, except char array in classes
d = chr(113) # <C: char> = chr(<I: integer>) method returns the character that has ascii=I
e = ord('q') # <I: integer> = ord(<C: char>) method returns the ascii that char=C
print("\nType of a is :", type(a), "a :", a,
"\nType of b is :", type(b), "b :", b,
"\nType of c is :", type(c), "c :", c,
"\nType of d is :", type(d), "d :", d,
"\nType of e is :", type(e), "d :", e)
e = dict() # dict :: e[<Key>] = <Val>
e["asd"] = 12 # assigning num to string key
e[1] = a # integer to integer
e[b] = 'L' # char (string) to float
print("\nType of e is :", type(e), "\n e :", e)
print("e[\"asd\"] is : ", e["asd"])
f = list() # alternate for list / f = [1, 1.1, list]
f.append(1) # appending an integer
f.append(1.1) # appending a float
f.append("paganini") # appending a string
f.append(e) # appending a dictionary!
print("\nType of e is :", type(f), "e :", f)
f.pop(2) # pop 2nd element
print("\nType of e is :", type(f), "e :", f)
f.pop() # pop last element
print("\nType of e is :", type(f), "e :", f)
f = set() # alternate for list / f = [1, 1.1, list]
f.add(1) # add an integer to the set
f.add(1.1) # appending a float
f.add(2)
f.add(-1)
f.add("vivaldi") # appending a string
f.add("antonio")
# f.add(e) # appending a dictionary!
print("\nType of e is :", type(f), "e :", f)
f.remove("antonio") # remove one existing element
try: # prevents unexcepted errors and interrupts in program
f.remove("bach") # key is not in the set
except KeyError:
print("\nKey is not in the set")
except:
print("Unknown error")
print("\nType of e is :", type(f), "e :", f)
f.pop() # pop last element
print("\nType of e is :", type(f), "e :", f)
In this subsection, we will do some maths computations and evaluate errors.
import math
import numpy as np
a = 12.250
b = 27.601
c = a * b
d = a / b
print(a, b, c, d)
a = 3.131
b = 13.313
c = a ** b # a power b
d = np.sqrt(b) # radical b
print(a, b, c, d)
a = 3.131
b = np.sin(a) # numpy sinus; is radian based
c = np.cos(a) # numpy cosinus; is radian based
d = np.exp(c) # exponential
print(a, b, c, d)
a = -3.131 + 4.023j # complex number
b = np.sin(a) # numpy sinus; is radian based
c = np.cos(a) # numpy cosinus; is radian based
d = np.exp(c) # exponential
print(a, b, c, d)
import numpy as np
first = -4
last = 6
step = 2
cnt = 0.5
np_array1 = np.array([1, 0.7, -4, 2.0 + 2.0j, -0.5j])
np_array2 = np.array([[1, 2], [0.5j, -2], [4.0, 0.3j]])
for i in range(first, last, step): # ranged loop
print(i**2.5)
for j in np_array1: # structured loop
print(j)
for i, j in np_array2: # structured double loop
print(i, j)
for i in range(7):
print(i)
while cnt < 10:
cnt *= 2.7
print(cnt)
if step > 10:
pass # the command that does nothing!
# the code
elif step == 10: # Else If equivalent
pass
# the code
elif step == 9:
quit() # Exit from the code or program !
else: # otherwise
print("Nothing else matters")
import numpy as np
# Basic recursive function
def gcd(a, b): # Greatest Common Divisor (GCD)
if b > a:
(a, b) (b, a) # nice swap in Python!
if b == 0:
return a
return gcd(b, a%b)
R = 14
a = -1
u = lambda x : (x + abs(x))/2
f = lambda x : u(x) * (np.exp(3*x) + 3)
print(gcd(2436, 504))
print(f(1.2 + 1.0j), f(-1 - 0.01j), u(1), u(0.0), u(-1))
# IMPORTANT NOTE: Python has recursion limit, default recursion limit is 1000.
# you can change it by setrecursionlimit(<New Limit>)
import sys
sys.setrecursionlimit(10000)
# Void function
def say_hello():
print("Hello World :]")
return # void return
say_hello()
# lambda function
hemographic1 = lambda x : (2 * x + 4) / (3 * x - 1)
print(hemographic1(4))
a = np.random.rand(5) # 10 random numbers uniformly between 0-1
b = np.random.randn(1000) # 1000 Gaussian standard distributed random numbers with mean = 0, std = 1
c = np.random.randint(-3, 14, 10) # 10 random integers between -3 and 13
d = np.random.gamma(2.5, 1, [2, 2]) # beta, gumbel, ... other distributions are available
print(a)
print(np.mean(b))
print(np.var(b))
print(c)
print(d)
numpy has built-in important statistic functions, In the plotting part we will show simple statistical plotting
import numpy as np
interval_first = 0
interval_last = 10
step = 2
amount = 33
e = np.arange(interval_first, interval_last, step) # equivalent to matlab's i:j with step
f = np.linspace(interval_first, interval_last, amount) # Splits line space between 1 and 10 into 25 numbers
g = np.mean(e)
h = np.var(f)
i = np.std(f)
j = np.max(e)
k = np.min(f)
l = np.median(e)
print(e)
print(f)
print(len(e)) # returns length of structure
print(len(f)) # returns length of structure
print(g, h, i)
print(j, k, l)
from matplotlib import pyplot as plt
x = np.linspace(-3, 3, 100)
y1 = np.exp(x)
y2 = np.cos(x)
y3 = np.random.randn(100)
p1 = plt.plot(x, y3, label='test', color='green', marker='o', linestyle='dashed',
linewidth=2, markersize=2)
p1[0].set_label('test')
plt.xlabel("X")
plt.ylabel("Y")
plt.title("Test")
plt.legend(handles=p1, labels='G', loc='lower right')
plt.grid(1)
plt.axis('on')
plt.show()
# print(dir(plt))
fig, ax = plt.subplots(nrows=2, ncols=2) # number of subplots
fig.set_figwidth(19) # figure width in cm
fig.set_figheight(11) # figure height in cm
ax[0, 0].plot(x, y2, label='Cos', color='red') # plotting with specs
ax[0, 0].plot(x, y3, label='Rnd') # colors, scatters and ...
ax[0, 0].plot(x, y1, label='Sin')
ax[0, 1].scatter(x, y2, label='Cos', color='cyan')
ax[1, 0].plot(x, y3, label='Rnd')
ax[1, 1].plot(x, y2, label='Cos')
ax[1, 1].plot(x, y3, label='Rnd')
ax[0, 1].annotate('Cos', (1, np.cos(1)),
xytext=(0.8, 0.9), textcoords='axes fraction',
arrowprops=dict(facecolor='black', shrink=0.05),
fontsize=16,
horizontalalignment='right', verticalalignment='top') # annotation cursor
ax[0, 0].set_title("The title !")
ax[0, 0].set_xlabel("X description")
ax[0, 0].set_ylabel("Y description")
ax[1, 0].set_title("The title !")
ax[1, 0].set_xlabel("X description")
ax[1, 0].set_ylabel("Y description")
ax[0, 1].set_title("The scatter")
ax[0, 1].set_xlabel("X description")
ax[0, 1].set_ylabel("Y description")
ax[1, 1].grid('on')
ax[1, 1].axis('off')
ax[0, 0].legend()
fig.show()
from matplotlib import pyplot as plt
import numpy
class plotter:
def __init__(self, rows, cols, width, height):
self.fig, self.ax = plt.subplots(nrows=rows, ncols=cols)
self.fig.set_figwidth(width)
self.fig.set_figwidth(height)
return
@staticmethod
def plot(ax, x, y, title="", x_label="", y_label="", grid='on', axis='on'):
ax.plot(x, y)
ax.set_title(title)
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
ax.grid(grid)
ax.axis(axis)
return
class painter(plotter): # inheritance from plotter class
def __init__(self, boom_width, boom_height, width=21, height=12):
super().__init__(rows=1, cols=1, width=width, height=height)
self.boom_width = boom_width
self.boom_height = boom_height
return
def sinusy(self, x):
self.ax.plot(x, np.sin(x))
return
def exponenty(self, x):
self.ax.plot(x, np.exp(x))
return
def free_draw(self, x, y):
self.ax.plot(x, y)
print("The rawing")
return
msr = painter(100, 100)
msr.sinusy(x=np.linspace(1, 19, 50))
msr.exponenty(x=np.linspace(-2, 2, 60))
msr.plot(ax=msr.ax, x=np.random.rand(30), y=np.linspace(10, 20, 30), title="Drawing")
msr.plot(ax=msr.ax, x=np.random.randint(5, 10, 50), y=np.linspace(2, 18, 50), title="Drawing")
msr.plot(ax=msr.ax, x=np.linspace(2, 18, 50), y=np.random.randint(1, 20, 50), title="Drawing")
import numpy as np
import matplotlib.pyplot as plt
u = lambda x : (np.sign(x) + 1)/2
f1 = lambda x : (np.exp(-x) * np.sin(10*x)) * u(t)
f2 = lambda x : (np.exp(-2*x) * np.cos(5*x)) * u(t)
t = np.linspace(-2, 6, 1000)
fig, ax = plt.subplots(1, 1)
fig.set_figwidth(19)
fig.set_figheight(11)
p1 = ax.plot(t, f1(t))
p2 = ax.plot(t, f2(t))
p1[0].set_label('F1')
p2[0].set_label('F2')
ax.legend(loc='upper right')
ax.set_xlabel("tau")
ax.set_ylabel("expression value")
ax.grid(True)
ax.set_ylim(ymin=-1, ymax=1)
ax.set_xlim(xmin=-1, xmax=5)
fig.show()
from matplotlib.pyplot import *
import numpy as np
x = np.linspace(0, 10, 1000)
y = np.sin(x**2)
plot(x, y)
axis('on')
grid('on')
show()
from sympy import * # imports all required functions, methods and types
x, y, z, t = symbols('x y z t') # symbols
f1 = cos(-t) + sin(2*t) + t**3 * exp(-3*t)
fd1 = diff(f1, t, 1) # First order derivative
print("\n First function:")
print(fd1)
print(fd1.subs(t, 3)) # string output
print(fd1.subs(t, 27.76275)) # float output
f2 = sin(x + y) + x*y**3 + x*y**4
fd2 = diff(diff(f2, y, 1), x, 1) # second-order derivative based on y
print("\n Second function:")
print(fd2)
print(fd2.subs(x, 2.).subs(y, 1.)) # getting values from the function
example: symbolic integration :
$$ f_{(t)} = te^{-3t} + cos(2t) + t^{2}cos(t) $$$$ \int^{t}_{-\infty} f_{(t)} d t = integrate(f, t) $$
from sympy import * # imports all required functions, methods and types
x, y, z, t = symbols('x y z t') # symbols
f1 = cos(-t) + sin(2*t) + t**3 * exp(-3*t)
fd1 = integrate(f1, t) # First order derivative
print("\n First function:")
print(fd1)
print(fd1.subs(t, 3)) # string output
print(fd1.subs(t, 27.76275)) # float output
f2 = sin(x + y) + x*y**3 + x*y**4
fd2 = integrate(integrate(f2, y), x) # second-order derivative based on y
print("\n Second function:")
print(fd2)
print(fd2.subs(x, 2.).subs(y, 1.)) # getting values from the function
example: symbolic convolution :
This is very slow method for convolution. later we will introduce a better method
from sympy import *
def convolve(f, g, t):
tau = Symbol('tau')
return integrate(f.subs(t, tau) * g.subs(t, t - tau), tau)
t = Symbol('t')
f1 = exp(-t) * Heaviside(t)
f2 = Heaviside(t)
f3 = convolve(f1, f2, t)
print(f3)
import numpy as np
x = np.random.randint(-3, 3, 10)
y1 = np.gradient(x, 1) # the interpolated gradient
y2 = x[0:len(x)-1] - x[1:len(x)] # normal gradient
print(x)
print(y1)
print(y2)
from sympy.integrals import laplace_transform
from sympy.abc import t, s, a
F1 = t**2 + sin(t) + exp(3*t)
F2 = t + cos(t)
L1 = laplace_transform(F1, t, s)
L2 = laplace_transform(F2, t, s)
L3 = L1[0]*L2[0]
print(L1, L2, L3)
from sympy.integrals.transforms import inverse_laplace_transform
from sympy import exp, Symbol
from sympy.abc import s, t
a = Symbol('a', positive=True)
# F3 = inverse_laplace_transform(L3, s, t)
L4 = exp(-a*s)/s**2
F4 = inverse_laplace_transform(L4, s, t)
print(F4)
from sympy.integrals.transforms import mellin_transform
from sympy import exp
from sympy.abc import x, s
F1 = exp(-x) + sin(x)
M1 = mellin_transform(F1, x, s)
print(M1)
from sympy import fourier_transform, exp, inverse_fourier_transform
from sympy.abc import x, k
F1 = exp(-x*2) * Heaviside(x)
Q1 = fourier_transform(F1, x, k)
Q2 = fourier_transform(F2, x, k, noconds=False)
F2 = inverse_fourier_transform(Q1, k, x)
print(Q1)
print(Q2)
print(F2.simplify())
from sympy import Function, dsolve, Eq, Derivative, sin, cos, symbols
from sympy.abc import x
f = Function('f')(x) # Defining F as function of x
fp = Derivative(f, x) # F prime (1st derivative)
fz = Derivative(f, x, x) # F zegond (2nd derivative)
fy = Derivative(fz, x)
deq1 = fz + 9*f
p1 = dsolve(deq1, f)
print(p1)