<jupyter_start><jupyter_text>Les bases du langage Python- Langage de haut niveau- Langage interprété- Langage orienté objet- Langage le plus proche possible du langage naturel*Attention Python est sensible à la casse*  Quelques rappels en Python- Tout est objet même les "variables"- Un objet possède toujours des propriétés et des méthodesAvec iPython, on utilise pour connaître les méthodes et les propriétés :```mon_objet.[tabulation]```  Les types en PythonLes types de base :- int- float- boolean- stringEn Python pas besoin de déclarer le type de données, le langage s’en charge tout seulPython est basé sur un typage fort - une fois un objet d'un type donné, Python ne l'adapte pasPour connaître le type d’une variable, on utilise : type(nom_de_variable)<jupyter_code>nom_de_variable=44.8
print(type(nom_de_variable))<jupyter_output><class 'float'><jupyter_text>**Exercice**Définir quatre variables distinctes, un entier, un float, un booléen et une chaîne de caractère et affichez-les dans la console.<jupyter_code>entier = 33
dec = 44.44
booleen = True
chaine = "Python"
chaine2 = 'Python'
chaine3 = """Python"""
print(entier,dec,booleen,chaine,chaine2,chaine3,sep = "\n")<jupyter_output>33
44.44
True
Python
Python
Python<jupyter_text>Les opérations arithmétiques- +- -- *- /- ** (puissance)- % (modulo)  Les chaînes de caractères 3 codages équivalents :<jupyter_code>print("Python",'Python',"""Python""")<jupyter_output>Python Python Python<jupyter_text>De nombreuses opérations sur les chaînes :<jupyter_code>chaine1="Python"
print(len(chaine1), chaine1.lower(), chaine1.upper(), str(chaine1))<jupyter_output>6 python PYTHON Python<jupyter_text>**Exercice**Définir une variable comprenant la chaîne 'Python pour la Science', utilisez des opérations sur les chaînes pour afficher 'PYTHON POUR LA DATA SCIENCE'<jupyter_code>chaine_python = "Python pour la Science"
print(chaine_python.replace("la","la data").upper())
print((chaine_python[0:15] + "data" + chaine_python[14:]).upper())
print(chaine_python[-7:])<jupyter_output>Science<jupyter_text>Les opérateurs booléens- `not`, `and` et `or`- Ordres de priorité    - `not`    - `and`    - `or`<jupyter_code>print(not True)
print(True or False)
print(True and False)
print(not True and False)
print(not True or False)<jupyter_output>False
True
False
False
False<jupyter_text>Les convertisseurs de typeOn peut convertir des types en utilisant :<jupyter_code>entier1 = 44
print(type(float(entier1)))
print(type(bool('True')))
print(type(bool(0)))<jupyter_output><class 'bool'>
<class 'bool'><jupyter_text>Les opérateurs pour les conditionsPermet de mettre en place des conditionsOn utilise la syntaxe :```{python}if ... :    ...elif ... :    ...else:    ...```<jupyter_code>bool1=True<jupyter_output><empty_output><jupyter_text>**Exercice :** Testez si bool1 est vrai et d'autres conditions<jupyter_code>if bool1 is True:
    print("C'est vrai")
elif bool1 is False:
    print("c'est faux")
else :
    print("???")
if bool1 == True:
    print("C'est vrai")
elif bool1 == False:
    print("c'est faux")
else :
    print("???")
if bool1:
    print("C'est vrai")
elif not bool1:
    print("c'est faux")
else :
    print("???")<jupyter_output>C'est vrai<jupyter_text>- Attention dans une condition, si on teste l'égalité des valeurs, il faut utiliser == (les autres opérateurs sont , >=, !=)- Il existe aussi l'opérateur is qui est très important en Python (lisibilité)    - Il permet de tester non pas uniquement les valeurs mais les objets similaires ( True == 1 est vrai mais True is 1 est faux) - Veillez à bien respecter l’indentation  La boucle for- Les boucles for de Python ont une structure spécifique- L'itérateur prend comme valeur les éléments d'une liste- Structure :```for indice in sequence:    instructions```- On peut utiliser `break` pour casser une boucle - `while` peut aussi être utilisé<jupyter_code>list_col = ["a","b","c"]
for i in range(3):
    print(list_col[i])
for col in list_col:
    print(col)<jupyter_output>a
b
c<jupyter_text>**La boucle de Python est un outil à manier avec parcimonie (elle est très lente)** L'utilisation de `range(n)` permet de créer une liste de valeur de 0 à n-1 **Exercice :**    Créez une boucle for qui parcourt un range de 0 à 5 et qui ajoute 1 à une variable à chaque boucle<jupyter_code>var = 0
for i in range(6):
    var += 1
var<jupyter_output><empty_output><jupyter_text>**Exercice :**        Créer une boucle permettant d’afficher des phrases avec une valeur différente à chaque boucle "Nous sommes ..."<jupyter_code>liste_jours = ["lundi","mardi","mercredi"]
for jour in liste_jours:
    print("Nous sommes", jour )
liste_jours = ["lundi","mardi","mercredi"]
liste_temp = [12,14,20]
for jour, temp in zip(liste_jours,liste_temp):
    print("Nous sommes {} et il fait {} degrés".format(jour,temp))
for i in range(min(len(liste_jours),len(liste_temp))):
    print("Nous sommes {} et il fait {} degrés".format(liste_jours[i],liste_temp[i]))<jupyter_output>Nous sommes lundi et il fait 12 degrés
Nous sommes mardi et il fait 14 degrés
Nous sommes mercredi et il fait 20 degrés<jupyter_text>Les collections d’objet sous PythonIl existe 3 structures principales de données sous Python :- Les tuples : suite de valeurs immuable définie par `( )`- Les listes : suite de valeurs modulable définie par `[ ]`- Les dictionnaires : valeurs indexées clé – valeur défini par `{ }`     En Python, quelle que soit la structure, on utilise `[ ]` pour accéder à un élément d’une structure- Dans un tuple : `tup1[0]` permet d’accéder au premier élément  Les tuples- On ne peut pas modifier un tuple une fois créé**Attention sous Python, le premier indice est toujours le 0**<jupyter_code>#on définit un tuple
tu = (1,3,5,7)
#on peut rechercher sa taille
print(len(tu))
#on accès aux indices en utilisant []
tu2=tu[1:3]
print(tu2)<jupyter_output>4
(3, 5)<jupyter_text>Les listesUne liste est un tuple de taille dynamique et modifiableLes listes sont définies avec des `[ ]`Les méthodes sur les listes comprennent :- `.append()`			ajoute une valeur en fin de liste- `.insert(i,val)`		insert une valeur à l’indice i- `.pop(i)`			extrait la valeur de l’indice i- `.reverse()`			inverse la liste- `.extend()` 			étend la liste**Attention la plupart des méthodes des listes modifient la liste.** **Exercice:**    Créez une liste composée de 5 éléments, affichez la longueur de la liste.<jupyter_code>ma_liste = [2, 5 , 'python', True, 2.5]
ma_liste[2].upper()
ma_liste.append('youpi')
ma_liste<jupyter_output><empty_output><jupyter_text>Modifiez la dernière valeur de la liste et construisez une seconde liste à partir des 3 dernières valeurs de la liste initiale.  Les listes (suite)On peut faire des recherché plus avancées dans les listes :- `val in list` renvoie `True` si la valeur `val` est dans la liste `list`- `.index(val)` renvoie l'indice de la valeur `val`- `.count(val)` renvoie le nombre d'occurrence de `val`- `.remove(val)` retire la valeur `val` de la liste (que la 1ère)Pour supprimer une liste ou un élément d’une liste, on utilise la commande `del`  Un générateur de liste : la List ComprehensionOn peut définir des listes de manière plus complexe :<jupyter_code>listinit=[5,2,6,7]
res = [x**2 for x in listinit if (x % 2 == 0)] 
res=[]
for x in listinit:
    if x % 2 == 0:
        res.append(x**2)
        #res = res + [x**2]
print(res)<jupyter_output>[4, 36]<jupyter_text>**Exercice :**    A partir d’une liste de valeurs en degrés celsius `[0, 10, 20, 35]`, créez une liste en degrés fahrenheits sachant que la formule de transfert est `(9/5*temp+32)` et qu'on désire afficher uniquement le température plus grandes que 50.<jupyter_code>temp_celsius = [0, 10, 20, 35]
temp_fahr = [(9/5*temp+32) for temp in temp_celsius]
print(temp_fahr)<jupyter_output>[32.0, 50.0, 68.0, 95.0]<jupyter_text>Les chaînes de caractères, des listes spécifiquesUne chaîne de caractère est une liste avec des méthodes spécifiques (`.upper()`, `.find()`, `.count()`, `.replace()`...)Pour aller plus loin, on peut transformer une chaîne en liste :- Caractère par caractère : `list(str)`- En fonction de séparateurs : `.split(sep)`- Et effectuer l’opération inverse :```sep.join(list)```  Chaîne de caractèresPour intégrer des valeurs spécifiques dans une phrase, on utilisera :- `%f`, `%s`, `%i` à l’intérieur de la chaîne (`float`, `str` ou `entier`)- `%( , )` après la chaîne de caractères<jupyter_code>print("Aujourd'hui nous sommes %s" %("mercredi"))<jupyter_output>Aujourd'hui nous sommes mercredi<jupyter_text>Si on veut intégrer d'autres types, on utilisera la méthode `.format()` et le codage `{}` dans la chaîne<jupyter_code>print("La liste {} est utilisée".format([4.5,3.6,6]))<jupyter_output>La liste [4.5, 3.6, 6] est utilisée<jupyter_text>Les dictionnairesIl s'agit d’une collection d’objets non ordonnés associant les notions "clé – valeur".<jupyter_code>dico = {'machine_learning':['gbm','rf'], 'deep_learning':['rnn','cnn'], 'statistique':['test','ACP']}
# On utilise pour afficher la liste des clés et des valeurs
print(dico.keys())
print(dico.values())
# On accède aux valeurs par clé
print(dico["machine_learning"])<jupyter_output>['gbm', 'rf']<jupyter_text>**Exercice :**Accédez à l'élément rnn du dictionnaire.  Affichez le mot en majuscule.<jupyter_code>dico['deep_learning'][0].upper()<jupyter_output><empty_output><jupyter_text>Les fonctions - Les niveaux d’une fonction dépendent de l’indentation- On définit une fonction avec `def fonction():`- Le contenu de la fonction suit- La valeur que retourne la fonction est définie par `return` - Les commentaires liés à une fonction sont inclus dans un docstring (commentaire utilisant `"""`)<jupyter_code>def calcul_produit(val1,val2):
    """Cette fonction affiche le produit de 2 valeurs"""
    return val1*val2
produit = calcul_produit(4,6)
print(produit)<jupyter_output>24<jupyter_text>Une fonction peut renvoyer plusieurs objets<jupyter_code>def produit_division(val1,val2):
    """Cette fonction affiche le produit de 2 valeurs"""
    return val1*val2, val1/val2
produit_division(4,6)
produit2, division = produit_division(4,6)
print(produit2,division)<jupyter_output>24 0.6666666666666666<jupyter_text>Une fonction peut avoir de multiples arguments dont des argument facultatifs (avec des valeurs par défaut) :<jupyter_code>def test(a,b,c=3):
    return a+b+c
# Que renvoie :
# test(2,3)
# test(2,3,4)<jupyter_output><empty_output><jupyter_text>On peut avoir un nombre indéfini d’arguments en utilisant `*args` (ils sont rassemblés dans un tuple)<jupyter_code>def test2(a:int,b:float,*args):
    val=0
    for i in args:
        val+=i
    return a+b+val
# Que renvoie :
# test2(2,4,6,8)
test2(2.5,4)<jupyter_output><empty_output><jupyter_text>On peut aussi stocker les options d'une fonction dans un dictionnaire avec `**kwargs`<jupyter_code>def test3(a,b,*args,w=1,**kwargs):
    x=0
    for val in args:
        x+=val
    x+=a
    x-=b
    x*=w
    option1=kwargs.get('option1',False)
    option2=kwargs.get('option2',False)
    if option1 is True and option2 is True:
        print("Tout est vrai")
        return x
    else:
        return x/2
# Que renvoie
#print(test3(1,2))
#print(test3(1,2,3,4))
#print(test3(1,2,option1=True))
print(test3(1,2,option1=True,option2=True))
#print(test3(1,2,3,4,w=2,option1=True,option2=True))
dico_csv = {"sep": ";","decimal":","}
dico_csv
import pandas as pd
data = pd.read_csv("./data/base-dpt.csv",**dico_csv)<jupyter_output><empty_output><jupyter_text>Les fonctions lambda- Il s’agit de fonction très courte visant à simplifier votre code- On les trouve souvent dans du code en python- Les fonctions lambda peuvent être assigné a une variable qui devient une fonction ou être traitées toutes seules- On utilise le mot clé lambda- Il y a deux limites aux fonctions lambda :     - Elle ne peut s’écrire que sur une ligne     - Il n’y a qu’une instruction (on peut avoir plusieurs paramètres)- Quand utiliser des fonctions lambda :    - A l’intérieur d’autres fonctions    - Lorsqu’on veut appliquer une transformation sur des données<jupyter_code>f=lambda x:x**3
print(f(5))
print((lambda x:x**3)(5))<jupyter_output>125
125<jupyter_text>Exemples d'utilisations :<jupyter_code># Dans une fonction :
def ma_fonction_puissance(x):
    return lambda a,b : (a+b)**x
fonction_cube=ma_fonction_puissance(3)
print(fonction_cube(2,3))
# Pour une transformation
from pandas import Series
ser1=Series([2,5,7,9])
ser1
ser1.apply(lambda x:x**2+2*x-5)<jupyter_output><empty_output><jupyter_text>**Exercice sur les fonctions :**Construire une fonction prenant en entrée deux listes et qui retourne la moyenne de tous les éléments des deux listes (une seule valeur)<jupyter_code>def moyenne2listes(liste1,liste2):
    return sum(liste1+liste2)/len(liste1+liste2)
print(moyenne2listes([1,4,6],[3,6,8]))
def moyenne_n_listes(*listes):
    """ Cette fonction calcule la moyenne de tous les éléments de n listes
    Entrée : listes
    Sortie : moyenne:float
    """
    
    somme = 0
    longueur = 0
    for liste in listes:
        somme += sum(liste)
        longueur += len(liste)
    return somme / longueur
print(moyenne_n_listes([1,4,6],[3,6,8]))
print(moyenne_n_listes([1,4,6]))<jupyter_output>3.6666666666666665<jupyter_text>Les packages et les modules en Python- Il s’agit d’un ensemble de fonctions qui pourront être appelées d’un autre programme- On utilise :```import nom_pkg```- Si on utilise `as nm` par exemple, ceci permet de raccourcir l'appel aux fonctions du package- On peut utiliser `from mon_pkg import *`, dans ce cas plus de préfixes mais attention aux conflits- On peut aussi utiliser des fonctions ou des classes spécifiques (plus besoin de préfixe)```from pandas import Series```*Si on cherche des informations sur un module, on peut utiliser `dir()` et `help()`*<jupyter_code>import pandas
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression<jupyter_output><empty_output><jupyter_text>Créer votre module et votre package- Il est très simple de faire appel à des fonctions ou à des classes depuis un autre fichier- Il suffit de stocker vos classes et fonctions dans un fichier `.py`- Celui-ci pourra être stocké dans un répertoire appartenant aux chemins de Python ou dans votre répertoire de travail- Vous avez alors la possibilité de faire :```import mon_fichier```et d’utiliser les fonctions et classes de ce fichiers- Vous venez de créer un module. - **Attention ceci n’est pas un package !**  Créer votre module et votre package (suite)- Un package est constitué de plus d’informations qu’un simple fichier `.py`- Voici les étapes pour créer votre premier package :    - Choisissez un nom simple et traduisant bien ce que va faire votre package (si possible un nom qui n’est pas déjà utilisé)    - Créez un répertoire du nom de votre package    - Créez un fichier `__init__.py` dans ce répertoire, ce fichier peut être vide dans un premier temps    - Dans le même répertoire ajoutez des fichiers avec vos fonctions et vos classesSi votre objectif est de publier le package sur PyPi, il faudra ajouter un fichier setup et les dépendances associéesPour importer votre package, il vous suffit de faire:```import nom_dossier.nom_fichier```  Quelques détails sur la création de package- Le fichier `__init__.py` peut être vide mais il peut aussi contenir des informations- Ce fichier est lancé à chaque chargement du package, il peut inclure des dépendances, des vérifications de versions, des import vers les fonctions et classes des fichiers de votre package...- Où peut-on mettre nos packages ?    - Par défaut, si le package se trouve dans le même répertoire que le script exécuté, ça fonctionne    - Le package doit se trouver dans un répertoire lié au PythonPath    - Pour rechercher les répertoire, on utilise le module `sys` et la fonction `path`    - Pour créer un répertoire "temporaire", on utilise la fonction `path.insert()` du module `sys`  Les classesNous avons manipuler de nombreux objets de classes variées. Si nous voulons aller plus loin, il va falloir manipuler des classes et savoir les créer.Une classe est un type permettant de regrouper dans la même structure :- les informations (champs, propriétés, attributs) relatives à une entité ; - les procédures et fonctions permettant de les manipuler (méthodes). Une classe commence par un constructeur :<jupyter_code>class MaClasse:
    
    def __init__(self,nom="Emmanuel",ville="Paris"):
        self.nom= nom
        self.ville=ville
objet_classe=MaClasse("Emmanuel","Lyon")
objet_classe.nom="Aslane"
objet_classe.nom<jupyter_output><empty_output><jupyter_text>Ensuite on peut définir d’autres fonctions dans la classeOn définit ensuite une nouvelle instance et on peut ainsi remplir le nouvel objet  Les classes- La programmation orientée objet, c’est un style de programmation qui permet de regrouper au même endroit le comportement (les fonctions) et les données (les structures) qui sont faites pour aller ensemble- La notion d’objet en Python concerne toutes les structures Python- L’idée est de créer vos propres objets- La création d’un objet se fait en deux étapes :    - Description de l’objet    - Fabrication de l’objet- La notion de classe en python représente la description de l’objet- La deuxième étape se fait uniquement en allouant des informations à l’objet. L’objet obtenu est une instance de la classe<jupyter_code># Une classe va ressembler à :
class MaClasse():
    def __init__(self,val1=0,val2=0):
        self.val1=val1
        self.val2=val2
    
    def methode1(self,param1):
        self.val1+=param1
        print(self.val1)
objet_maclasse = MaClasse(val1=10,val2=20)
type(objet_maclasse)
objet_maclasse.methode1(10)
objet_maclasse.val1<jupyter_output><empty_output><jupyter_text>**Exercice :**    Définissez une classe `CompteBancaire()`, qui permette d'instancier des objets tels que `compte1`, `compte2`, etc.Le constructeur de cette classe initialisera deux attributs d'instance `nom` et `solde`, avec les valeurs par défaut `'A'` et `0`.Trois autres méthodes sont définies :- `depot(somme)` permettra d'ajouter une certaine somme au solde- `retrait(somme)` permettra de retirer une certaine somme du solde- `affiche()` permettra d'afficher le solde du compte et un message d’alerte en cas de solde négatif.<jupyter_code>class CompteBanquaire():
    
    def __init__(self, nom = "", solde = 0):
        self.nom = nom
        self.solde = solde
        
    def depot(self,somme):
        self.solde += somme
        
    def retrait(self,somme):
        self.solde -= somme
        
    def affiche(self):
        print("Votre solde est de {}".format(self.solde))
mon_compte = CompteBanquaire(nom="Emmanuel",solde=100)
mon_compte.depot(10)
mon_compte.affiche()<jupyter_output>Votre solde est de 110<jupyter_text>La gestion des exceptions- Python possède un système pour gérer les exceptions- On utilise `try:` et `except:`- On peut intercepter différents types d’erreurs    - `NameError`, `TypeError`, `ZeroDivisionError`- Si on ne veut rien faire dans l’exception on peut utiliser le mot clé `pass`<jupyter_code>def ma_fonction(val):
    try:
        print(val**2)
    except:
        print("Erreur")
ma_fonction("r")<jupyter_output>Erreur<jupyter_text>**Exercices :**    Créer une gestion d’exception pour une fonction prenant en entrée deux `input()` puis divisez-les, utiliser les différentes erreurs d’exceptions<jupyter_code>def division():
    try:
        val1 = float(input("Entrez un nombre "))
        val2 = float(input("Entrez un nombre "))
        return val1/val2
    except ZeroDivisionError as e:
        print(e)
        print("Erreur : ne pas diviser par 0")
    except ValueError as e:
        print("Erreur : vous devez entrer des valeurs numériques", e)
    
division()<jupyter_output>Entrez un nombre 5
Entrez un nombre 0
float division by zero
Erreur : ne pas diviser par 0
{'fraction_non_alphanumeric': 0.10397061043732071, 'fraction_numerical': 0.01781490614463288, 'mean_word_length': 5.001812141347025, 'pattern_counts': {'":': 2, '<': 117, '<?xml version=': 0, '>': 118, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '34', 'dir': 'jupyter-structured-clean-dedup', 'id': '507503', 'license': 'MIT', 'n_tokens_mistral': 7113, 'n_tokens_neox': 6984, 'n_words': 2279, 'path': '03_bases_python.ipynb', 'repo_name': 'stat4decision/python-data-lbp-fev20', 'size': '19871'}