#Macro de traitement
NpsVoixClients <- function(donnee, nps_client, commentaire, nom_sondage) {
#Vérification des requis
require(tcltk)
if (tk_messageBox(icon = 'warning',type = c("okcancel"), message = "Pour utiliser cet outil, vous devez pointer vers le sondage CSV et indiqué les colonnes de NPS et de commentaires.\n\nDe plus, les catégories de NPS autorisées doivent être libellées ainsi 'Promoteur', 'Passif' ou 'Détracteur'",caption = 'Avertissement') == 'cancel')
stop("Arrêté par l'utilisateur")
#Importation des transformers
transformers <- reticulate::import('transformers')
#Importation des modèles de classification
classifier_tokenizer <- transformers$AutoTokenizer$from_pretrained('camembert-base')
classifier_zsl <- transformers$pipeline("zero-shot-classification", model="BaptisteDoyen/camembert-base-xnli", tokenizer = classifier_tokenizer, use_fast = TRUE)
#Définition des mégas catégories
prix <- c('prix', 'prime', 'renouvellement', 'rabais', 'soumission', 'cher', 'augmentation', 'tarif', 'coût', 'taux')
employe_agent <- c('employé', 'personne', 'courtoisie', 'parler', 'réponse', 'personnel', 'professionnel', 'clair', 'explication', 'demande', 'information', 'agent')
efficacite_processus_delai <- c('efficacité', 'processus', 'délai', 'rapide', 'attente', 'temps', 'mois', 'problème', 'réclamation', 'dossier', 'contrat', 'satisfaction')
produits_et_services <- c('produit','service', 'besoin', 'police', 'qualité', 'compagnie', 'téléphone', 'couverture', 'la capitale', 'ssq', 'beneva')
categories_zsl <- c(prix, employe_agent, efficacite_processus_delai, produits_et_services)
#Lecture des données sources
donnee <- read.csv2(donnee)
#Conserve le jeu de données, le nps et le commentaire déclaré dans la fonction
donnee <- dplyr::select(donnee, nps_client, commentaire)
#Renomme les colonnes
colnames(donnee) <- c('nps','commentaire')
#Retrait des lignes vides
donnee <- donnee[!is.na(donnee$nps) && !is.na(donnee$commentaire)]
#Retrait des stop word
donnee$commentaire <- tm::removeWords(donnee$commentaire, tm::stopwords("french"))
#Tout en minuscule, retrait des nombres, retrait des ponctuations, retrait des stop words, retrait des espaces superflus
donnee$commentaire <- tm::stripWhitespace(tm::removeWords(tm::removePunctuation(tm::removeNumbers(tolower(donnee$commentaire))), tm::stopwords("french")))
#Stemmatisation
donnee$commentaire <- SnowballC::wordStem(donnee$commentaire, language = "french")
#Analyse des sentiments avec syuzhet
donnee$syuzhet_temp <-syuzhet::get_nrc_sentiment(donnee$commentaire, language = "french")
donnee$colere <- as.numeric(donnee$syuzhet_temp$anger)
donnee$anticipation <- as.numeric(donnee$syuzhet_temp$anticipation)
donnee$degout <- as.numeric(donnee$syuzhet_temp$disgust)
donnee$peur <- as.numeric(donnee$syuzhet_temp$fear)
donnee$joie <- as.numeric(donnee$syuzhet_temp$joy)
donnee$tristesse <- as.numeric(donnee$syuzhet_temp$sadness)
donnee$surprise <- as.numeric(donnee$syuzhet_temp$surprise)
donnee$confiance <- as.numeric(donnee$syuzhet_temp$trust)
donnee$syuzhet_negatif <- as.numeric(donnee$syuzhet_temp$negative)
donnee$syuzhet_positif <- as.numeric(donnee$syuzhet_temp$positive)
donnee <- donnee[,-c(3)]
#Conserve le nombre de ligne
nb_ligne <- nrow(donnee)
#Zero shot classification
#Chaque commentaire est traité individuellement
donnee$ligne <- seq.int(nrow(donnee))
#On boucle sur toutes les lignes, une à la fois
for (i in 1:max(donnee$ligne)) {
#Cette étape est la plus longue, c'est pourquoi j'y mets un suivi
print(glue::glue("Traitement de la ligne {i} de {max(donnee$ligne)}"))
#On conserve que la ligne en cours
tempo <- donnee[c(i),]
#On isole le commentaire
tempo2 <- NLP::as.String(tempo[tempo$ligne == i, c("commentaire")])
#On passe le commentaire dans la classification
tempo3 <- classifier_zsl(tempo2, categories_zsl)
#Les labels et scores obtenus sont convertis en caractères pour l'instant
tempo3$labels <- as.character(tempo3$labels)
tempo3$scores <- as.character(tempo3$scores)
#On recrée le jeu de données en 'recollant' les lignes
if (i == 1){
final = tempo3
} else {
final <- rbind(final, tempo3)
}
i <- i + 1
}
rownames(final) <- NULL
categorie <- as.data.frame(final)
donnee <- merge(x = donnee, y = categorie, by.x = 'commentaire', by.y = 'sequence', all.x = TRUE)
#Création des colonnes vides pour accueillir les résultats
donnee[,prix] <- as.numeric(NA)
donnee[,employe_agent] <- as.numeric(NA)
donnee[,efficacite_processus_delai] <- as.numeric(NA)
donnee[,produits_et_services] <- as.numeric(NA)
#Traitement pour reclasser les labels et les scores
#Le modèle classe chaque ligne en fonction du plus gros score
#Chaque ligne est indépendante
#Il faut donc tout remettre en ordre
nom_col <- colnames(donnee)
#Boucle à travers toutes les lignes
for (i in 1:max(donnee$ligne)) {
#On isole les données de la ligne en cours, plus particulièrement les labels et les scores
tempo4 <- donnee[i,]
a <- donnee[i,c("labels")][[1]]
b <- donnee[i,c("scores")][[1]]
#On scanne parmi tous les labels
for (j in 1:length(a)) {
#On cherche à matcher le nom de la colonne avec le label en cours
for (k in 16 : length(nom_col)) {
if (a[j] == nom_col[k]) {
#Une fois trouvé, on lui attribue la valeur
modif <- glue::glue("tempo4$`{a[j]}` <- {b[j]}")
eval(parse(text = modif))
#On ramène les données ensemble
if (i == 1){
final = tempo4
}
final <- rbind(final, tempo4)
i <- i + 1
}
}
}
}
#On enlève les colonnes de labels et de scores
donnee <- na.omit(final[,-c(13:15)])
rownames(donnee) <- NULL
#Pour chaque catégorie, on additionneles valeurs de la classification
#et on retire les valeurs qui l'ont composées
donnee$categorie_prix <- rowSums(donnee[13:(13+length(prix)-1)], na.rm=TRUE)
donnee <- donnee[,-c(13:(13+length(prix)-1))]
donnee$categorie_employe_agent <- rowSums(donnee[13:(13+length(employe_agent)-1)], na.rm=TRUE)
donnee <- donnee[,-c(13:(13+length(employe_agent)-1))]
donnee$categorie_efficacite_processus_delai <- rowSums(donnee[13:(13+length(efficacite_processus_delai)-1)], na.rm=TRUE)
donnee <- donnee[,-c(13:(13+length(efficacite_processus_delai)-1))]
donnee$categorie_produits_et_services<- rowSums(donnee[13:(13+length(produits_et_services)-1)], na.rm=TRUE)
donnee <- donnee[,-c(13:(13+length(produits_et_services)-1))]
#On génère la moyenne et l'erreur type des variables
donnee <- as.data.frame(sqldf::sqldf("select distinct nps,
avg(colere) as colere_moyenne, stdev(colere) as colere_erreur_type,
avg(anticipation) as anticipation_moyenne, stdev(anticipation) as anticipation_erreur_type,
avg(degout) as degout_moyenne, stdev(degout) as degout_erreur_type,
avg(peur) as peur_moyenne, stdev(peur) as peur_erreur_type,
avg(joie) as joie_moyenne, stdev(joie) as joie_erreur_type,
avg(tristesse) as tristesse_moyenne, stdev(tristesse) as tristesse_erreur_type,
avg(surprise) as surprise_moyenne, stdev(surprise) as surprise_erreur_type,
avg(confiance) as confiance_moyenne, stdev(confiance) as confiance_erreur_type,
avg(syuzhet_negatif) as syuzhet_negatif_moyenne, stdev(syuzhet_negatif) as syuzhet_negatif_erreur_type,
avg(syuzhet_positif) as syuzhet_positif_moyenne, stdev(syuzhet_positif) as syuzhet_positif_erreur_type,
avg(categorie_prix) as prix_moyenne, stdev(categorie_prix) as prix_erreur_type,
avg(categorie_employe_agent) as employe_agent_moyenne, stdev(categorie_employe_agent) as employe_agent_erreur_type,
avg(categorie_efficacite_processus_delai) as efficacite_processus_delai_moyenne, stdev(categorie_efficacite_processus_delai) as efficacite_processus_delai_erreur_type,
avg(categorie_produits_et_services) as produits_et_services_moyenne, stdev(categorie_produits_et_services) as produits_et_services_erreur_type
from donnee
group by nps
order by nps"))
donnee$colere_erreur_type <- donnee$colere_erreur_type / nb_ligne
donnee$anticipation_erreur_type <- donnee$anticipation_erreur_type / nb_ligne
donnee$degout_erreur_type <- donnee$degout_erreur_type / nb_ligne
donnee$peur_erreur_type <- donnee$peur_erreur_type / nb_ligne
donnee$joie_erreur_type <- donnee$joie_erreur_type / nb_ligne
donnee$tristesse_erreur_type <- donnee$tristesse_erreur_type / nb_ligne
donnee$surprise_erreur_type <- donnee$surprise_erreur_type / nb_ligne
donnee$confiance_erreur_type <- donnee$confiance_erreur_type / nb_ligne
donnee$syuzhet_negatif_erreur_type <- donnee$syuzhet_negatif_erreur_type / nb_ligne
donnee$syuzhet_positif_erreur_type <- donnee$syuzhet_positif_erreur_type / nb_ligne
donnee$prix_erreur_type <- donnee$prix_erreur_type / nb_ligne
donnee$employe_agent_erreur_type <- donnee$employe_agent_erreur_type / nb_ligne
donnee$efficacite_processus_delai_erreur_type <- donnee$efficacite_processus_delai_erreur_type / nb_ligne
donnee$produits_et_services_erreur_type <- donnee$produits_et_services_erreur_type / nb_ligne
#Le format des données passe sous un format long
donnee <- tidyr::gather(data = donnee, key = variables, value = valeurs, -c(nps))
#On crée la table finale
donnee <- as.data.frame(sqldf::sqldf("select distinct nps,
replace(replace(variables, '_moyenne', ''), '_erreur_type', '') as variables,
sum(case when variables like '%moyenne%' then valeurs else 0 end) as moyenne,
sum(case when variables like '%erreur_type%' then valeurs else 0 end) as erreur_type
from donnee
group by nps, replace(replace(variables, '_moyenne', ''), '_erreur_type', '')
order by 1, 2"))
#Génération du graphique GGplot
donnee <- dplyr::filter(.data = donnee, variables %in% c("efficacite_processus_delai", "employe_agent", "produits_et_services", "prix"))
donnee <- ggplot2::ggplot(data = donnee) +
ggplot2::aes(x = variables, fill = nps, weight = moyenne) +
ggplot2::geom_bar(position = "dodge") +
ggplot2::labs(
x = "Thématiques exprimées",
y = "Valeurs en pourcentage",
title = "Voix des clients - ce que nos clients disent",
caption = "Barres d'erreur ± 1 erreur type",
subtitle = nom_sondage,
fill = "NPS") +
ggplot2::theme_minimal() +
ggplot2::theme(legend.position = "bottom")+
ggplot2::geom_errorbar(ggplot2::aes(ymin = moyenne - erreur_type, ymax = moyenne + erreur_type), width=.2,
position = ggplot2::position_dodge(.9)) +
ggplot2::scale_x_discrete(labels=c("Efficacité du processus et des délais", "Employ / agent", "Prix",
"Produits et services")) +
ggplot2::scale_y_continuous(labels = scales::percent)
ggplot2::ggplot_build(donnee)
print(donnee)
plot(donnee)
print("Donnes par NPS et variables")
print(donnee$data)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.