From c91ab67951d7a99ba8eb093f13887c20d68ff7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9o=20BARAS=20=28IUT1=20Grenoble=29?= Date: Thu, 7 Mar 2024 19:41:30 +0100 Subject: [PATCH] =?UTF-8?q?Etat=20interm=C3=A9diaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/pe/moys/pe_rcstag.py | 140 +++++++++++++++++++++++---------------- tests/unit/test_pe.py | 14 ++++ 2 files changed, 96 insertions(+), 58 deletions(-) create mode 100644 tests/unit/test_pe.py diff --git a/app/pe/moys/pe_rcstag.py b/app/pe/moys/pe_rcstag.py index e4a15517..f7cb4d5b 100644 --- a/app/pe/moys/pe_rcstag.py +++ b/app/pe/moys/pe_rcstag.py @@ -55,8 +55,7 @@ class RCSemXTag(pe_tabletags.TableTag): semXs_suivis: dict[int, dict], ): """Calcule les moyennes par tag (orientées compétences) - d'un regroupement de SxTag - (RCRCF), pour extraire les classements par tag pour un + d'un regroupement de SxTag, pour extraire les classements par tag pour un groupe d'étudiants donnés. Le groupe d'étudiants est formé par ceux ayant tous participé au même semestre terminal. @@ -139,13 +138,13 @@ class RCSemXTag(pe_tabletags.TableTag): # en "aggrégant" les données des sxstags, compétence par compétence ( inscr_df, - inscr_cube, + inscr_cube, # données d'inscriptions notes_df, - notes_cube, + notes_cube, # notes coeffs_df, - coeffs_cube, + coeffs_cube, # coeffs pour la moyenne générale (par UEs) coeffs_rcues_df, - coeffs_rcues_cube, + coeffs_rcues_cube, # coeffs pour la moyenne de regroupement d'UEs ) = self.assemble_cubes(tag) # Calcule les moyennes, et synthétise les coeffs @@ -363,67 +362,92 @@ class RCSemXTag(pe_tabletags.TableTag): coeffs_rcue: np.array, inscr_mask: np.array, ): - """Calcule la moyenne par compétences (à un tag donné) sur plusieurs semestres (partant du set_cube). + """Calcule la moyenne par UEs|Compétences en moyennant sur les semestres et renvoie les résultats (notes + et coeffs) sous la forme de DataFrame""" + (etud_moy_tag, coeff_tag) = compute_moyennes_par_RCS( + notes_cube, coeffs_cube, coeffs_rcue, inscr_mask + ) - La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles - - *Remarque* : Adaptation de moy_ue.compute_ue_moys_apc au cas des moyennes de tag - par aggrégat de plusieurs semestres. - - Args: - notes_cube: notes moyennes aux compétences ndarray - (etuds x UEs|compétences x sxtags), des floats avec des NaN - coeffs_cube: coeffs appliqués aux compétences - (etuds x UEs|compétences x sxtags) dans le calcul des moyennes générales, - des floats avec des NaN - coeffs_rcue_cube: coeffs des RCUE appliqués dans les moyennes de RCS - inscr_mask: inscriptions aux compétences ndarray - (etuds x UEs|compétences x sxtags), des 0 et des 1 - Returns: - Un DataFrame avec pour columns les moyennes par tags, - et pour rows les etudid - """ - # etudids_sorted: liste des étudiants (dim. 0 du cube) - # competences_sorted: list (dim. 1 du cube) - nb_etuds, nb_comps, nb_semestres = notes_cube.shape - # assert nb_etuds == len(etudids_sorted) - # assert nb_comps == len(competences_sorted) - - # Applique le masque d'inscriptions aux notes et aux coeffs - notes_significatives = notes_cube * inscr_mask - coeffs_significatifs = coeffs_cube * inscr_mask - - # Enlève les NaN des cubes pour les entrées manquantes - notes_no_nan = np.nan_to_num(notes_significatives, nan=0.0) - coeffs_no_nan = np.nan_to_num(coeffs_significatifs, nan=0.0) - - # Les moyennes par tag - with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN) - mask = ~np.isnan( - notes_significatives - ) # Quelles entrées contiennent des notes ? - etud_moy_tag = np.sum(notes_no_nan, axis=2) / np.sum(mask, axis=2) - - coeffs_pris_en_compte = coeffs_no_nan * mask - coeff_tag = np.sum(coeffs_pris_en_compte, axis=2) - - inscr_prise_en_compte = inscr_mask * mask - inscr_prise_en_compte = np.nan_to_num(inscr_prise_en_compte, nan=-1.0) - inscr_tag = np.max(inscr_prise_en_compte, axis=2) - inscr_tag[inscr_tag < 0] = np.NaN # fix les max non calculés (-1) -> Na? - - # Le dataFrame des notes moyennes - etud_moy_tag = etud_moy_tag * inscr_tag etud_moy_tag_df = pd.DataFrame( etud_moy_tag, index=self.etudids_sorted, # les etudids columns=self.competences_sorted, # les competences ) - # Le dataFrame des coeffs - coeff_tag = coeff_tag * inscr_tag # Réapplique le masque des inscriptions coeffs_df = pd.DataFrame( coeff_tag, index=self.etudids_sorted, columns=self.competences_sorted ) return etud_moy_tag_df, coeffs_df + + +def compute_moyennes_par_RCS( + notes_cube: np.array, + coeffs_cube: np.array, + coeffs_rcue: np.array, + inscr_mask: np.array, +): + """Partant d'une série de notes (fournie sous forme d'un cube + etudids_sorted x UEs|Compétences x semestres) + chaque note étant pondérée par un coeff dépendant du semestre (fourni dans coeffs_rcue), + et pondérée par un coeff dépendant de l'UE|Compétence pour calculer une moyenne générale + (fourni dans coeffs_cube), + calcule : + * la moyenne par UEs|Compétences sur plusieurs semestres (partant du set_cube). + * les coeffs "cumulés" à appliquer pour le calcul de la moyenne générale + + La moyenne est un nombre (note/20), ou NaN si pas de notes disponibles + + Args: + notes_cube: notes moyennes aux compétences ndarray + (etuds_sorted x UEs|compétences x sxtags), + des floats avec des NaN + coeffs_cube: coeffs appliqués aux compétences dans le calcul des moyennes générales, + (etuds_sorted x UEs|compétences x sxtags), + des floats avec des NaN + coeffs_rcue_cube: coeffs des RCUE appliqués dans les moyennes de RCS + inscr_mask: inscriptions aux compétences ndarray + (etuds_sorted x UEs|compétences x sxtags), + des 0 et des 1 + Returns: + Un DataFrame avec pour columns les moyennes par tags, + et pour rows les etudid + """ + # Applique le masque d'inscriptions aux notes et aux coeffs + notes_significatives = notes_cube * inscr_mask + coeffs_moy_gen_significatifs = coeffs_cube * inscr_mask + coeffs_rcues_significatifs = coeffs_rcue * inscr_mask + + # Enlève les NaN des cubes pour les entrées manquantes + notes_no_nan = np.nan_to_num(notes_significatives, nan=0.0) + coeffs_no_nan = np.nan_to_num(coeffs_moy_gen_significatifs, nan=0.0) + + # Les moyennes par tag + with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN) + # Quelles entrées contiennent des notes et des coeffs? + mask = ~np.isnan(notes_significatives) & ~np.isnan(coeffs_rcues_significatifs) + + # La moyenne (pondérée) sur le regroupement cohérent de semestres + coeffs_rcues_non_nan = np.nan_to_num(coeffs_rcues_significatifs * mask, nan=0.0) + notes_ponderes = notes_no_nan * coeffs_rcues_non_nan + etud_moy_tag = np.sum(notes_ponderes, axis=2) / np.sum( + coeffs_rcues_non_nan, axis=2 + ) + + # Les coeffs pour la moyenne générale + coeffs_pris_en_compte = coeffs_moy_gen_significatifs * mask + coeffs_no_nan = np.nan_to_num(coeffs_pris_en_compte, nan=0.0) + coeff_tag = np.sum(coeffs_no_nan, axis=2) + + # Le masque des inscriptions prises en compte + inscr_prise_en_compte = inscr_mask * mask + inscr_prise_en_compte = np.nan_to_num(inscr_prise_en_compte, nan=-1.0) + inscr_tag = np.max(inscr_prise_en_compte, axis=2) + inscr_tag[inscr_tag < 0] = np.NaN # fix les max non calculés (-1) -> Na? + + # Le dataFrame des notes moyennes, en réappliquant le masque des inscriptions + etud_moy_tag = etud_moy_tag * inscr_tag + # Le dataFrame des coeffs pour la moyenne générale, en réappliquant le masque des inscriptions + coeff_tag = coeff_tag * inscr_tag # Réapplique le masque des inscriptions + + return (etud_moy_tag, coeff_tag) diff --git a/tests/unit/test_pe.py b/tests/unit/test_pe.py new file mode 100644 index 00000000..81b4d9c0 --- /dev/null +++ b/tests/unit/test_pe.py @@ -0,0 +1,14 @@ +""" +Test calcul moyennes pour les poursuites d'études +""" +import numpy as np +from tests.unit import setup + +from app import db + +import app.pe.moys.pe_rcstag as pe_rcstag + + +def test_compute_moyennes_par_RCS: + """Test""" + pass \ No newline at end of file