<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Ateliers
Step1:  nltk
Si vous utilisez la librairie nltk pour la première fois, il est nécessaire d'utiliser la commande suivante. Cette commande permet de télécharger de nombreux corpus de texte, mais également des informations grammaticales sur différentes langues. Information notamment nécessaire à l'étape de racinisation.
Step2:  Les données
Dans le dossier Cdiscount/data de ce répértoire vous trouverez les fichiers suivants 
Step3:  Bien que déjà réduit par rapport au fichier original du concours, contenant plus de 15M de lignes, le fichier cdiscount_test.csv.zip, contenant 1M de lignes est encore volumineux. 
Nous allons charger en mémoire qu'une partie de ce fichier grace à l'argument nb_line afin d'éviter des temps de calcul trop couteux. 
Nous allons extraire 5% de ces 1M de lignes commes échantillons de validation.
Step4:  La commande suivante permet d'afficher les premières lignes du fichiers. 
Vous pouvez observer que chaque produit possède 3 niveaux de Catégories, qui correspondent au différents niveaux de l'arborescence que vous retrouverez sur le site.
Il y a 44 catégories de niveau 1, 428 de niveau 2 et 3170 de niveau 3. 
Dans ce TP, nous nous interesserons uniquement à classer les produits dans la catégorie de niveau 1.
Step5:  La commande suivante permet d'afficher un exemple de produits pour chaque Catégorie de niveau 1.
Step6:  Distribution des classes
Step7:  Q Que peut-on dire sur la distribution de ces classes?
Sauvegarde des données
On sauvegarde dans des csv les fichiers train et validation afin que ces mêmes fichiers soit ré-utilisés plus tard dans d'autre calepin
Step8:  Nettoyage des données
Afin de limiter la dimension de l'espace des variables ou features (i.e les mots présents dans le document), tout en conservant les informations essentielles, il est nécessaire de nettoyer les données en appliquant plusieurs étapes
Step9:  Suppression des posibles balises HTML dans la description
Les descriptions produits étant parfois extraites d'autres sites commerçant, des balises HTML peuvent être incluts dans la description. 
La librairie 'BeautifulSoup' permet de supprimer ces balises
Step10:  Conversion du texte en minuscule
Certaines mots peuvent être écrits en majuscule dans les descriptions textes, cela à pour conséquence de dupliquer le nombre de features et une perte d'information.
Step11:  Remplacement de caractères spéciaux
Certains caractères spéciaux sont supprimés comme par exemple 
Step12:  Suppression des accents
Step13:  Supprime les caractères qui ne sont ne sont pas des lettres minuscules
Une fois ces premières étapes passées, on supprime tous les caractères qui sont pas des lettres minusculres, c'est à dire les signes de ponctuation, les caractères numériques etc...
Step14:  Remplace la description par une liste de mots (tokens), supprime les mots de moins de 2 lettres ainsi que les stopwords
On va supprimer maintenant tous les mots considérés comme "non-informatif". Par exemple 
Step15:  On applique également la suppression des accents à cette liste
Step16:  Enfin on crée des tokens, liste de mots dans la description produit, en supprimant les éléments de notre description produit qui sont présent dans la liste de stopword.
Step17:  Racinisation (Stem) chaque tokens
Pour chaque mot de notre liste de token, on va ramener ce mot à sa racine au sens de l'algorithme de Snowball présent dans la librairie nltk. 
Cette liste de mots néttoyé et racinisé va constitué les features de cette description produits.
Step18:  Fonction de nettoyage de texte
On définit une fonction clean-txt qui prend en entrée un texte de description produit et qui retourne le texte nettoyé en appliquant successivement les étapes présentés précedemment. 
On définit également une fonction clean_marque qui contient signifcativement moins d'étape de nettoyage.
Step19:  Applique le nettoyage sur toutes les lignes de la DataFrame et créé deux nouvelles Dataframe (avant et sans l'étape de racinisation).
Step20:  Nettoyage des DataFrames
Step21:  Affiche les 5 premières lignes de la DataFrame d'apprentissage après nettoyage.
Step22:  Taille du dictionnaire de mots pour le dataset avant et après la racinisation.
Step23:  Wordcloud
Les représentations Wordcloud permettent des représentations de l'ensemble des mots d'un corpus de documents. Dans cette représentation plus un mot apparait de manière fréquent dans le corpus, plus sa taille sera grande dans la représentation du corpus.
Step24:  Wordcloud de l'ensemble des description à l'état brut.
Step25:  Wordcloud après racinisation et nettoyage
Step26:  Vous pouvez observer que les mots "voir et "present" sont les plus représentés. Cela est du au fait que la pluspart des descriptions se terminent par "Voir la présentation". C'est deux mots ne sont donc pas informatif car présent dans beaucoup de catégorie différente. C'est une bon exemple de stopword propre à un problème spécifique.
Exercice Ajouter les mots voiret présentationà la liste des stopwords plus hauts et refaites tourner le nettoyage.
Exercice Générer les wordcloud par catégorie pour 3 catégories de votre choix.
Sauvegarde des jeux de données nettoyés dans des fichiers csv.
<ASSISTANT_TASK:>
Python Code:
#Importation des librairies utilisées
import unicodedata 
import time
import pandas as pd
import numpy as np
import random
import nltk
import re 
import collections
import itertools
import warnings
warnings.filterwarnings("ignore")
import matplotlib.pyplot as plt
import seaborn as sb
sb.set_style("whitegrid")
import sklearn.cross_validation as scv
Explanation: Ateliers: Technologies de l'intelligence Artificielle
<center>
<a href="http://www.insa-toulouse.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/logo-insa.jpg" style="float:left; max-width: 120px; display: inline" alt="INSA"/></a> 
<a href="http://wikistat.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/wikistat.jpg" width=400, style="max-width: 150px; display: inline"  alt="Wikistat"/></a>
<a href="http://www.math.univ-toulouse.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/logo_imt.jpg" width=400,  style="float:right;  display: inline" alt="IMT"/> </a>
</center>
Traitement Naturel du Langage (NLP) : Catégorisation de Produits Cdiscount
Il s'agit d'une version simplifiée du concours proposé par Cdiscount et paru sur le site datascience.net. Les données d'apprentissage sont accessibles sur demande auprès de Cdiscount mais les solutions de l'échantillon test du concours ne sont pas et ne seront pas rendues publiques. Un échantillon test est donc construit pour l'usage de ce tutoriel.  L'objectif est de prévoir la catégorie d'un produit à partir de son descriptif (text mining). Seule la catégorie principale (1er niveau, 47 classes) est prédite au lieu des trois niveaux demandés dans le concours. L'objectif est plutôt de comparer les performances des méthodes et technologies en fonction de la taille de la base d'apprentissage ainsi que d'illustrer sur un exemple complexe le prétraitement de données textuelles. 
Le jeux de données complet (15M produits) permet un test en vrai grandeur du passage à l'échelle volume des phases de préparation (munging), vectorisation (hashage, TF-IDF) et d'apprentissage en fonction de la technologie utilisée.
La synthèse des résultats obtenus est développée par Besse et al. 2016 (section 5).
Partie 1-1 : Exploration et Nettoyage de données textuelles
Dans ce premier notebook nous verrons différent traitements généralement opérés sur des données textuelles :
Nettoyage : Suppression des caractères mal codés et de ponctuation, transformation des majuscules en minuscules, en remarquant que ces transformations ne seraient pas pertinentes pour un objectif de détection de pourriels.
StopWord : Suppression des mots inutiles ou mots de liaison, articles qui n'ont a priori pas de pouvoir discriminant.
Stemming (ou Racinisation): Les mots sont réduits à leur seule racine afin de réduire la taille du dictionnaire.
Librairies
End of explanation
# nltk.download("all")
Explanation: nltk
Si vous utilisez la librairie nltk pour la première fois, il est nécessaire d'utiliser la commande suivante. Cette commande permet de télécharger de nombreux corpus de texte, mais également des informations grammaticales sur différentes langues. Information notamment nécessaire à l'étape de racinisation.
End of explanation
def split_dataset(input_path, nb_line, tauxValid):
    data_all = pd.read_csv(input_path,sep=",", nrows=nb_line)
    data_all = data_all.fillna("")
    data_train, data_valid = scv.train_test_split(data_all, test_size = tauxValid)
    time_end = time.time()
    return data_train, data_valid
Explanation: Les données
Dans le dossier Cdiscount/data de ce répértoire vous trouverez les fichiers suivants :
cdiscount_test.csv.zip: Fichier d'apprentissage constitué de 1.000.000 de lignes
cdisount_test: Fichier test constitué de 50.000 lignes
### Read & Split Dataset
On définit une fonction permettant de lire le fichier d'apprentissage et de créer deux DataFrame Pandas, un pour l'apprentissage, l'autre pour la validation.
   La fonction créée un DataFrame en lisant entièrement le fichier. Puis elle scinde ce DataFrame en deux grâce à la fonction dédiée de sklearn.
End of explanation
input_path = "data/cdiscount_train.csv.zip"
nb_line=100000  # part totale extraite du fichier initial ici déjà réduit
tauxValid = 0.05
data_train, data_valid = split_dataset(input_path, nb_line, tauxValid)
# Cette ligne permet de visualiser les 5 premières lignes de la DataFrame 
N_train = data_train.shape[0]
N_valid = data_valid.shape[0]
print("Train set : %d elements, Validation set : %d elements" %(N_train, N_valid))
Explanation: Bien que déjà réduit par rapport au fichier original du concours, contenant plus de 15M de lignes, le fichier cdiscount_test.csv.zip, contenant 1M de lignes est encore volumineux. 
Nous allons charger en mémoire qu'une partie de ce fichier grace à l'argument nb_line afin d'éviter des temps de calcul trop couteux. 
Nous allons extraire 5% de ces 1M de lignes commes échantillons de validation.
End of explanation
data_train.head(5)
Explanation: La commande suivante permet d'afficher les premières lignes du fichiers. 
Vous pouvez observer que chaque produit possède 3 niveaux de Catégories, qui correspondent au différents niveaux de l'arborescence que vous retrouverez sur le site.
Il y a 44 catégories de niveau 1, 428 de niveau 2 et 3170 de niveau 3. 
Dans ce TP, nous nous interesserons uniquement à classer les produits dans la catégorie de niveau 1.
End of explanation
data_train.groupby("Categorie1").first()[["Description","Libelle","Marque"]]
Explanation: La commande suivante permet d'afficher un exemple de produits pour chaque Catégorie de niveau 1.
End of explanation
#Count occurence of each Categorie
data_count = data_train["Categorie1"].value_counts()
#Rename index to add percentage
new_index = [k+ ": %.2f%%" %(v*100/N_train) for k,v in data_count.iteritems()]
data_count.index = new_index
fig=plt.figure(figsize= (10,10))
ax = fig.add_subplot(1,1,1)
data_count.plot.barh(logx = False)
plt.show()
Explanation: Distribution des classes
End of explanation
data_valid.to_csv("data/cdiscount_valid.csv", index=False)
data_train.to_csv("data/cdiscount_train_subset.csv", index=False)
Explanation: Q Que peut-on dire sur la distribution de ces classes?
Sauvegarde des données
On sauvegarde dans des csv les fichiers train et validation afin que ces mêmes fichiers soit ré-utilisés plus tard dans d'autre calepin
End of explanation
i = 0
description = data_train.Description.values[i]
print("Original Description : " + description)
Explanation: Nettoyage des données
Afin de limiter la dimension de l'espace des variables ou features (i.e les mots présents dans le document), tout en conservant les informations essentielles, il est nécessaire de nettoyer les données en appliquant plusieurs étapes:
Chaque mot est écrit en minuscule.
Les termes numériques, de ponctuation et autres symboles sont supprimés.
155 mots-courants, et donc non informatifs, de la langue française sont supprimés (STOPWORDS). Ex: le, la, du, alors, etc...
Chaque mot est "racinisé", via la fonction STEMMER.stem de la librairie nltk. La racinisation transforme un mot en son radical ou sa racine. Par exemple, les mots: cheval, chevaux, chevalier, chevalerie, chevaucher sont tous remplacés par "cheva".
Exemple
Observons dans un premier temps l'effet de ces différentes étapes sur un  exemple. 
Ligne Originale
End of explanation
from bs4 import BeautifulSoup #Nettoyage d'HTML
txt = BeautifulSoup(description,"html.parser",from_encoding='utf-8').get_text()
print(txt)
Explanation: Suppression des posibles balises HTML dans la description
Les descriptions produits étant parfois extraites d'autres sites commerçant, des balises HTML peuvent être incluts dans la description. 
La librairie 'BeautifulSoup' permet de supprimer ces balises
End of explanation
txt = txt.lower()
print(txt)
Explanation: Conversion du texte en minuscule
Certaines mots peuvent être écrits en majuscule dans les descriptions textes, cela à pour conséquence de dupliquer le nombre de features et une perte d'information.
End of explanation
txt = txt.replace(u'\u2026','.')    
txt = txt.replace(u'\u00a0',' ')
print(txt)
Explanation: Remplacement de caractères spéciaux
Certains caractères spéciaux sont supprimés comme par exemple :
\u2026: …
\u00a0: NO-BREAK SPACE
Cette liste est non exhaustive et peut être etayée en fonction du jeu de donées étudié, de l'objectif souhaité ou encore du résultat de l'étude explorative.
End of explanation
txt = unicodedata.normalize('NFD', txt).encode('ascii', 'ignore').decode("utf-8")
print(txt)
Explanation: Suppression des accents
End of explanation
txt = re.sub('[^a-z_]', ' ', txt)
print(txt)
Explanation: Supprime les caractères qui ne sont ne sont pas des lettres minuscules
Une fois ces premières étapes passées, on supprime tous les caractères qui sont pas des lettres minusculres, c'est à dire les signes de ponctuation, les caractères numériques etc...
End of explanation
## listes de mots à supprimer dans la description des produits
## Depuis NLTK
nltk_stopwords = nltk.corpus.stopwords.words('french') 
## Depuis Un fichier externe.
lucene_stopwords =open("data/lucene_stopwords.txt","r").read().split(",") #En local
## Union des deux fichiers de stopwords 
stopwords = list(set(nltk_stopwords).union(set(lucene_stopwords)))
stopwords[:10]
Explanation: Remplace la description par une liste de mots (tokens), supprime les mots de moins de 2 lettres ainsi que les stopwords
On va supprimer maintenant tous les mots considérés comme "non-informatif". Par exemple : "le", "la", "de" ...
Des listes contenants ces mots sont proposés dans des libraires tels que nltk ou encore lucène.
End of explanation
stopwords = [unicodedata.normalize('NFD', sw).encode('ascii', 'ignore').decode("utf-8") for sw in stopwords]
stopwords[:10]
Explanation: On applique également la suppression des accents à cette liste
End of explanation
tokens = [w for w in txt.split() if (len(w)>2) and (w not in stopwords)]
remove_words = [w for w in txt.split() if (len(w)<2) or (w in stopwords)]
print(tokens)
print(remove_words)
Explanation: Enfin on crée des tokens, liste de mots dans la description produit, en supprimant les éléments de notre description produit qui sont présent dans la liste de stopword.
End of explanation
## Fonction de setmming de stemming permettant la racinisation
stemmer=nltk.stem.SnowballStemmer('french')
tokens_stem = [stemmer.stem(token) for token in tokens]
print(tokens_stem)
Explanation: Racinisation (Stem) chaque tokens
Pour chaque mot de notre liste de token, on va ramener ce mot à sa racine au sens de l'algorithme de Snowball présent dans la librairie nltk. 
Cette liste de mots néttoyé et racinisé va constitué les features de cette description produits.
End of explanation
# Fonction clean générale
def clean_txt(txt):
    ### remove html stuff
    txt = BeautifulSoup(txt,"html.parser",from_encoding='utf-8').get_text()
    ### lower case
    txt = txt.lower()
    ### special escaping character '...'
    txt = txt.replace(u'\u2026','.')
    txt = txt.replace(u'\u00a0',' ')
    ### remove accent btw
    txt = unicodedata.normalize('NFD', txt).encode('ascii', 'ignore').decode("utf-8")
    ###txt = unidecode(txt)
    ### remove non alphanumeric char
    txt = re.sub('[^a-z_]', ' ', txt)
    ### remove french stop words
    tokens = [w for w in txt.split() if (len(w)>2) and (w not in stopwords)]
    ### french stemming
    tokens_stem = [stemmer.stem(token) for token in tokens]
    ### tokens = stemmer.stemWords(tokens)
    return ' '.join(tokens), " ".join(tokens_stem)
def clean_marque(txt):
    txt = re.sub('[^a-zA-Z0-9]', '_', txt).lower()
    return txt
Explanation: Fonction de nettoyage de texte
On définit une fonction clean-txt qui prend en entrée un texte de description produit et qui retourne le texte nettoyé en appliquant successivement les étapes présentés précedemment. 
On définit également une fonction clean_marque qui contient signifcativement moins d'étape de nettoyage.
End of explanation
# fonction de nettoyage du fichier(stemming et liste de mots à supprimer)
def clean_df(input_data, column_names= ['Description', 'Libelle', 'Marque']):
    nb_line = input_data.shape[0]
    print("Start Clean %d lines" %nb_line)
    
    # Cleaning start for each columns
    time_start = time.time()
    clean_list=[]
    clean_stem_list=[]
    for column_name in column_names:
        column = input_data[column_name].values
        if column_name == "Marque":
            array_clean = np.array(list(map(clean_marque,column)))
            clean_list.append(array_clean)
            clean_stem_list.append(array_clean)
        else:
            A = np.array(list(map(clean_txt,column)))
            array_clean = A[:,0]
            array_clean_stem = A[:,1]
            clean_list.append(array_clean)
            clean_stem_list.append(array_clean_stem)
    time_end = time.time()
    print("Cleaning time: %d secondes"%(time_end-time_start))
    
    #Convert list to DataFrame
    array_clean = np.array(clean_list).T
    data_clean = pd.DataFrame(array_clean, columns = column_names)
    
    array_clean_stem = np.array(clean_stem_list).T
    data_clean_stem = pd.DataFrame(array_clean_stem, columns = column_names)
    return data_clean, data_clean_stem
Explanation: Applique le nettoyage sur toutes les lignes de la DataFrame et créé deux nouvelles Dataframe (avant et sans l'étape de racinisation).
End of explanation
# Take approximately 2 minutes fors 100.000 rows
warnings.filterwarnings("ignore")
data_valid_clean, data_valid_clean_stem = clean_df(data_valid)
warnings.filterwarnings("ignore")
data_train_clean, data_train_clean_stem = clean_df(data_train)
Explanation: Nettoyage des DataFrames
End of explanation
data_train_clean.head(5)
data_train_clean_stem.head(5)
Explanation: Affiche les 5 premières lignes de la DataFrame d'apprentissage après nettoyage.
End of explanation
concatenate_text = " ".join(data_train["Description"].values)
list_of_word = concatenate_text.split(" ")
N = len(set(list_of_word))
print(N)
concatenate_text = " ".join(data_train_clean["Description"].values)
list_of_word = concatenate_text.split(" ")
N = len(set(list_of_word))
print(N)
concatenate_text = " ".join(data_train_clean_stem["Description"].values)
list_of_word_stem = concatenate_text.split(" ")
N = len(set(list_of_word_stem))
print(N)
Explanation: Taille du dictionnaire de mots pour le dataset avant et après la racinisation.
End of explanation
from wordcloud import WordCloud
A=WordCloud(background_color="black")
A.generate_from_text?
Explanation: Wordcloud
Les représentations Wordcloud permettent des représentations de l'ensemble des mots d'un corpus de documents. Dans cette représentation plus un mot apparait de manière fréquent dans le corpus, plus sa taille sera grande dans la représentation du corpus.
End of explanation
all_descr = " ".join(data_valid.Description.values)
wordcloud_word = WordCloud(background_color="black", collocations=False).generate_from_text(all_descr)
plt.figure(figsize=(10,10))
plt.imshow(wordcloud_word,cmap=plt.cm.Paired)
plt.axis("off")
plt.show()
Explanation: Wordcloud de l'ensemble des description à l'état brut.
End of explanation
all_descr_clean_stem = " ".join(data_valid_clean_stem.Description.values)
wordcloud_word = WordCloud(background_color="black", collocations=False).generate_from_text(all_descr_clean_stem)
plt.figure(figsize=(10,10))
plt.imshow(wordcloud_word,cmap=plt.cm.Paired)
plt.axis("off")
plt.show()
Explanation: Wordcloud après racinisation et nettoyage
End of explanation
data_valid_clean.to_csv("data/cdiscount_valid_clean.csv", index=False)
data_train_clean.to_csv("data/cdiscount_train_clean.csv", index=False)
data_valid_clean_stem.to_csv("data/cdiscount_valid_clean_stem.csv", index=False)
data_train_clean_stem.to_csv("data/cdiscount_train_clean_stem.csv", index=False)
Explanation: Vous pouvez observer que les mots "voir et "present" sont les plus représentés. Cela est du au fait que la pluspart des descriptions se terminent par "Voir la présentation". C'est deux mots ne sont donc pas informatif car présent dans beaucoup de catégorie différente. C'est une bon exemple de stopword propre à un problème spécifique.
Exercice Ajouter les mots voiret présentationà la liste des stopwords plus hauts et refaites tourner le nettoyage.
Exercice Générer les wordcloud par catégorie pour 3 catégories de votre choix.
Sauvegarde des jeux de données nettoyés dans des fichiers csv.
End of explanation
<END_TASK>