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

*Esc-a*and*Esc-b*, to create a cell Above, or Below*Esc-d-d*to delete a cell (be careful, this can not be reverted)*Esc-m*and*Esc-y*to switch from text mode to python mode.

*Markdown* is a simple text format which allows some formatting. you can:

- make lists.
- decorate with
*italics*,**boldfonts**,~~strikethrough~~, etc. - create links
- write mathematical formulae with $\LaTeX$ (see here for a short (French) introduction to $\LaTeX$).

**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**.

In [1]:

```
# 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

In [2]:

```
**Exercice**: This cell should be in Markdown, not Python...
```

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)`

?

In [3]:

```
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).

In [4]:

```
%pylab inline
```

Populating the interactive namespace from numpy and matplotlib

In [5]:

```
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("...")
```

**Exercice**: The next cell has errors. Correct them (and read the error messages)

In [7]:

```
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")
```

In [8]:

```
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).

In [9]:

```
#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))
```

In addition, we work with numpy, which has the type array (= matrix)

In [10]:

```
# 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**.

In [11]:

```
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)
```

The `print`

function allows to print strings, as well as many other types

In [12]:

```
print("We have : pi =", pi, "et e =", e)
```

We have : pi = 3.141592653589793 et e = 2.718281828459045

In [13]:

```
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 :

- we use the letters {:d} (int), {:f} (float), {:s} (string) to precise the type (not mandatory)
- we use {:.8f} to display 8 digits after the . of a float
- we use {:<10f} to allocate 10 spaces to print (the < means flushleft). This is very useful for pretty print.

In [14]:

```
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

In [15]:

```
# 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
```

Booleans have values **True** or **False**. The `if`

command takes a boolean. Here are some tricks.

In [16]:

```
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.

In [17]:

```
#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)))
```

We access an element of a list (or an array) with brakets. `[]`

.

**Remark:** In Python,

- we use parenthesis to evaluate functions;
- we use brakets to access a memory.
- indices still start at 0.

In [18]:

```
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`

.

In [19]:

```
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!

In [20]:

```
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

In [21]:

```
# 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 ))
```

Python knows some reference matrices.

In [22]:

```
# 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 `@`

.

In [23]:

```
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.

In [24]:

```
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.

In [25]:

```
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`

.

In [26]:

```
linspace?
```

Here is a full example to plot $f$.

In [27]:

```
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")
```

Out[27]:

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

In [29]:

```
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)")
```

Out[29]:

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)
$$

In [30]:

```
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
```

Out[30]:

<matplotlib.colorbar.Colorbar at 0x114dae670>

We can also easily create histograms.

In [31]:

```
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')
```

Out[31]:

[<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

In [32]:

```
# your code here
```

In [33]:

```
# 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))
```

Out[33]:

[<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

In [34]:

```
%%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

In [35]:

```
%%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

In [36]:

```
%%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

In [37]:

```
%load_ext line_profiler
```

In [38]:

```
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...).