genererTableau-checkpoint.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. from io import StringIO, BytesIO
  2. import pyexcel as pe
  3. import csv
  4. import math
  5. class genererTableau ():
  6. """
  7. ecrire_tableau :
  8. Permet de créer un tableau et l'exporter au format excel, ods ou csv à partir de données d'analyses.
  9. Input :
  10. data : données aggrégées généré par les méthodes de analyseStatistiques
  11. precision : nombre de décimales de précision
  12. quantitative_string_format : format d'écriture de la moyenne, écart type et taille d'échantillon des variables quantitatives
  13. qualitative_string_format : format d'écriture du n et de la proportion des variables qualitatives
  14. """
  15. def __init__ (self, data, precision = 2, quantitative_string_format = "{mean:.2f} +/- {std:.2f} ({n})", qualitative_string_format = "{n} ({p:.2%})"):
  16. """
  17. Data : data contenant les données aggrégées, généré par analyseStatistiques.analyse_univarie
  18. """
  19. # Parametres
  20. self.quantitative_string_format = quantitative_string_format
  21. self.qualitative_string_format = qualitative_string_format
  22. self.absence_test = "Absence de test effectué (CI des tests conventionnels non remplis)"
  23. self.precision = precision
  24. self.data = data
  25. self.liste_variables = list(data.keys()) # Liste des variables analysables
  26. self.liste_axes = list(list(data.values())[0]["sous_groupes"].keys()) # Liste des axes d'analyses
  27. # On récupère la liste des modalités de chaque axes
  28. self.liste_modalites = dict(zip(
  29. self.liste_axes,
  30. [self._obtenir_modalite_axe(x, True) for x in self.liste_axes]
  31. ))
  32. # On récupère la liste des modalités de chaque variable
  33. self.liste_modalites_variables = dict(zip(
  34. self.liste_variables,
  35. [self._obtenir_modalite_variable(x, True) for x in self.liste_variables]
  36. ))
  37. def _generer_texte_propre (self, texte):
  38. """
  39. Nettoie un texte
  40. Si il s'agit d'un flottant : le met en int
  41. """
  42. if type(texte) != type(str()):
  43. texte = str(int(texte))
  44. return(texte)
  45. def _generer_str_mean_std_n(self, mean, std, n):
  46. """
  47. Simple fonction qui écrit un string mélangeant moyenne, écart-type et taille de l'effectif
  48. Format par défault :
  49. {mean:.2f} +/- {std:.2f} ({n})
  50. Peut-être personalisé par le paramètre string_format.
  51. Input :
  52. mean, std et n
  53. """
  54. texte = self.quantitative_string_format.format(
  55. mean = round(mean, self.precision),
  56. std=round(std, self.precision),
  57. n=round(n, self.precision)
  58. )
  59. return(texte)
  60. def _generer_str_n_p(self, n, p):
  61. """
  62. Simple fonction qui écrit un string mélangeant moyenne, écart-type et taille de l'effectif
  63. Format par défault :
  64. {n} ({p:.0%})
  65. Peut-être personalisé par le paramètre string_format.
  66. Input :
  67. mean, std et n
  68. Sortie :
  69. texte
  70. """
  71. texte = self.qualitative_string_format.format(
  72. p = round(p, self.precision),
  73. n = round(n, self.precision)
  74. )
  75. return(texte)
  76. def _generer_str_valeur(self, valeur):
  77. """
  78. Simple fonction qui écrit un string à partir d'une valeur
  79. Input :
  80. valeur
  81. Sortie :
  82. texte
  83. """
  84. texte = "{}".format(
  85. round(valeur, self.precision)
  86. )
  87. return(texte)
  88. def _generer_str_ci(self, liste_ci):
  89. """
  90. Simple fonction qui écrit un string à partir d'une liste représentant un intervale de confiance
  91. Input :
  92. liste_ci : liste de 2 items décrivant un intervalle de confiance
  93. Sortie :
  94. texte
  95. """
  96. texte = "{}-{}".format(
  97. self._generer_str_valeur(liste_ci[0]),
  98. self._generer_str_valeur(liste_ci[1])
  99. )
  100. return(texte)
  101. def _generer_str_resultat_test(self, test):
  102. """
  103. Génère une liste avec formatage textuel du résultat d'un test
  104. Vérifie la bonne application du test avant.
  105. Input :
  106. test : liste contenant le résultat du test
  107. Sortie :
  108. liste de 3 éléments comprenant : Test, Paramètre et p
  109. """
  110. resultat = []
  111. if len(test[1].keys()) > 1:
  112. resultat.append(test[0]) # Nom du test
  113. resultat.append(self._generer_str_valeur(test[1]["statistic"])) # Paramètre de test
  114. resultat.append(self._formater_p_value(test[1]["p_value"])) # Petit p du test
  115. else:
  116. resultat.append(self.absence_test)
  117. resultat = resultat + ["", ""]
  118. return(resultat)
  119. def _formater_p_value(self, p_value):
  120. # Nombre de décimales concernées
  121. n_digit = len(str(p_value).split(".")[-1].split("0"))
  122. borne_basse = 5*math.pow(10, -n_digit)
  123. borne_sup = math.pow(10, -n_digit+1)
  124. estimation_p = borne_basse if p_value <= borne_basse else borne_sup
  125. # Affichage
  126. if p_value > 0.01:
  127. formated_p_value = str(round(p_value, 3))
  128. else:
  129. if p_value > 0.001:
  130. formated_p_value = "< "+str(estimation_p)
  131. else:
  132. formated_p_value = "< 10^"+str(n_digit-1)
  133. return(formated_p_value)
  134. def _verifier_existence_variables(self, variables):
  135. """
  136. Vérifie que les variables existent bien dans le jeu de données.
  137. Input : variables : liste des variables à tester
  138. Output : None
  139. """
  140. variables_manquantes = [x for x in variables if x not in self.liste_variables]
  141. if len(variables_manquantes) > 0:
  142. raise Exception("Erreur, les variables {} sont inexistantes des données aggrégées.".format(
  143. ", ".join(variables_manquantes)
  144. ))
  145. def _verifier_existence_axes(self, axes):
  146. """
  147. Vérifie que les axes existent bien dans le jeu de données.
  148. Input : variables : liste des axes à tester
  149. Output : None
  150. """
  151. if axes is not None:
  152. axes_manquants = [x for x in axes if x not in self.liste_axes]
  153. else:
  154. axes_manquants = []
  155. if len(axes_manquants) > 0:
  156. raise Exception("Erreur, les axes {} sont inexistantes des données aggrégées.".format(
  157. ", ".join(axes_manquants)
  158. ))
  159. def _obtenir_modalite_axe (self, axe, ajouter_nom = True):
  160. """
  161. Retourne une liste contenant toutes les modalités pour un axe donné
  162. Input :
  163. axe : nom de l'axe à analyse
  164. ajouter_nom : Si True, alors le nom de l'axe est ajouté à la modalité (concaténation)
  165. """
  166. # Liste des modalités
  167. modalites = list(self.data.values())[0]["sous_groupes"][axe].keys()
  168. # Post-traitement : on corrige les float et boolean pour les afficher en int
  169. modalites = [int(x) if type(x) != type(str()) else x for x in modalites]
  170. # On affiche le nom de l'axe avec si nécessaire
  171. modalites_avec_str = [axe+" "+str(x) for x in modalites]
  172. if ajouter_nom:
  173. return(modalites_avec_str)
  174. else:
  175. return(modalites)
  176. def _obtenir_modalite_variable (self, variable, ajouter_nom = True):
  177. """
  178. Retourne une liste contenant toutes les modalités pour une variable donnée
  179. Input :
  180. variable : nom de la variable à analyser
  181. ajouter_nom : Si True, alors le nom de l'axe est ajouté à la modalité (concaténation)
  182. """
  183. # Liste des modalités
  184. modalites = [x for x in list(self.data[variable]["global"].keys()) if x != "total"]
  185. # Post-traitement : on corrige les float et boolean pour les afficher en int
  186. modalites = [int(x) if type(x) != type(str()) else x for x in modalites]
  187. # On affiche le nom de l'axe avec si nécessaire
  188. modalites_avec_str = [axe+" "+str(x) for x in modalites]
  189. if ajouter_nom:
  190. return(modalites_avec_str)
  191. else:
  192. return(modalites)
  193. def _generer_en_tete_descriptif(self, axes):
  194. """
  195. Genere l'en-tête d'un tableau Descriptif
  196. Input :
  197. axes : liste des axes d'analyse
  198. Output : None
  199. """
  200. # Initialisation de l'en-tête
  201. en_tete = ["","",""]
  202. # Lecture des axes
  203. for axe in axes:
  204. en_tete = en_tete+self.liste_modalites[axe]+["Test","Paramètre", "p"]
  205. return en_tete
  206. def _generer_en_tete_detail(self, variable):
  207. """
  208. Genere l'en-tête d'un tableau Descriptif
  209. Input :
  210. axes : liste des axes d'analyse
  211. Output : None
  212. """
  213. # Type de variable
  214. type_variable = self.data[variable]["type"]
  215. # Initialisation de l'en-tête
  216. en_tete = []
  217. en_tete.append(variable) # Nom de la variable
  218. en_tete.append("") # Espace technique
  219. ## Pour les variables quantitatives
  220. if type_variable == "quantitative":
  221. en_tete = en_tete + ["n", "mean", "std", "IC 95%"]
  222. elif type_variable == "qualitative":
  223. en_tete.append("n")
  224. en_tete = en_tete + self.liste_modalites_variables[variable]
  225. en_tete = en_tete + ["Test", "Parameter", "p"]
  226. return en_tete
  227. def _generer_lignes_descriptif (self, variable, axes):
  228. """
  229. Genere des ligne d'analyse d'un tableau descriptif pour une variable
  230. Input :
  231. variable : nom de la variable
  232. axes : axes sur lesquels générer la ligne
  233. """
  234. lignes = []
  235. if self.data[variable]["type"] == "quantitative":
  236. # Ligne d'une variable quantitative : une seule ligne
  237. ligne = [] # On instancie la ligne
  238. ligne.append(variable+" mean +/- std (n)") # Ajout du nom de variable
  239. ligne.append("") # Espace vide, technique
  240. ligne.append(self._generer_str_mean_std_n(
  241. self.data[variable]["global"]["mean"],
  242. self.data[variable]["global"]["std"],
  243. self.data[variable]["global"]["n"]
  244. )) # Données globales
  245. # Données liées à chaque axe
  246. for axe in axes:
  247. donnees_axe = self.data[variable]["sous_groupes"][axe]
  248. # Pour chaque valeur de l'axe
  249. for valeur in donnees_axe.keys():
  250. ligne.append(self._generer_str_mean_std_n(
  251. donnees_axe[valeur]["mean"],
  252. donnees_axe[valeur]["std"],
  253. donnees_axe[valeur]["n"]
  254. )) # Données hors test
  255. ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe]) # Données du test
  256. lignes.append(ligne)
  257. else:
  258. # Lignes d'un variable qualitative : une ligne par modalité
  259. ## On écrit la première ligne : nom de variable et test
  260. ligne = [] # On instancie la ligne
  261. ligne.append(variable+" no. (%)") # Nom de variable
  262. ligne = ligne + ["", ""] # Variable vides
  263. # Données liées à chaque axe
  264. for axe in axes:
  265. blank_list = ["" for x in range(len(self.liste_modalites[axe]))]
  266. ligne = ligne + blank_list # Blanc lié aux tests
  267. ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe]) # Données du test
  268. lignes.append(ligne)
  269. ## On écrit les lignes propres à chaque variable
  270. liste_labels = self.data[variable]["global"].keys()
  271. for label in liste_labels:
  272. # On ignore le label total
  273. if label != "total":
  274. ligne = []
  275. ligne.append("") # Première ligne vide
  276. ligne.append(self._generer_texte_propre(label)) # Libelé analysé
  277. ligne.append(self._generer_str_n_p(
  278. n = self.data[variable]["global"][label]["n"],
  279. p = self.data[variable]["global"][label]["p"]
  280. )) # Données générales
  281. # Valeur par axe
  282. for axe in axes:
  283. liste_valeurs = list(self.data[variable]["sous_groupes"][axe].keys())
  284. # Pour chaque valeur de l'axe
  285. for valeur in liste_valeurs: # On met 0 si la valeur n'existe pas
  286. donnees_axe = self.data[variable]["sous_groupes"][axe][valeur]
  287. n = donnees_axe[label]["n"] if label in donnees_axe.keys() else 0
  288. p = donnees_axe[label]["p"] if label in donnees_axe.keys() else 0
  289. ligne.append(self._generer_str_n_p(
  290. n = n,
  291. p = p
  292. )) # Données spécifique à chaque axe
  293. ligne = ligne + ["", "", ""] # Espaces vides entre 2 axes
  294. lignes.append(ligne)
  295. return(lignes)
  296. def _generer_lignes_detail(self, variable, axe = None):
  297. """
  298. Genere des ligne d'analyse d'un tableau détaillé pour une variable
  299. Input :
  300. variable : nom de la variable
  301. axe : axe sur lesquels générer les ligne
  302. """
  303. lignes = []
  304. if self.data[variable]["type"] == "quantitative":
  305. # Génération de la ligne simple
  306. if axe is None:
  307. ligne = [] # On instancie la ligne
  308. ligne = ligne + ["", ""] # Blanc technique
  309. ligne.append(self._generer_str_valeur(self.data[variable]["global"]["n"])) # Taille d'échantillon
  310. ligne.append(self._generer_str_valeur(self.data[variable]["global"]["mean"])) # Moyenne
  311. ligne.append(self._generer_str_valeur(self.data[variable]["global"]["std"])) # Ecart type
  312. ligne.append(self._generer_str_ci(self.data[variable]["global"]["ci_95"])) # IC à 95%
  313. ligne = ligne + ["", "", ""] # Blanc technique
  314. lignes.append(ligne)
  315. else:
  316. # On vérifie l'axe
  317. self._verifier_existence_axes([axe])
  318. # Données sur l'axe
  319. donnees_axe = self.data[variable]["sous_groupes"][axe]
  320. # On écrit le nom de l'axe
  321. ligne = []
  322. ligne.append(axe) # Nom de l'axe
  323. ligne = ligne + ["","","","",""]
  324. # Résultat du test
  325. ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe])
  326. lignes.append(ligne)
  327. # On parcours toutes les modalités de l'axe
  328. for valeur_axe in donnees_axe.keys():
  329. ligne = []
  330. ligne.append("") # Ligne blanche technique
  331. ligne.append(self._generer_texte_propre(valeur_axe)) # Valeur sur l'axe
  332. ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["n"])) # Taille d'échantillon
  333. ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["mean"])) # Moyenne
  334. ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["std"])) # Ecart type
  335. ligne.append(self._generer_str_ci(donnees_axe[valeur_axe]["ci_95"])) # IC à 95%
  336. ligne = ligne + ["", "", ""] # Blanc technique
  337. lignes.append(ligne)
  338. else:
  339. # Génération de la ligne simple
  340. if axe is None:
  341. ligne = [] # On instancie la ligne
  342. ligne = ligne + ["", ""] # Blanc technique
  343. ligne.append(self._generer_str_valeur(self.data[variable]["global"]["total"])) # Taille d'échantillon
  344. for variable_valeur in self.data[variable]["global"].keys():
  345. if variable_valeur != "total":
  346. ligne.append(self._generer_str_n_p(
  347. n = self.data[variable]["global"][variable_valeur]["n"],
  348. p = self.data[variable]["global"][variable_valeur]["p"]
  349. ))
  350. ligne = ligne + ["", "", ""] # Blanc technique
  351. lignes.append(ligne)
  352. else:
  353. # On vérifie l'axe
  354. self._verifier_existence_axes([axe])
  355. # Données sur l'axe
  356. donnees_axe = self.data[variable]["sous_groupes"][axe]
  357. # On écrit le nom de l'axe
  358. ligne = []
  359. ligne.append(axe) # Nom de l'axe
  360. ligne = ligne + ["",""] + ["" for x in range(len(self.liste_modalites_variables[variable]))] # Blanc technique
  361. # Résultat du test
  362. ligne = ligne + self._generer_str_resultat_test(self.data[variable]["test"][axe])
  363. lignes.append(ligne)
  364. # On parcours toutes les modalités de l'axe
  365. for valeur_axe in donnees_axe.keys():
  366. ligne = []
  367. ligne.append("") # Ligne blanche technique
  368. ligne.append(self._generer_texte_propre(valeur_axe)) # Valeur sur l'axe
  369. ligne.append(self._generer_str_valeur(donnees_axe[valeur_axe]["total"])) # Taille d'échantillon
  370. for variable_valeur in donnees_axe[valeur_axe].keys():
  371. if variable_valeur != "total":
  372. ligne.append(self._generer_str_n_p(
  373. n = donnees_axe[valeur_axe][variable_valeur]["n"],
  374. p = donnees_axe[valeur_axe][variable_valeur]["p"]
  375. ))
  376. ligne = ligne + ["", "", ""] # Blanc technique
  377. lignes.append(ligne)
  378. return lignes
  379. def _generer_sortie (self, chemin, tableau, format_fichier):
  380. """
  381. Génère le fichier de sortie
  382. input :
  383. chemin : chemin du fichier de sortie
  384. tableau : liste contenant les lignes à écrire
  385. format_fichier : format du fichier de sortie
  386. output :
  387. si chemin textuel : aucune sortie, écriture du fichier
  388. si chemin du fichier vaut None : retour du stream
  389. """
  390. # Création du fichier
  391. if format_fichier in ["csv", "tsv"]:
  392. f = StringIO()
  393. elif format_fichier in ["ods", "xlsx"]:
  394. f = BytesIO()
  395. # Ecriture du fichier en stream
  396. if format_fichier == 'csv':
  397. csv.writer(f, delimiter = ",").writerows(tableau)
  398. elif format_fichier == 'tsv':
  399. csv.writer(f, delimiter = "\t").writerows(tableau)
  400. elif format_fichier in ['ods','xlsx']:
  401. sheet = pe.Sheet(data)
  402. f = sheet.save_to_memory(format_fichier, f)
  403. elif format_fichier == 'raw':
  404. f = tableau
  405. else:
  406. raise Exception("Format de sortie non supporté.")
  407. if type(chemin) == type(str()):
  408. # On écrit le fichier
  409. if format_fichier in ["csv", "tsv"]:
  410. fichier_sortie = open(chemin, "w")
  411. else:
  412. fichier_sortie = open(chemin, "wb")
  413. fichier_sortie.write(file)
  414. else:
  415. # On renvoie le buffer
  416. return (f)
  417. def tableau_descriptif (self, chemin, variables, axes = None, format_sortie = "csv"):
  418. """
  419. Décrit le contenu de plusieurs variables selon un axe ou plusieurs axes donnés.
  420. L'axe correspond forcément à une variable qualitative.
  421. Input :
  422. chemin : chemin du fichier de sortie
  423. variables : liste des variables à traiter
  424. axes : liste des axes d'analyse
  425. format_sortie :
  426. 'tsv' pour un tableau séparé par une tabulation
  427. 'csv' pour un tableau séparé par une virgule
  428. 'ods' pour un fichier ods
  429. 'xlsx' pour un fichier xlsx
  430. 'raw' pour un envoie brut des données (liste python)
  431. Output :
  432. si chemin textuel : aucune sortie, écriture du fichier
  433. si chemin du fichier vaut None : retour du stream
  434. """
  435. # Vérification que toutes les variables et axes existent
  436. self._verifier_existence_variables(variables)
  437. self._verifier_existence_axes(axes)
  438. # Récupération des donnée
  439. # Génération de l'en-tête
  440. en_tete = self._generer_en_tete_descriptif(axes)
  441. # Création du tableau
  442. tableau = []
  443. tableau.append(en_tete)
  444. # Traitement des variables une à une
  445. for variable in variables:
  446. lignes = self._generer_lignes_descriptif(variable, axes)
  447. tableau = tableau+lignes
  448. # Generation de la sortie
  449. sortie = self._generer_sortie(chemin, tableau, format_sortie)
  450. return(sortie)
  451. def tableau_detail_variable (self, chemin, variable, axes = None, format_sortie = "csv"):
  452. """
  453. Décrit le contenu d'une variable selon un axe ou plusieurs axes donnés.
  454. Les axes correspondent forcément à des variable qualitatives.
  455. Input :
  456. chemin : chemin du fichier de sortie
  457. variable : nom de la variable à traiter
  458. axes : liste des axes d'analyse
  459. format_sortie :
  460. 'tsv' pour un tableau séparé par une tabulation
  461. 'csv' pour un tableau séparé par une virgule
  462. 'ods' pour un fichier ods
  463. 'xlsx' pour un fichier xlsx
  464. 'raw' pour un envoi brut des données pythons
  465. Output :
  466. si chemin textuel : aucune sortie, écriture du fichier
  467. si chemin du fichier vaut None : retour du stream
  468. """
  469. # Vérification que toutes les variables et axes existent
  470. self._verifier_existence_variables([variable])
  471. self._verifier_existence_axes(axes)
  472. # Récupération des donnée
  473. # Génération de l'en-tête
  474. en_tete = self._generer_en_tete_detail(variable)
  475. # Création du tableau
  476. tableau = []
  477. tableau.append(en_tete)
  478. # On génère la première ligne d'analyse
  479. tableau = tableau + self._generer_lignes_detail(variable, None)
  480. # Ligne vide
  481. tableau.append(["" for x in range(len(tableau[-1]))])
  482. # Ajout des axes
  483. for axe in axes:
  484. tableau = tableau + self._generer_lignes_detail(variable, axe)
  485. return(tableau)