Commit 18988014 authored by Florent Chehab's avatar Florent Chehab
Browse files

Update

parent cadb1130
%% Cell type:raw id: tags:
<script>
function code_toggle() {
if (code_shown){
$('div.input').hide('200');
$('.output_prompt').css({visibility: 'hidden'});
$('#toggleButton').val('Afficher le code source')
} else {
$('div.input').show('200');
$('.output_prompt').removeAttr('style');
$('#toggleButton').val('Cacher le code source')
}
code_shown = !code_shown
}
$( document ).ready(function(){
code_shown=false;
$('div.input').hide();
$('.output_prompt').css({visibility: 'hidden'});
$('table').addClass("table table-hover table-bordered table-striped");
});
</script>
<form action="javascript:code_toggle()">
<input type="submit" id="toggleButton" value="Afficher le code source" class="btn btn-info">
</form>
%% Cell type:markdown id: tags:
<center>
<img src="https://gitlab.utc.fr/LaTeX-UTC/Graphismes-UTC/raw/56dd9762de926727aa45c8279dcdf54a753335c8/logos/UTC/logo_UTC.png" alt="Logo UTC" width="200px"/>
<br>
<font size="15">Observatoire des évaluations d'UVs</font>
</center>
%% Cell type:code id: tags:
``` python
# For auto completion inside jupyter notebook
%config IPCompleter.greedy=True
import yaml # for the config file
import warnings # to issue warnings when needed
import os.path # for easy work with file paths
# Simple functions
from src.generate_data import generate_data_file
from src.tools import is_connected, to_mardown_table_str
# For managing dataa
import json
from collections import OrderedDict
from datetime import datetime
import pandas as pd
import numpy as np
# For plotting data
from IPython.core.display import HTML, display, Markdown
from plotly.offline import init_notebook_mode, plot, iplot
import plotly.graph_objs as go
# Some magic for file size optimization
if is_connected(debug=True):
# do not include plotly js directly
init_notebook_mode(connected=True)
else:
# include plotly js directly
import cufflinks as cf
cf.set_config_file(offline=True, world_readable=True, theme='ggplot')
# First we take care of the configuration file
CONFIGURATION = yaml.load(open('./config.yml'))
ALL_SEMESTERS = {info['sem']:info['end_date'] for info in CONFIGURATION["other_semesters"]}
ALL_SEMESTERS[CONFIGURATION["main_semester"]['sem']] = CONFIGURATION["main_semester"]['end_date']
MAIN_SEMESTER = CONFIGURATION["main_semester"]['sem']
OTHER_SEMESTERS = [d['sem'] for d in CONFIGURATION["other_semesters"]]
ALL_SEMESTERS_SORTED = sorted(list(ALL_SEMESTERS.keys()), key=lambda sem:sem[1:5]+str((ord(sem[0]) + 20)%26 ))
for semester in ALL_SEMESTERS.keys():
if not os.path.isfile('./data/' + semester + '.json'):
warnings.warn("Il manque des fichiers de données des évaluations d'UVs pour le semestre "
+ semester
+ ". Nous allons en générer pour pouvoir continer.")
generate_data_file(semester)
ALL_SEMESTERS[semester] = datetime.strptime(ALL_SEMESTERS[semester], '%d/%m/%Y')
# import data
EVALS = {}
EVALS_DATE_CREATED = {}
for semester in ALL_SEMESTERS.keys():
data_json = json.load(open('./data/' + semester + '.json'))
date = data_json["date"]
if data_json["semester"] != semester:
print(data_json["semester"], semester)
raise Exception("Incoherent data file naming !")
EVALS_DATE_CREATED[semester] = datetime.strptime(date, '%d/%m/%Y')
data = OrderedDict(data_json["data"])
for uv, description in data.items():
if description["date_review"] is not None and type(description["date_review"]) != 'datetime.datetime':
description["date_review"] = datetime.strptime(description["date_review"], '%d/%m/%Y')
EVALS[semester] = pd.DataFrame.from_dict(data, orient = "index")
EVALS_MAIN_SEM = EVALS[MAIN_SEMESTER]
```
%% Cell type:code id: tags:
``` python
str_tmp = "</br><center><span style='font-size:40px'>Semestre d'étude : "
str_tmp += MAIN_SEMESTER
str_tmp += "</span><br><br>"
str_tmp += "<span style='font-size:30px'>Autre(s) semestre(s) pour comparaison : "
str_tmp += " ".join(x for x in OTHER_SEMESTERS)
str_tmp += "</span></center><br>"
HTML(str_tmp)
```
%% Cell type:markdown id: tags:
# Introduction
## Présentation des données
### Âge des données
%% Cell type:code id: tags:
``` python
str_tmp = "Les données pour chaque semestre ont été extraites aux dates : \n\n "
t = [["Semestre", "Date d'extraction"]]
for sem in EVALS_DATE_CREATED.keys():
t.append([sem, EVALS_DATE_CREATED[sem].strftime('%d/%m/%Y')])
Markdown(str_tmp + to_mardown_table_str(t))
```
%% Cell type:markdown id: tags:
### Structure des données
Pour chaque semestre, nous avons les informations des évaluations telles que visibles par les étudiants sur l'ENT aux dates d'extraction précisées ci-dessus. Nous connaissons notamment :
- Le code de l'UV ;
- L'intitulé de l'UV ;
- Le nombre d'étudiants inscrits ;
- Le nombre d'étudiants marqués comme _absents_ ;
- Le nombre d'étudiants qui ont obtenu l'UV ;
- Le nombre d'évaluations saisies par les étudiants ;
- Les statistiques pour chacune des questions (i.e. le nombre de '++', etc. pour chaque question) ;
- La longueur du commentaire saisi par le responsable de l'UV, s'il en a saisi un ;
- La date à laquelle le responsable de l'UV a « regardé » évaluations.
Voici un exemple pour l'UV MT23 :
%% Cell type:code id: tags:
``` python
print(EVALS_MAIN_SEM.loc["MT23"])
```
%% Cell type:markdown id: tags:
## Informations générales et analyse comparative
### Remarque
Lorsque les statistiques des évaluations d'UVs (le nombre de `++`, de `+`, etc.) seront analysées de manière quantitative la table de conversion _symétrique_ suivante sera employée :
| Sigle | Valeur associée |
|-------|-----------------|
| `++` | +3 |
| `+` | +1 |
| `-` | -1 |
| `--` | -3 |
%% Cell type:code id: tags:
``` python
POSSIBLE_ANSWERS = ["--","-","+","++"]
POSSIBLE_ANSWERS_COEFFS = [-3,-1,1,3]
def get_nb_evals(stats):
return sum([stats['1'][s] for s in POSSIBLE_ANSWERS])
def get_mark_q(stat, q):
if type(q) is int:
q = str(q)
return sum([stat[q][POSSIBLE_ANSWERS[i]] * POSSIBLE_ANSWERS_COEFFS[i] for i in range(len(POSSIBLE_ANSWERS_COEFFS))]) / get_nb_evals(stat)
POSSIBLE_ANSWERS_COL = ['rgb(255,48,48)','rgb(255,165,0)', 'rgb(144,238,144)', 'rgb(48,221,48)']
ORANGE = 'rgb(255,126,24)'
# Lets have some standard scales
SCALE_PERCENTAGE = [-1,101]
SCALE_MARK = [-3.1, 3.1]
```
%% Cell type:markdown id: tags:
## Informations générales
L'analyse comparative se fait par rapport aux données des précédents semestres.
Lorsque'une analyse comparative est réalisée, elle est faite par rapport aux données des précédents semestres.
### Taux de réponse
%% Cell type:code id: tags:
``` python
t = [["Semestre","Nombre d'UVs", "Taux de réponse moyen des étudiants" , "Nombre d'UV qui ont été vu", "avec un commentaire"]]
SEMESTERS_INFO = {}
for sem in ALL_SEMESTERS_SORTED:
sem_data = EVALS[sem]
nb_uvs = len(sem_data.index)
taux_eval = sem_data.loc[:, ("nb_evals","nb_etu_registered")].min(axis=1)/sem_data["nb_etu_registered"]
taux_eval = taux_eval.fillna(1) # Sometimes there 0 students in a course but there are evaluations
taux_eval = sum(taux_eval)/nb_uvs
uvs_with_evals = sem_data.loc[pd.notna(sem_data["date_review"])]
nb_uvs_with_evals = len(uvs_with_evals.index)
uvs_with_com = sem_data.loc[sem_data["comment"]>0]
nb_uvs_with_com = len(uvs_with_com)
SEMESTERS_INFO[sem] = {
'nb_uvs': nb_uvs,
'nb_uvs_with_com': nb_uvs_with_com,
'nb_uvs_with_evals': nb_uvs_with_evals,
'taux_rep_avg': taux_eval
}
t.append([sem, str(nb_uvs),
"{0:.1f}%".format(taux_eval * 100),
str(nb_uvs_with_evals)+" ({0:.1f}%)".format(nb_uvs_with_evals / nb_uvs * 100),
str(nb_uvs_with_com)+" ({0:.1f}%)".format(nb_uvs_with_com / nb_uvs * 100)])
Markdown(to_mardown_table_str(t))
```
%% Cell type:code id: tags:
``` python
traces = []
for sem in ALL_SEMESTERS_SORTED:
info = SEMESTERS_INFO[sem]
traces += [go.Bar(
x=["Taux de réponse moyen des étudiants", "Taux d'UVs où les resps. ont 'vu' les éval.", "Taux d'UVs où le resp. a laissé un commentaire"],
y=[
info['taux_rep_avg']*100,
info['nb_uvs_with_evals']/info['nb_uvs'] *100,
info['nb_uvs_with_com']/info['nb_uvs']*100
],
name=sem
)]
layout = go.Layout(
barmode='group',
title = "Taux de réponses aux évaluations",
yaxis=dict(
range=[0, 100],
range=SCALE_PERCENTAGE,
title='Pourcentage',
),
)
iplot(go.Figure(data=traces, layout=layout))
```
%% Cell type:code id: tags:
``` python
traces = []
for sem in ALL_SEMESTERS_SORTED:
sem_data = EVALS[sem]
taux_eval = sem_data.loc[:, ("nb_evals","nb_etu_registered")].min(axis=1)/sem_data["nb_etu_registered"]
taux_eval = taux_eval.fillna(1) # Sometimes there 0 students in a course but there are evaluations
traces += [go.Box(
y=taux_eval*100,
text=taux_eval.index,
name=sem
)]
layout = go.Layout(
title = "Taux de réponse moyen des étudiants aux évaluations",
yaxis=dict(
range=[0, 100],
range=SCALE_PERCENTAGE,
title='Taux de réponse',
),