<jupyter_start><jupyter_text>Remise à niveau PythonToutes les séances de notre module seront présentées dans des documents au format iPython notebook, associé à l'extension `.ipynb`.  Ce format permet de joindre dans le même document:- du texte dans un format à balises simple, le format [markdown](http://markdowntutorial.com/),- du code à exécuter en [Python](https://docs.python.org/3.7/) (ici la version 3.7),- le résultat des commandes Python à la suite des cellules, que ce soit un résultat textuel ou une image. À noterLes cellules en jaune (sur le modèle suivant) sont des exercices à faire!Exercice: Faire les exercices  Quelques éléments de syntaxe PythonPython est un langage typé dynamiquement, on n’est donc pas obligé de déclarer le type des variables.  On pourra par exemple exécuter le programme suivant en plaçant le curseur dans la cellule et en tapant `Maj+Enter`.<jupyter_code>a = 3
b = 4.0
c = "a * b : "
print(c + str(a * b))
# On préférera cette notation
print("a * b : {}".format(a * b))<jupyter_output><empty_output><jupyter_text>Les opérateurs habituels sur les entiers et les flottants sont disponibles.  La boucle `for` permet de parcourir des objets itérables comme des listes, des tuples etc.  La fonction `range` permet de générer une séquence d’entiers :<jupyter_code>for x in range(2, 7):  # le 7 est exclu
    print(x)<jupyter_output><empty_output><jupyter_text>Important : vous remarquerez ici que l’indentation est importante en Python. Elle permet de définir les blocs.  La conditionnelle est définie classiquement :<jupyter_code>if a == 3:
    print("a = 3")
else:
    print("a = something else...")<jupyter_output><empty_output><jupyter_text>Le mot clé pour définir une fonction est `def`.La documentation d'une fonction peut-être écrite au format `__doctest__` en utilisant les triples guillemets.Les exemples d'utilisation indiqués dans la documentation `__doctest__` servent de test unitaire.<jupyter_code>def fact(n):
    """Renvoie la factorielle de n.
    
    >>> fact(6)
    720
    >>> [fact(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    
    Si n est négatif, une exception de type ValueError est levée.
    >>> fact(-1)
    Traceback (most recent call last):
        ...
    ValueError: n doit être un entier positif
    """
    res = 1
    if n < 0:
        raise ValueError("n doit être un entier positif")
    while n > 0:
        res = n * res
        n = n - 1
    return res

fact(6)<jupyter_output><empty_output><jupyter_text>Il est important de bien remplir la documentation, on pourrait en avoir besoin par la suite:<jupyter_code>help(fact)<jupyter_output><empty_output><jupyter_text>La fonction `doctest.testmod()` permet de tester toutes les fonctions d'un module donné (ici `__main__`):<jupyter_code>import doctest
doctest.testmod()<jupyter_output><empty_output><jupyter_text>Le format iPython notebook permet également de consulter la documentation dans une pop-up à part:<jupyter_code>?fact<jupyter_output><empty_output><jupyter_text>**Note :**Si vous rencontrez des difficultés de syntaxe Python, vous pouvez consulter:- la documentation officielle https://docs.python.org/3.7/,- ce [site](https://www.google.fr) bien pratique, ou mieux, [celui-ci](https://duckduckgo.com/).  Structures de donnéesPython propose un certain nombres de structures de données de base munies de facilités syntaxiques et algorithmiques.Règle no.1 Tout l'art de la programmation Python consiste à choisir (et adapter) les bonnes structures de données. Chaînes de caractèresLes chaînes de caractères sont écrites à l’aide de guillemets simples, doubles ou retriplées (multi-lignes).<jupyter_code>"hel" + 'lo'
a = """Hello
- dear students;
- dear all"""

print (a)
a[0]
a[2:4]
a[-1]
a[-8:]
a = "heLLo"
(a + ' ') * 2
len(a)
a.capitalize()
" hello ".strip()
"hello y’all".split()<jupyter_output><empty_output><jupyter_text>Listes<jupyter_code>a = [1, "deux", 3.0]
a[0]
len(a)
a.append(1)
a
a.count(1)
3 in a
a[1] = 2
a
a.sort()
a
a[1:3]<jupyter_output><empty_output><jupyter_text>On peut utiliser le mécanisme de *compréhension de liste* pour construire une liste facilement.  Par exemple, pourconstruire une liste contenant les carrés des valeurs comprises entre 1 et 5 :<jupyter_code>[i for i in range(5)]
# similar to list(i for i in range(5))
# similar to list(range(5))
[str(i) for i in range(5)]
[i**2 for i in range(5)]
[i**2 for i in range(5) if i&1 == 0] # smarter than i%2 == 0 
[x.upper() for x in "hello"] # even with strings<jupyter_output><empty_output><jupyter_text>Rappelons également le constructeur `sorted` qui crée une liste triée à partir d'une structure itérable ou d'un générateur:<jupyter_code>sorted(i**2 for i in range(-5, 5))<jupyter_output><empty_output><jupyter_text>Ensembles<jupyter_code>a = set()
a.add(1)
a.add(2)
a.add(3)
a.add(1)
a
a.intersection({2, 3, 4}) # set theory 
a.isdisjoint({4, 5})
[i == 2 for i in a] # list comprehension 
set([1, 2, 4, 1]) # conversion<jupyter_output><empty_output><jupyter_text>Dictionnaires<jupyter_code>d = dict()
d[1] = ('Ain', "Bourg-en-Bresse")
d[2] = ('Aisne', "Laon")
d
d[1]
d['1']
d
d.keys()
d.values()
d.items()
e = {}
for key, value in d.items():
    e[("%02d" % key)] = value
e # No order in dictionary in Python 3.5 (but ok in 3.6)
{("%02d" % key):  value for key, value in d.items()}<jupyter_output><empty_output><jupyter_text>Exercice: Construire et documenter une fonction qui calcule la liste des nombres premiers inférieurs ou égaux à n.<jupyter_code># write your code here!

# %load solutions/prime.py

", ".join(str(i) for i in prime(100))<jupyter_output><empty_output><jupyter_text>Exercice: Écrire un programme qui lit le fichier 00-lorem-ipsum.txt et y compte le nombre d’occurrences de chaque mot.On pourra partir du modèle suivant :```pythonwith open("data/00-lorem-ipsum.txt") as f:    print(f.readlines())```<jupyter_code># write your code here!

# %load solutions/lorem_ipsum.py

# %load solutions/lorem_ipsum_alt.py

# %load solutions/lorem_ipsum_alt2.py<jupyter_output><empty_output><jupyter_text>Rappel Tout l'art de la programmation Python consiste à choisir les bonnes structures de données. (voir par exemple la page sur les collections) La bibliothèque `numpy` de Python`numpy` est une extension du langage Python qui permet de manipuler des tableaux multi-dimensionnels et/ou des matrices.  Elle est souvent utilisée conjointement à l'extension `scipy` qui contient des outils relatifs:- aux intégrateurs numériques (`scipy.integrate`);- à l'algèbre linéaire (`scipy.linalg`);- etc.Des fonctionnalités simples illustrant le fonctionnement de `numpy` sont présentées ci-dessous.  En cas de souci, n'hésitez pas à vous référer à:- la documentation de `numpy` [ici](https://docs.scipy.org/doc/numpy/reference/);- la documentation de `scipy` [ici](https://docs.scipy.org/doc/scipy/reference/) pour ce que vous ne trouvez pas dans `numpy`.<jupyter_code>import numpy as np<jupyter_output><empty_output><jupyter_text>Types des données embarquéesOn peut créer un tableau `numpy` à partir d'une structure itérable (tableau, tuple, liste) Python.  La puissance de `numpy` vient du fait que tous les éléments du tableau sont forcés au même type (le moins disant).<jupyter_code># Définition d'un tableau à partir d'une liste
tableau = [2, 7.3, 4]
print('>>> Liste Python: type %s' % type(tableau))
for l in tableau:
    print('{%s, %s}' % (l, type(l)), end=" ")
print()
print()

# Création d'un tableau numpy
tableau = np.array(tableau)
print('>>> Tableau numpy: type %s' % type(tableau))
for l in tableau:
    print('{%s, %s}' % (l, type(l)), end=" ")
print()

print('On retrouve alors le type de chaque élément dans dtype: %s' % tableau.dtype)<jupyter_output><empty_output><jupyter_text>PerformanceOn reproche souvent à Python d'être lent à l'exécution. C'est dû à de nombreux paramètres, notamment la flexibilité du langage, les nombreuses vérifications faites à notre insu (Python ne présume de rien sur vos données), et surtout au **typage dynamique**.  Avec `numpy`, on connaît désormais une fois pour toute le type de chaque élément du tableau; de plus les opérations mathématiques sur ces tableaux sont alors codées en C (rapide!)Observez plutôt:<jupyter_code>tableau = [i for i in range(1, 10000000)]
array = np.array(tableau)
%timeit double = [x * 2 for x in tableau]
%timeit double = array * 2<jupyter_output><empty_output><jupyter_text>Vues sur des sous-ensembles du tableauIl est possible avec `numpy` de travailler sur des vues d'un tableau à $n$ dimensions qu'on aura construit.  On emploie ici le mot **vue** parce qu'une modification des données dans la vue modifie les données dans le tableau d'origine.Observons plutôt:<jupyter_code>tableau = np.array([[i+2*j for i in range(5)] for j in range(4)])
print(tableau)
# On affiche les lignes d'indices 0 à 1 (< 2), colonnes d'indices 2 à 3 (< 4)
sub = tableau[0:2, 2:4]
print(sub)
# L'absence d'indice signifie "début" ou "fin"
sub = tableau[:3, 2:]
print(sub)
# On modifie sub
sub *= 0
print(sub)<jupyter_output><empty_output><jupyter_text>Attention: voici pourquoi on parlait de vue !<jupyter_code>print(tableau)<jupyter_output><empty_output><jupyter_text>Opérations matricielles`numpy` donne accès aux opérations matricielles de base.<jupyter_code>a = np.array([[4,6,7,6]])
b = np.array([[i+j for i in range(5)] for j in range(4)])

print(a.shape, a, sep="\n")
print()
print(b.shape, b, sep="\n")
# Produit matriciel (ou vectoriel)
print(np.dot (a, b))<jupyter_output><empty_output><jupyter_text>Attention: Contrairement à Matlab, les opérateurs arithmétiques +, -, * sont des opérations terme à terme.Pour bien comprendre la différence:<jupyter_code>import numpy.linalg

a = np.array([[abs(i-j) for i in range(5)] for j in range(5)])
inv_a = numpy.linalg.inv(a) # L'inverse
print(a)
print(inv_a)
print(inv_a * a)
print("\nDiantre!!")
print(np.dot(inv_a, a))
print("\nC'est si facile de se faire avoir...")<jupyter_output><empty_output><jupyter_text>Note: Depuis Python 3.5, l'opérateur @ est utilisable pour la multiplication de matrice.<jupyter_code>print(inv_a @ a)<jupyter_output><empty_output><jupyter_text>La bibliothèque `matplotlib` de Python`matplotlib` propose un ensemble de commandes qui permettent d'afficher des données de manière graphique, d'afficher des lignes, de remplir des zones avec des couleurs, d'ajouter du texte, etc.L'instruction `%matplotlib inline` avant l'import permet de rediriger la sortie graphique vers le notebook.<jupyter_code>%matplotlib inline
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>L'instruction `plot` prend une série de points en abscisses et une série de points en ordonnées:<jupyter_code>plt.plot([1, 2, 3, 4], [1, 4, 9, 16])<jupyter_output><empty_output><jupyter_text>Il y a un style par défaut qui est choisi de manière automatique, mais il est possible de sélectionner:    - les couleurs;- le style des points de données;- la longueur des axes;- etc.<jupyter_code>plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro-')
plt.xlim(0, 6)
plt.ylim(0, 20)
plt.xlabel("Temps")
plt.ylabel("Argent")<jupyter_output><empty_output><jupyter_text>Il est recommandé d'utiliser `matplotlib` avec des tableaux `numpy`.<jupyter_code># échantillon à 200ms d'intervalle
t = np.arange(0., 5., 0.2)

# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')<jupyter_output><empty_output><jupyter_text>Enfin il est possible d'afficher plusieurs graphes côte à côte.  Notez que l'on peut également gérer la taille de la figure (bitmap) produite.<jupyter_code>fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(10, 10))

# Vous pouvez choisir des palettes de couleurs « jolies » avec des sites comme celui-ci :
# http://paletton.com/#uid=7000u0kllllaFw0g0qFqFg0w0aF

ax[0,0].plot(t, np.sin(t), '#aa3939')
ax[0,1].plot(t, np.cos(t), '#aa6c39')
ax[1,0].plot(t, np.tan(t), '#226666')
ax[1,1].plot(t, np.sqrt(t), '#2d882d')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12176751192568416, 'fraction_numerical': 0.018076826512678886, 'mean_word_length': 5.353003721424774, 'pattern_counts': {'":': 0, '<': 116, '<?xml version=': 0, '>': 129, 'https://': 6, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '28', 'dir': 'jupyter-structured-clean-dedup', 'id': '36311', 'license': 'MIT', 'n_tokens_mistral': 4554, 'n_tokens_neox': 4445, 'n_words': 1390, 'path': '00-python.ipynb', 'repo_name': 'poitou/back2python', 'size': '11949'}