repytex/repytex/tools/evaluation.py

283 lines
8.7 KiB
Python

#!/usr/bin/env python
# encoding: utf-8
import pandas as pd
import numpy as np
from repytex.tools.marks_plottings import (pie_pivot_table,
parallel_on,
radar_on,
hist_boxplot
)
import seaborn as sns
class Student(object):
"""
Informations on a student which can be use inside template.
Those informations should not be modify or use for compute analysis otherwise they won't be spread over other POV.
"""
def __init__(self, quest_df, exo_df, eval_df):
"""
Description of a student from quest, exo and eval
"""
name = {*quest_df["Eleve"].unique(),
*exo_df["Eleve"].unique(),
*eval_df["Eleve"].unique(),
}
if len(name) != 1:
raise ValueError("Can't initiate Student: dfs contains different student names")
self.name = name.pop()
evalname = {*quest_df["Nom"].unique(),
*exo_df["Nom"].unique(),
*eval_df["Nom"].unique(),
}
if len(evalname) != 1:
raise ValueError(f"Can't initiate Student: dfs contains different evaluation names ({'-'.join(evalname)})")
self.quest_df = quest_df
self.exo_df = exo_df
self.eval = eval_df.to_dict('records')[0]
@property
def latex_exo_tabulars(self):
""" Return list of latex tabulars. One by exercise of the evaluation """
try:
self._latex_exo_tabulars
except AttributeError:
self._latex_exo_tabulars = self.build_latex_exo_tabulars()
return self._latex_exo_tabulars
def build_latex_exo_tabulars(self):
tabulars = []
for t in self.exo_df["Exercice"]:
tabulars.append(self.build_latex_exo_tabular(t))
return tabulars
def build_latex_exo_tabular(self, exo_name):
exo = self.exo_df[self.exo_df["Exercice"] == exo_name]
quest = self.quest_df[self.quest_df["Exercice"] == exo_name]
tabular = [r"\begin{tabular}{|p{2cm}|p{1cm}|}"]
tabular.append(r"\hline")
tabular.append(r"\rowcolor{highlightbg}")
if type(exo_name) == int:
l = f"Exercice {exo_name} & {exo['Mark_barem'].all()}"
tabular.append(l + r" \\")
else:
l = f"{exo_name} & {exo['Mark_barem'].all()}"
tabular.append(l + r" \\")
tabular.append(r"\hline")
if len(quest) > 1:
for _, q in quest.iterrows():
line = ""
if not pd.isnull(q["Question"]):
line += " "+str(q['Question'])
if not pd.isnull(q["Commentaire"]):
line += " "+str(q['Commentaire'])
line += " & "
if q["Niveau"] == 1:
line += q['Latex_rep']
else:
line += str(q['Mark'])
line += r" \\"
tabular.append(line)
tabular.append(r"\hline")
tabular.append(r"\end{tabular}")
return '\n'.join(tabular)
@property
def pies_on_competence(self):
""" Pies chart on competences """
return pie_pivot_table(self.quest_df,
index = "Level",
columns = "Competence",
values = "Eleve",
aggfunc = len,
fill_value = 0,
)
@property
def pies_on_domaine(self):
""" Pies chart on domaines """
return pie_pivot_table(self.quest_df,
index = "Level",
columns = "Domaine",
values = "Eleve",
aggfunc = len,
fill_value = 0,
)
@property
def radar_on_competence(self):
""" Radar plot on competence """
return radar_on(self.quest_df,
"Competence")
@property
def radar_on_domaine(self):
""" Radar plot on domaine """
return radar_on(self.quest_df,
"Domaine")
@property
def heatmap_on_domain(self):
""" Heatmap over evals on domains """
comp = pd.pivot_table(self.quest_df,
index = "Competence",
columns = ["Exercice", "Question"],
values = ["Normalized"],
aggfunc = np.mean,
)
comp.columns = [f"{i['Exercice']} {i['Question']}" for _,i in self.quest_df[["Exercice", "Question"]].drop_duplicates().iterrows()]
return sns.heatmap(comp)
class Classe(object):
"""
Informations on a class which can be use inside template.
Those informations should not be modify or use for compute analysis otherwise they won't be spread over other POV.
"""
def __init__(self, quest_df, exo_df, eval_df):
""" Init of a class from quest, exo and eval """
names = {*quest_df["Nom"].unique(),
*exo_df["Nom"].unique(),
*eval_df["Nom"].unique(),
}
if len(names) != 1:
raise ValueError("Can't initiate Classe: dfs contains different evaluation names")
self.name = names.pop()
self.quest_df = quest_df
self.exo_df = exo_df
self.eval_df = eval_df
@property
def marks_tabular(self):
""" Latex tabular with marks of students"""
try:
self._marks_tabular
except AttributeError:
self._marks_tabular = self.eval_df[["Eleve", "Mark_barem"]]
self._marks_tabular.columns = ["Élèves", "Note"]
return self._marks_tabular.to_latex()
@property
def hist_boxplot(self):
""" Marks histogram and associed box plot """
return hist_boxplot(self.eval_df)
@property
def level_heatmap(self):
""" Heapmap on acheivement level """
pv = pd.pivot_table(self.quest_df,
index = "Eleve",
columns = ["Exercice", "Question", "Commentaire"],
values = ["Normalized"],
aggfunc = "mean",
)
def lines_4_heatmap(c):
lines = []
ini = ''
for k,v in enumerate(c.labels[1][::-1]):
if v != ini:
lines.append(k)
ini = v
return lines[1:]
exercice_sep = lines_4_heatmap(pv.columns)
pv.columns = [f"{i[3]:.15} {i[1]} {i[2]}" for i in pv.columns.get_values()]
level_heatmap = sns.heatmap(pv.T)
level_heatmap.hlines(exercice_sep,
*level_heatmap.get_xlim(),
colors = "orange",
)
return level_heatmap
@property
def pies_eff_pts_on_competence(self):
""" Pie charts on competence with repartition of evaluated times and attributed points """
return pie_pivot_table(self.quest_df[["Competence", "Bareme", "Exercice", "Question", "Commentaire"]].drop_duplicates(),
index = "Competence",
#columns = "Level",
values = "Bareme",
aggfunc=[len,np.sum],
fill_value=0)
@property
def pies_eff_pts_on_domaine(self):
""" Pie charts on domaine with repartition of evaluated times and attributed points """
return pie_pivot_table(self.quest_df[["Domaine", "Bareme", "Exercice", "Question", "Commentaire"]].drop_duplicates(),
index = "Domaine",
#columns = "Level",
values = "Bareme",
aggfunc=[len,np.sum],
fill_value=0)
# TODO: à factoriser Il y a la même dans term.py |jeu. mars 23 19:36:28 EAT 2017
def select(quest_df, exo_df, eval_df, index, value):
""" Return quest, exo and eval rows which correspond index == value
:param quest_df: TODO
:param exo_df: TODO
:param eval_df: TODO
"""
qu = quest_df[quest_df[index] == value]
exo = exo_df[exo_df[index] == value]
ev = eval_df[eval_df[index] == value]
return qu, exo, ev
def select_contains(quest_df, exo_df, eval_df, index, name_part):
""" Return quest, exo and eval rows which contains name_part
:param quest_df: TODO
:param exo_df: TODO
:param eval_df: TODO
"""
qu = quest_df[quest_df[index].str.contains(name_part)]
exo = exo_df[exo_df[index].str.contains(name_part)]
ev = eval_df[eval_df[index].str.contains(name_part)]
return qu, exo, ev
def students_pov(quest_df, exo_df, eval_df):
es = []
for e in eval_df["Eleve"].unique():
d = select(quest_df, exo_df, eval_df, "Eleve", e)
eleve = Student(*d)
es.append(eleve)
return es
def class_pov(quest_df, exo_df, eval_df):
return Classe(quest_df, exo_df, eval_df)
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del