Examples

Automatisation

  1. Clone many folders in one row
  2. Collect email addresses from mails in an inbox folder)

Clone many folders in one row

eleves = "project1;project2;..."
root = r"destination"

for el in eleves.split(";"):
    cl = el.lower().replace(".","-")
    fold = os.path.join(root, el)
    if not os.path.exists(fold):
        print("clone", el)
        url = "https://<gitlab>/<group>/{0}.git".format(cl)
        git_clone(  fold, url,user=user,password=password, init=False,fLOG=print)

(entrée originale : git_helper.py:docstring of ensae_teaching_cs.automation_students.git_helper.git_clone, line 32)

Collect email addresses from mails in an inbox folder)

from ensae_teaching_cs.automation_students import grab_addresses
from pymmails import MailBoxImap

user = "xavier.dupre"
pwd = "***"
server = "imap.gmail.com"
mailfolder = ["ensae/ENSAE_2016", "ensae/ensae_interro_2015"]
date = "1-Dec-2015"

box = MailBoxImap(user, pwd, server, ssl=True, fLOG=fLOG)
box.login()
emails = grab_addresses(box, mailfolder, date, fLOG=fLOG)
box.logout()

(entrée originale : mail_helper.py:docstring of ensae_teaching_cs.automation_students.mail_helper.grab_addresses, line 13)

Computer Science, Machine Learning

  1. DictVectorizer or CategoriesToIntegers

DictVectorizer or CategoriesToIntegers

Example which transforms text into integers:

Code

import pandas
from ensae_teaching_cs.ml import CategoriesToIntegers
df = pandas.DataFrame( [{"cat": "a"}, {"cat": "b"}] )
trans = CategoriesToIntegers()
trans.fit(df)
newdf = trans.transform(df)
print(newdf)

Sortie

       cat=a  cat=b
    0    1.0    NaN
    1    NaN    1.0

(entrée originale : categories_to_integers.py:docstring of ensae_teaching_cs.ml.categories_to_integers.CategoriesToIntegers, line 9)

  1. algorithme de Arrow-Hurwicz
  2. opérations avec numpy.matrix
  3. solver.cp de cvxopt

algorithme de Arrow-Hurwicz

On résoud le problème suivant avec l’algorithme de Arrow-Hurwicz.

\left\{ \begin{array}{l} \min_{x,y} \left \{ x^2 + y^2 - xy + y \right \}  \\ sous \; contrainte \; x + 2y = 1 \end{array}\right.

Qui s’implémente à l’aide de la fonction suivante :

def f_df(X) :
    x,y = X
    f = x**2 + y**2 - x*y + y
    d = [ x*2 - y, y*2 - x + 1  ]
    return f, d

def contrainte(X) :
    x,y = X
    f = x+2*y-1
    d = [ 1,2]
    return f, d

X0  = [ random.random(),random.random() ]
p0  = random.random()
sol = Arrow_Hurwicz(f_df, contrainte, X0, p0, do_print=False)

(entrée originale : optimisation_contrainte.py:docstring of ensae_teaching_cs.td_1a.optimisation_contrainte.exercice_particulier2, line 1)

opérations avec numpy.matrix

Voici quelques écritures classiques avec le module numpy.

import numpy as np
mat = np.matrix ( [[1,2],[3,4]] ) # crée une matrice 2*2
s   = mat.shape           # égale à (nombre de lignes, nombre de colonnes)
l   = mat [0,:]           # retourne la première ligne
c   = mat [:,0]           # retourne la première colonne
iv  = mat.I               # inverse la matrice
mat [:,0] = mat [:,1]     # la première ligne est égale à la seconde
o   = np.ones ( (10,10) ) # crée un matrice de 1 10x10
d   = np.diag (mat)       # extrait la diagonale d'une matrice
dd  = np.matrix (d)       # transforme d en matrice
t   = mat.transpose ()    # obtient la transposée
e   = mat [0,0]           # obtient de première élément
k   = mat * mat           # produit matriciel
k   = mat @ mat           # produit matriciel à partir de Python 3.5
m   = mat * 4             # multiplie la matrice par 4
mx  = np.max (mat [0,:])  # obtient le maximum de la première ligne
s   = np.sum (mat [0,:])  # somme de la première ligne


mat = np.diagflat ( np.ones ( (1,4) ) )
print (mat)  # matrice diagonale
t   =  mat == 0
print (t)    # matrice de booléens
mat [ mat == 0 ] = 4
print (mat)  # ...
print (iv)  # ...

(entrée originale : numpys.py:docstring of ensae_teaching_cs.td_1a.numpys.numpy_matrix2list, line 6)

solver.cp de cvxopt

On résoud le problème suivant avec cvxopt :

\left\{ \begin{array}{l} \min_{x,y} \left \{ x^2 + y^2 - xy + y \right \}  \\ sous \; contrainte \; x + 2y = 1 \end{array}\right.

Qui s’implémente à l’aide de la fonction suivante :

def f_df_H(x=None,z=None) :
    if x is None :
        # cas 1
        x0 = matrix ( [[ random.random(), random.random() ]])
        return 0,x0
    f = x[0]**2 + x[1]**2 - x[0]*x[1] + x[1]
    d = matrix ( [ x[0]*2 - x[1], x[1]*2 - x[0] + 1 ] ).T
    h = matrix ( [ [ 2.0, -1.0], [-1.0, 2.0] ])
    if z is None:
        # cas 2
        return  f, d
    else :
        # cas 3
        return f, d, h

solvers.options['show_progress'] = False
A = matrix([ [ 1.0, 2.0 ] ]).trans()
b = matrix ( [[ 1.0] ] )
sol = solvers.cp ( f_df_H, A = A, b = b)

(entrée originale : optimisation_contrainte.py:docstring of ensae_teaching_cs.td_1a.optimisation_contrainte.exercice_particulier1, line 1)

  1. Enregistrer plusieurs DataFrame dans un seul fichier Excel ?

Enregistrer plusieurs DataFrame dans un seul fichier Excel ?

Le code suivant enregistre deux DataFrame dans un seul fichier Excel.

import pandas
writer = pandas.ExcelWriter('example.xlsx')
df1.to_excel(writer, 'Data 0')
df2.to_excel(writer, 'Data 1')
write.save()

Ou en utilisant cette fonction :

dfs2excel( { 'Data 0':df1, 'Data 1':df2 }, "example.xlsx" )

(entrée originale : session_pandas.py:docstring of ensae_teaching_cs.td_2a.session_pandas.dfs2excel, line 7)

Geek

  1. Convertir le notebook en cours au format HTML
  2. Utiliser R depuis un notebook
  3. Utiliser une DLL implémentée en C#

Convertir le notebook en cours au format HTML

C’est l’objet du notebook Convert a notebook into a document.

(entrée originale : faq_jupyter.py:docstring of ensae_teaching_cs.faq.faq_jupyter.jupyter_convert_notebooks, line 1)

Utiliser R depuis un notebook

C’est l’objet des deux notebooks Python autres langages (R, Cython) et la correction.

(entrée originale : faq_jupyter.py:docstring of ensae_teaching_cs.faq.faq_jupyter.r_and_notebook, line 4)

Utiliser une DLL implémentée en C#

Le code de la DLL est le suivant. Il a été compilé sous forme de DLL.

namespace ENSAE.Voice
{
    public static class Speech
    {
        public static void VocalSynthesis(string text, string culture, string filename, string voice)
        {
            SpeechSynthesizer synth = new SpeechSynthesizer();

            synth.SelectVoiceByHints(VoiceGender.Neutral, VoiceAge.NotSet, 1, new CultureInfo(culture));

            if (!string.IsNullOrEmpty(filename))
                synth.SetOutputToWaveFile(filename);
            if (!string.IsNullOrEmpty(voice))
                synth.SelectVoice(voice);

            synth.Speak(text);
        }
    }
}

Pour l’utiliser, il faut utiliser l’instruction :

from ensae_teaching_cs.pythonnet import clr
from clr import AddReference
AddReference("ENSAE.Voice")

Si le programme répond qu’il ne trouve pas le fichier, il suffit d’inclure de la répertoire où se trouve la DLL dans la liste sys.path. Ensuite on écrit simplement :

from ENSAE.Voice import Speech
Speech.VocalSynthesis(text, lang, voice, filename)

Il faut voir le notebook Python et C Sharp.

(entrée originale : __init__.py:docstring of ensae_teaching_cs.pythonnet.vocal_synthesis, line 8)

Python, constructions classiques

  1. calcul d'une somme
  2. calcul de la somme des dix premiers entiers au carré
  3. comptage
  4. conversion d'un vecteur en une matrice
  5. conversion d'une chaîne de caractère en datetime
  6. conversion d'une chaîne de caractère en matrice
  7. conversion d'une matrice en chaîne de caractères
  8. conversion d'une matrice en un vecteur
  9. fonction comme paramètre
  10. minimum avec position
  11. recherche avec index
  12. recherche dichotomique
  13. tri, garder les positions initiales

calcul d’une somme

Le calcul d’une somme fait toujours intervenir une boucle car le langage Python ne peut faire des additions qu’avec deux nombres. Le schéma est toujours le même : initialisation et boucle.

li = [ 0, 434, 43, 6456 ]
s  = 0                       # initialisation
for l in li :                # boucle
    s += l                   # addition

Ce code est équivalent à la fonction sum. Dans ce cas où la somme intègre le résultat d’une fonction et non les éléments d’une liste, il faudrait écrire :

def fonction (x) : return x*x
s  = 0
for l in li : s += fonction (l)

Et ces deux lignes pourraient être résumées en une seule grâce à l’une de ces instructions :

s = sum ( [fonction (l) for l in li] )
s = sum ( fonction (l) for l in li )
s = sum ( map (fonction, li) )

L’avantage des deux dernières instructions est qu’elles évitent la création d’une liste intermédiaire, c’est un point à prendre en compte si la liste sur laquelle opère la somme est volumineuse.

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.somme, line 6)

calcul de la somme des dix premiers entiers au carré

Ce calcul simple peut s’écrire de diffèrentes manières.

s = 0
for i in range(1,11):
    s += i**2

D’une façon abrégée :

s = sum ( [ i**2 for i in range(1,11) ] )

(entrée originale : classiques.py:docstring of ensae_teaching_cs.td_1a.classiques.dix_entiers_carre, line 18)

comptage

On souhaite ici compter le nombre d’occurrences de chaque élément d’un tableau. Par exemple, on pourrait connaître par ce moyen la popularité d’un mot dans un discours politique ou l’étendue du vocabulaire utilisé. L’exemple suivant compte les mots d’une liste de mots.

li = ["un", "deux", "un", "trois"]
d  = { }
for l in li :
    if l not in d : d [l] = 1
    else : d [l] += 1
print (d)   # affiche {'un': 2, 'trois': 1, 'deux': 1}

La structure la plus appropriée ici est un dictionnaire puisqu’on cherche à associer une valeur à un élément d’une liste qui peut être de tout type. Si la liste contient des éléments de type modifiable comme une liste, il faudrait convertir ceux-ci en un type immuable comme une chaîne de caractères. L’exemple suivant illustre ce cas en comptant les occurrences des lignes d’une matrice.

mat = [ [1,1,1], [2,2,2], [1,1,1]]
d  = { }
for l in mat :
    k = str (l)    # k = tuple (l) lorsque cela est possible
    if k not in d : d [k] = 1
    else : d [k] += 1
print (d)   # affiche {'[1, 1, 1]': 2, '[2, 2, 2]': 1}

Les listes ne peuvent pas être les clés du dictionnaire : Why Lists Can’t Be Dictionary Keys.

On peut également vouloir non pas compter le nombre d’occurrence mais mémoriser les positions des éléments tous identiques. On doit utiliser un dictionnaire de listes :

li = ["un", "deux", "un", "trois"]
d  = { }
for i,v in enumerate (li) :
    if v not in d : d [v] = [ i ]
    else : d [v].append (i)
print (d)   # affiche {'un': [0, 2], 'trois': [3], 'deux': [1]}

S’il suffit juste compter, l’écriture la plus simple est :

for x in li : r[x] = r.get(x,0)+1

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.compte, line 6)

conversion d’un vecteur en une matrice

Dans un langage comme le C++, il arrive fréquemment qu’une matrice ne soit pas représentée par une liste de listes mais par une seule liste car cette représentation est plus efficace. Il faut donc convertir un indice en deux indices ligne et colonne. Il faut bien sûr que le nombre de colonnes sur chaque ligne soit constant. Le premier programme convertit une liste de listes en une seule liste.

vect = [0, 1, 2, 3, 4, 5]
mat = [ vect[i*ncol: (i+1)*ncol] for i in range(0,len(vect)//ncol) ]

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.vect2mat, line 8)

conversion d’une chaîne de caractère en datetime

C’est le genre de fonction qu’on n’utilise pas souvent mais qu’on peine à retrouver lorsqu’on en a besoin. Il faut utiliser la fonction strftime.

import datetime
dt = datetime.datetime.strptime ("16/01/2014", "%d/%m/%Y")

(entrée originale : classiques.py:docstring of ensae_teaching_cs.td_1a.classiques.str2date, line 7)

conversion d’une chaîne de caractère en matrice

Les quelques lignes qui suivent permettent de décomposer une chaîne de caractères en matrice. Chaque ligne et chaque colonne sont séparées par des séparateurs différents. Ce procédé intervient souvent lorsqu’on récupère des informations depuis un fichier texte lui-même provenant d’un tableur.

s       = "case11;case12;case13|case21;case22;case23"
# décomposition en matrice
ligne   = s.split ("|")                     # lignes
mat     = [ l.split (";") for l in ligne ]  # colonnes

Comme cette opération est très fréquente lorsqu’on travaille avec les données, on ne l’implémente plus soi-même. On préfère utiliser un module comme pandas qui est plus robuste et considère plus de cas. Pour écrire, utilise la méthode to_csv, pour lire, la fonction read_csv. On peut également directement enregistrer au format Excel read_excel et écrire dans ce même format to_excel.

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.text2mat, line 9)

conversion d’une matrice en chaîne de caractères

mat     = [['case11', 'case12', 'case13'], ['case21', 'case22', 'case23']]
ligne   = [ ";".join (l) for l in mat ]     # colonnes
s       = "|".join (ligne)                  # lignes

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.mat2text, line 9)

conversion d’une matrice en un vecteur

Dans un langage comme le C++, il arrive fréquemment qu’une matrice ne soit pas représentée par une liste de listes mais par une seule liste car cette représentation est plus efficace. Il faut donc convertir un indice en deux indices ligne et colonne. Il faut bien sûr que le nombre de colonnes sur chaque ligne soit constant. Le premier programme convertit une liste de listes en une seule liste.

mat = [[0,1,2],[3,4,5]]
lin = [ i * len (mat [i]) + j \
            for i in range (0, len (mat)) \
            for j in range (0, len (mat [i])) ]

Vous pouvez aussi utiliser des fonctions telles reduce.

lin = reduce ( lambda x,y: x+y, mat )

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.mat2vect, line 7)

fonction comme paramètre

Une fonction peut aussi recevoir en paramètre une autre fonction. L’exemple suivant inclut la fonction calcul_n_valeur qui prend comme paramètres l et f. Cette fonction calcule pour toutes les valeurs x de la liste l la valeur f(x). fonction_carre ou fonction_cube sont passées en paramètres à la fonction calcul_n_valeur qui les exécute.

def fonction_carre(x) : return x*x def fonction_cube (x) : return x*x*x

def calcul_n_valeur (l,f):
res = [ f(i) for i in l ] return res

l = [0,1,2,3] print l # affiche [0, 1, 2, 3]

l1 = calcul_n_valeur (l, fonction_carre) print l1 # affiche [0, 1, 4, 9]

l2 = calcul_n_valeur (l, fonction_cube) print l2 # affiche [0, 1, 8, 27]

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.integrale, line 9)

minimum avec position

La fonction min retourne le minium d’un tableau mais pas sa position. Le premier réflexe est alors de recoder le parcours de la liste tout en conservant la position du minimum.

li = [ 0, 434, 43, 6436, 5 ]
m  = 0
for i in range (0, len (li)) :
    if li [m] < li [i] : m = i

Mais il existe une astuce pour obtenir la position sans avoir à le reprogrammer.

k = [ (v,i) for i,v in enumerate (li) ]
m = min(k)

La fonction min choisit l’élément minimum d’un tableau dont les éléments sont des couples (élément du premier tableau, sa position). Le minimum est choisi en comparant les éléments, et la position départegera les exaequo.

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.minindex, line 7)

recherche avec index

Lorsqu’on cherche un élément dans un tableau, on cherche plus souvent sa position que le fait que le tableau contient cet élément.

def recherche (li, c) :
    for i,v in enumerate (li) :
        if v == c : return i
    return -1
li = [ 45, 32, 43, 56 ]
print (recherche (li, 43))  # affiche 2

En python, il existe un fonction simple qui permet de faire ça :

print (li.index (43))  # affiche 2

Lorsque l’élément n’y est pas, on retourne souvent la position -1 qui ne peut être prise par aucun élément :

if c in li : return li.index(c)
else: return -1

Même si ce bout de code parcourt deux fois le tableau (une fois déterminer sa présence, une seconde fois pour sa position), ce code est souvent plus rapide que la première version et la probabilité d’y faire une erreur plus faible.

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.recherche, line 7)

recherche dichotomique

La recherche dichotomique est plus rapide qu’une recherche classique mais elle suppose que celle-ci s’effectue dans un ensemble trié. L’idée est de couper en deux l’intervalle de recherche à chaque itération. Comme l’ensemble est trié, en comparant l’élément cherché à l’élément central, on peut éliminer une partie de l’ensemble : la moitié inférieure ou supérieure.

def recherche_dichotomique (li, c) :
    a,b = 0, len (li)-1
    while a <= b :
        m = (a+b)/2
        if   c == li [m] : return m
        elif c <  li [m] : b = m-1   # partie supérieure éliminée
        else             : a = m+1   # partie inférieure éliminée
    return -1  # élément non trouvé

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.recherche_dichotomique, line 7)

tri, garder les positions initiales

Le tri est une opération fréquente. On n’a pas toujours le temps de programmer le tri le plus efficace comme un tri quicksort et un tri plus simple suffit la plupart du temps. Le tri suivant consiste à recherche le plus petit élément puis à échanger sa place avec le premier élément du tableau du tableau. On recommence la même procédure à partir de la seconde position, puis la troisième et ainsi de suite jusqu’à la fin du tableau.

for i in xrange (0, len (li)) :
    # recherche du minimum entre i et len (li) exclu
    pos = i
    for j in range (i+1, len (li)) :
        if li [j] < li [pos] : pos = j
    # échange
    ech      = li [pos]
    li [pos] = li [i]
    li [i]   = ech

La fonction sorted trie également une liste mais selon un algorithme plus efficace que celui-ci (voir Timsort). On est parfois amené à reprogrammer un tri parce qu’on veut conserver la position des éléments dans le tableau non trié. Cela arrive quand on souhaite trier un tableau et appliquer la même transformation à un second tableau. Il est toujours préférable de ne pas reprogrammer un tri (moins d’erreur). Il suffit d’applicer la même idée que pour la fonction minindex.

tab = ["zero", "un", "deux"]                       # tableau à trier
pos = sorted(  (t,i) for i,t in enumerate(tab)  )  # tableau de couples
print (pos)                # affiche [('deux', 2), ('un', 1), ('zero', 0)]

Si cette écriture est trop succincte, on peut la décomposer en :

tab = ["zero", "un", "deux"]
tab_position = [ (t,i) for i,t in enumerate(tab) ]
tab_position.sort()
print(tab_position)

(entrée originale : construction_classique.py:docstring of ensae_teaching_cs.td_1a.construction_classique.triindex, line 6)

  1. Eviter d'effectuer le même appel deux fois
  2. Modifier un dictionnaire en le parcourant

Eviter d’effectuer le même appel deux fois

Dans cette fonction on calcule la variance d’une série d’observations.

def moyenne(serie):
    return sum(serie) / len(serie)

def variance_a_eviter(serie):
    s = 0
    for obs in serie :
        s += (obs-moyenne(serie))**2
    return s / len(serie)

La fonction variance_a_eviter appelle la fonction moyenne à chaque passage dans la boucle. Or, rien ne change d’un passage à l’autre. Il vaut mieux stocker le résultat dans une variable :

def moyenne(serie):
    return sum(serie) / len(serie)

def variance(serie):
    s = 0
    moy = moyenne(serie)
    for obs in serie :
        s += (obs-moy)**2
    return s / len(serie)

Le coût de la variance passe alors d’un coût en O(n^2) à O(n). Ce n’est pas le seul endroit où cette erreur survient. Dans le code suivant, on appelle deux fois la fonction major avec le même argument. C’est à éviter.

meilleur = major(data)[0]  # retourne ("quelque chose", True)
if major(data)[1]:
    return {"leaf":guess}

(entrée originale : classiques.py:docstring of ensae_teaching_cs.td_1a.classiques.repetition_a_eviter, line 3)

Modifier un dictionnaire en le parcourant

Il faut éviter de modifier un container lorsqu’on le parcourt. Lorsqu’on supprime un élément d’un dictionnaire, la structure de celui-ci s’en trouve modifiée et affecte la boucle qui le parcourt. La boucle parcourt toujours l’ancienne structure du dictionnaire, celle qui existait au début au début de la boucle.

d = { k: k for k in range(10) }
for k, v in d.items():
    if k == 4 :
        del d[k]

En Python, cela produit l’erreur qui suit mais d’autres langages ne préviennent pas (C++) et cela aboutit à une erreur qui intervient plus tard dans le code (comme une valeur numérique inattendue).

Traceback (most recent call last):
File "session1.py", line 176, in <module>
    l = liste_modifie_dans_la_boucle()
File "session1.py", line 169, in liste_modifie_dans_la_boucle
    for k,v in d.items():
RuntimeError: dictionary changed size during iteration

Il faut pour éviter cela stocker les éléments qu’on veut modifier pour les supprimer ensuite.

d = { k:k for k in l }
rem = [ ]
for k,v in d.items():
    if k == 4 :
        rem.append(k)
for r in rem :
    del d[r]

Même si Python autorise cela pour les listes, il est conseillé de s’en abstenir ainsi que pour tout type d’objets qui en contient d’autres. C’est une habitude qui vous servira pour la plupart des autres langages.

(entrée originale : classiques.py:docstring of ensae_teaching_cs.td_1a.classiques.dictionnaire_modifie_dans_la_boucle, line 3)

  1. calcul du PGCD avec la méthode des soustractions
  2. code de Vigenère

calcul du PGCD avec la méthode des soustractions

La fonction qui suit est l’implémentation en Python de la méthode décrite ici : Algorithme de calcul du PGCD par soustractions successives.

def PGCD (m,n) :
    if m == 1 or n == 1 : return 1
    if m == n : return m
    if m < n : return PGCD (m, n-m)
    return PGCD (n, m-n)

(entrée originale : vigenere.py:docstring of ensae_teaching_cs.td_1a.vigenere.PGCD, line 7)

code de Vigenère

def code_vigenere ( message, cle, decode = False) :
    message_code = ""
    for i,c in enumerate(message) :
        d = cle[ i % len(cle) ]
        d = ord(d) - 65
        if decode : d = 26 - d
        message_code += chr((ord(c)-65+d)%26+65)
    return message_code

m = "JENESUISPASCODE"
c = code_vigenere (m, "DOP")
d = code_vigenere (c, "DOP", True)
print(c,d)

(entrée originale : vigenere.py:docstring of ensae_teaching_cs.td_1a.vigenere.code_vigenere, line 9)

  1. Récupérer des discours du président de la république

Récupérer des discours du président de la république

for i,disc in enumerate(enumerate_speeches_from_elysees()):
    print(disc)

(entrée originale : discours_politique.py:docstring of ensae_teaching_cs.td_1a.discours_politique.enumerate_speeches_from_elysees, line 7)