Optimize compute_module_moy

This commit is contained in:
Emmanuel Viennet 2021-11-26 17:26:34 +01:00
parent 83ba9cf186
commit 5a3c25e67f
2 changed files with 33 additions and 32 deletions

View File

@ -121,8 +121,8 @@ def df_load_modimpl_notes(moduleimpl_id: int) -> pd.DataFrame:
def compute_module_moy(
evals_notes: pd.DataFrame,
evals_poids: pd.DataFrame,
evals_notes_df: pd.DataFrame,
evals_poids_df: pd.DataFrame,
evaluations: list,
) -> pd.DataFrame:
"""Calcule les moyennes des étudiants dans ce module
@ -140,34 +140,35 @@ def compute_module_moy(
ou NaN si les évaluations (dans lesquelles l'étudiant à des notes)
ne donnent pas de coef vers cette UE.
"""
nb_etuds = len(evals_notes)
nb_ues = evals_poids.shape[1]
etud_moy_module_arr = np.zeros((nb_etuds, nb_ues))
evals_poids_arr = evals_poids.to_numpy().transpose() * [
e.coefficient for e in evaluations
]
# -> evals_poids_arr shape : (nb_ues, nb_evals)
nb_etuds, nb_evals = evals_notes_df.shape
nb_ues = evals_poids_df.shape[1]
assert evals_poids_df.shape[0] == nb_evals # compat notes/poids
evals_coefs = np.array([e.coefficient for e in evaluations], dtype=float).reshape(
-1, 1
)
evals_poids = evals_poids_df.values * evals_coefs
# -> evals_poids_arr shape : (nb_evals, nb_ues)
assert evals_poids.shape == (nb_evals, nb_ues)
# Remet les notes sur 20 (sauf notes spéciales <= -1000):
evals_notes_arr = np.where(evals_notes.values > -1000, evals_notes.values, 0.0) / [
e.note_max / 20.0 for e in evaluations
]
for i in range(nb_etuds):
# note_vect: array [note_ue1, note_ue2, ...] de l'étudiant i
note_vect = evals_notes_arr[i]
# Les poids des évals pour cet étudiant: là où il a des notes non neutralisées
evals_notes = np.where(
evals_notes_df.values > -1000, evals_notes_df.values, 0.0
) / [e.note_max / 20.0 for e in evaluations]
# Les poids des évals pour les étudiant: là où il a des notes non neutralisées
# Attention: les NaN (codant les absents) sont remplacés par des 0 dans
# evals_notes_arr mais pas dans evals_poids_etud_arr
# evals_notes_arr mais pas dans evals_poids_etuds_arr
# (la comparaison est toujours false face à un NaN)
evals_poids_etud_arr = np.where(
evals_notes.values[i] <= -1000, 0, evals_poids_arr
# shape: (nb_etuds, nb_evals, nb_ues)
poids_stacked = np.stack([evals_poids] * nb_etuds)
evals_poids_etuds = np.where(
np.stack([evals_notes_df.values] * nb_ues, axis=2) <= -1000.0, 0, poids_stacked
)
# Calcule la moyenne pondérée sur les notes disponibles
evals_notes_stacked = np.stack([evals_notes] * nb_ues, axis=2)
with np.errstate(invalid="ignore"): # ignore les 0/0 (-> NaN)
etud_moy_module_arr[i] = (note_vect * evals_poids_etud_arr).sum(
axis=1
) / evals_poids_etud_arr.sum(axis=1)
etud_moy_module = np.sum(
evals_poids_etuds * evals_notes_stacked, axis=1
) / np.sum(evals_poids_etuds, axis=1)
etud_moy_module_df = pd.DataFrame(
etud_moy_module_arr, index=evals_notes.index, columns=evals_poids.columns
etud_moy_module, index=evals_notes_df.index, columns=evals_poids_df.columns
)
return etud_moy_module_df

View File

@ -234,7 +234,7 @@ def test_module_moy_elem(test_client):
"EVAL2": np.NaN, # et une ABS
},
]
evals_notes = pd.DataFrame(
evals_notes_df = pd.DataFrame(
data, index=["etud1", "etud2", "etud3", "etud4", "etud5"]
)
# Poids des évaluations (1 ligne / évaluation)
@ -242,13 +242,13 @@ def test_module_moy_elem(test_client):
{"UE1": 1, "UE2": 0, "UE3": 0},
{"UE1": 2, "UE2": 5, "UE3": 0},
]
evals_poids = pd.DataFrame(data, index=["EVAL1", "EVAL2"], dtype=float)
evals_poids_df = pd.DataFrame(data, index=["EVAL1", "EVAL2"], dtype=float)
evaluations = [
Evaluation(note_max=20.0, coefficient=1.0),
Evaluation(note_max=20.0, coefficient=1.0),
]
etud_moy_module_df = moy_mod.compute_module_moy(
evals_notes.fillna(0.0), evals_poids, evaluations
evals_notes_df.fillna(0.0), evals_poids_df, evaluations
)
NAN = 666.0 # pour pouvoir comparer NaN et NaN (car NaN != NaN)
r = etud_moy_module_df.fillna(NAN)