pyphyschemtools 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyphyschemtools/Chem3D.py +831 -0
- pyphyschemtools/ML.py +42 -0
- pyphyschemtools/PeriodicTable.py +289 -0
- pyphyschemtools/__init__.py +43 -0
- pyphyschemtools/aithermo.py +350 -0
- pyphyschemtools/cheminformatics.py +230 -0
- pyphyschemtools/core.py +119 -0
- pyphyschemtools/icons-logos-banner/Logo_pyPhysChem_border.svg +1109 -0
- pyphyschemtools/icons-logos-banner/__init__.py +0 -0
- pyphyschemtools/icons-logos-banner/logo.png +0 -0
- pyphyschemtools/icons-logos-banner/tools4pyPC_banner.png +0 -0
- pyphyschemtools/icons-logos-banner/tools4pyPC_banner.svg +193 -0
- pyphyschemtools/kinetics.py +193 -0
- pyphyschemtools/resources/css/BrainHalfHalf-120x139.base64 +1 -0
- pyphyschemtools/resources/css/BrainHalfHalf-120x139.png +0 -0
- pyphyschemtools/resources/css/BrainHalfHalf.base64 +8231 -0
- pyphyschemtools/resources/css/BrainHalfHalf.png +0 -0
- pyphyschemtools/resources/css/BrainHalfHalf.svg +289 -0
- pyphyschemtools/resources/css/visualID.css +325 -0
- pyphyschemtools/resources/img/Tranformative_3.webp +0 -0
- pyphyschemtools/resources/img/Tranformative_3_banner.png +0 -0
- pyphyschemtools/resources/img/pyPhysChem_1.png +0 -0
- pyphyschemtools/resources/svg/BrainHalfHalf.png +0 -0
- pyphyschemtools/resources/svg/BrainHalfHalf.svg +289 -0
- pyphyschemtools/resources/svg/GitHub-Logo-C.png +0 -0
- pyphyschemtools/resources/svg/GitHub-Logo.png +0 -0
- pyphyschemtools/resources/svg/Logo-Universite-Toulouse-n-2023.png +0 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_1-translucentBgd-woName.png +0 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_1-translucentBgd.png +0 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_1.png +0 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_1.svg +622 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_5.png +0 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_5.svg +48 -0
- pyphyschemtools/resources/svg/Logo_pyPhysChem_border.svg +1109 -0
- pyphyschemtools/resources/svg/Python-logo-notext.svg +265 -0
- pyphyschemtools/resources/svg/Python_logo_and_wordmark.svg.png +0 -0
- pyphyschemtools/resources/svg/UT3_logoQ.jpg +0 -0
- pyphyschemtools/resources/svg/UT3_logoQ.png +0 -0
- pyphyschemtools/resources/svg/Universite-Toulouse-n-2023.svg +141 -0
- pyphyschemtools/resources/svg/X.png +0 -0
- pyphyschemtools/resources/svg/logoAnaconda.png +0 -0
- pyphyschemtools/resources/svg/logoAnaconda.webp +0 -0
- pyphyschemtools/resources/svg/logoCNRS.png +0 -0
- pyphyschemtools/resources/svg/logoDebut.svg +316 -0
- pyphyschemtools/resources/svg/logoEnd.svg +172 -0
- pyphyschemtools/resources/svg/logoFin.svg +172 -0
- pyphyschemtools/resources/svg/logoPPCL.svg +359 -0
- pyphyschemtools/resources/svg/logoPytChem.png +0 -0
- pyphyschemtools/resources/svg/logo_lpcno_300_dpi_notexttransparent.png +0 -0
- pyphyschemtools/resources/svg/logo_pyPhysChem.png +0 -0
- pyphyschemtools/resources/svg/logo_pyPhysChem_0.png +0 -0
- pyphyschemtools/resources/svg/logo_pyPhysChem_0.svg +390 -0
- pyphyschemtools/resources/svg/logopyPhyschem.png +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_2.webp +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_3.webp +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_4.webp +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_5.png +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_5.webp +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_6.webp +0 -0
- pyphyschemtools/resources/svg/logopyPhyschem_7.webp +0 -0
- pyphyschemtools/resources/svg/logos-Anaconda-pyPhysChem.png +0 -0
- pyphyschemtools/resources/svg/logos-Anaconda-pyPhysChem.svg +58 -0
- pyphyschemtools/resources/svg/pyPCBanner.svg +309 -0
- pyphyschemtools/resources/svg/pyPhysChem-GitHubSocialMediaTemplate.png +0 -0
- pyphyschemtools/resources/svg/pyPhysChem-GitHubSocialMediaTemplate.svg +295 -0
- pyphyschemtools/resources/svg/pyPhysChemBanner.png +0 -0
- pyphyschemtools/resources/svg/pyPhysChemBanner.svg +639 -0
- pyphyschemtools/resources/svg/qrcode-pyPhysChem.png +0 -0
- pyphyschemtools/resources/svg/repository-open-graph-template.png +0 -0
- pyphyschemtools/spectra.py +451 -0
- pyphyschemtools/survey.py +1048 -0
- pyphyschemtools/sympyUtilities.py +51 -0
- pyphyschemtools/tools4AS.py +960 -0
- pyphyschemtools/visualID.py +101 -0
- pyphyschemtools/visualID_Eng.py +175 -0
- pyphyschemtools-0.1.0.dist-info/METADATA +38 -0
- pyphyschemtools-0.1.0.dist-info/RECORD +80 -0
- pyphyschemtools-0.1.0.dist-info/WHEEL +5 -0
- pyphyschemtools-0.1.0.dist-info/licenses/LICENSE +674 -0
- pyphyschemtools-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
__author__ = "Romuald POTEAU"
|
|
2
|
+
__maintainer__ = "Romuald POTEAU"
|
|
3
|
+
__email__ = "romuald.poteau@univ-tlse3.fr"
|
|
4
|
+
__status__ = "Development"
|
|
5
|
+
|
|
6
|
+
####################################################################################################################################
|
|
7
|
+
# F E U I L L E S D E S T Y L E
|
|
8
|
+
####################################################################################################################################
|
|
9
|
+
|
|
10
|
+
from .visualID_Eng import fg, hl, bg
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from IPython.display import HTML
|
|
14
|
+
|
|
15
|
+
def css_styling():
|
|
16
|
+
styles = open("./tools4AS.css", "r").read()
|
|
17
|
+
return HTML(styles)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
####################################################################################################################################
|
|
21
|
+
# F O N C T I O N S M A I S O N
|
|
22
|
+
####################################################################################################################################
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
import matplotlib.pyplot as plt
|
|
26
|
+
# importation de la libairie pandas
|
|
27
|
+
import pandas as pd
|
|
28
|
+
import dataframe_image as dfim
|
|
29
|
+
import seaborn as sns
|
|
30
|
+
|
|
31
|
+
def verifNotes(dfCC,labelNoteCC,nomCC,NI,absents='aabs'):
|
|
32
|
+
"""
|
|
33
|
+
entrée :
|
|
34
|
+
- dfCC = dataframe dont 1 colonne contient les notes de CC
|
|
35
|
+
- labelNoteCC = label de la colonne qui contient les notes
|
|
36
|
+
- nomCC = label de l'épreuve de CC (ex CC1), utilisé pour l'affichage
|
|
37
|
+
- NI = nombre d'inscrits dans le module
|
|
38
|
+
- absents = 'aabs' : analyser s'il y a des étudiants qui n'ont pas été pointés au CC (défaut)
|
|
39
|
+
= 'noabs' : ne pas analyser s'il y a des étudiants qui n'ont pas été pointés au CC
|
|
40
|
+
(ça n'a plus de sens une fois les fichiers concaténés)
|
|
41
|
+
|
|
42
|
+
sortie :
|
|
43
|
+
- la moyenne et la déviation standard de liste de notes contenues dans le dataframe dfCC
|
|
44
|
+
- le nombre d'étudiants qui n'ont pas composé au CC
|
|
45
|
+
|
|
46
|
+
affichages :
|
|
47
|
+
- nombre d'étudiants avec label 'ABS'|'Abs'|'abs'
|
|
48
|
+
- nombre d'étudiants sans note ni label ABS. En général c'est problématique. Vérifier le PV
|
|
49
|
+
"""
|
|
50
|
+
print()
|
|
51
|
+
#pd.set_option("display.max_rows", len(dfCC))
|
|
52
|
+
#display(dfCC[labelNoteCC])
|
|
53
|
+
if (absents == 'aabs'):
|
|
54
|
+
nABS = ((dfCC[labelNoteCC] == "ABS") | (dfCC[labelNoteCC] == "abs") | (dfCC[labelNoteCC] == "Abs") ).sum()
|
|
55
|
+
print(f"{hl.BOLD}{fg.BLUE}Etudiants de {nomCC} avec label 'ABS' = {nABS}{fg.OFF}")
|
|
56
|
+
nEMPTY = (dfCC[labelNoteCC].isna()).sum()
|
|
57
|
+
print(f"{hl.BOLD}{fg.BLUE}Etudiants de {nomCC} sans label ni note = {nEMPTY}{fg.OFF}")
|
|
58
|
+
if ((nEMPTY != 0) & (absents == 'aabs')):
|
|
59
|
+
print(f"{fg.RED}{hl.BOLD}Attention !!! Ça n'est pas normal. Vérifier la liste d'appel{fg.OFF}")
|
|
60
|
+
#pandas.to_numeric(arg, errors='raise', downcast=None)
|
|
61
|
+
#Convert argument to a numeric type
|
|
62
|
+
#errors{‘ignore’, ‘raise’, ‘coerce’}, default ‘raise’
|
|
63
|
+
# If ‘raise’, then invalid parsing will raise an exception.
|
|
64
|
+
# If ‘coerce’, then invalid parsing will be set as NaN.
|
|
65
|
+
# If ‘ignore’, then invalid parsing will return the input.
|
|
66
|
+
nCC_Absents = pd.to_numeric(dfCC[labelNoteCC], errors='coerce').isna().values.sum()
|
|
67
|
+
nCC_Notes = (~pd.to_numeric(dfCC[labelNoteCC], errors='coerce').isna()).values.sum()
|
|
68
|
+
av = pd.to_numeric(dfCC[labelNoteCC], errors='coerce').mean()
|
|
69
|
+
std = pd.to_numeric(dfCC[labelNoteCC], errors='coerce').std()
|
|
70
|
+
print(f"{fg.BLUE}Nombre d'étudiants sur les listes du {nomCC} = {len(dfCC)}. Nombre de notes vs. nombre d'absents = {nCC_Notes} vs. {nCC_Absents}{fg.OFF}")
|
|
71
|
+
print(f"Somme des copies notées & des absents = {nCC_Notes + nCC_Absents}")
|
|
72
|
+
print(f"{NI - nCC_Notes}/{NI} étudiants n'ont pas composé au {nomCC}")
|
|
73
|
+
print(f"{hl.BOLD}Moyenne = {av:.1f}, Écart-type = {std:.1f}{fg.OFF}")
|
|
74
|
+
return av, std, (NI - nCC_Notes)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def RenameDfHeader(dfCC,dfCCname,labelNoteCC,nomCC):
|
|
78
|
+
"""
|
|
79
|
+
entrée :
|
|
80
|
+
- dfCC = dataframe dont 1 colonne contient les notes de CC
|
|
81
|
+
- dfCCname = nom (string) du dataframe. Il est recommandé d'utiliser f'{dfCC=}'.split('=')[0]
|
|
82
|
+
- labelNoteCC = label de la colonne qui contient les notes
|
|
83
|
+
- nomCC = label de l'épreuve de CC (ex CC1), utilisé pour l'affichage
|
|
84
|
+
sortie : aucune
|
|
85
|
+
|
|
86
|
+
la fonction change le nom 'labelNoteCC' en 'nomCC'
|
|
87
|
+
"""
|
|
88
|
+
print(f"{hl.BOLD}{fg.BLUE}Normalisation du nom des colonnes de notes{fg.OFF}")
|
|
89
|
+
print(f"{hl.BOLD}Dataframe = {dfCCname}.{fg.OFF} {labelNoteCC} --> {nomCC}")
|
|
90
|
+
dfCC.rename(columns = {labelNoteCC:nomCC}, inplace=True)
|
|
91
|
+
|
|
92
|
+
def mentionD(row, Mention):
|
|
93
|
+
rowV = row[Mention]
|
|
94
|
+
CHIMIE1 = "L1 CHI"
|
|
95
|
+
CHIMIE2 = "L2 CHI"
|
|
96
|
+
PHYSIQUE1 = "L1 PHY"
|
|
97
|
+
PHYSIQUE2 = "L2 PHY"
|
|
98
|
+
PHYSIQUE3 = "L3 PHY"
|
|
99
|
+
MATHS1 = "L1 MAT"
|
|
100
|
+
MATHS2 = "L2 MAT"
|
|
101
|
+
MATHS3 = "L2 MAT"
|
|
102
|
+
MECA = "L1 MECA"
|
|
103
|
+
MIASHS = "L1 MIASHS"
|
|
104
|
+
EEA = "L1 EEA"
|
|
105
|
+
INFO1 = "L1 INFO"
|
|
106
|
+
INFO2 = "L2 INFO"
|
|
107
|
+
PC1 = "L1 PHYSIQUE CHIMIE"
|
|
108
|
+
PC2 = "L1 PC"
|
|
109
|
+
GC1 = "L1 GC"
|
|
110
|
+
GC2 = "L1 GENIE CIVIL"
|
|
111
|
+
MOBINT = "MOBILITE INTERNAT"
|
|
112
|
+
DUMMY = "DUMMY"
|
|
113
|
+
if (CHIMIE1 in rowV) | (CHIMIE2 in rowV):
|
|
114
|
+
return "CHIMIE"
|
|
115
|
+
elif ((PHYSIQUE1 in rowV) | (PHYSIQUE2 in rowV) | (PHYSIQUE3 in rowV)) & ~(PC1 in rowV):
|
|
116
|
+
return "PHYSIQUE"
|
|
117
|
+
elif (MATHS1 in rowV) | (MATHS2 in rowV) | (MATHS3 in rowV):
|
|
118
|
+
return "MATHS"
|
|
119
|
+
elif MIASHS in rowV:
|
|
120
|
+
return "MIASHS"
|
|
121
|
+
elif MECA in rowV:
|
|
122
|
+
return "MECA"
|
|
123
|
+
elif (INFO1 in rowV) | (INFO2 in rowV):
|
|
124
|
+
return "INFO"
|
|
125
|
+
elif EEA in rowV:
|
|
126
|
+
return "EEA"
|
|
127
|
+
elif (GC1 in rowV) | (GC2 in rowV):
|
|
128
|
+
return "GC"
|
|
129
|
+
elif (PC1 in rowV) | (PC2 in rowV):
|
|
130
|
+
return "PC"
|
|
131
|
+
elif MOBINT in rowV:
|
|
132
|
+
return "MobInt"
|
|
133
|
+
elif DUMMY in rowV:
|
|
134
|
+
return "DUMMY"
|
|
135
|
+
else:
|
|
136
|
+
print(f"Quelle est cette Mention ? {rowV}")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
def parcours(row, Parcours):
|
|
140
|
+
rowV = row[Parcours]
|
|
141
|
+
CUPGE = "CUPGE"
|
|
142
|
+
SANTE = "OPT° SANTE"
|
|
143
|
+
ACCOMP = "ACCOMP"
|
|
144
|
+
DUMMY = "DUMMY"
|
|
145
|
+
if CUPGE in rowV:
|
|
146
|
+
return "CUPGE"
|
|
147
|
+
elif SANTE in rowV:
|
|
148
|
+
return "SANTE"
|
|
149
|
+
elif ACCOMP in rowV:
|
|
150
|
+
return "ACCOMPAGNEMENT"
|
|
151
|
+
elif DUMMY in rowV:
|
|
152
|
+
return "DUMMY"
|
|
153
|
+
else:
|
|
154
|
+
# print(f"Pas de parcours dans la mention {rowV}")
|
|
155
|
+
return "Standard"
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
def MentionAuModule(note, Seuil):
|
|
159
|
+
"""
|
|
160
|
+
entrée :
|
|
161
|
+
- note = valeur numérique ou NaN
|
|
162
|
+
- Seuil = seuil de réussite
|
|
163
|
+
sortie :
|
|
164
|
+
- m = mention au module (AJ, P, AB, B, TB ou PB!! dans le cas où la colonne contiendrait une valeur numérique non comprise entre 0 et 20, ou bien toute autre contenu (caractères etc)
|
|
165
|
+
"""
|
|
166
|
+
if (note >=0) and (note < Seuil):
|
|
167
|
+
m = "AJ"
|
|
168
|
+
elif (note >=10) and (note < 12):
|
|
169
|
+
m = "P"
|
|
170
|
+
elif (note>=12) and (note < 14):
|
|
171
|
+
m = "AB"
|
|
172
|
+
elif (note >= 14) and (note <16):
|
|
173
|
+
m = "B"
|
|
174
|
+
elif (note >=16) and (note <= 20) :
|
|
175
|
+
m = "TB"
|
|
176
|
+
elif (np.isnan(note)):
|
|
177
|
+
m = np.NaN
|
|
178
|
+
else:
|
|
179
|
+
print(note,"PB")
|
|
180
|
+
m = 'PB!!'
|
|
181
|
+
return m
|
|
182
|
+
|
|
183
|
+
def concat2ApoG(df2Complete, ID_ApoG, dfCC, Col2SaveInCC, IDCC, nomCC):
|
|
184
|
+
"""
|
|
185
|
+
entrée :
|
|
186
|
+
- df2Complete = dataframe à compléter (merge = 'left')
|
|
187
|
+
- au premier appel, ce soit être le fichier de Référence
|
|
188
|
+
- ensuite c'est le fichier de notes lui-même, en cours d'update
|
|
189
|
+
- ID_ApoG = label de la colonne qui contient les numéros étudiants dans le fichier de référence
|
|
190
|
+
- dfCC = dataframe dont 1 colonne contient les notes de CC
|
|
191
|
+
- Col2SaveInCC = liste avec les en-têtes de colonnes qui contiennent les colonnes de dfCC à reporter dans dfNotes
|
|
192
|
+
- IDCC = label de la colonne qui contient les numéros étudiants dans le fichier de notes
|
|
193
|
+
- nomCC = à ce stade, c'est aussi bien le label de la colonne qui contient les notes que le label de l'épreuve de CC (ex CC1), utilisé pour l'affichage
|
|
194
|
+
|
|
195
|
+
sortie :
|
|
196
|
+
- le dataframe dfNotes. Contient la version concaténée du dataframe d'entrée df2complete et certaines colonnes du dataframe des notes dfCC
|
|
197
|
+
([Col2SaveInCC + IDCC + nomCC])
|
|
198
|
+
- un dataframe dfnotFoundInRef qui contient la liste des étudiants qui sont dans le fichier de notes et pas dans le fichier de référence
|
|
199
|
+
|
|
200
|
+
affichages :
|
|
201
|
+
- liste des étudiants qui sont dans le fichier de notes et pas dans le fichier de référence
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
#display(df2Complete)
|
|
205
|
+
dfNotes = df2Complete.copy()
|
|
206
|
+
|
|
207
|
+
pd.set_option('display.max_rows', 12)
|
|
208
|
+
#print(f"{hl.BOLD}{fg.BLUE}Fichier de notes copié de l'export Apogée{fg.OFF}")
|
|
209
|
+
#display(dfNotes)
|
|
210
|
+
Col2SaveInCCExt = Col2SaveInCC.copy()
|
|
211
|
+
Col2SaveInCCExt.extend([IDCC])
|
|
212
|
+
Col2SaveInCCExt.extend([nomCC])
|
|
213
|
+
dfNotes = dfNotes.merge(dfCC[Col2SaveInCCExt], left_on=ID_ApoG, right_on=IDCC, how='left')
|
|
214
|
+
print(f"{hl.BOLD}{fg.BLUE}{nomCC} ajouté dans fichier de notes{fg.OFF}")
|
|
215
|
+
print(f"Les colonnes qui ont été ajoutées sont : {Col2SaveInCCExt}")
|
|
216
|
+
#display(dfNotes)
|
|
217
|
+
|
|
218
|
+
dfNotesTmp = df2Complete.copy()
|
|
219
|
+
dfNotesOuter = dfNotesTmp.merge(dfCC[Col2SaveInCCExt], left_on=ID_ApoG, right_on=IDCC, how='outer')
|
|
220
|
+
#display(dfNotesOuter)
|
|
221
|
+
|
|
222
|
+
dfnotFoundInReftmp = dfNotesOuter[dfNotesOuter[ID_ApoG].isna()]
|
|
223
|
+
if dfnotFoundInReftmp.shape[0] != 0:
|
|
224
|
+
print(f"{hl.BOLD}{fg.RED}Problème !! Ces étudiants du {nomCC} ne sont pas dans le dataframe de Référence{fg.OFF}")
|
|
225
|
+
print(f"{hl.BOLD}{fg.RED}Ils ne sont pas rajoutés dans ce dataframe, mais dans un dataframe dfnotFoundInRef{fg.OFF}")
|
|
226
|
+
display(dfnotFoundInReftmp)
|
|
227
|
+
else:
|
|
228
|
+
print(f"{hl.BOLD}{fg.GREEN}Tous les étudiants sont bien dans le dataframe de Référence{fg.OFF}")
|
|
229
|
+
|
|
230
|
+
return dfNotes, dfnotFoundInReftmp
|
|
231
|
+
|
|
232
|
+
def checkNoID_DuplicateID(df, dfname, ID, nom, NomPrenom):
|
|
233
|
+
import numpy as np
|
|
234
|
+
"""
|
|
235
|
+
entrée :
|
|
236
|
+
- df = dataframe à analyser
|
|
237
|
+
- dfname = nom (string) du dataframe à analyser. Il est recommandé d'utiliser f'{dfCC=}'.split('=')[0]
|
|
238
|
+
- ID = label de la colonne qui contient les numéros étudiants dans df
|
|
239
|
+
- nom = label du dataframe, utilisé pour l'affichage
|
|
240
|
+
- NomPrenom = liste avec les en-têtes de colonnes qui contiennent les noms et les prénoms dans df
|
|
241
|
+
|
|
242
|
+
affichage : diagnostic et éventuellement la liste des étudiants sans numéros d'étudiant
|
|
243
|
+
"""
|
|
244
|
+
print(f"{hl.BOLD}{fg.BLUE}Dataframe {dfname} (alias {nom}){fg.OFF}")
|
|
245
|
+
noID = df[df[ID].isnull()]
|
|
246
|
+
if (noID.shape[0] != 0):
|
|
247
|
+
print(f"{hl.BOLD}{fg.RED}Etudiants sans ID !{fg.OFF}")
|
|
248
|
+
display(noID.sort_values(by=NomPrenom[0], ascending = True))
|
|
249
|
+
else:
|
|
250
|
+
print(f"{hl.BOLD}{fg.GREEN}Etudiants sans ID ? Pas de problème{fg.OFF}")
|
|
251
|
+
|
|
252
|
+
values, counts = np.unique(df[ID].to_numpy(), return_counts=True)
|
|
253
|
+
|
|
254
|
+
duplicateID = False
|
|
255
|
+
for c in counts:
|
|
256
|
+
if c != 1: duplicateID = True
|
|
257
|
+
if (not duplicateID):
|
|
258
|
+
print(f"{hl.BOLD}{fg.GREEN}Doublon sur les ID ? Pas de problème{fg.OFF}")
|
|
259
|
+
else:
|
|
260
|
+
print(f"{hl.BOLD}{fg.RED}ID en double!{fg.OFF}")
|
|
261
|
+
for i, c in enumerate(counts):
|
|
262
|
+
if c != 1: print(f"{values[i]} x {c}")
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
def read_excel(xlFile,decimal,name):
|
|
266
|
+
"""
|
|
267
|
+
entrée :
|
|
268
|
+
- xlFile = nom du ficher excel
|
|
269
|
+
- decimal = "." ou "," selon le cas
|
|
270
|
+
- name = label du dataframe, utilisé pour l'affichage
|
|
271
|
+
sortie :
|
|
272
|
+
- le dataframe qui contient l'intégralité du fichier excel
|
|
273
|
+
- le nombre de lignes de ce tableau (à l'exclusion de l'en-tête des colonnes)
|
|
274
|
+
affichage :
|
|
275
|
+
- statistiques descriptives (describe) de toutes les colonnes, y compris celles qui ne contiennent pas de valeurs numériques
|
|
276
|
+
"""
|
|
277
|
+
print(f"{hl.BOLD}{fg.BLUE}{name}{fg.OFF}")
|
|
278
|
+
print(f"Reading... {xlFile}")
|
|
279
|
+
df=pd.read_excel(xlFile,decimal=decimal)
|
|
280
|
+
|
|
281
|
+
#pd.set_option('display.max_rows', 12)
|
|
282
|
+
#display(dfCC1)
|
|
283
|
+
display(df.describe(include='all'))
|
|
284
|
+
return df, df.shape[0]
|
|
285
|
+
|
|
286
|
+
def ReplaceABSByNan(df,nomCC):
|
|
287
|
+
"""
|
|
288
|
+
entrée :
|
|
289
|
+
- dataframe qui contient les notes
|
|
290
|
+
- nom des colonnes qui contiennent les notes
|
|
291
|
+
sortie
|
|
292
|
+
- nouveau dataframe où toutes les notes des colonnes nomCC = ABS sont remplacées par des nan
|
|
293
|
+
"""
|
|
294
|
+
dfcopy = df.copy()
|
|
295
|
+
for nom in nomCC:
|
|
296
|
+
# correction introduite le 24/01/2026
|
|
297
|
+
# dfcopy[nom].mask((dfcopy[nom] == "ABS") | (dfcopy[nom] == "abs") | (dfcopy[nom] == "Abs"), np.nan ,inplace=True)
|
|
298
|
+
# On convertit en chaîne, on met en majuscules, et on compare à "ABS"
|
|
299
|
+
# L'assignation directe (df[nom] = ...) évite le ChainedAssignmentError
|
|
300
|
+
dfcopy[nom] = dfcopy[nom].mask(dfcopy[nom].astype(str).str.upper() == "ABS", np.nan)
|
|
301
|
+
return dfcopy
|
|
302
|
+
|
|
303
|
+
# deprecated à cause de la nouvelle version de pandas (26/01/2026)
|
|
304
|
+
def ReplaceNanBy0OLD(df,nomCC):
|
|
305
|
+
"""
|
|
306
|
+
entrée :
|
|
307
|
+
- df = dataframe avec les notes
|
|
308
|
+
- nomCC = liste des en-têtes de colonnes qui contiennt les notes
|
|
309
|
+
sortie :
|
|
310
|
+
- le dataframe avec Nan remplacé par 0 pour chaque étudiant qui a au moins une note de CC
|
|
311
|
+
"""
|
|
312
|
+
dfCopy = df.copy()
|
|
313
|
+
for nom in nomCC:
|
|
314
|
+
nomCCred = nomCC.copy()
|
|
315
|
+
nomCCred.remove(nom)
|
|
316
|
+
mask = pd.DataFrame([False]*dfCopy.shape[0],index=dfCopy.index,columns=["mask"])
|
|
317
|
+
for nomred in nomCCred:
|
|
318
|
+
mask["mask"] = (mask["mask"] | dfCopy[nomred].notnull())
|
|
319
|
+
mask["mask"] = (mask["mask"] & dfCopy[nom].isna())
|
|
320
|
+
# correction le 24/01/2026
|
|
321
|
+
# dfCopy[nom].mask(mask["mask"],0.0,inplace=True)
|
|
322
|
+
dfCopy[nom] = dfCopy[nom].mask(mask["mask"], 0.0)
|
|
323
|
+
return dfCopy
|
|
324
|
+
|
|
325
|
+
def ReplaceNanBy0(df, nomCC):
|
|
326
|
+
dfCopy = df.copy()
|
|
327
|
+
for nom in nomCC:
|
|
328
|
+
# 1. On identifie les autres colonnes
|
|
329
|
+
autres_cols = [c for c in nomCC if c != nom]
|
|
330
|
+
|
|
331
|
+
# 2. On crée le masque :
|
|
332
|
+
# (Au moins une autre note n'est pas nulle) ET (La note actuelle est nulle)
|
|
333
|
+
condition = dfCopy[autres_cols].notnull().any(axis=1) & dfCopy[nom].isna()
|
|
334
|
+
|
|
335
|
+
# 3. Application directe (On s'assure que la colonne accepte les flottants)
|
|
336
|
+
dfCopy[nom] = dfCopy[nom].astype(float)
|
|
337
|
+
dfCopy[nom] = dfCopy[nom].mask(condition, 0.0)
|
|
338
|
+
|
|
339
|
+
return dfCopy
|
|
340
|
+
|
|
341
|
+
def dropColumnsByIndex(df,idropC):
|
|
342
|
+
"""
|
|
343
|
+
entrée :
|
|
344
|
+
- df = dataframe dont on veut supprimer des colonnes
|
|
345
|
+
- idropC = indices des colonnes dont on veut se débarasser
|
|
346
|
+
sortie :
|
|
347
|
+
- dfCleaned = dataframe originel dont les colonnes indexées par idropC ont été supprimées
|
|
348
|
+
"""
|
|
349
|
+
listC = list(df.columns)
|
|
350
|
+
dropC = [listC[idropC[i]] for i in range(len(idropC))]
|
|
351
|
+
print(f"On va se débarrasser des colonnes {dropC}")
|
|
352
|
+
dfCleaned = df.drop(dropC,axis=1)
|
|
353
|
+
return dfCleaned
|
|
354
|
+
|
|
355
|
+
def CreationDfADMAJGH(df,note,Seuil,prefix,MD,Parc,IDApoG):
|
|
356
|
+
"""
|
|
357
|
+
entrée :
|
|
358
|
+
- df = dataframe avec les mentions/parcours/moyennes
|
|
359
|
+
- note = nom de la colonne qui contient la moyenne globale
|
|
360
|
+
- Seuil = seuil de réussite pour départager ADM & AJ
|
|
361
|
+
- prefix = préfixe du nom de fichier temporaire, incluant ou nom le nom d'un sous-répertoire de sauvegarde
|
|
362
|
+
- MD = nom de la colonne qui contient la dénomination simplifiée de la mention de diplôme ("MentionD")
|
|
363
|
+
- Parc = nom de la colonne qui contient le parcours
|
|
364
|
+
- IDApoG = nom de la colonne qui contient l'ID des étudiants
|
|
365
|
+
|
|
366
|
+
sortie :
|
|
367
|
+
- dfADM = dataframe des admis
|
|
368
|
+
- dfAJ = dataframe des ajournés
|
|
369
|
+
- dfGhosts = dataframe des fantômes (aka Ghosts ; i.e. n'ont passé aucun des CC)
|
|
370
|
+
|
|
371
|
+
affichage : stats rapides (describe & sum) de chacun des sous-ensembles ADM, AJ, Ghosts
|
|
372
|
+
|
|
373
|
+
sauvegarde : fichier excel tmp{Note}.xlsx avec 3 onglets (ADM, AJ, Ghosts)
|
|
374
|
+
|
|
375
|
+
"""
|
|
376
|
+
print(f"{hl.BOLD}{bg.LIGHTRED}Construction des dataframes avec les ADM & les AJ sur la base de la {note} >= ou < à {Seuil}{fg.OFF}")
|
|
377
|
+
print(f"{hl.BOLD}{bg.LIGHTRED}Construction également d'un dataframe 'Ghosts', c'est-à-dire avec les fantômes i.e. n'ont passé aucun des CC{fg.OFF}")
|
|
378
|
+
#dataframe reçus
|
|
379
|
+
dfADM = df[df[note]>=Seuil]
|
|
380
|
+
#dataframe ajournés
|
|
381
|
+
dfAJ = df[df[note]<Seuil]
|
|
382
|
+
#dataframe fantômes
|
|
383
|
+
dfGhosts = df[df[note].isnull()]
|
|
384
|
+
|
|
385
|
+
Ftmp = prefix+'tmp'+note+'.xlsx'
|
|
386
|
+
|
|
387
|
+
exceltest = pd.ExcelWriter(Ftmp, engine='xlsxwriter')
|
|
388
|
+
dfADM.to_excel(exceltest, sheet_name='ADM')
|
|
389
|
+
dfAJ.to_excel(exceltest, sheet_name='AJ')
|
|
390
|
+
dfGhosts.to_excel(exceltest, sheet_name='Fantomes')
|
|
391
|
+
exceltest.close()
|
|
392
|
+
|
|
393
|
+
print(f"{hl.BOLD}{fg.BLUE}Total{fg.OFF}")
|
|
394
|
+
display(df.groupby(MD)[note].describe().style.format("{0:.1f}"))
|
|
395
|
+
print(f"{hl.BOLD}{fg.BLUE}Admis{fg.OFF}")
|
|
396
|
+
display(dfADM.groupby(MD)[note].describe().style.format("{0:.1f}"))
|
|
397
|
+
print(f"{hl.BOLD}{fg.BLUE}Ajournés{fg.OFF}")
|
|
398
|
+
display(dfAJ.groupby(MD)[note].describe().style.format("{0:.1f}"))
|
|
399
|
+
|
|
400
|
+
print(f"{hl.BOLD}{fg.BLUE}Total{fg.OFF}")
|
|
401
|
+
display(df.groupby(MD)[note].describe().sum())
|
|
402
|
+
print(f"{hl.BOLD}{fg.BLUE}Admis{fg.OFF}")
|
|
403
|
+
display(dfADM.groupby(MD)[note].describe().sum())
|
|
404
|
+
print(f"{hl.BOLD}{fg.BLUE}Ajournés{fg.OFF}")
|
|
405
|
+
display(dfAJ.groupby(MD)[note].describe().sum())
|
|
406
|
+
print(f"{hl.BOLD}{fg.BLUE}Fantômes{fg.OFF}")
|
|
407
|
+
display(dfGhosts.groupby(MD)[IDApoG].count())
|
|
408
|
+
return dfADM, dfAJ, dfGhosts
|
|
409
|
+
|
|
410
|
+
def StatsRéussiteParMentionOuParcours(dfT, dfADM, dfAJ, dfGhosts, Category, note):
|
|
411
|
+
"""
|
|
412
|
+
entrée :
|
|
413
|
+
- dfT = dataframe qui contient toutes les notes, ainsi qu'au moins une catégorisation (exemple : Mention ou Parcours ou Section etc...)
|
|
414
|
+
- dfADM = dataframe qui contient uniquement les étudiants admis
|
|
415
|
+
- dfAJ = dataframe qui contient uniquement les étudiants ajournés (sans les fantômes)
|
|
416
|
+
- dfGhosts = dataframe qui contient la liste des fantômes
|
|
417
|
+
- Category = nom de la colonne sur laquelle on veut faire des analyses statistiques
|
|
418
|
+
- note = nom de la colonne qui contient la note qu'on veut analyser par catégorie (généralement la moyenne finale)
|
|
419
|
+
|
|
420
|
+
sortie :
|
|
421
|
+
- dfStats
|
|
422
|
+
|
|
423
|
+
affichage :
|
|
424
|
+
"""
|
|
425
|
+
def incrementer(row,Category,Catunique,note,NN,Presents="Presents"):
|
|
426
|
+
"""
|
|
427
|
+
conçu pour pour une analyse ligne par ligne
|
|
428
|
+
si Category = une des catégories de Catunique NN[de cette catégorie] +=1
|
|
429
|
+
nécessite au préalable
|
|
430
|
+
- de fabriquer la liste des catégories uniques (df[Category].unique())
|
|
431
|
+
- d'initialiser à 0 un tableau qui a la même dimension que Catunique
|
|
432
|
+
|
|
433
|
+
entrée :
|
|
434
|
+
- row = ligne d'un dataframe à analyser
|
|
435
|
+
- Category = nom de la colonne qui contient les catégories à comptabiliser
|
|
436
|
+
- Catunique = liste exhaustive des catégories du dataframe analysé
|
|
437
|
+
- note = nom de la colonne qui contient les notes
|
|
438
|
+
- NN = tableau dont la colonne qui correspond à l'une des catégories uniques pour cet étudiant est incrémenté de 1
|
|
439
|
+
- Presents (valeur par défaut = "Present") : compte les présents uniquement
|
|
440
|
+
sinon ce sont les fantômes qui sont comptés
|
|
441
|
+
|
|
442
|
+
"""
|
|
443
|
+
i = 0
|
|
444
|
+
for C in Catunique:
|
|
445
|
+
if (((row[Category] == C) & (not np.isnan(row[note]))) & (Presents=="Presents")) |\
|
|
446
|
+
(((row[Category] == C) & (np.isnan(row[note]))) & (Presents!="Presents")):
|
|
447
|
+
NN[i] +=1
|
|
448
|
+
#print(i,row[CategoryInRow],C,NN[i],row[note],np.isnan(row[note]))
|
|
449
|
+
i += 1
|
|
450
|
+
return
|
|
451
|
+
|
|
452
|
+
print(f"{hl.BOLD}-- Statistiques uniquement sur les présents --{fg.OFF}")
|
|
453
|
+
CatUnique = dfT[Category].unique()
|
|
454
|
+
print(f"{Category} = {CatUnique}")
|
|
455
|
+
# création des tableaux pour comptabiliser les reçus (NRP), ajournés (NAJP) par parcours
|
|
456
|
+
# NTP contient le nombre total d'étudiants par parcours
|
|
457
|
+
NT = np.zeros(len(CatUnique))
|
|
458
|
+
NADM = np.zeros(len(CatUnique))
|
|
459
|
+
NAJ = np.zeros(len(CatUnique))
|
|
460
|
+
NGH = np.zeros(len(CatUnique))
|
|
461
|
+
dfT.apply(lambda row: incrementer(row, Category, CatUnique, note, NT), axis=1)
|
|
462
|
+
dfADM.apply(lambda row: incrementer(row, Category, CatUnique, note, NADM), axis=1)
|
|
463
|
+
dfAJ.apply(lambda row: incrementer(row, Category, CatUnique, note, NAJ), axis=1)
|
|
464
|
+
dfGhosts.apply(lambda row: incrementer(row, Category, CatUnique, note, NGH, "Fantomes"), axis=1)
|
|
465
|
+
dfADMAJ = pd.concat([dfADM,dfAJ])
|
|
466
|
+
# display(dfADMAJ.describe(include='all'))
|
|
467
|
+
# print(dfADMAJ[Moyenne].mean())
|
|
468
|
+
|
|
469
|
+
print(f" NT/Cat = {NT}")
|
|
470
|
+
print(f"{fg.GREEN}NADM/Cat = {NADM}{fg.OFF}")
|
|
471
|
+
print(f"{fg.RED} NAJ/Cat = {NAJ}{fg.OFF}")
|
|
472
|
+
print(f"{fg.LIGHTGRAY} NGH/Cat = {NGH}{fg.OFF}")
|
|
473
|
+
|
|
474
|
+
print(f"{hl.BOLD}{fg.GREEN}Reçus par {Category}{fg.OFF}")
|
|
475
|
+
i = 0
|
|
476
|
+
nADM = 0
|
|
477
|
+
nAJ = 0
|
|
478
|
+
nTOT = 0
|
|
479
|
+
nGH = 0
|
|
480
|
+
Moy = np.zeros(len(CatUnique))
|
|
481
|
+
StD = np.zeros(len(CatUnique))
|
|
482
|
+
Med = np.zeros(len(CatUnique))
|
|
483
|
+
MoyGlob = np.zeros(len(CatUnique))
|
|
484
|
+
MoyGlobT = 0
|
|
485
|
+
for C in CatUnique:
|
|
486
|
+
print(f"{hl.BOLD}{C:>20}{fg.OFF} = {hl.BOLD}{fg.GREEN}{100*NADM[i]/NT[i]:.1f} %{fg.OFF} ({fg.RED}AJ : {NAJ[i]:3.0f}{fg.OFF},"\
|
|
487
|
+
f" {fg.GREEN}ADM : {NADM[i]:3.0f}{fg.OFF}, Tot : {NT[i]:3.0f} {fg.LIGHTGRAY}+Fantômes : {NGH[i]:3.0f}){fg.OFF}")
|
|
488
|
+
# display(dfADM.loc[dfADM[Category] == C][note].sum())
|
|
489
|
+
Moy[i] = (dfADM.loc[dfADM[Category] == C][note].sum()+dfAJ.loc[dfAJ[Category] == C][note].sum())
|
|
490
|
+
MoyGlob[i] = Moy[i]
|
|
491
|
+
MoyGlobT += Moy[i]
|
|
492
|
+
Moy[i] = np.round(Moy[i] / (NADM[i]+NAJ[i]),2)
|
|
493
|
+
MoyGlob[i] = np.round(MoyGlob[i] / (NADM[i]+NAJ[i]+NGH[i]),2)
|
|
494
|
+
StD[i] = np.round(dfADMAJ.loc[dfADMAJ[Category] == C][note].std(),1)
|
|
495
|
+
Med[i] = np.round(dfADMAJ.loc[dfADMAJ[Category] == C][note].median(),1)
|
|
496
|
+
# print("Moyennes ",Moy[i],MoyGlob[i])
|
|
497
|
+
nADM += NADM[i]
|
|
498
|
+
nAJ += NAJ[i]
|
|
499
|
+
nGH += NGH[i]
|
|
500
|
+
nTOT += NT[i]
|
|
501
|
+
i+=1
|
|
502
|
+
print(f"{hl.BOLD}{fg.GREEN} ADM = {nADM:3.0f}{fg.OFF}")
|
|
503
|
+
print(f"{hl.BOLD}{fg.RED} AJ = {nAJ:3.0f}{fg.OFF}")
|
|
504
|
+
print(f"{hl.BOLD}{fg.BLACK} TOT = {nTOT:3.0f}{fg.OFF}")
|
|
505
|
+
print(f"{hl.BOLD}{fg.LIGHTGRAY}(+Fantomes = {nGH:3.0f}){fg.OFF}")
|
|
506
|
+
MoyT = np.round(dfADMAJ[note].mean(),2)
|
|
507
|
+
StDT = np.round(dfADMAJ[note].std(),1)
|
|
508
|
+
MedT = np.round(dfADMAJ[note].median(),1)
|
|
509
|
+
MoyGlobT = np.round(MoyGlobT/(nTOT+nGH),2)
|
|
510
|
+
rowTotal = [NT.sum(),NADM.sum(),NAJ.sum(),np.round(100*NADM.sum()/NT.sum(),1),MoyT,StDT,MedT,NGH.sum(),NT.sum()+NGH.sum(),np.round(100*NADM.sum()/(NT.sum()+NGH.sum()),1),MoyGlobT]
|
|
511
|
+
defCol = ["Présents","ADM","AJ","Taux ADM (présents)","Moy.","StDev","Med.","Fantômes","Total","Taux ADM (tot.)","Moy."]
|
|
512
|
+
dfStats=pd.DataFrame(zip(NT,NADM,NAJ,np.round(100*NADM/NT,1),Moy,StD,Med,NGH,NT+NGH,np.round(100*NADM/(NT+NGH),1),MoyGlob),index=CatUnique,columns=defCol)
|
|
513
|
+
rowTotal = pd.DataFrame([rowTotal],index=["Total"],columns=dfStats.columns)
|
|
514
|
+
dfStats=pd.concat([dfStats,rowTotal])
|
|
515
|
+
dfStats.style.set_caption(f"Statistiques par {Category}")
|
|
516
|
+
return dfStats
|
|
517
|
+
|
|
518
|
+
def StatsRéussiteParMentionOuParcoursWithAb(dfT, dfADM, dfAJ, dfGhosts, dfAb, Category, note):
|
|
519
|
+
"""
|
|
520
|
+
entrée :
|
|
521
|
+
- dfT = dataframe qui contient toutes les notes, ainsi qu'au moins une catégorisation (exemple : Mention ou Parcours ou Section etc...)
|
|
522
|
+
- dfADM = dataframe qui contient uniquement les étudiants admis
|
|
523
|
+
- dfAJ = dataframe qui contient uniquement les étudiants ajournés (sans les fantômes)
|
|
524
|
+
- dfGhosts = dataframe qui contient la liste des fantômes
|
|
525
|
+
- dfAb = dataframe qui contient la liste des étudiants qui ont abandonné
|
|
526
|
+
- Category = nom de la colonne sur laquelle on veut faire des analyses statistiques
|
|
527
|
+
- note = nom de la colonne qui contient la note qu'on veut analyser par catégorie (généralement la moyenne finale)
|
|
528
|
+
|
|
529
|
+
sortie :
|
|
530
|
+
- dfStats
|
|
531
|
+
|
|
532
|
+
affichage :
|
|
533
|
+
"""
|
|
534
|
+
def incrementer(row,Category,Catunique,note,NN,Presents="Presents"):
|
|
535
|
+
"""
|
|
536
|
+
conçu pour pour une analyse ligne par ligne
|
|
537
|
+
si Category = une des catégories de Catunique NN[de cette catégorie] +=1
|
|
538
|
+
nécessite au préalable
|
|
539
|
+
- de fabriquer la liste des catégories uniques (df[Category].unique())
|
|
540
|
+
- d'initialiser à 0 un tableau qui a la même dimension que Catunique
|
|
541
|
+
|
|
542
|
+
entrée :
|
|
543
|
+
- row = ligne d'un dataframe à analyser
|
|
544
|
+
- Category = nom de la colonne qui contient les catégories à comptabiliser
|
|
545
|
+
- Catunique = liste exhaustive des catégories du dataframe analysé
|
|
546
|
+
- note = nom de la colonne qui contient les notes
|
|
547
|
+
- NN = tableau dont la colonne qui correspond à l'une des catégories uniques pour cet étudiant est incrémenté de 1
|
|
548
|
+
- Presents (valeur par défaut = "Present") : compte les présents uniquement
|
|
549
|
+
sinon ce sont les fantômes qui sont comptés
|
|
550
|
+
|
|
551
|
+
"""
|
|
552
|
+
i = 0
|
|
553
|
+
for C in Catunique:
|
|
554
|
+
if (((row[Category] == C) & (not np.isnan(row[note]))) & (Presents=="Presents")) |\
|
|
555
|
+
(((row[Category] == C) & (np.isnan(row[note]))) & (Presents!="Presents")):
|
|
556
|
+
NN[i] +=1
|
|
557
|
+
#print(i,row[CategoryInRow],C,NN[i],row[note],np.isnan(row[note]))
|
|
558
|
+
i += 1
|
|
559
|
+
return
|
|
560
|
+
def incrementerAbandons(row,Category,Catunique,NN):
|
|
561
|
+
i = 0
|
|
562
|
+
for C in Catunique:
|
|
563
|
+
if (row[Category] == C):
|
|
564
|
+
NN[i] +=1
|
|
565
|
+
i += 1
|
|
566
|
+
return
|
|
567
|
+
|
|
568
|
+
print(f"{hl.BOLD}-- Statistiques uniquement sur les présents --{fg.OFF}")
|
|
569
|
+
CatUnique = dfT[Category].unique()
|
|
570
|
+
print(f"{Category} = {CatUnique}")
|
|
571
|
+
# création des tableaux pour comptabiliser les reçus (NRP), ajournés (NAJP) par parcours
|
|
572
|
+
# NTP contient le nombre total d'étudiants par parcours
|
|
573
|
+
NT = np.zeros(len(CatUnique))
|
|
574
|
+
NADM = np.zeros(len(CatUnique))
|
|
575
|
+
NAJ = np.zeros(len(CatUnique))
|
|
576
|
+
NAb = np.zeros(len(CatUnique))
|
|
577
|
+
NGH = np.zeros(len(CatUnique))
|
|
578
|
+
dfT.apply(lambda row: incrementer(row, Category, CatUnique, note, NT), axis=1)
|
|
579
|
+
dfADM.apply(lambda row: incrementer(row, Category, CatUnique, note, NADM), axis=1)
|
|
580
|
+
dfAJ.apply(lambda row: incrementer(row, Category, CatUnique, note, NAJ), axis=1)
|
|
581
|
+
dfAb.apply(lambda row: incrementerAbandons(row, Category, CatUnique, NAb), axis=1)
|
|
582
|
+
dfGhosts.apply(lambda row: incrementer(row, Category, CatUnique, note, NGH, "Fantomes"), axis=1)
|
|
583
|
+
dfADMAJ = pd.concat([dfADM,dfAJ])
|
|
584
|
+
# display(dfADMAJ.describe(include='all'))
|
|
585
|
+
# print(dfADMAJ[Moyenne].mean())
|
|
586
|
+
|
|
587
|
+
print(f" NT/Cat = {NT}")
|
|
588
|
+
print(f"{fg.GREEN}NADM/Cat = {NADM}{fg.OFF}")
|
|
589
|
+
print(f"{fg.RED} NAJ/Cat = {NAJ}{fg.OFF}")
|
|
590
|
+
print(f"{fg.RED} NAb/Cat = {NAb}{fg.OFF}")
|
|
591
|
+
print(f"{fg.LIGHTGRAY} NGH/Cat = {NGH}{fg.OFF}")
|
|
592
|
+
|
|
593
|
+
print(f"{hl.BOLD}{fg.GREEN}Reçus par {Category}{fg.OFF}")
|
|
594
|
+
i = 0
|
|
595
|
+
nADM = 0
|
|
596
|
+
nAJ = 0
|
|
597
|
+
nAb = 0
|
|
598
|
+
nTOT = 0
|
|
599
|
+
nGH = 0
|
|
600
|
+
Moy = np.zeros(len(CatUnique))
|
|
601
|
+
StD = np.zeros(len(CatUnique))
|
|
602
|
+
Med = np.zeros(len(CatUnique))
|
|
603
|
+
MoyGlob = np.zeros(len(CatUnique))
|
|
604
|
+
MoyGlobT = 0
|
|
605
|
+
for C in CatUnique:
|
|
606
|
+
print(f"{hl.BOLD}{C:>20}{fg.OFF} = {hl.BOLD}{fg.GREEN}{100*NADM[i]/NT[i]:.1f} %{fg.OFF} ({fg.RED}AJ : {NAJ[i]:3.0f} dont {NAb[i]:3.0f} Ab{fg.OFF},"\
|
|
607
|
+
f" {fg.GREEN}ADM : {NADM[i]:3.0f}{fg.OFF}, Tot : {NT[i]:3.0f} {fg.LIGHTGRAY}+Fantômes : {NGH[i]:3.0f}){fg.OFF}")
|
|
608
|
+
# display(dfADM.loc[dfADM[Category] == C][note].sum())
|
|
609
|
+
Moy[i] = (dfADM.loc[dfADM[Category] == C][note].sum()+dfAJ.loc[dfAJ[Category] == C][note].sum())
|
|
610
|
+
MoyGlob[i] = Moy[i]
|
|
611
|
+
MoyGlobT += Moy[i]
|
|
612
|
+
Moy[i] = np.round(Moy[i] / (NADM[i]+NAJ[i]),2)
|
|
613
|
+
MoyGlob[i] = np.round(MoyGlob[i] / (NADM[i]+NAJ[i]+NGH[i]),2)
|
|
614
|
+
StD[i] = np.round(dfADMAJ.loc[dfADMAJ[Category] == C][note].std(),1)
|
|
615
|
+
Med[i] = np.round(dfADMAJ.loc[dfADMAJ[Category] == C][note].median(),1)
|
|
616
|
+
# print("Moyennes ",Moy[i],MoyGlob[i])
|
|
617
|
+
nADM += NADM[i]
|
|
618
|
+
nAJ += NAJ[i]
|
|
619
|
+
nAb += NAb[i]
|
|
620
|
+
nGH += NGH[i]
|
|
621
|
+
nTOT += NT[i]
|
|
622
|
+
i+=1
|
|
623
|
+
print(f"{hl.BOLD}{fg.GREEN} ADM = {nADM:3.0f}{fg.OFF}")
|
|
624
|
+
print(f"{hl.BOLD}{fg.RED} AJ = {nAJ:3.0f}{fg.OFF}")
|
|
625
|
+
print(f"{hl.BOLD}{fg.RED} dont Ab = {nAb:3.0f} (= abandons){fg.OFF}")
|
|
626
|
+
print(f"{hl.BOLD}{fg.BLACK} TOT = {nTOT:3.0f}{fg.OFF}")
|
|
627
|
+
print(f"{hl.BOLD}{fg.LIGHTGRAY}(+Fantomes = {nGH:3.0f}){fg.OFF}")
|
|
628
|
+
MoyT = np.round(dfADMAJ[note].mean(),2)
|
|
629
|
+
StDT = np.round(dfADMAJ[note].std(),1)
|
|
630
|
+
MedT = np.round(dfADMAJ[note].median(),1)
|
|
631
|
+
MoyGlobT = np.round(MoyGlobT/(nTOT+nGH),2)
|
|
632
|
+
rowTotal = [NT.sum(),NADM.sum(),NAJ.sum(),NAb.sum(),np.round(100*NADM.sum()/NT.sum(),1),MoyT,StDT,MedT,NGH.sum(),NT.sum()+NGH.sum(),np.round(100*NADM.sum()/(NT.sum()+NGH.sum()),1),MoyGlobT]
|
|
633
|
+
defCol = ["Présents","ADM","AJ","dont Ab","Taux ADM (présents)","Moy.","StDev","Med.","Fantômes","Total","Taux ADM (tot.)","Moy."]
|
|
634
|
+
dfStats=pd.DataFrame(zip(NT,NADM,NAJ,NAb,np.round(100*NADM/NT,1),Moy,StD,Med,NGH,NT+NGH,np.round(100*NADM/(NT+NGH),1),MoyGlob),index=CatUnique,columns=defCol)
|
|
635
|
+
rowTotal = pd.DataFrame([rowTotal],index=["Total"],columns=dfStats.columns)
|
|
636
|
+
dfStats=pd.concat([dfStats,rowTotal])
|
|
637
|
+
dfStats.style.set_caption(f"Statistiques par {Category}")
|
|
638
|
+
return dfStats
|
|
639
|
+
|
|
640
|
+
def ApplySecondeChance(df,nomCC,nomCCSC,nomCCdelaSC):
|
|
641
|
+
"""
|
|
642
|
+
modification du dataframe df
|
|
643
|
+
|
|
644
|
+
entrée :
|
|
645
|
+
- df = dataframe auquel on va ajouter une nouvelle colonne nomCCSC qui va contenir la note de la colonne nomCC soit celle de la colonne nomCCdelaSC, si cell-ci est supérieure à la notede nomCC
|
|
646
|
+
- nomCC = nom de la colonne à laquelle on applique la seconde chance
|
|
647
|
+
- nomCCSC = nom de la nouvelle colonne qui contient la note du CC après application de la seconde chance
|
|
648
|
+
- nomCCdelaSC = nom de la colonne qui contient la note "seconde chance
|
|
649
|
+
sortie :
|
|
650
|
+
- moyenne après application de la seconde chance
|
|
651
|
+
- écart-type après application de la seconde chance
|
|
652
|
+
"""
|
|
653
|
+
df[nomCCSC] = df[[nomCC,nomCCdelaSC]].max(axis=1)
|
|
654
|
+
moySC = pd.to_numeric(df[nomCCSC], errors='coerce').mean()
|
|
655
|
+
stdSC = pd.to_numeric(df[nomCCSC], errors='coerce').std()
|
|
656
|
+
return moySC, stdSC
|
|
657
|
+
|
|
658
|
+
def ComparaisonMoyennesDMCC(df,nomCC1,nomCC2,SeuilBadDMCC1,SeuilGoodDMCC1):
|
|
659
|
+
"""
|
|
660
|
+
entrée :
|
|
661
|
+
- df = dataframe avec uniquement les étudiants AJ ou ADM, c'est-à-dire qu'il n'y a aucun fantôme
|
|
662
|
+
- nomCC1 = en-tête de la colonne de df qui contient la note du premier CC
|
|
663
|
+
- nomCC2 = en-tête de la colonne de df qui contient la note du 2nd CC
|
|
664
|
+
- SeuilBadDMCC1 = Seuil en-dessous duquel la note au nomCC1 est considérée comme médiocre
|
|
665
|
+
- SeuilGoodDMCC1 = Seuil au-deçà duquel la note au nomCC1 est considérée comme bonne
|
|
666
|
+
affichage :
|
|
667
|
+
- moyenne au nomCC2 de la cohorte d'étudiant(e)s
|
|
668
|
+
- en-dessous du SeuilBadDMCC1 au nomCC1
|
|
669
|
+
- au-dessus du SeuilGoodDMCC1 au nomCC1
|
|
670
|
+
- entre les deux seuils au nomCC1
|
|
671
|
+
- jointplot entre nomCC1 et nomCC2 uniquement pour les étudiant(e)s dont la note au CC1 est considérée comme médiocre
|
|
672
|
+
"""
|
|
673
|
+
print(f"{hl.BOLD}Corrélation entre {fg.BLUE}{nomCC1}{fg.BLACK} et {fg.BLUE}{nomCC2}{fg.BLACK} ?{hl.OFF}")
|
|
674
|
+
dfBadCC1 = df[df[nomCC1] < SeuilBadDMCC1]
|
|
675
|
+
dfAvCC1 = df[(df[nomCC1] >= SeuilBadDMCC1) & (df[nomCC1] <= SeuilGoodDMCC1)]
|
|
676
|
+
dfGCC1 = df[df[nomCC1] > SeuilGoodDMCC1]
|
|
677
|
+
MoyenneAuCC2desBadCC1 = np.round(dfBadCC1[nomCC2].mean(),1)
|
|
678
|
+
MoyenneAuCC2desAvCC1 = np.round(dfAvCC1[nomCC2].mean(),1)
|
|
679
|
+
MoyenneAuCC2desGoodCC1 = np.round(dfGCC1[nomCC2].mean(),1)
|
|
680
|
+
MoyenneAuCC1 = np.round(df[nomCC1].mean(),1)
|
|
681
|
+
MoyenneAuCC2 = np.round(df[nomCC2].mean(),1)
|
|
682
|
+
print(f"Les étudiant(e)s qui ont eu moins de {SeuilBadDMCC1}/20 au {nomCC1} ont en moyenne {MoyenneAuCC2desBadCC1}/20 au {nomCC2}")
|
|
683
|
+
print(f"Les étudiant(e)s qui ont eu entre {SeuilBadDMCC1}/20 et {SeuilGoodDMCC1}/20 au {nomCC1} ont en moyenne {MoyenneAuCC2desAvCC1}/20 au {nomCC2}")
|
|
684
|
+
print(f"Les étudiant(e)s qui ont eu plus de {SeuilGoodDMCC1}/20 au {nomCC1} ont en moyenne {MoyenneAuCC2desGoodCC1}/20 au {nomCC2}")
|
|
685
|
+
print(f"Pour rappel, la moyenne au {nomCC1} = {MoyenneAuCC1}/20 et celle au {nomCC2} = {MoyenneAuCC2}/20")
|
|
686
|
+
sns.jointplot(x = nomCC1, y = nomCC2, data = dfBadCC1)
|
|
687
|
+
plt.show()
|
|
688
|
+
return
|
|
689
|
+
|
|
690
|
+
####################################################################################################################################
|
|
691
|
+
# G R A P H E S
|
|
692
|
+
####################################################################################################################################
|
|
693
|
+
|
|
694
|
+
def Histogrammes(df,nomCC,Moyenne,NomGraphique,w,moy,std,moyT,stdT,legende):
|
|
695
|
+
"""
|
|
696
|
+
entrée :
|
|
697
|
+
- df = dataframe qui contient ID, Noms, Prénoms, notes de CC, et Moyenne pondérée
|
|
698
|
+
- nomCC = liste qui contient noms d'en-têtes des colonnes qui contiennent les notes de CC
|
|
699
|
+
- Moyenne = nom de l'en-tête de la colonne qui contient la moyenne
|
|
700
|
+
- NomGraphique = nom du fichier png qui va contenir la figure
|
|
701
|
+
- w = liste qui contient les coeffs des CC
|
|
702
|
+
- moy = liste qui contient la moyenne de chaque CC
|
|
703
|
+
- std = liste qui contient l'écart-type calculé pour chaque CC
|
|
704
|
+
- moyT = moyenne des 4 CC
|
|
705
|
+
- stdT = écart-type calculé pour la note globale
|
|
706
|
+
- legende = titre qui sera affiché sur l'histogramme principal
|
|
707
|
+
affichage :
|
|
708
|
+
- 1 petit histogramme /CC
|
|
709
|
+
- 1 grand histogramme avec la moyenne
|
|
710
|
+
sauvegarde :
|
|
711
|
+
- fichier graphique 'NomGraphique' avec 1 petit histogramme par CC + 1 grand histogramme avec la moyenne
|
|
712
|
+
"""
|
|
713
|
+
|
|
714
|
+
import matplotlib.gridspec as gridspec
|
|
715
|
+
|
|
716
|
+
wt = sum(w)
|
|
717
|
+
|
|
718
|
+
fig = plt.figure(figsize=(18, 12))
|
|
719
|
+
plt.rcParams["font.size"] = (14) #font size
|
|
720
|
+
gs = gridspec.GridSpec(2, 4, height_ratios=[1, 1], width_ratios=[1, 1, 1, 1])
|
|
721
|
+
|
|
722
|
+
fontpropertiesT = {'family':'sans-serif', 'weight' : 'bold', 'size' : 16}
|
|
723
|
+
fontpropertiesA = {'family':'sans-serif', 'weight' : 'bold', 'size' : 18}
|
|
724
|
+
|
|
725
|
+
sns.set_style("whitegrid")
|
|
726
|
+
ax00=plt.subplot(gs[0,0])
|
|
727
|
+
fig00=sns.histplot(data=df, x=nomCC[0], discrete=True, kde=True, color="#3e95ff", alpha=1.0)
|
|
728
|
+
ax00.set_xlabel("note / 20",fontsize=16)
|
|
729
|
+
ax00.set_ylabel("Count",fontsize=16)
|
|
730
|
+
ax00.set_title(f"{nomCC[0]} ({w[0]}%). <N> = {moy[0]:.1f}, $\sigma$ = {std[0]:.1f}",color = "red", font=fontpropertiesT)
|
|
731
|
+
|
|
732
|
+
ax01=plt.subplot(gs[0,1])
|
|
733
|
+
fig01=sns.histplot(data=df, x=nomCC[1], discrete=True, kde=True, color="#3e95ff", alpha=1.0)
|
|
734
|
+
ax01.set_xlabel("note / 20",fontsize=16)
|
|
735
|
+
ax01.set_ylabel("Count",fontsize=16)
|
|
736
|
+
ax01.set_title(f"{nomCC[1]} ({w[1]}%) <N> = {moy[1]:.1f}, $\sigma$ = {std[1]:.1f}",color = "red", font=fontpropertiesT)
|
|
737
|
+
|
|
738
|
+
ax02=plt.subplot(gs[0,2])
|
|
739
|
+
fig02=sns.histplot(data=df, x=nomCC[2], discrete=True, kde=True, color="#3e95ff", alpha=1.0)
|
|
740
|
+
ax02.set_xlabel("note / 20",fontsize=16)
|
|
741
|
+
ax02.set_ylabel("Count",fontsize=16)
|
|
742
|
+
ax02.set_title(f"{nomCC[2]} ({w[2]}%) <N> = {moy[2]:.1f}, $\sigma$ = {std[2]:.1f}",color = "red", font=fontpropertiesT)
|
|
743
|
+
|
|
744
|
+
ax03=plt.subplot(gs[0,3])
|
|
745
|
+
fig03=sns.histplot(data=df, x=nomCC[3], discrete=True, kde=True, color="#3e95ff", alpha=1.0)
|
|
746
|
+
ax03.set_xlabel("note / 20",fontsize=16)
|
|
747
|
+
ax03.set_ylabel("Count",fontsize=16)
|
|
748
|
+
ax03.set_title(f"{nomCC[3]} ({w[3]}%) <N> = {moy[3]:.1f}, $\sigma$ = {std[3]:.1f}",color = "red", font=fontpropertiesT)
|
|
749
|
+
|
|
750
|
+
axTot=plt.subplot(gs[1,0:4])
|
|
751
|
+
figTot=sns.histplot(data=df, x=Moyenne, discrete=True, kde=True, color="#737cff", alpha=1.0, stat='count', label = legende)
|
|
752
|
+
axTot.set_xlabel("note / 20",fontsize=16)
|
|
753
|
+
axTot.set_ylabel("Count",fontsize=16)
|
|
754
|
+
axTot.set_xticks(np.arange(0,19,2))
|
|
755
|
+
axTot.set_title(f"Moyenne ({wt}%). <N> = {moyT:.1f}, $\sigma$ = {stdT:.1f}",color = "red", font=fontpropertiesT)
|
|
756
|
+
axTot.legend()
|
|
757
|
+
|
|
758
|
+
fig.savefig(NomGraphique, dpi=300)
|
|
759
|
+
plt.show()
|
|
760
|
+
return
|
|
761
|
+
|
|
762
|
+
def kdePlotByMentionEtParcours(df,Moyenne,Mention,Parcours,NomGraphique):
|
|
763
|
+
"""
|
|
764
|
+
entrée :
|
|
765
|
+
- df = dataframe qui contient ID, Noms, Prénoms, notes de CC, Moyenne pondérée, Mention et Parcours
|
|
766
|
+
- Mention = nom de l'en-tête de la colonne qui contient la Mention de diplôme simplifiée d'un étudiant
|
|
767
|
+
- Parcours = nom de l'en-tête de la colonne qui contient le parcours suivi par un étudiant
|
|
768
|
+
affichage :
|
|
769
|
+
- 1 graphe avec des plots kde par Mention
|
|
770
|
+
- 1 graphe avec des plots kde par Parcours
|
|
771
|
+
sauvegarde :
|
|
772
|
+
- fichier graphique 'NomGraphique' avec les 2 graphes
|
|
773
|
+
"""
|
|
774
|
+
import matplotlib.gridspec as gridspec
|
|
775
|
+
fig = plt.figure(figsize=(18, 12))
|
|
776
|
+
plt.rcParams["font.size"] = (14) #font size
|
|
777
|
+
gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1], width_ratios=[1])
|
|
778
|
+
|
|
779
|
+
fontpropertiesT = {'family':'sans-serif', 'weight' : 'bold', 'size' : 16}
|
|
780
|
+
fontpropertiesA = {'family':'sans-serif', 'weight' : 'bold', 'size' : 18}
|
|
781
|
+
|
|
782
|
+
sns.set_style("whitegrid")
|
|
783
|
+
axP=plt.subplot(gs[0,0])
|
|
784
|
+
hue_order = np.sort(df[Mention].unique())
|
|
785
|
+
figP=sns.kdeplot(data=df.sort_values(by = Mention), x=Moyenne, alpha=0.4, hue=Mention, bw_adjust=1, cut=0, fill=True, linewidth=3, hue_order=hue_order)
|
|
786
|
+
axP.set_xlabel("note / 20",fontsize=16)
|
|
787
|
+
axP.set_ylabel("Count",fontsize=16)
|
|
788
|
+
#axP.set_title(f"Total/Mention. <N> = {moy1:.1f}, $\sigma$ = {std1:.1f}",color = "red", font=fontpropertiesT)
|
|
789
|
+
|
|
790
|
+
axO=plt.subplot(gs[1,0])
|
|
791
|
+
hue_order = np.sort(df[Parcours].unique())
|
|
792
|
+
figO=sns.kdeplot(data=df.sort_values(by = Parcours), x=Moyenne, alpha=0.4, hue=Parcours, bw_adjust=1, cut=0, fill=True, linewidth=3, hue_order=hue_order)
|
|
793
|
+
axO.set_xlabel("note / 20",fontsize=16)
|
|
794
|
+
axO.set_ylabel("Count",fontsize=16)
|
|
795
|
+
axO.set_xticks(np.arange(0,19,2))
|
|
796
|
+
#axO.set_title(f"Total/Parcours. <N> = {moyT:.1f}, $\sigma$ = {stdT:.1f}",color = "red", font=fontpropertiesT)
|
|
797
|
+
|
|
798
|
+
fig.savefig(NomGraphique, dpi=300)
|
|
799
|
+
plt.show()
|
|
800
|
+
return
|
|
801
|
+
|
|
802
|
+
def BoiteAMoustachesByMentionEtParcours(df, Moyenne, Mention, Parcours, NomGraphique):
|
|
803
|
+
import matplotlib.gridspec as gridspec
|
|
804
|
+
|
|
805
|
+
plt.style.use('seaborn-v0_8-white')
|
|
806
|
+
fig = plt.figure(figsize=(16, 12))
|
|
807
|
+
plt.rcParams["font.size"] = (14) #font size
|
|
808
|
+
nP = (df[Mention].unique().shape[0])
|
|
809
|
+
nO = (df[Parcours].unique().shape[0])
|
|
810
|
+
gs = gridspec.GridSpec(2, 2, height_ratios=[1,2*nO/nP], width_ratios=[1,1])
|
|
811
|
+
|
|
812
|
+
fontpropertiesT = {'family':'sans-serif', 'weight' : 'bold', 'size' : 16}
|
|
813
|
+
fontpropertiesA = {'family':'sans-serif', 'weight' : 'bold', 'size' : 18}
|
|
814
|
+
|
|
815
|
+
sns.set_style("whitegrid")
|
|
816
|
+
axP=plt.subplot(gs[:,0])
|
|
817
|
+
figP=sns.boxplot(data=df.sort_values(by = Mention), x=Moyenne, y=Mention, palette='tab20c')
|
|
818
|
+
axP.set_xlabel("note / 20",fontsize=16)
|
|
819
|
+
axP.set_ylabel("",fontsize=16)
|
|
820
|
+
axP.set_xticks(np.arange(0,20,2))
|
|
821
|
+
#axP.set_title(f"Total/Mention. <N> = {moy1:.1f}, $\sigma$ = {std1:.1f}",color = "red", font=fontpropertiesT)
|
|
822
|
+
plt.xticks(fontweight='bold',fontsize=16)
|
|
823
|
+
plt.yticks(fontweight='bold',fontsize=16)
|
|
824
|
+
|
|
825
|
+
axO=plt.subplot(gs[-1,-1])
|
|
826
|
+
figO=sns.boxplot(data=df.sort_values(by = Parcours), x=Moyenne, y=Parcours, palette='tab20c')
|
|
827
|
+
axO.set_xlabel("note / 20",fontsize=16)
|
|
828
|
+
axO.set_ylabel("",fontsize=16)
|
|
829
|
+
axO.yaxis.tick_right()
|
|
830
|
+
axO.set_xticks(np.arange(0,20,2))
|
|
831
|
+
plt.xticks(fontweight='bold',fontsize=16)
|
|
832
|
+
plt.yticks(fontweight='bold',fontsize=16)
|
|
833
|
+
|
|
834
|
+
plt.savefig(NomGraphique, dpi=300)
|
|
835
|
+
plt.show()
|
|
836
|
+
return
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def BoiteAMoustachesByMentionEtParcoursHue(df, Moyenne, Mention, Parcours, NomGraphique):
|
|
840
|
+
import matplotlib.gridspec as gridspec
|
|
841
|
+
|
|
842
|
+
fig = plt.figure(figsize=(18, 12))
|
|
843
|
+
plt.style.use('seaborn-v0_8-whitegrid')
|
|
844
|
+
plt.rcParams["font.size"] = (14) #font size
|
|
845
|
+
|
|
846
|
+
fontpropertiesT = {'family':'sans-serif', 'weight' : 'bold', 'size' : 16}
|
|
847
|
+
fontpropertiesA = {'family':'sans-serif', 'weight' : 'bold', 'size' : 18}
|
|
848
|
+
|
|
849
|
+
sns.set_style("whitegrid")
|
|
850
|
+
hue_order = np.sort(df[Parcours].unique())
|
|
851
|
+
sns.boxplot(data=df.sort_values(by = Mention), x=Moyenne, y=Mention, hue=Parcours, palette='hsv', hue_order=hue_order)
|
|
852
|
+
plt.xlabel("note / 20",fontsize=16)
|
|
853
|
+
plt.ylabel("",fontsize=16)
|
|
854
|
+
plt.xticks(np.arange(0,20,2))
|
|
855
|
+
plt.xticks(fontweight='bold',fontsize=16)
|
|
856
|
+
plt.yticks(fontweight='bold',fontsize=16)
|
|
857
|
+
plt.legend(loc='lower left')
|
|
858
|
+
fig.savefig(NomGraphique, dpi=300)
|
|
859
|
+
plt.show()
|
|
860
|
+
return
|
|
861
|
+
|
|
862
|
+
def plotTauxADMasBars(dfwoSC,dfwSC,xCol,hueCol,NomGraphique):
|
|
863
|
+
"""
|
|
864
|
+
entrée :
|
|
865
|
+
- dfwoSC = dataframe contenant les stats ADM/AJ/Ghosts avant l'application de la seconde chance
|
|
866
|
+
- dfwSC = dataframe contenant les stats ADM/AJ/Ghosts après l'application de la seconde chance
|
|
867
|
+
- xCol = nom de la colonne qui contient les taux de réussite
|
|
868
|
+
- hueCol = nom de la colonne qui contient les paramètres avant ou après seconde chance
|
|
869
|
+
- NomGraphique = nom du fichier png qui va être sauvé
|
|
870
|
+
affichage :
|
|
871
|
+
- bar plot de seaborn
|
|
872
|
+
sauvegarde :
|
|
873
|
+
fichier graphique 'NomGraphique' au format png
|
|
874
|
+
"""
|
|
875
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
876
|
+
plt.style.use('seaborn-v0_8-whitegrid')
|
|
877
|
+
my_colors = ['#ff696b','#00aa7f']
|
|
878
|
+
my_cmap = LinearSegmentedColormap.from_list("mycolors",my_colors)
|
|
879
|
+
df = pd.concat([dfwoSC, dfwSC])
|
|
880
|
+
nbars=df.shape[0]
|
|
881
|
+
fig = plt.figure(figsize=(18, nbars*0.6))
|
|
882
|
+
plt.rcParams["font.size"] = (16) #font size
|
|
883
|
+
ax = sns.barplot(data=df,y=df.index.values,x = xCol,hue=hueCol,palette=my_colors)
|
|
884
|
+
ax.bar_label(ax.containers[0],fontweight='bold',fontsize=18)
|
|
885
|
+
ax.bar_label(ax.containers[1],fontweight='bold',fontsize=18)
|
|
886
|
+
plt.xticks(fontweight='bold',fontsize=14)
|
|
887
|
+
plt.yticks(fontweight='bold',fontsize=16)
|
|
888
|
+
plt.xlim(0,100)
|
|
889
|
+
fig.savefig(NomGraphique, dpi=300)
|
|
890
|
+
plt.show()
|
|
891
|
+
return
|
|
892
|
+
|
|
893
|
+
def StackedBarPop(df,ListCols,ylabel,NomGraphique):
|
|
894
|
+
"""
|
|
895
|
+
entrée :
|
|
896
|
+
- df : dataframe contenant l'analyse statistique globale, dont les moyennes, effectifs, etc. i.e. le dataframe renvoyé par StatsRéussiteParMentionOuParcours()
|
|
897
|
+
- ListCols = liste avecles labels des colonnes contenant les valeurs numériques qu'on veut tracer comme un histogramme empilé
|
|
898
|
+
- ylabel = label de l'axe des y
|
|
899
|
+
- NomGraphique = nom du fichier png qui va être sauvé
|
|
900
|
+
affichage :
|
|
901
|
+
- bar plot empilé de pandas
|
|
902
|
+
sauvegarde :
|
|
903
|
+
fichier graphique 'NomGraphique' au format png
|
|
904
|
+
"""
|
|
905
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
906
|
+
plt.style.use('seaborn-v0_8-whitegrid')
|
|
907
|
+
my_colors = ['#b95651','#01bec3']
|
|
908
|
+
my_cmap = LinearSegmentedColormap.from_list("mycolors",my_colors)
|
|
909
|
+
nbars=df.shape[0]
|
|
910
|
+
bplot = df[ListCols].plot(kind='bar',stacked=True, figsize=(nbars*1.5,10), fontsize=16, width=0.85, colormap=my_cmap, edgecolor='black')
|
|
911
|
+
bplot.set_ylabel(ylabel,fontdict={'fontsize':18})
|
|
912
|
+
bplot.bar_label(bplot.containers[0],label_type='center',color='w',fontweight='bold',fontsize=18)
|
|
913
|
+
bplot.bar_label(bplot.containers[1],padding=5,fontweight='bold',fontsize=18)
|
|
914
|
+
bplot.legend(fontsize=20)
|
|
915
|
+
plt.xticks(fontweight='bold')
|
|
916
|
+
plt.savefig(NomGraphique, dpi=300)
|
|
917
|
+
plt.show()
|
|
918
|
+
return
|
|
919
|
+
|
|
920
|
+
def StackedBarPopPO(dfRef,Mention,Parcours,NomGraphique):
|
|
921
|
+
"""
|
|
922
|
+
entrée :
|
|
923
|
+
- dfRef : dataframe contenant la liste des étudiants avec leur Mention et leur otpion, tel que généré par mentionD()
|
|
924
|
+
- Mention = nom de la colonne qui contient le nom simplifié d'une Mention
|
|
925
|
+
- Parcours = nom de la colonne qui contient la version simplifiée d'un Parcours
|
|
926
|
+
- NomGraphique = nom du fichier png qui va être sauvé
|
|
927
|
+
affichage :
|
|
928
|
+
- histplot "hue" de seaborn, càd les effectifs par Parcours empilés pour chaque Mention
|
|
929
|
+
sauvegarde :
|
|
930
|
+
fichier graphique 'NomGraphique' au format png
|
|
931
|
+
"""
|
|
932
|
+
plt.style.use('seaborn-v0_8-whitegrid')
|
|
933
|
+
nP = (dfRef[Mention].unique().shape[0])
|
|
934
|
+
fig = plt.figure(figsize=(nP*2,8))
|
|
935
|
+
hue_order = np.sort(dfRef[Parcours].unique())
|
|
936
|
+
hplot = sns.histplot(data=dfRef, x=Mention, hue=Parcours, multiple="stack", stat='count', palette='terrain', hue_order=hue_order)
|
|
937
|
+
# print(len(hplot.containers[0]))
|
|
938
|
+
ntot = np.zeros(len(hplot.containers[0]))
|
|
939
|
+
xtot = np.zeros(len(hplot.containers[0]))
|
|
940
|
+
# print(ntot)
|
|
941
|
+
for c in hplot.containers:
|
|
942
|
+
# print(c[0].get_height())
|
|
943
|
+
for i in range(nP):
|
|
944
|
+
# print(c[i])
|
|
945
|
+
x = c[i].get_x()
|
|
946
|
+
y = c[i].get_y()
|
|
947
|
+
n = c[i].get_height()
|
|
948
|
+
ntot[i] += n
|
|
949
|
+
xtot[i] = x
|
|
950
|
+
if (n!=0) : hplot.annotate(n, (x+0.5, y+n/2), size = 16, weight="bold",ha="center",va="center",color="black")
|
|
951
|
+
# print(ntot,xtot)
|
|
952
|
+
# print(ntot.max())
|
|
953
|
+
[hplot.annotate(ntot[i], (xtot[i]+0.5, ntot[i]+4), size = 20, weight="bold",ha="center",va="center",color="#407ba6") for i in range(nP)]
|
|
954
|
+
plt.rcParams["font.size"] = (16) #font size
|
|
955
|
+
plt.xticks(fontweight='bold',fontsize=16)
|
|
956
|
+
plt.yticks(fontweight='bold',fontsize=16)
|
|
957
|
+
plt.savefig(NomGraphique, dpi=300)
|
|
958
|
+
plt.show()
|
|
959
|
+
return
|
|
960
|
+
|