123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- from io import StringIO, BytesIO
- import pyexcel as pe
- import csv
- import math
- class genererTableau ():
-
- """
- ecrire_tableau :
- Permet de créer un tableau et l'exporter au format excel, ods ou csv à partir de données d'analyses.
-
- Input :
- data : données aggrégées généré par les méthodes de analyseStatistiques
- precision : nombre de décimales de précision
- quantitative_string_format : format d'écriture de la moyenne, écart type et taille d'échantillon des variables quantitatives
- qualitative_string_format : format d'écriture du n et de la proportion des variables qualitatives
- """
-
- def __init__ (self, data, precision = 2, quantitative_column_title = "{variable} mean +/- std (n)", quantitative_string_format = "{mean:.2f} +/- {std:.2f} ({n})", qualitative_column_title="{variable} no. (%)", qualitative_string_format = "{n} ({p:.2%})"):
-
- """
- Data : data contenant les données aggrégées, généré par analyseStatistiques.analyse_univarie
- """
-
- # Parametres
- self.quantitative_column_title = quantitative_column_title
- self.quantitative_string_format = quantitative_string_format
- self.qualitative_column_title = qualitative_column_title
- self.qualitative_string_format = qualitative_string_format
- self.absence_test = "Absence de test effectué (CI des tests conventionnels non remplis)"
- self.precision = precision
-
- self.data = data
- self.liste_variables = list(data.keys()) # Liste des variables analysables
- if "sous_groupes" in list(data.values())[0]:
- self.liste_axes = list(list(data.values())[0]["sous_groupes"].keys()) # Liste des axes d'analyses
- else:
- self.liste_axes = []
-
- # On récupère la liste des modalités de chaque axes
- self.liste_modalites = dict(zip(
- self.liste_axes,
- [self._obtenir_modalite_axe(x, True) for x in self.liste_axes]
- ))
-
- # On récupère la liste des modalités de chaque variable
- self.liste_modalites_variables = dict(zip(
- self.liste_variables,
- [self._obtenir_modalite_variable(x, True) for x in self.liste_variables]
- ))
-
- def _generer_texte_propre (self, texte):
- """
- Nettoie un texte
- Si il s'agit d'un flottant : le met en int
- """
-
- if type(texte) != type(str()):
- texte = str(int(texte))
-
- return(texte)
-
- def _generer_str_mean_std_n(self, mean, std, n):
-
- """
- Simple fonction qui écrit un string mélangeant moyenne, écart-type et taille de l'effectif
- Format par défault :
- {mean:.2f} +/- {std:.2f} ({n})
-
- Peut-être personalisé par le paramètre string_format.
-
- Input :
- mean, std et n
- """
-
- texte = self.quantitative_string_format.format(
- mean=round(mean, self.precision),
- std=round(std, self.precision),
- n=round(n, self.precision)
- )
-
- return(texte)
-
- def _generer_str_n_p(self, n, p):
-
- """
- Simple fonction qui écrit un string mélangeant moyenne, écart-type et taille de l'effectif
- Format par défault :
- {n} ({p:.0%})
-
- Peut-être personalisé par le paramètre string_format.
-
- Input :
- mean, std et n
- Sortie :
- texte
- """
-
- texte = self.qualitative_string_format.format(
- p = round(p, self.precision),
- n = round(n, self.precision)
- )
-
- return(texte)
-
- def _generer_str_valeur(self, valeur):
-
- """
- Simple fonction qui écrit un string à partir d'une valeur
-
- Input :
- valeur
- Sortie :
- texte
- """
-
- texte = "{}".format(
- round(valeur, self.precision)
- )
-
- return(texte)
-
- def _generer_str_ci(self, liste_ci):
-
- """
- Simple fonction qui écrit un string à partir d'une liste représentant un intervale de confiance
-
- Input :
- liste_ci : liste de 2 items décrivant un intervalle de confiance
- Sortie :
- texte
- """
-
- texte = "{}-{}".format(
- self._generer_str_valeur(liste_ci[0]),
- self._generer_str_valeur(liste_ci[1])
- )
-
- return(texte)
-
- def _generer_str_resultat_test(self, test):
-
- """
- Génère une liste avec formatage textuel du résultat d'un test
- Vérifie la bonne application du test avant.
-
- Input :
- test : liste contenant le résultat du test
- Sortie :
- liste de 3 éléments comprenant : Test, Paramètre et p
- """
-
- resultat = []
-
- if len(test[1].keys()) > 1:
- resultat.append(test[0]) # Nom du test
- resultat.append(self._generer_str_valeur(test[1]["statistic"])) # Paramètre de test
- resultat.append(self._formater_p_value(test[1]["p_value"])) # Petit p du test
- else:
- resultat.append(self.absence_test)
- resultat = resultat + ["", ""]
-
- return(resultat)
-
- def _formater_p_value(self, p_value):
-
- # Nombre de décimales concernées
- n_digit = len(str(p_value).split(".")[-1].split("0"))
- borne_basse = 5*math.pow(10, -n_digit)
- borne_sup = math.pow(10, -n_digit+1)
- estimation_p = borne_basse if p_value <= borne_basse else borne_sup
- # Affichage
- if p_value > 0.01:
- formated_p_value = str(round(p_value, 3))
- else:
- if p_value > 0.001:
- formated_p_value = "< "+str(estimation_p)
- else:
- formated_p_value = "< 10^"+str(n_digit-1)
- return(formated_p_value)
-
- def _verifier_existence_variables(self, variables):
-
- """
- Vérifie que les variables existent bien dans le jeu de données.
- Input : variables : liste des variables à tester
- Output : None
- """
-
- variables_manquantes = [x for x in variables if x not in self.liste_variables]
-
- if len(variables_manquantes) > 0:
- raise Exception("Erreur, les variables {} sont inexistantes des données aggrégées.".format(
- ", ".join(variables_manquantes)
- ))
-
- def _verifier_existence_axes(self, axes):
-
- """
- Vérifie que les axes existent bien dans le jeu de données.
- Input : variables : liste des axes à tester
- Output : None
- """
-
- if axes is not None:
- axes_manquants = [x for x in axes if x not in self.liste_axes]
- else:
- axes_manquants = []
-
- if len(axes_manquants) > 0:
- raise Exception("Erreur, les axes {} sont inexistantes des données aggrégées.".format(
- ", ".join(axes_manquants)
- ))
-
- def _obtenir_modalite_axe (self, axe, ajouter_nom = True):
-
- """
- Retourne une liste contenant toutes les modalités pour un axe donné
- Input :
- axe : nom de l'axe à analyse
- ajouter_nom : Si True, alors le nom de l'axe est ajouté à la modalité (concaténation)
- """
-
- # Liste des modalités
- modalites = list(self.data.values())[0]["sous_groupes"][axe].keys()
-
- # Post-traitement : on corrige les float et boolean pour les afficher en int
- modalites = [int(x) if type(x) != type(str()) else x for x in modalites]
-
- # On affiche le nom de l'axe avec si nécessaire
- modalites_avec_str = [axe+" "+str(x) for x in modalites]
-
- if ajouter_nom:
- return(modalites_avec_str)
- else:
- return(modalites)
-
- def _obtenir_modalite_variable (self, variable, ajouter_nom = True):
-
- """
- Retourne une liste contenant toutes les modalités pour une variable donnée
- Input :
- variable : nom de la variable à analyser
- ajouter_nom : Si True, alors le nom de l'axe est ajouté à la modalité (concaténation)
- """
-
- # Liste des modalités
- modalites = [x for x in list(self.data[variable]["global"].keys()) if x != "total"]
-
- # Post-traitement : on corrige les float et boolean pour les afficher en int
- modalites = [int(x) if type(x) != type(str()) else x for x in modalites]
-
- # On affiche le nom de l'axe avec si nécessaire
- modalites_avec_str = [variable+" "+str(x) for x in modalites]
-
- if ajouter_nom:
- return(modalites_avec_str)
- else:
- return(modalites)
-
- def _generer_en_tete_descriptif(self, axes):
-
- """
- Genere l'en-tête d'un tableau Descriptif
- Input :
- axes : liste des axes d'analyse
- Output : None
- """
-
- # Initialisation de l'en-tête
- en_tete = ["","",""]
-
- # Lecture des axes
- for axe in axes:
- en_tete = en_tete+self.liste_modalites[axe]+["Test","Paramètre", "p"]
-
- return en_tete
-
- def _generer_en_tete_detail(self, variable, iqr=False, extremes=False):
-
- """
- Genere l'en-tête d'un tableau Descriptif
- Input :
- axes : liste des axes d'analyse
- iqr, boolean: Si true, intègle les intervalles interquartiles
- extremes, boolean: Si true, intègle les valeurs extrêmes
- Output : None
- """
-
- # Type de variable
- type_variable = self.data[variable]["type"]
-
- # Initialisation de l'en-tête
- en_tete = []
-
- en_tete.append(variable) # Nom de la variable
- en_tete.append("") # Espace technique
-
- ## Pour les variables quantitatives
- if type_variable == "quantitative":
- en_tete = en_tete + ["n", "mean", "std", "IC 95%"]
- extremes_iqr = [
- ("min", extremes),
- ("Q25", iqr),
- ("median", iqr),
- ("Q75", iqr),
- ("max", extremes),
- ]
- for title, rule in extremes_iqr:
- if rule:
- en_tete.append(title)
- elif type_variable == "qualitative":
- en_tete.append("n")
- en_tete = en_tete + self.liste_modalites_variables[variable]
-
- en_tete = en_tete + ["Test", "Parameter", "p"]
-
- return en_tete
-
- def _generer_lignes_descriptif (self, variable, axes):
-
- """
- Genere des ligne d'analyse d'un tableau descriptif pour une variable
- Input :
- variable : nom de la variable
- axes : axes sur lesquels générer la ligne
- """
-
- lignes = []
-
- if self.data[variable]["type"] == "quantitative":
- # Ligne d'une variable quantitative : une seule ligne
-
- ligne = [] # On instancie la ligne
- ligne.append(self.quantitative_column_title.format(variable=variable)) # Ajout du nom de variable
- ligne.append("") # Espace vide, technique
- ligne.append(self._generer_str_mean_std_n(
- self.data[variable]["global"]["mean"],
- self.data[variable]["global"]["std"],
- self.data[variable]["global"]["n"]
- )) # Données globales
-
- # Données liées à chaque axe
- for axe in axes:
-
- donnees_axe = self.data[variable]["sous_groupes"][axe]
- # Pour chaque valeur de l'axe
- for valeur in donnees_axe.keys():
-
- ligne.append(self._generer_str_mean_std_n(
- donnees_axe[valeur]["mean"],
- donnees_axe[valeur]["std"],
- donnees_axe[valeur]["n"]
- )) # Données hors test
-
- ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe]) # Données du test
-
- lignes.append(ligne)
- else:
- # Lignes d'un variable qualitative : une ligne par modalité
-
- ## On écrit la première ligne : nom de variable et test
- ligne = [] # On instancie la ligne
- ligne.append(self.qualitative_column_title.format(variable=variable)) # Nom de variable
- ligne = ligne + ["", ""] # Variable vides
-
- # Données liées à chaque axe
- for axe in axes:
-
- blank_list = ["" for x in range(len(self.liste_modalites[axe]))]
-
- ligne = ligne + blank_list # Blanc lié aux tests
- ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe]) # Données du test
-
- lignes.append(ligne)
-
- ## On écrit les lignes propres à chaque variable
-
- liste_labels = self.data[variable]["global"].keys()
-
- for label in liste_labels:
-
- # On ignore le label total
- if label != "total":
-
- ligne = []
-
- ligne.append("") # Première ligne vide
- ligne.append(self._generer_texte_propre(label)) # Libelé analysé
- ligne.append(self._generer_str_n_p(
- n = self.data[variable]["global"][label]["n"],
- p = self.data[variable]["global"][label]["p"]
- )) # Données générales
-
- # Valeur par axe
- for axe in axes:
-
- liste_valeurs = list(self.data[variable]["sous_groupes"][axe].keys())
-
- # Pour chaque valeur de l'axe
- for valeur in liste_valeurs: # On met 0 si la valeur n'existe pas
-
- donnees_axe = self.data[variable]["sous_groupes"][axe][valeur]
-
- n = donnees_axe[label]["n"] if label in donnees_axe.keys() else 0
- p = donnees_axe[label]["p"] if label in donnees_axe.keys() else 0
- ligne.append(self._generer_str_n_p(
- n = n,
- p = p
- )) # Données spécifique à chaque axe
-
- ligne = ligne + ["", "", ""] # Espaces vides entre 2 axes
-
- lignes.append(ligne)
-
- return(lignes)
-
- def _generer_lignes_detail(self, variable, axe = None, extremes=False, iqr=False):
-
- """
- Genere des ligne d'analyse d'un tableau détaillé pour une variable
- Input :
- variable : nom de la variable
- axe : axe sur lesquels générer les ligne
- iqr, boolean: Si true, intègle les intervalles interquartiles
- extremes, boolean: Si true, intègle les valeurs extrêmes
- """
-
- lignes = []
-
- if self.data[variable]["type"] == "quantitative":
- # Génération de la ligne simple
- if axe is None:
- ligne = [] # On instancie la ligne
- ligne = ligne + ["", ""] # Blanc technique
- ligne.append(self._generer_str_valeur(self.data[variable]["global"]["n"])) # Taille d'échantillon
- ligne.append(self._generer_str_valeur(self.data[variable]["global"]["mean"])) # Moyenne
- ligne.append(self._generer_str_valeur(self.data[variable]["global"]["std"])) # Ecart type
- ligne.append(self._generer_str_ci(self.data[variable]["global"]["ci_95"])) # IC à 95%
- extremes_iqr = [
- ("min", extremes),
- ("Q25", iqr),
- ("median", iqr),
- ("Q75", iqr),
- ("max", extremes),
- ]
- for value, rule in extremes_iqr:
- if rule:
- ligne.append(
- self._generer_str_valeur(
- self.data[variable]["global"][value]
- )
- )
- ligne = ligne + ["", "", ""] # Blanc technique
- lignes.append(ligne)
- else:
- # On vérifie l'axe
- self._verifier_existence_axes([axe])
-
- # Données sur l'axe
- donnees_axe = self.data[variable]["sous_groupes"][axe]
-
- # On écrit le nom de l'axe
- ligne = []
- ligne.append(axe) # Nom de l'axe
- ligne = ligne + ["","","","",""]
-
- # Résultat du test
- ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe])
-
- lignes.append(ligne)
-
- # On parcours toutes les modalités de l'axe
- for valeur_axe in donnees_axe.keys():
-
- ligne = []
-
- ligne.append("") # Ligne blanche technique
- ligne.append(self._generer_texte_propre(valeur_axe)) # Valeur sur l'axe
- ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["n"])) # Taille d'échantillon
- ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["mean"])) # Moyenne
- ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["std"])) # Ecart type
- ligne.append(self._generer_str_ci(donnees_axe[valeur_axe]["ci_95"])) # IC à 95%
- extremes_iqr = [
- ("min", extremes),
- ("Q25", iqr),
- ("median", iqr),
- ("Q75", iqr),
- ("max", extremes),
- ]
- for value, rule in extremes_iqr:
- if rule:
- ligne.append(
- self._generer_str_valeur(
- self.data[variable]["global"][value]
- )
- )
- ligne = ligne + ["", "", ""] # Blanc technique
- lignes.append(ligne)
- else:
- # Génération de la ligne simple
-
- if axe is None:
- ligne = [] # On instancie la ligne
- ligne = ligne + ["", ""] # Blanc technique
- ligne.append(self._generer_str_valeur(self.data[variable]["global"]["total"])) # Taille d'échantillon
- for variable_valeur in self.data[variable]["global"].keys():
- if variable_valeur != "total":
- ligne.append(self._generer_str_n_p(
- n = self.data[variable]["global"][variable_valeur]["n"],
- p = self.data[variable]["global"][variable_valeur]["p"]
- ))
- ligne = ligne + ["", "", ""] # Blanc technique
-
- lignes.append(ligne)
-
- else:
- # On vérifie l'axe
- self._verifier_existence_axes([axe])
-
- # Données sur l'axe
- donnees_axe = self.data[variable]["sous_groupes"][axe]
-
- # On écrit le nom de l'axe
- ligne = []
- ligne.append(axe) # Nom de l'axe
- ligne = ligne + ["",""] + ["" for x in range(len(self.liste_modalites_variables[variable]))] # Blanc technique
-
- # Résultat du test
- ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe])
-
- lignes.append(ligne)
-
- # On parcours toutes les modalités de l'axe
- for valeur_axe in donnees_axe.keys():
-
- ligne = []
-
- ligne.append("") # Ligne blanche technique
- ligne.append(self._generer_texte_propre(valeur_axe)) # Valeur sur l'axe
- ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["total"])) # Taille d'échantillon
-
- for variable_valeur in donnees_axe[valeur_axe].keys():
- if variable_valeur != "total":
- ligne.append(self._generer_str_n_p(
- n = donnees_axe[valeur_axe][variable_valeur]["n"],
- p = donnees_axe[valeur_axe][variable_valeur]["p"]
- ))
-
- ligne = ligne + ["", "", ""] # Blanc technique
- lignes.append(ligne)
-
- return lignes
-
- def _generer_sortie (self, chemin, tableau, format_fichier):
-
- """
- Génère le fichier de sortie
- input :
- chemin : chemin du fichier de sortie
- tableau : liste contenant les lignes à écrire
- format_fichier : format du fichier de sortie
- output :
- si chemin textuel : aucune sortie, écriture du fichier
- si chemin du fichier vaut None : retour du stream
- """
-
- # Création du fichier
- if format_fichier in ["csv", "tsv"]:
- f = StringIO()
- elif format_fichier in ["ods", "xlsx"]:
- f = BytesIO()
-
- # Ecriture du fichier en stream
- if format_fichier == 'csv':
- csv.writer(f, delimiter = ",").writerows(tableau)
- elif format_fichier == 'tsv':
- csv.writer(f, delimiter = "\t").writerows(tableau)
- elif format_fichier in ['ods','xlsx']:
- sheet = pe.Sheet(tableau)
- f = sheet.save_to_memory(format_fichier, f)
- elif format_fichier == 'raw':
- f = tableau
- else:
- raise Exception("Format de sortie non supporté.")
-
- if type(chemin) == type(str()):
- # On écrit le fichier
- if format_fichier in ["csv", "tsv"]:
- fichier_sortie = open(chemin, "w")
- else:
- fichier_sortie = open(chemin, "wb")
-
- fichier_sortie.write(f.getvalue())
- else:
- # On renvoie le buffer
- return (f)
-
- def tableau_descriptif (self, chemin, variables, axes = None, format_sortie = "csv"):
-
- """
- Décrit le contenu de plusieurs variables selon un axe ou plusieurs axes donnés.
- L'axe correspond forcément à une variable qualitative.
-
- Input :
- chemin : chemin du fichier de sortie
- variables : liste des variables à traiter
- axes : liste des axes d'analyse
- format_sortie :
- 'tsv' pour un tableau séparé par une tabulation
- 'csv' pour un tableau séparé par une virgule
- 'ods' pour un fichier ods
- 'xlsx' pour un fichier xlsx
- 'raw' pour un envoie brut des données (liste python)
- Output :
- si chemin textuel : aucune sortie, écriture du fichier
- si chemin du fichier vaut None : retour du stream
- """
-
- # Vérification que toutes les variables et axes existent
- self._verifier_existence_variables(variables)
- self._verifier_existence_axes(axes)
-
- # Récupération des donnée
-
- # Génération de l'en-tête
- en_tete = self._generer_en_tete_descriptif(axes)
-
- # Création du tableau
- tableau = []
- tableau.append(en_tete)
-
- # Traitement des variables une à une
- for variable in variables:
-
- lignes = self._generer_lignes_descriptif(variable, axes)
- tableau = tableau+lignes
-
- # Generation de la sortie
- sortie = self._generer_sortie(chemin, tableau, format_sortie)
-
- return(sortie)
-
- def tableau_detail_variable (self, chemin, variable, axes = None, iqr=False, extremes=False, format_sortie = "csv"):
-
- """
- Décrit le contenu d'une variable selon un axe ou plusieurs axes donnés.
- Les axes correspondent forcément à des variable qualitatives.
-
- Input :
- chemin : chemin du fichier de sortie
- variable : nom de la variable à traiter
- axes : liste des axes d'analyse
- iqr, boolean: Si True, inclus les intervalles interquartiles
- extremes, boolean: Si True, inclus les extremes (min, max)
- format_sortie :
- 'tsv' pour un tableau séparé par une tabulation
- 'csv' pour un tableau séparé par une virgule
- 'ods' pour un fichier ods
- 'xlsx' pour un fichier xlsx
- 'raw' pour un envoi brut des données pythons
- Output :
- si chemin textuel : aucune sortie, écriture du fichier
- si chemin du fichier vaut None : retour du stream
- """
-
- # Vérification que toutes les variables et axes existent
- self._verifier_existence_variables([variable])
- self._verifier_existence_axes(axes)
-
- # Récupération des donnée
-
- # Génération de l'en-tête
- en_tete = self._generer_en_tete_detail(variable, extremes=extremes, iqr=iqr)
-
- # Création du tableau
- tableau = []
- tableau.append(en_tete)
-
- # On génère la première ligne d'analyse
- tableau = tableau + self._generer_lignes_detail(variable, None, extremes=extremes, iqr=iqr)
-
- # Ligne vide
- tableau.append(["" for x in range(len(tableau[-1]))])
-
- # Ajout des axes
- if axes is not None:
- for axe in axes:
- tableau = tableau + self._generer_lignes_detail(variable, axe, extremes=extremes, iqr=iqr)
- # Generation de la sortie
- sortie = self._generer_sortie(chemin, tableau, format_sortie)
-
- return(sortie)
|