#!/usr/bin/env python
# coding: utf-8
# # Introduction to scientific computing with Python
# *Maxime Sangnier*
#
# September, 2024
#
# ## Part 1: Basics of Python
# # Table of contents
# 1. [What is and why Python](#part1)
# - [Overview](#part1sec1)
# - [Comparison with other languages](#part1sec2)
# 1. [Practical points](#part2)
# - [Versions of Python](#part2sec1)
# - [Installation](#part2sec2)
# - [Usage](#part2sec3)
# - [Getting help](#part2sec4)
# 1. [An introduction to JupyterLab](#part3)
# - [Survival kit](#part3sec1)
# - [Useful features](#part3sec2)
# - [Text and equations](#part3sec3)
# 1. [Basics of the Python language](#part4)
# - [Numbers](#part4sec1)
# - [Data structures](#part4sec2)
# - [Conditional statements](#part4sec3)
# - [For loop](#part4sec4)
# - [While loop](#part4sec5)
# - [Function](#part4sec6)
# - [Modules](#part4sec7)
# 1. [Exercises](#part5)
# - [Exercise 1](#part5sec1)
# - [Exercise 2](#part5sec2)
# - [Exercise 3](#part5sec3)
# - [Exercise 4](#part5sec4)
# - [Exercise 5](#part5sec5)
# - [Exercise 6](#part5sec6)
# 1. [References](#part6)
#
# # What is and why Python
# ## Overview
# For scientific computing, one mainly needs three things:
# - workable toolboxes to applied mathematics, such as for sampling data, loading real-world data, processing, visualizing and analyzing them, solving equations, computing Fourier transformations, manipulating mathematical objects (polynomials, matrices, tensors), etc.
# - a perfect trade-off between power and easiness of the programming language (scientists are not developers);
# - efficient computations: code should execute quickly.
#
# As a consequence, a good language for scientific computing should gather these three aspects while providing also easy ways of communicating with collaborators (and students), as well as re-using and maintaining ad-hoc code produced by researchers.
# Python (in union with IPython and available packages such as Scipy) is a scripting language that is suitable for scientific computing regarding the previous requirements.
# The reasons of its popularity in the scientific community are that Python:
# - is free and open-source, making its development fast and the Python community excited;
# - comes with a rich collection of packages for scientific computing (not exhaustive yet);
# - can do much more than scientific computing (web server management, data bases, etc.). This is very useful since scientists interact with other domains of computer science;
# - is easy to learn, easy to write and easy to read;
# - can be structured and maintained efficiently (packages, modules, object oriented programming);
# - quite fast to execute thanks to a *precompilation*;
# - computationally demanding parts can be converted from Python to C ([Cython](http://cython.org/)).
#
# However, Python has also some drawbacks.
# In particular, it:
# - requires a more advanced knowledge in development (low-level commands, many types and containers) than other scientific languages such as Matlab and R, even though it is few compared to C;
# - comes with less packages than Matlab and R *in their own domain of research*;
# - is a language in progress. In particular, differences between Python 2 (still often used by operating systems) and Python 3 (used by researchers and scientists) can confuse the practitioner.
#
# ## Comparison with other languages
# ### R
# Advantages:
# - free and open-source;
# - very advanced features for statistics.
#
# Drawbacks:
# - the language is not so powerful;
# - dedicated to a single domain (statistics).
#
# ### Matlab
# Advantages:
# - rich collection of toolboxes for many scientific domains. Each toolbox contains implementations of numerous common algorithms;
# - by default linked with a fast linear algebra library ([MKL](https://software.intel.com/en-us/intel-mkl));
# - easy to learn, easy to write (not so easy to read).
#
# Drawbacks:
# - not free;
# - parallel computation not available in the base version;
# - language not so powerful.
#
# ### C, C++
# Advantages:
# - very fast execution (optimized compilers). Such a compiled language serves as a baseline for measuring running times of implementations;
# - common and very powerful language.
#
# Drawbacks:
# - may be difficult to learn (language addressed to developers);
# - painful usage: no interactivity, compilation mandatory.
#
# # Practical points
# ## Versions of Python
# There are currently two versions of Python: 2 and 3.
# The most commonly used version in the research and industrial community is now Python 3, even though Python 2 may still be the default choice on several operating systems.
#
# Let us remark that Python 3 is not backwards-compatible (see [differences between both versions](https://docs.python.org/3/whatsnew/3.0.html)).
# In practice, this means that a Python 2 script cannot always be interpreted by Python 3 (and respectively).
#
# In this tutorial, we focus on Python 3.
#
# ## Installation
# ### Windows and macOS
# A Python essential kit can be easily obtained by installing [Anaconda](https://docs.anaconda.com/anaconda/install/).
# It includes all you need for scientific computing.
# In addition, an alternative to Anaconda is [Enthought Canopy](https://store.enthought.com/downloads/#default).
# Even though it is a commercial product, Canopy is free for academic purposes.
# Finally, another workable option on Windows is [Python(x,y)](http://python-xy.github.io/).
#
# ### Linux
# On Debian and Ubuntu, a good option is to install Python and its package manager pip:
#
# sudo apt-get install python3 python3-pip spyder3
#
# Then, use pip to install the packages needed for scientific computing:
#
# sudo pip3 install jupyterlab numpy scipy matplotlib pillow scikit-learn seaborn pandas statsmodels cvxopt ipympl tensorflow
#
# If you have trouble installing a package, use instead:
#
# sudo apt-get install python3-[package_name]
#
# ## Usage
# Since Python is a scripting language, there are several ways to use it.
# The main ones are detailed below.
#
# ### Command line
# In a shell, execute:
#
# python3
#
# Then, start using it:
#
# a = 3.14
# print("hello world")
#
#
#
# Press CTRL+D or type:
#
# quit()
#
# to exit.
#
# If your code is written in a file *script.py*, you can run it from a shell with:
#
# python3 script.py
#
# or in Python with:
#
# execfile('script.py')
#
# ### IPython
# [IPython](http://ipython.org/) is a powerful interactive shell that comes with numerous useful features such as:
# - interactivity;
# - tab-completion;
# - command history;
# - object introspection;
# - special (magic) commands;
# - numbered input/output.
#
#
#
# One can use up and down arrows to access command history, tab for completion and %[command] for [magic commands](http://ipython.org/ipython-doc/3/interactive/magics.html) (or directly [command]).
# The most used are:
# - *cd [path]*: change the current working directory;
# - *history*: print input history;
# - *load [file]*: load code into the current frontend;
# - *matplotlib [gui]*: set up matplotlib to work interactively;
# - *pwd*: return the current working directory path;
# - *reset*: reset the namespace;
# - *run [file]*: run the script inside IPython as a program;
# - *whos*: print all interactive variables with extra information.
#
# To run a script, use:
#
# run script.py
#
# ### Spyder
# [Spyder](http://pythonhosted.org/spyder/) is a Matlab-like IDE that shows both an editor and a Python shell.
# It is an efficient tool for developing and testing codes.
#
#
# In particular feature, it enables to run only part of the script while keeping the previous results (variable states) in memory.
#
# To run a script, you can edit it and press F5.
# If you want to execute only a selection of it, select the part of interest and press F9.
#
# ### JupyterLab
# JupyterLab is a novel web-based IDE to produce easy-to-read reports with text, equations, code and results (called a notebook).
# This is what is used here!
#
#
#
# To launch it, write in a shell:
#
# jupyter-lab
#
# ## Getting help
# Python and related modules are very well documented in local (docstrings) and on the Internet (documentation on the Internet may be clearer and updated compared to the local one).
# To access the docstring, use *help*:
# In[1]:
help(print)
# In a notebook (or IPython), you can also use *?*:
# In[2]:
get_ipython().run_line_magic('pinfo', 'print')
# or SHIFT+TAB:
#
#
# Otherwise, you can search the net for an answer.
# The easiest way is to google your query: [vandermonde matrix python](https://www.google.fr/?client=ubuntu#channel=fs&q=vandermonde+matrix+python&gfe_rd=cr).
#
# Finally, you can go directly to a package documentation website.
# The following links provide the documentation of the packages used in this introduction (see next sections for details on the packages):
# - [official documentation](https://docs.python.org/3.5/tutorial/index.html);
# - [Scipy](http://docs.scipy.org/doc/scipy/reference/);
# - [Numpy](http://docs.scipy.org/doc/numpy/reference/);
# - [Statsmodels](http://www.statsmodels.org/stable/index.html);
# - [Scikit-learn](http://scikit-learn.org/stable/documentation.html).
#
# # An introduction to JupyterLab
# A notebook is a sequence of cells that may be of two types:
# - code: it enables to edit, execute and see the output of a small Python script;
# - markdown: if offers a way to write text and equations, as well as to import images.
#
# ## Survival kit
# A Jupyter notebook can be modified in two modes:
# - the command mode: we can add, delete and modify cells;
# - the edit mode: we can edit and run the script inside a cell.
#
# The forthcoming sections describe the most useful keyboard shortcuts.
# An exhaustive list of the available actions and shortcuts can be found by hitting the palette in the menu bar (or CTRL+SHIFT+C).
#
# It is highly recommended to experience these shortcuts on your own on the test cell below.
#
# ### Command shortcuts
# If you are in the edit mode, press ESC to enter the command mode.
#
# - To edit a cell (enter the edit mode), go to the desired cell and press ENTER.
# - To add a cell below, press B.
# - To add a cell above, press A.
# - To cut a cell, press X.
# - To copy a cell, press C.
# - To paste a cell below, press V.
# - To delete a cell, press DD.
# - To convert a cell to a markdown cell, press M.
# - To convert a cell to a code cell, press Y.
#
# ### Edit shortcuts
# If you are in the command mode, press ENTER to enter the edit mode.
#
# - To run a cell, press CTRL+ENTER.
# - To run a cell and select the one below, press SHIFT+ENTER.
# - To run a cell and add one below, press ALT+ENTER.
# - To apply autocompletion, press TAB at the end of the word. For example, write "pri", then press TAB.
# It gives "print".
# - To get an inline help, press SHIFT+TAB.
# - To indent several lines, select them and press TAB.
# - To dedent several lines, select them and press SHIFT+TAB.
# - To (un)comment a line (or several ones after selection), press CTRL+/.
# - To exit the edit mode, press ESC.
#
# In[3]:
print("This is a test cell")
# ## Useful features
# ### Code console
# You can open a code console by clicking the + button in the file browser and selecting the kernel.
# It enables you to run interactively small part of codes in a console, keeping the history in full view.
#
# Such a code console can also be opened or linked to a Python file.
# This is a way to run a part of code directly from a Python file.
# To do so, when editing this file, right-click in it and select *Create Console for Editor*. Then, select a single line or a block of code and send it to the code console by hitting SHIFT+ENTER.
#
# ### Mirrored output
# You can create a new synchronized view of a cell output by right-clicking a cell and hitting *Create New View for Output*.
# This view can then be moved to a separate tab.
# ## Text and equations
# By default, cells are for code but they can be converted to markdown cells (see the shortcut above).
# Markdown cells accept both [Markdown](https://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Working%20With%20Markdown%20Cells.html) (text formatting) and [$\LaTeX$](https://fr.wikipedia.org/wiki/Aide:Formules_TeX) (equations).
#
# ### General formatting
# Headings are defined with the symbol `#`:
#
# # First level
# ## Second level
# ### Third level
# #### Fourth level
# ##### Fifth level
#
# Even though paragraphs are defined automatically, you can force a line break with the code `
` or draw a horizontal line with `***`.
# ***
#
# ### Items, emphasis and colors
# Lists can be characterized by bullets, using `-`:
# - Bullet 1;
# - Bullet 2;
# or by numbers, using `1.`:
# 1. first item;
# 1. second item.
#
# Lists can also be nested thanks to indented symbols:
# - Bullet 1;
# 1. Item 1;
# 2. Item 2.
# - Bullet 2;
# 1. a single item.
#
# Emphasis can be done by **bold** (using `**text**`) or *italic* font (using `*text*`).
#
# Colors are defined by the code `text`.
# This has to be used carefully since it is more html code than Markdown scripting.
#
# ### Raw text and quoting
# You can use inline raw text for path and file names using the single quote symbol ` or indentation for blocs.
#
# For instance, this is a `# raw text`, while this is a raw bloc:
#
# raw text
# with multiple lines.
#
# You can also quote by using `>`:
#
# > A mathematician is a device for turning coffee into theorems.
# Paul Erdos
#
# ### Images and references
#
# You can add an image with `![text](URL)`:
#
# ![Picture by Vincent Delerm](http://photo.vincent-delerm.com/wp-content/uploads/2018/03/F1060019.jpg)
#
# > Picture by [Vincent Delerm](http://vincent-delerm.com/).
#
# You may also use `` to resize the image.
#
#
#
#
#
# References can be made to external links with `[text](URL)` and to internal labels with `[text](#label)`, where a tag `` has been put somewhere in the notebook.
#
# ### Equations
# Equations should be written in the $\LaTeX$ language, for which help is [available](https://fr.wikipedia.org/wiki/Aide:Formules_TeX).
# If you know the symbol and want to obtain the $\LaTeX$ command, you can [detexify](http://detexify.kirelabs.org/classify.html) it.
#
# You only need basics of $\LaTeX$:
# - Inline equation with `$equation$`: $x = \sqrt{2}$.
# - Centered equation with
#
#
# $$
# [equation]
# $$
#
# For instance:
# $$
# \int_0^1 1 \, dx = 1.
# $$
# - Multiple line equation with
#
#
# \begin{align}
# item &= item \\
# &= item.
# \end{align}
#
# For instance:
# \begin{align}
# \int_0^1 1 \, dx &= \frac{1}{2} \int_0^1 2 \, dx\\
# &= \frac{1}{2} \cdot 2 \\
# &= 1.
# \end{align}
# - Exponents and subscripts with `^` and `_`: $x_{i}^2$.
# - Summation and product with `\sum_{i=1}^n` and `\prod_{i=1}^n`: $\sum_{i=1}^n \prod_{j=1}^m x_{ij}$.
# - Real numbers with `\mathbb`: $\forall x \in \mathbb R^d$.
# - Calligraphic letters with `\mathcal`: $\mathcal N(0, 1)$.
# - Big parentheses with `\left(… \right)`: $\left( \frac{1}{n} \sum_{i=1}^n x_i \right)$.
# # Basics of the Python language
# ## Numbers
# ### Integer, floats and complex
# In[4]:
a = 1 # Integer
b = 1. # Float
c = 1.+2.j # Complex
# In[5]:
print(a)
type(a)
# In[6]:
print(b)
type(b)
# In[7]:
print(c)
type(c)
# In[8]:
print(c.real, c.imag)
# **Question**
#
# What are the result and the type of the following expressions:
# - 3+5.;
# - 16 * 4;
# - 8 / 2;
# - 2 * 10 / 2?
# In[ ]:
# Answer
# ### Booleans
# In[10]:
test = True
type(test)
# In[11]:
print(test and a == 1)
# In[12]:
print(test or a == 2)
# In[13]:
print(a != 1)
# In[14]:
print(test and not a == 1)
# A Boolean is a number:
# In[15]:
print(int(test)) # Cast Boolean to int
# In[16]:
print(test+1, test-1, test*3.2)
# **Question**
#
# Write an expression that returns:
# - $-1$ if $x \le -1$;
# - $1$ if $x \ge 1$;
# - x otherwise.
# In[ ]:
# Answer
# ### Swap variables in a concise manner
# In[18]:
abis = 3
print(a, abis)
a, abis = abis, a # Swap variables
print(a, abis)
# ### Operations
# Integers, floats, complex numbers and Booleans benefit from usual arithmetic operations.
#
# - Addition:
# In[19]:
print(a+2.3)
# - Subtraction:
# In[20]:
print(b-3)
# - Multiplication:
# In[21]:
print(c * 3)
# - Division:
# Beware: integers inherit from integer division in Python 2 and floating division in Python 3.
# In[22]:
print(1/2) # Returns 0 in Python 2 and 0.5 in Python 3
# A good practice is to use floating numbers:
# In[23]:
print(1./2)
print(a/2.)
# As well as explicit integer division (note that the result is of type *int*):
# In[24]:
a = 3//2
print(a, type(a))
# - Power:
# In[25]:
print(10**3)
# - Modulo:
# In[26]:
print(5%2)
# **Question**
#
# Given two integers $x$ and $y$ such that $x \ge y$, compute and print the quotient of the Euclidean division of $x$ by $y$.
# Do the same for the remainder (with two different expressions).
# In[ ]:
# Answer
# ## Data structures
# ### Strings
# Strings are immutable objects (they cannot be modified), that can be defined with simple, double or triple quotes.
# Simple and double quotes are equivalent:
# In[28]:
test1 = "Wilcoxon"
test2 = 'Chi2'
print(test1)
# … Except for quotes themselves.
# We write:
# In[29]:
print("\"" + " or " + '"')
# And:
# In[30]:
print('\'' + ' or ' + "'")
# Triple quotes allow to have several lines:
# In[31]:
txt1 = """This is
a Wilcoxon test"""
print(txt1)
# This is equivalent to using the special character *\\n*:
# In[32]:
txt2 = "This is\na Chi2 test"
print(txt2)
# #### Operations on strings
# Strings can be concatenated and repeated.
# In[33]:
print(test1 + ", p=" + str(0.1)) # Concatenation
print(test2 * 3) # Repetition
# We can change the case of a string.
# In[34]:
print(test1.lower())
print(test2.upper())
print("mr. ".capitalize() + "tickle".capitalize())
# Strings can be joined with a given separator.
# In[35]:
"-".join([test1, test2])
# **Question**
#
# Given the three variables below, produce the string:
#
# outcome ~ xx + yy + zz
# In[36]:
x = "Xx"
y = "YY"
z = "zZ"
# In[ ]:
# Answer
# #### String formatting and printing
# Python 3 provides an improved string formatting syntax, called f-string.
# In a nutshell, it is enough to put an *f* at the beginning and curly braces around expressions that should be replaced with their values.
# In[38]:
print(f"The first test is {test1}, while the second is {test2}.")
# Multiline f-strings are allowed:
# In[39]:
pval = 0.03
(f"The p-value for the {test1} test is: "
f"{pval}")
# Or escaping a return with *\*:
# In[40]:
f"The p-value for the {test1} test is: " \
f"{pval}"
# Using triple " will lead to:
# In[41]:
print(f"""The p-value for the {test1} test is:
{pval}""")
# Format specifiers: `{value:{width}.{precision}}` can be used, mainly for numbers:
# In[42]:
root = 0.123456789
print(f"The root is {root:.3f}.")
print(f"The root is {root:.2e}.")
print(f"The root is {root:10.2f}.")
# For a full support, see the [Format Specification Mini-Language](https://docs.python.org/3.6/library/string.html#formatspec).
# Strings can also be formatted in the old way:
# In[43]:
print("The first test is {}, while the second is {}.".format(test1, test2)) # With positional arguments
print("The first test is {1}, while the second is {0}.".format(test1, test2)) # With numbered arguments
print("The first test is {T1}, while the second is {T2}."
.format(T1=test1, T2=test2)) # With keywords arguments
print("The first test is {}, while the second is {T2}."
.format(test1, T2=test2))
print("The root is {}.".format(0.123456789)) # Numbers can be printed directly or converted
print("The root is {0:.3f}.".format(0.123456789))
print("The root is {0:.2e}.".format(0.123456789))
print("The root is {0:10.2f}.".format(0.123456789))
# In the expression *x:y.zA*,
# - *x* is the positioning parameter;
# - *y* is the (minimum) number of (potentially blank) numbers before .;
# - *z* is the (maximum) number of numbers after .;
# - *A* is the formatting letter:
# - f: float;
# - e: exponential notation;
# - d: integer.
# Strings can also be formatted in the very old way:
# In[44]:
print("An integer: %d" % 2)
print("A float: %f" % 0.123456789)
print("A short float: %0.2f" % 0.123456789)
print("A nice p-value: %0.1e" % 0.05)
print("%s is %d years old" % ("Robert", 64))
# When several arguments are given, *print* prints them all (the behavior is slightly different between Python 2 and Python 3).
# In[45]:
print("string 1", "string 2")
# One can also control the end character:
# In[46]:
print("Something to say", end="") # Python 3
#print("Something to say"), # Python 2
print("… and something else")
# **Remark:** objects can be printed simply by evaluating them:
# In[47]:
test1
# To remove this side effect, use a semicolon:
# In[48]:
test1;
# **Question**
#
# Given the three variables below, produce the sentence:
#
# The expression x+y*z = 3+1.4142*5 approximately equals 1.01e+01.
# In[49]:
x = 3
y = 2**0.5
z = 5
# In[ ]:
# Answer
# ### List
# A list is an ordered collection of items, that may have different types.
# A list is a mutable object and can thus be modified.
# In[51]:
l = [1, 3, 5, 7, "odd numbers"]
print(l)
# #### Indexing and slicing
# Indexing starts at 0.
# In[52]:
print("First item: ", l[0]) # First item
print("Last item: ", l[-1]) # Last item
# The slicing syntax is: `l[start:stop:stride]`.
# If some arguments are omitted, they are replaced by the *natural* ones (`start=0`, `stride=1`).
# In[53]:
print("First two items: ", l[:2]) # Equivalent to l[0:2]
print("Sublists: ", l[::2], l[1:3]) # First sublist equivalent to l[0::2]
print("Reverse: ", l[::-1])
# Note that, when slicing, the last element is not considered.
# In[54]:
print(l[0:2]) # Items numbered 0 and 1 (2 excluded)
print(l[2:]) # All items after number 2 included
print(l[2:-1]) # All items after number 2 included, last item exluced
# **Question**
#
# Given the list below (of size denoted by $n$), print its head (the first $n-1$ items) and its tail (the last $n-1$ items).
# In[55]:
x = [5, 3, 7, 8, 3, 4, 6]
# In[ ]:
# Answer
# #### Concatenation, extension and repetition
# In[57]:
l = l+[9] # Concatenation
print(l)
# In[58]:
l += [11] # Concatenation
print(l)
# Extension is an in-place operation.
# In[59]:
l.extend(["extension"]) # Extension
print(l)
# In[60]:
l *= 2 # Repetition
print(l)
# #### Adding, deleting and indexing an item
# In[61]:
l.append(13) # Add an item at the end of the list
print(l)
# In[62]:
del l[0] # Delete the first item
print(l)
# In[63]:
print("9 is in position", l.index(9))
# **Question**
#
# Move the first item of $x$ to the last position.
# In[64]:
x = [5, 3, 7, 8, 3, 4, 6]
# In[ ]:
# Answer
# #### Presence
# In[66]:
print("2 is in l: ", 2 in l)
print("3 is in l: ", 3 in l)
# #### Other operations
# In[67]:
print(len(l))
# In[68]:
l.reverse()
print(l)
# More details: use *help(list)* or *list?* in Ipython and Jupyter notebook.
# ### Tuple
# Roughly speaking, a tuple is an immutable list (it cannot be changed).
# It can be defined in two ways:
# In[69]:
t = (1, 2) # Definition with parentheses
type(t)
# In[70]:
t = 1, 2 # Light definition
print(t)
# In[71]:
t += ("three",) # Concatenation with a singleton
print(t)
print("Length: ", len(t)) # Length
# In[72]:
t0, t1, t2 = t # Unpacking
print(t0, t1, t2)
# ### Sets
# A set is an unordered collection of unique items.
# Usual mathematical operations (union, difference) can be performed.
# In[73]:
odd = set([1, 3, 5, 5])
even = set([2, 4])
type(odd)
print(odd)
# In[74]:
print(odd - set([1])) # Difference of sets
# In[75]:
print(odd | even) # Union of sets
# In[76]:
odd.add(2)
print(odd & even) # Intersection of sets
# In[77]:
print(odd ^ even) # Complementary of the intersection of sets
# **Question**
#
# Print items that are simultaneously in $x$ and $y$.
# In[78]:
x = [5, 3, 7, 8, 3, 4, 6]
y = [1, 9, 3, 7, 6, 2]
# In[ ]:
# Answer
# ### Dictionary
# A dictionary is a table key/value.
# Keys can be any immutable type (string, numbers, …).
# In[80]:
d = {'x': [[1, -0.5], [-2, 1]], 'y': [0, 1]} # Definition
print(d['x'])
# In[81]:
d[10] = "ten" # Add an item
print(d)
# In[82]:
# Print keys and values
print(d.keys())
print(d.values())
# In[83]:
"x" in d # Check if a key is in the dictionary
# **Question**
#
# Add a new item to the dictionary defined below, with key "sigma2" and value $sigma^2$.
# Set $mu$ to $0$ and print the final dictionary.
# In[84]:
db = {'mu': 3, 'sigma': 1.5}
# In[ ]:
# Answer
# ### Assignment operator
# In Python the assignment operator `=` is used for two purposes:
# - modifying attributes and items of mutable objects;
# - binding a name to a value.
#
# The last point means that **`=` does not make a copy** but creates a new alias for an already existing value.
#
# Examples:
# - Immutable objects:
# In[86]:
a = 1.1
b = a
b is a # two names, same data (in memory)
# In[87]:
print(id(a), id(b))
# In[88]:
a = "monday"
b = a
b is a # two names, same data (in memory)
# In[89]:
a = 3, 10
b = a
b is a # two names, same data (in memory)
# - Mutable objects:
# In[90]:
a = [1, 2]
b = a
b is a # two names, same data (in memory)
# In[91]:
b[-1] = 3
print(a)
print(b)
# Modifications appear on both a and b (same data).
# To make a copy, use:
# In[92]:
b = a.copy() # In Python 3
#b = a[:] # In Python 2
b is a
# In[93]:
b[-1] = 2
print(a)
print(b)
# *a* and *b* are two different objects.
#
# This is the same for dictionaries and sets:
# In[94]:
a = {"k": 1}
b = a
b is a
# In[95]:
b = a.copy()
b is a
# In[96]:
a = set([1, 2])
b = a
b is a
# In[97]:
b = a.copy()
b is a
# ## Conditional statements
# In Python, the blocks of the control flows are delimited by indentation.
# See for instance the use of `if/elif/else` statement.
# The comparisons are made with `==`, `!=`, `is`, `in`, `not`, `<`, `<=`, …
# In[98]:
h = 3.14 # Target
i = 3 # Guess
print("The target is", end=" ")
if h < i:
print("less than %d." % i)
elif h > i:
print("greater than %d." % i)
else:
print("exactly %d." % i)
# **Question**
#
# Write a conditional statement that:
# - add y to x if y is missing in x;
# - change y to -y in x otherwise.
# In[99]:
x = [4, 2, 7]
y = 6
# In[ ]:
# Answer
# ### Evaluating objects
# `if [object]` is false for
# - 0, 0.0 numbers;
# - empty structures;
# - False and None;
#
# and true otherwise.
#
# **Example**: checking if a list is empty.
# In[101]:
a = []
if not a:
print("Empty list.")
# **Question**
#
# Write a script that prints the result of the operation $x/y$ if $y \neq 0$ and "infinity" otherwise.
# In[ ]:
# Answer
# ### Assignment operator
# Conditional statements can also be used in union with the assignment operator:
# In[104]:
res = "greater or equal" if h >= i else "less"
print("The target is " + res + " than %d." % i)
# ## For loop
# Example of a for loop:
# In[105]:
for it in range(10):
print(it, end=" ")
else:
print("\nthe loop was not broken")
# Here, the optional `else` part is executed when the loop goes until the end.
# If the loop is broken (such as in the following example), the `else` part is not executed.
# Besides `break`, another interested keyword is `continue`. It skips the end of the current iteration.
# In[106]:
for it in range(10):
if it % 2 == 0: # Even numbers
continue
if it > 8:
break
print(it, end=" ")
else:
print("\nthe loop was not broken")
# A special feature of Python is to being able to iterate over the items of any sequence (range, list, tuple, dictionary, …).
#
# ### Range
# The syntax for the range function is:
# `range(stop)` or `rang(start, stop, step)` with third parameter optional.
# In[107]:
for it in range(5):
print(it, end=" ")
# In[108]:
for it in range(10, 20, 3):
print(it, end=" ")
# ### List
# In[109]:
for car in ['WV', 'BMW', 2016]:
print(car)
# ### Tuple
# In[110]:
for it in ("My favorite number", "is", 7):
print(it, end=" ")
# ### Dictionary
# In[111]:
conf = {"Name": "NIPS", "Date": 2016, "Location": "Barcelona"}
for key in conf:
print(key)
# In[112]:
for key, value in conf.items():
print(key, ":", value)
# In[113]:
for key, value in sorted(conf.items()):
print(key, ":", value)
# **Question**
#
# Compute and print the first 10 items of the sequence
# $$
# \begin{cases}
# u_0 &=& 0 \\
# u_{n+1} &=& 3u_n+2, \forall n \in \mathbb N.
# \end{cases}
# $$
# In[ ]:
# Answer
# ### Useful commands: enumerate and zip
# The enumerate command provides the number associated to each item while zip makes a collection of pairs of items from two lists.
# In[115]:
for i, val in enumerate(['WV', 'BMW', 2016]):
print(val, "(item %d)" % i)
# In[116]:
for x, y in zip([1, 0, -1, 0], [0, 1, 0, -1]):
print("x =", x, ", y =", y)
# **Question**
#
# Produce the following output:
#
# BBC was launched in 1992.
# CNN was launched in 1980.
# FOX NEWS was launched in 1996.
# In[117]:
x = ['BBC', 'CNN', 'FOX NEWS']
y = [1992, 1980, 1996]
# In[ ]:
# Answer
# ### Making a list in a concise manner
# (also called *list comprehensions*)
# In[119]:
l = [a**2 for a in range(10)]
print(l)
# In[120]:
l = [a**2 for a in range(10) if a%2 == 0]
print(l)
# **Question**
#
# Build the list of square root values of items in $x$.
# In[121]:
x = [1.5, 41., .413, 5.13, 3.4, 8.74]
# In[ ]:
# Answer
# ## While loop
# Akin to the `for` statement, the `while` loop benefits from the keywords `break`, `continue` and `else`.
# In[123]:
v = 0
while v**2 < 10:
v += .01
else:
print("the loop was not broken")
print("sqrt(10) is approximately {0:.2f}".format(v-.01))
# **Question**
#
# Write a loop that finds the floor value of $x$.
# In[124]:
x = 12.3
# In[ ]:
# Answer
# ## Function
# A function is defined with the keyword `def`:
# In[126]:
def test():
"""Test function
This function prints \"This is a test\"
"""
print("This is a test")
test()
# The documentation string (or docstring) of the function appears when one uses *help* or *?*:
# In[127]:
get_ipython().run_line_magic('pinfo', 'test')
# Note that a function is an object like an integer or a list.
# In[128]:
type(test)
# In[129]:
f = test
f()
# Functions can also return a result:
# In[130]:
def add(a, b):
return a+b
print(add(1, 2))
# ### Parameters
# A function can have two kinds of parameters:
# - mandatory parameters;
# - optional parameters.
# Optional parameters always come after mandatory ones and are defined with default values.
# In the following example, *name* is mandatory, while *age* and *job* are optional.
# In[131]:
def identity(name, age=39, job="trader"):
print("My name is %s." % name)
print("I am a %s and I am %d years old." % (job, age))
print()
identity("Picasso", 40, "painter")
identity("Kerviel")
# In this example, the function is called with positional arguments.
# This means that the order of the parameters should be the same as in the definition.
# However, parameters can also be passed with their keyword.
# In this case, the order is not significant.
# In[132]:
identity(job="musician", name="Armstrong", age=42)
# When both techniques are mixed, positional arguments always come before keyword arguments.
# In[133]:
identity("Bach", job="composer")
# One can check if a parameter has been passed using the neutral value *None*:
# In[134]:
def init_f(a, b=None):
if b is None:
b = a + 1
return a, b
print(init_f(1, 3))
print(init_f(1))
# **Question**
#
# Write a function, called *sq*, that has two arguments: *x* and *gamma* (default value: 1) and that returns $gamma ~\times~ x^2$.
# In[ ]:
# Answer
# ### Packing and unpacking arguments
# Parameters can be packed in a tuple or a dictionary and unpacked when calling a function.
# In[136]:
tuple_arg = ("Diniz", 38, "race walker")
identity(*tuple_arg)
# In[137]:
dic_arg = {"job": "judoka", "age": 27, "name": "Riner"}
identity(**dic_arg)
# Respectively, a function can be defined with packed arguments.
# This makes it possible to allow an arbitrary number of arguments.
# In[138]:
def student(level="B1", *args, **kwargs):
identity(**kwargs)
print("I am also a student (level {}).".format(level))
print("I study ", end="")
for it in args[:-1]:
print(it, end=", ")
print("and ", args[-1], ".")
student("M2", "Statistics", "Machine learning", name="John", job="violonist", age=22)
# **Question**
#
# Write a function that prints the number of arguments and each of its arguments on a separate line.
# In[ ]:
# Answer
# ### Modifying parameters
# Like in other languages, a function can modifier some parameters.
# The rule is:
# - if an argument is mutable, then it can be modified inside a function;
# - if an argument is immutable, it cannot be modified.
# In[140]:
def repeat(*args):
for it in args:
it *= 2
a = (1, 2) # Immutable
b = [1, 2] # Mutable
repeat(a, b)
print(a, b)
# ### Lambda expressions
# A lambda function is a small anonymous function, that is restricted to a single expression.
# It is generally used as an argument to or an output from a usual function.
# In[141]:
def apply(x, fun=lambda x: x):
return [fun(item) for item in x]
def arithmetic_progression(a=0, b=1):
return lambda n: a + n*b
# In[142]:
f = arithmetic_progression(1, 2)
print(apply(range(10), f))
# **Question**
#
# Given the function $sq$ (created just before), define a lambda function that returns $4x^2$.
# In[ ]:
# Answer
# ### Methods
# Since Python is an object-oriented programming language, objects always come with functions linked to them.
# These functions are called *methods* and generally modify directly the variable they are called with.
# For instance:
# In[144]:
l = [1, 3, 5]
l.reverse() # l is reversed (thus modified)
print(l)
# To know the methods associated to an object (here the list `l`), write:
#
# l.
#
# then press TAB.
# To obtain an inline help concerning a method (here list.reverse), write:
#
# l.reverse
#
# then press SHIFT+TAB.
# **Question**
#
# Sort the previous list.
# In[ ]:
# Answer
# ## Modules
# ### Loading modules
# Up to now, we only experienced internal features of Python.
# Yet, our interest will next focus on external tools.
# These tools are stored in modules, which can be loaded in several manners.
# In[146]:
import sys # Load the sys module
import numpy as np # Load the numpy module with the name np
from scipy import stats # Load the stats submodule from the scipy module
from scipy.linalg import inv # Load the matrix inversion function from a submodule
from statsmodels import * # Import evrything from the statsmodels module
# The last manner is not recommended since it can create name clashes between modules and makes the code harder to read and to understand.
#
# To know the content of a module, use `dir`:
# In[147]:
dir(sys)[-10:]
# Now, modules content can be accessed in the following manner:
# In[148]:
print(np.pi)
# **Question**
#
# Compute $e^{-1}$.
# In[ ]:
# Answer
# ### Handling the path and creating modules
# To be found by Python, modules should be stored in a directory of *sys.path*:
# In[150]:
sys.path
# If your module is stored in another directory, add it to the Python path:
# In[151]:
sys.path.append('./aux/')
# Now, to create a module in the directory `./aux/`, store the functions and variables definitions (see cell below) in a Python file, named `my_module.py` (for example using Spyder).
# Then, you can write:
#
# %load aux/my_module.py
#
# to know the content of the file `aux/my_module.py`.
# In[152]:
# %load aux/my_module.py
"""
Test module.
Author: Maxime Sangnier
"""
def f1():
print("Function 1")
def f2():
print("Function 2")
pi = 3.14
if __name__ == "__main__":
f1() # Example of using this module
# In[153]:
import my_module as m
get_ipython().run_line_magic('pinfo', 'm')
# In[154]:
m.f2()
# In Python 2, if you modify your module, reload it this way:
#
# reload(m)
#
# otherwise changes won't be considered.
# The last part of the module is executed when `my_module.py` is run as a script:
# In[155]:
run aux/my_module.py
# # Exercises
# ## Exercise 1
#
# In order to practice Markdown and $\LaTeX$, create a separate notebook and try to reproduce the output provided below.
# Some remarks:
# - you have to use headings;
# - you have to write inline $\LaTeX$ formulas;
# - you have to produce centered $\LaTeX$ equations;
# - the image is inserted with its URL: [https://upload.wikimedia.org/wikipedia/commons/7/74/Normal_Distribution_PDF.svg](https://upload.wikimedia.org/wikipedia/commons/7/74/Normal_Distribution_PDF.svg);
# - you have to alternate Markdown and code cells;
# - you have to write centered and and aligned sequences of formulas in $\LaTeX$.
#
# Then, export your new notebook to an `html` file and send it to this [remote repository](https://www.dropbox.com/request/mJw1HxVjOkMSAqD5WHZD).
#
# The result to reproduce:
#
#
#
# ## Exercise 2
# Create the following list with loops:
#
# [['car', 0, 1, 4, 9, 16],
# ['bus', 1, 4, 9, 16, 25],
# ['train', 4, 9, 16, 25, 36]]
#
# In[ ]:
# Answer
# Create a script that prints this list in the following manner:
#
# car 0 1 4 9 16
# bus 1 4 9 16 25
# train 4 9 16 25 36
#
# In[ ]:
# Answer
# ## Exercise 3
# Create a function that returns the sum log of all its parameters (use the `log` function from the `math` module).
#
# In[ ]:
# Answer
# ## Exercise 4
# For the following list of dictionaries, write a script that adds a field `registrations` which is twice the number of accepted papers.
# In[160]:
confs = [{"Name": "NIPS", "Date": 2016, "Location": "Barcelona", "acc_papers": 300},
{"Name": "ICML", "Date": 2016, "Location": "New York City", "acc_papers": 450},
{"Name": "ICML", "Date": 2015, "Location": "Lille", "acc_papers": 250},
{"Name": "AISTATS", "Date": 2016, "Location": "Cadiz", "acc_papers": 100}]
# In[ ]:
# Answer
# ## Exercise 5
# Write a function `append`, that produces the following results:
#
# l = [1]
# append(l, 5)
# print(l)
# [1, 5]
# append(l, "-1")
# print(l)
# [1, 5, '-1']
# append(l)
# print(l)
# []
#
# In[ ]:
# Answer
# ## Exercise 6
# With the code editor, create a module, that contains two functions:
# - fibonacci(n): computes the $n^{th}$ [Fibonacci number](https://en.wikipedia.org/wiki/Fibonacci_number);
# - wallis(n): computes an approximation of $\pi$ using the [Wallis product](https://en.wikipedia.org/wiki/Wallis_product).
#
# Write a script, that uses these two functions.
# In[ ]:
# Answer
# # References
# - [JupyterLab documentation](https://jupyterlab.readthedocs.io/en/latest/index.html).
# - [Official documentation](https://docs.python.org/3/tutorial/index.html).
# - [Scipy lecture notes](http://www.scipy-lectures.org/index.html).
# - [Allen Downey's book](http://www.greenteapress.com/thinkpython/thinkpython.pdf).