CI1 : Introduction à Python
            Installer un environnement de développement et pratiquer Python.
	
	Installation (∼10mn, – easy)
Dans le cadre de ce cours, nous allons utiliser Pycharm. Pour le télécharger, vous pouvez vous rendre sur le site officiel. Vous aurez le choix en la version professionnelle ou community (qui est gratuite). En tant qu'étudiant, vous pouvez avoir gratuitement accès à la version professionnelle en vous inscrivant sur le site de Jetbrains.
Créez un nouveau projet appelé CI1. Pour ce projet, créez un envrionnement virtuel directement depuis
    la fenêtre de configuration du projet.
Si Pycharm vous propose d'activer l'assistant AI, refusez. Nous sommes là pour apprendre, vous pourrez
    l'utiliser quand vous connaîtrez parfaitement Python.
Analyse de données (∼45mn, – easy)
    Dans cet exercice, nous allons implémenter diverses fonctions pouvant intervenir dans une analyse de données.
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    
        Créez un fichier data_analysis.py contenant une classe DataAnalysis. Dans cette classe,
        ajoutez un constructeur. Ce constructeur prend en argument une chaîne de caractères représentant une liste d'entiers
        séparés par une virgule. Le constructeur transforme la chaîne de caractère en une liste d'entiers qui sont ensuite
        stockés dans le champ data.
        
Ce format de donnée est très fréquent et forme la base des fichiers csv.
        Il faut faire un split et convertir toutes les valeurs en entiers.
    
class DataAnalysis:
    def __init__(self, data_str):
        self.data = [int(x) for x in data_str.split(',')]
        
        Écrivez une méthode max retournant la valeur maximale dans les données. On n'utilisera pas la
        fonction intégrée max. On pourra utiliser float('-inf') comme étant la valeur la plus
        petite possible.
        
Oui, max(l) retourne la valeur maximale d'une liste.
    
    def max(self):
        maxi = float('-inf')
        for value in self.data:
            if value > maxi:
                maxi = value
        return maxi
        
        Écrivez une fonction main instanciant un objet DataAnalysis avec comme entrée '20,90,50,50,22,46,99,54,55,44,66,15,18,50,88,2,59,25,69,2'.
        Afficher ensuite la valeur maximale, qui devrait être 99.
        
Pycharm contient des raccourcis vers les fonctionnalités les plus populaires. En particulier, si vous tapez
            main, Pycharm devrait vous proposer de créer la "fonction" main pour vous.
    
if __name__ == '__main__':
    da = DataAnalysis(data_str='20,90,50,50,22,46,99,54,55,44,66,15,18,50,88,2,59,25,69,2')
    print(da.max())
        
        Écrivez une fonction mean retournant la moyenne des données sans utiliser la méthode intégrée
        sum. Dans notre exemple, la moyenne est 46.2.
    
    def mean(self):
        temp = 0
        for value in self.data:
            temp += value
        return temp / len(self.data)
        
        Écrivez une fonction get_bins prenant en argument n_bins le nombre de bacs. Cette fonction
        prend nos données, découpe la distance séparant la valeur minimale et maximale en n_bins intervalles
        égaux. Ensuite, elle stocke dans une liste n_bins listes et met chaque entier dans son bac. Par
        exemple, si l'on découpe l'intervalle de 1 à 10 en 2 bacs, les chiffres 1, 2, 3, 4, 5 vont dans le premier bac,
        et 6, 7, 8, 9, 10 dans le deuxième, ce qui donnerait [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]].
        La formule pour obtenir le bac d'une valeur est int((value - mini) / (maxi - mini) * n_bins)
        sauf pour la valeur maximale qui va dans le dernier bac..
    
    def get_bins(self, n_bins):
        res = [[] for _ in range(n_bins)]
        mini = min(self.data)
        maxi = self.max()
        for value in self.data:
            if value == maxi:
                res[-1].append(value)
            else:
                res[int((value - mini) / (maxi - mini) * n_bins)].append(value)
        return res
        
        Obtenir les bacs est très utile pour représenter un histogramme qui est une représentation graphique du nombre
        d'éléments dans chaque bac. Écrivez une fonction print_histogram prenant en argument un nombre de
        bacs n_bins et affichant l'histogramme de nos
        données dans le terminal. Pour l'affichage, on pourra utiliser un dièse # pour représenter une
        unité. Dans notre exemple, vous devriez obtenir :
        
    
    #     
    #     
 #  ##    
### ###  #
### ### ##
        
    def print_histogram(self, n_bins):
        values = [len(x) for x in self.get_bins(n_bins)]
        maxi = max(values)
        for i in range(maxi):
            temp = ""
            for value in values:
                if value >= maxi - i:
                    temp += "#"
                else:
                    temp += " "
            print(temp)
        
Word Count (∼45mn, – medium)
Dans cet exercice, nous allons nous initier à la manipulation des chaînes de caractères à travers un compteur de mot.
Créez un nouveau fichier word_count.py contenant une classe WordCount. Ajoutez un constructeur
    prenant en argument une chaîne de caractère et initialisant le champ text avec l'argument.
class WordCount:
    def __init__(self, text):
        self.text = text
        
        Implémenter la fonction permettant de définir la représentation textuelle de WordCount, que l'on considèrera
        égale à self.text.
    
    def __str__(self):
        return self.text
        
        Sous la classe (à un niveau d'indentation 0), ajouter la "fonction" main. Dans le main, instanciez
        un objet WordCount avec comme argument "The little boy was called Tom. Tom was happy because the dog was happy.".
        Affichez cet objet.
    
if __name__ == '__main__':
    wc = WordCount("The little boy was called Tom. Tom was happy because the dog was happy.")
    print(wc)
        
        Dans WordCount, écrire une méthode statique from_file prenant en argument un nom
        de fichier. Cette méthode devra retourner un objet WordCount avec un texte initialisé à la concaténation
        des lignes du fichier.
    
    @staticmethod
    def from_file(filename):
        with open(filename) as f:
            temp = f.readlines()
        return WordCount("".join(temp))
        
        Écrivez une fonction preprocess qui retournera le résultat obtenu en effectuant
        les opérations suivantes :
        
- Mettre en minuscule tout le texte
 - Replace les . et les \n (nouvelle ligne) par un espace
 - Séparer tous les mots en utilisant l'espace comme délimiteur
 - Enlèver les mots vides
 
    def preprocess(self):
        return [x for x in self.text.lower().replace(".", " ").replace("\n", " ").split(" ")
                if x]
        
        Écrivez la fonction permettant d'obtenir la "longueur" d'un objet WordCount. On définit la longueur
        comme étant le nombre de mots unique dans le texte prétraité. Dans notre exemple, vous devriez obtenir
        une longueur de 9.
        On veut pouvoir obtenir la longueur en faisant len(word_count)
    
        def __len__(self):
            return len(set(self.words_list))
        
        Écrivez une fonction count_words retournant un dictionnaire dont les clefs sont les mots dans la
        phrase et les valeurs le nombre occurrence de chaque mot. wc.count_words()["the"] devrait retourner
        2 sur notre exemple.
    
        def count_words(self):
            res = dict()
            for word in self.words_list:
                res[word] = res.get(word, 0) + 1
            return res
        
Écrivez une fonction get_most_common_word retournant le mot le plus présent dans le texte
    prétraité. Dans notre exemple, vous devriez obtenir was
        def get_most_common_word(self):
            counts = self.count_words()
            best_key = None
            maxi = 0
            for key, value in counts.items():
                if value > maxi:
                    maxi = value
                    best_key = key
            return best_key