First you need Jupyter. If you have Jupyter, you can launch it in a Terminal with
jupyter notebook
This should open a web navigator (such as Firefox) in which you can work. In the Home tab, you find your files.
Notebooks are composed of cells, which either contains some Python code, or some text. These notebooks offer a great tool to run code interactively, and is a must for teaching and learning.
You can edit a cell by double-clicking on it, and you can evaluate it with Ctrl+Enter (We often use Maj+Enter to evaluate and go to the next cell). The buttons in the toolbar are quite clear. Have a look at them.
Do not forget to save you work on a regular basis (even though Jupyter saves your work regularly).
Exercice : Click on Help -> User Interface Tour
Exercice : Delete the next cell. (see Help -> Keyboard Shortcuts) :
----- Please delete me ------------
The top shortcuts are
Markdown is a simple text format which allows some formatting. you can:
Fun fact: You can write in Markdown in the famous App WhatsApp.
Exercice: Transform this word from italics to boldfont (hind : double click on the previous cell).
The next cell is a Python cell. You can evaluate it with Maj+Enter.
# This is a comment
# Please comment your code
# You write codes for yourself AND for others
#
#
#
#
# No really, do it!
a = 4
print("a = {} et 2*a = {}".format(a,2*a))
a = 4 et 2*a = 8
**Exercice**: This cell should be in Markdown, not Python...
File "<ipython-input-2-fe8c94933eda>", line 1 **Exercice**: This cell should be in Markdown, not Python... ^ SyntaxError: invalid syntax
You can have interactive help. For instance, if you wish to know what a Python function does (say abs
), you can either type help(abs)
or abs?
, or abs
and Maj+Tab or Maj+2xTab.
Exercice: What is the function str()
? What is the difference between 2
and str(2)
?
str?
In Python, we upload libraries with import
. For instance, most notebooks start with
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
Here, in order to avoid writing plt
and np
everytime, one can use the shortcut
%pylab inline
It loads many useful mathematical functions (numpy) and plot functions (matplotlib).
%pylab inline
Populating the interactive namespace from numpy and matplotlib
N = 10
for i in range(N):
# For loop
if mod(i,2) == 0: # identation
# If condition.
print("Ping")
else:
# Else condition
print("Pong") #singular
# the indentation here shows that we finished the if/else loop
print("i = ", i)
# we leave the for loop
print("...")
Ping i = 0 Pong i = 1 Ping i = 2 Pong i = 3 Ping i = 4 Pong i = 5 Ping i = 6 Pong i = 7 Ping i = 8 Pong i = 9 ...
Exercice: The next cell has errors. Correct them (and read the error messages)
for i in [12,15,6,20]
if i > 12:
print(i,"is bigger than 12")
else:
print("12 is bigger than",i)
print("Good work")
File "<tokenize>", line 4 else: ^ IndentationError: unindent does not match any outer indentation level
a, b = 3, 4 # Shortcut for a = 3 and b = 4
print("a = ", a, "and b = ", b)
a, b = b, a # Useful shortcut for switch
print("a = ", a, "and b = ", b)
a = 3 and b = 4 a = 4 and b = 3
Python has several types (see next cell).
#Integer
a = 4
print("a = ", a, "\t\t\t\t is of type", type(a))
# Float
a = 3.5
print("a = ", a, "\t\t\t is of type", type(a))
a = 1e7 # shortcut for 10^7, --> float
print("a = ", a, "\t\t is of type", type(a))
a = pi # numpy knows some constants (pi is for np.pi)
print("a = ", a, "\t\t is of type", type(a))
# Boolean
a = True
print("a = ", a, "\t\t\t is of type", type(a))
# String
a = "Hello World!"
print("a = ", a, "\t\t is of type", type(a))
# List (variable size)
a = [1,2,3]
print("a = ", a, "\t\t\t is of type", type(a))
# Tuples (fixed size)
a = (1.5, [1,2], "Hi")
print("a = ", a, "\t is of type", type(a))
a = 4 is of type <class 'int'> a = 3.5 is of type <class 'float'> a = 10000000.0 is of type <class 'float'> a = 3.141592653589793 is of type <class 'float'> a = True is of type <class 'bool'> a = Hello World! is of type <class 'str'> a = [1, 2, 3] is of type <class 'list'> a = (1.5, [1, 2], 'Hi') is of type <class 'tuple'>
In addition, we work with numpy, which has the type array (= matrix)
# Array (matrix)
a = array([[1,2], [3,4]]) # or np.array
print("a = \n", a, "\n is of type", type(a))
a = [[1 2] [3 4]] is of type <class 'numpy.ndarray'>
Python can handle the usual operations +,-,/,*
, and many more. Here are some traps.
print("sqrt{2} = ", 2**.5) # Power is with **
print("17 modulo 3 = ", 17%3) # Modulo with %
print("3/2 = ", 3/2, "is a float in Python 3")
print("3//2 = ", 3//2, "is an integer\n")
a = 4
print("a = ", a)
a += 2 #shortcut for a = a+2 (also works with *=, /=, etc.)
print("a = ", a)
sqrt{2} = 1.4142135623730951 17 modulo 3 = 2 3/2 = 1.5 is a float in Python 3 3//2 = 1 is an integer a = 4 a = 6
The print
function allows to print strings, as well as many other types
print("We have : pi =", pi, "et e =", e)
We have : pi = 3.141592653589793 et e = 2.718281828459045
print("You can indent with \t and go to nextline with \n.")
You can indent with and go to nextline with .
The correct way to format strings is with {}
and format
, see here. In short :
print("We have : 1 = {}, 2 = {}".format(1, 2))
print("This is a Bool : {}, this is an integer {:d}, and this is a float {:f} ".format(True, 3, 3.))
We have : 1 = 1, 2 = 2 This is a Bool : True, this is an integer 3, and this is a float 3.000000
# pi
for n in range(20): # n number of digits of pi after .
s = "pi = {:>21."+str(n)+"f}" # prepare the string
print(s, " \t gives \t", s.format(pi)) # then format it and print it
pi = {:>21.0f} gives pi = 3 pi = {:>21.1f} gives pi = 3.1 pi = {:>21.2f} gives pi = 3.14 pi = {:>21.3f} gives pi = 3.142 pi = {:>21.4f} gives pi = 3.1416 pi = {:>21.5f} gives pi = 3.14159 pi = {:>21.6f} gives pi = 3.141593 pi = {:>21.7f} gives pi = 3.1415927 pi = {:>21.8f} gives pi = 3.14159265 pi = {:>21.9f} gives pi = 3.141592654 pi = {:>21.10f} gives pi = 3.1415926536 pi = {:>21.11f} gives pi = 3.14159265359 pi = {:>21.12f} gives pi = 3.141592653590 pi = {:>21.13f} gives pi = 3.1415926535898 pi = {:>21.14f} gives pi = 3.14159265358979 pi = {:>21.15f} gives pi = 3.141592653589793 pi = {:>21.16f} gives pi = 3.1415926535897931 pi = {:>21.17f} gives pi = 3.14159265358979312 pi = {:>21.18f} gives pi = 3.141592653589793116 pi = {:>21.19f} gives pi = 3.1415926535897931160
Booleans have values True or False. The if
command takes a boolean. Here are some tricks.
a = 2
if a == 2: # Be careful, == checks the equality
print("this sentence is printed")
if not (a != 2): # != stands for "is different than"
print("this one as well")
# an assertion can check something
assert (a == 2) and (1 < 0), "There is a probleme here"
this sentence is printed this one as well
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-16-2307d1ee357b> in <module> 7 8 # an assertion can check something ----> 9 assert (a == 2) and (1 < 0), "There is a probleme here" AssertionError: There is a probleme here
Python codes must be simple and effective. Here are some best practices.
#The empty list
L = []
print("The empty list: L =", L, "\n")
# range(n) or range(0, n) is the list (0, 1, ..., n-1) (actually an iterator)
print("range(5) = {}".format( range(5)) )
print("but also")
print("range(5) = {}\n".format( [i for i in range(5)] ) )
# List by comprehension (this is both more readable AND faster !)
powerOf2 = [2**n for n in range(0,11)]
print("Powers of 2 are ", powerOf2)
# Add an element with .append()
powerOf2.append(2**11)
print("Powers of 2 are ", powerOf2, "\n")
# Access the lenght of a list with len()
print("There are {} elements in this list.".format(len(powerOf2)))
The empty list: L = [] range(5) = range(0, 5) but also range(5) = [0, 1, 2, 3, 4] Powers of 2 are [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] Powers of 2 are [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048] There are 12 elements in this list.
We access an element of a list (or an array) with brakets. []
.
Remark: In Python,
L = [1,2,3,4,5]
print("L =", L)
print("First element is", L[0])
print("Last element is", L[-1]) # shortcut for the last element
# Access a sub-list
print("L contains the sublist", L[1:4]) # Here, [1:4] is a shortcut for range(1,4), that is [1, 2, 3]
L = [1, 2, 3, 4, 5] First element is 1 Last element is 5 L contains the sublist [2, 3, 4]
We now deal with vectors and matrices. Lists are not suitable for numerical computations (see the next example), and the correct type is array
from numpy
.
L = [1,2,3]
Larray = array(L)
print("L = {}, \t\t\t\t Larray = {}".format(L, Larray) )
print("2*L = {}, \t\t 2*Larray = {}".format( 2*L, 2*Larray) )
L = [1, 2, 3], Larray = [1 2 3] 2*L = [1, 2, 3, 1, 2, 3], 2*Larray = [2 4 6]
As for lists, we can access the elements with brakets. For a matrix, we need 2 indices!
A = array([[1, 2, 3], [4, 5, 6], [7,8,9]])
print("A = {}\n".format (A) )
print("The middle bottom element is {}".format( A[2,1] ) ) # A[i,j] or A[i][j]
A = [[1 2 3] [4 5 6] [7 8 9]] The middle bottom element is 8
# Create from a list
x = array([1,2,3]) #Here, one dimension --> a vector
# A (2d) matrix is a list of lists
# Matrices are defined "lines per lines"
A = array([[1,2,3], [4,5,6], [7,8,9]])
print("A = \n {} \n\n and \n\n A.transpose() = \n {} \n".format(A, A.transpose()) )
# We can also use the definition by comprehension
# Here, a Vandermonde matrix
B = array([[i**j for j in range(3)] for i in range(1,4)])
print("\n B = \n {}".format ( B ))
A = [[1 2 3] [4 5 6] [7 8 9]] and A.transpose() = [[1 4 7] [2 5 8] [3 6 9]] B = [[1 1 1] [1 2 4] [1 3 9]]
Python knows some reference matrices.
# the null matrix, takes the dimensions as arguments:
A = zeros([2,3]) # of size2 x 3.
print ("A = \n", A)
# the matrix full of 1s :
B = ones([2,3])
print ("\nB = \n", B)
# The identity matrix :
A = eye(3)
print("\n A = \n", A)
A = [[0. 0. 0.] [0. 0. 0.]] B = [[1. 1. 1.] [1. 1. 1.]] A = [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
Warning: the product with *
is a the term by term product (Hadamard product). To multiply two matrices, we use dot
or @
.
x = array([1,2,3])
y = array([4,5,6])
print ("x =", x, "y =", y, ", x*y =", x*y, "and dot(x,y) =", dot(x,y))
x = [1 2 3] y = [4 5 6] , x*y = [ 4 10 18] and dot(x,y) = 32
Warning: 1 is not the identity matrix.
A = eye(3)
print("A + 1 =\n", A + 1)
A + 1 = [[2. 1. 1.] [1. 2. 1.] [1. 1. 2.]]
Python knows some "usual" functions such as determinant, trace, norm, inv, ... You can easily solve systems.
A = array([[(i-j)**2 for j in range(3)] for i in range(3)])
b = array([0,0,1])
x = solve(A,b) # the solution of Ax = b
print("We have | Ax - b | = ", norm(A@x - b))
We have | Ax - b | = 0.0
Exercice: What is exp(eye(3))? How to compute the exponent of eye(3) ? (remember, internet is your friend... have a look at expm
from scipy...)
We use matplotlib to plot graphics.
The basic function is plot(x,y)
where $x$ et $y$ are lists/array of same size. In pratice, $x$ usually represents the interval $[a,b]$, so you can use the command linspace
.
linspace?
Here is a full example to plot $f$.
def f(x) : return x**2 - cos(2*pi*x) # The fonction f
xx=linspace(-1,1,100) # 100 points between -1 et 1 (including -1 et 1)
plot(xx, f(xx), 'r') # 'r' is for red
axis('equal') # same scale in the two axis
title("the graph of $f(x) = x^2 - \cos(2 \pi x)$") # Title do accept Latex
legend("f")
xlabel("x")
Text(0.5, 0, 'x')
Remark: I personnaly use xx
for the interval in which x
lives. And similarly for all variables.
You can easily plot parametrized curves. For instance, to plot $$ c(t) = (\sin^3(t), \cos(t) - \cos^4(t)), $$ we can use the following code
tt = linspace(0, 2*pi, 100)
xx = sin(tt)**3 # f(list) = list(f)
yy = cos(tt) - cos(tt)**4
plot(xx,yy)
axis("equal")
title("Laporte's heart (1993)")
Text(0.5, 1.0, "Laporte's heart (1993)")
Exercice: How to have a red curve? How to make a "scatter" plot?
Finally, we can plot the level lines of a function with contour
. Here for instance, we plot the level lines of
$$
f(x,y) = e^{-y^2} \sin(x-y)
$$
def f(x,y): return exp(-y**2)*sin(x-y)
xx =linspace(-3,3,150) # 150 points between -3 and 3
yy =linspace(-2,2,100) # 100 points between -2 and 2
xgrid, ygrid = meshgrid(xx, yy) #meshgrid to prepare an array for x and y
Z = f(xgrid, ygrid)
# another possibility is to use the following
#Z = [[f(x,y) for x in xx] for y in yy] # A list of lists containing the values of f.
contour(xx,yy,Z,30) # Plot 30 level lines
colorbar() # For the color bar
<matplotlib.colorbar.Colorbar at 0x114dae670>
We can also easily create histograms.
N = 100000 # number of points
X = randn(N) # an array of N random points according to the normal (gaussian) distribution
hist(X, 100, density=True)
title("The histogram of X")
def f(x): return 1/sqrt(2*pi)*exp(-x**2/2) #The Gaussian function
tt = linspace(-3, 3, 100)
plot(tt, f(tt), 'r')
[<matplotlib.lines.Line2D at 0x1118bfd90>]
Exercice: Consider the rolling of a dice. Prove that the mean is $\mu := \mathbb{E}[X] = 7/2$ and the variance is $\sigma^2 = \mathbb{E}[(X - 7/2)^2] = 35/12$.
The Central Limit theorem states that if we roll M dices and average the result to get $Y$, then $$\dfrac{(Y - \mu)}{\sigma/\sqrt{M}} \approx \mathcal{N}(0, 1).$$
Exercice: Check the central limit theorem. Take $M = 1000$, and consider $N = 10000$ realisations of the experiment
# your code here
# and here is the correction...
M, N = 1000, 10000
mu, sigma = 7/2, sqrt(35/12)
X = np.random.randint(1, 7, [M,N]) # Random integers in \{1, ..., 6 \}
Y = sum(X, axis = 0)/M #Average over M
hist( (Y - mu)/(sigma/sqrt(M)), 50, density = True ) # the law of Y
plot(tt, f(tt))
[<matplotlib.lines.Line2D at 0x111a442e0>]
We end this notebook with useful tricks from Jupyter. You can time a cell with the magic command %%time
. Compare the two following codes
%%time
N = int(1e7)
xx = range(N)
yy = [x**2 for x in xx]
CPU times: user 5 s, sys: 180 ms, total: 5.18 s Wall time: 5.19 s
%%time
# This code is faster...
N = int(1e7)
xx = array ( range(N) )
yy = xx**2 # ... because there is no "for loop"
CPU times: user 964 ms, sys: 230 ms, total: 1.19 s Wall time: 1.19 s
%%time
# This code is even faster
N = int(1e7)
xx = linspace ( 0, N-1, N) # ...because we do not use lists !
yy = xx**2 #
CPU times: user 40 ms, sys: 14.6 ms, total: 54.6 ms Wall time: 52.9 ms
You can check what is time-consuming in your code with the profiler. You can install one with
pip install line_profiler
See the following example. First, we load the profiler
%load_ext line_profiler
def time_consuming_function(N, M):
for n in range(N):
A = rand(M, M)
B = eye(M) + A.T@A
Am1 = inv(B)
N,M = 1000, 100
%prun time_consuming_function(N,M)
If you want to use an external Python file and interface it with Jupyter, you can use %autoreload
, etc., etc.
Python has an active community. If you have a question about anything ("how to... ?", "why... ?", "what happens if... ?"), you can always ask on the internet.
In most cases, your question already has an answer somewhere on a forum
UPDATE: Do not hesitate to ask ChatGPT (or Perplexity for an anonymous equivalent) for Python code. The produced code is excellent, and you learn a lot by reading and studying it (of course, you may have to correct some bugs...).