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

update

parent 8fb1dc5e
%% 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']
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: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"]]
t = [["Semestre", "Date d'extraction"]]
for sem in EVALS_DATE_CREATED.keys():
t.append([sem, EVALS_DATE_CREATED[sem].strftime('%d/%m/%Y')])
Markdown(to_mardown_table_str(t))
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(list(EVALS_MAIN_SEM))
print(EVALS_MAIN_SEM.loc["MT23"])
```
%% Cell type:markdown id: tags:
## Informations générales et analyse comparative
L'analyse comparative se fait par rapport aux données des précédents semestres.
### Taux de réponse
%% Cell type:code id: tags:
``` python
print(EVALS_MAIN_SEM.iloc[10])
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:markdown id: tags:
%% Cell type:code id: tags:
## Informations générales
``` python
traces = []
### Taux de réponse
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
)]
%% Cell type:markdown id: tags:
layout = go.Layout(
barmode='group',
title = "Taux de réponses aux évaluations",
yaxis=dict(
range=[0, 100],
title='Pourcentage',
),
)
Voici un exemple de ligne (il y a une ligne par UV) :
<div class="alert alert-success">
<strong>Success!</strong> Indicates a successful or positive action.
</div>
iplot(go.Figure(data=traces, layout=layout))
```
%% Cell type:code id: tags:
``` python
nb_uvs = len(EVALS_MAIN_SEM.index)
print("Nombre d'UVs étudiées : "+str(nb_uvs))
traces = []
uvs_with_evals = EVALS_MAIN_SEM.loc[pd.notna(EVALS_MAIN_SEM["date_review"])]
nb_uvs_with_evals = len(uvs_with_evals.index)
print("Nombre d'UVs où le responsable a visualisé les évaluations : "+str(nb_uvs_with_evals))
print("Taux de visualisation : "+"{0:.0f}%".format(float(nb_uvs_with_evals)/nb_uvs * 100))
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],
title='Taux de réponse',
),
)
iplot(go.Figure(data=traces, layout=layout))
```
%% Cell type:markdown id: tags:
### Délai de réponse des responsables d'UVs
%% Cell type:code id: tags:
``` python
uvs_with_evals = EVALS_MAIN_SEM.loc[pd.notna(EVALS_MAIN_SEM["date_review"])]
graph_data = [
go.Scatter(
x=uvs_with_evals.sort_values(by=['date_review'])['date_review'],
x=uvs_with_evals['date_review'].sort_values(),
y=[100*float(i+1)/nb_uvs for i,e in enumerate(uvs_with_evals.index)],
text=uvs_with_evals.sort_values(by=['date_review']).index
)
]
layout = go.Layout(
title = "Délai de réponse",
yaxis=dict(
range=[0, 100],
title='Taux de réponse',
),
xaxis=dict(
title="Date"
)
)
iplot(go.Figure(data=graph_data, layout=layout))
```
%% Cell type:code id: tags:
``` python
traces = []
for sem in ALL_SEMESTERS_SORTED:
sem_end_date = ALL_SEMESTERS[sem].toordinal()
nb_uvs = SEMESTERS_INFO[sem]["nb_uvs"]
sem_data = EVALS[sem]
uvs_with_evals = sem_data.loc[pd.notna(sem_data["date_review"])]
traces.append(
go.Scatter(
x=uvs_with_evals['date_review'].sort_values().apply(datetime.toordinal)-sem_end_date ,
y=[100*float(i+1)/nb_uvs for i,e in enumerate(uvs_with_evals.index)],
text=uvs_with_evals.sort_values(by=['date_review']).index,
name=sem
)
)
layout = go.Layout(
title = "Délai de réponse",
yaxis=dict(
range=[0, 100],
title="Taux des resps. d'UVs qui ont 'vu' les évaluations",
),
xaxis=dict(
title="Nombre de jours après la fin des finaux."
)
)
iplot(go.Figure(data=traces, layout=layout))
```
%% Cell type:markdown id: tags:
# Analyse quantitative
%% Cell type:code id: tags:
``` python
def get_stats_uv_question(stats, question):
q = str(question)
return stats[q]["++"], stats[q]["+"], stats[q]["-"], stats[q]["--"]
nb_pp, nb_p, nb_m, nb_mm = 0, 0, 0, 0
for i, row in EVALS_MAIN_SEM.iterrows():
for q in range(1,11):
a, b, c, d = get_stats_uv_question(row["stats"],q)
nb_pp += a
nb_p += b
nb_m += c
nb_mm += d
print(nb_pp, nb_p, nb_m, nb_mm)
```
%% Cell type:code id: tags:
``` python
color_mm = 'rgb(255,48,48)'
color_m = 'rgb(255,165,0)'
color_p = 'rgb(144,238,144)'
color_pp = 'rgb(48,221,48)'
q_lists_pd = [str(i) for i in range(1,11)] # TODO BETTER
```
%% Cell type:code id: tags:
``` python
def get_stat_by_question(question, s = "++"):
q = str(question)
res = 0
for i, row in EVALS_MAIN_SEM.iterrows():
res += row["stats"][q][s]
return res
q_list = ["q"+str(i) for i in range(1,11)]
trace1 = go.Bar(
x=q_list,
y=[get_stat_by_question(i, "--") for i in range(1,11)],
name='--',
marker=dict(color=color_mm)
)
trace2 = go.Bar(
x=q_list,
y=[get_stat_by_question(i, "-") for i in range(1,11)],
name='-',
marker=dict(color=color_m)
)
trace3 = go.Bar(
x=q_list,
y=[get_stat_by_question(i, "+") for i in range(1,11)],
name='+',
marker=dict(color=color_p)
)
trace4 = go.Bar(
x=q_list,
y=[get_stat_by_question(i, "++") for i in range(1,11)],
name='++',
marker=dict(color=color_pp)
)
data = [trace1, trace2, trace3, trace4]
layout = go.Layout(
barmode='stack',
title="Total cumulé de l'ensemble des évaluations d'UVs pour chaque question"
)
fig = go.Figure(data=data, layout=layout)
iplot(fig)
```
%% Cell type:code id: tags:
``` python
def get_mm_per_row(row, relative = True):
nb_mm = 0
# for q in q_lists_pd :
# nb_mm += row["stats"][q]["--"]
nb_mm = row["stats"]["10"]["--"]
if relative:
nb_eval = sum([row["stats"]["1"][i] for i in ["--","-","+","++"] ])
# return nb_mm / float(nb_eval*len(q_list))
return nb_mm / float(nb_eval)
else :
return nb_mm
tmp = EVALS_MAIN_SEM.apply(get_mm_per_row, axis=1)
graph_data = [
go.Scatter(
x=tmp.sort_values().index,
y=tmp.sort_values(),
)
]
layout = go.Layout(
title="Pourcentage d'avis très négatif (--) sur l'appréciation finale de l'UV (question 10 des évaluations)"
)
fig = go.Figure(data=graph_data, layout=layout)
iplot(fig)
```
%% Cell type:markdown id: tags:
## Remarques
Pour la suite de l'analyse il est plus simple d'affecter une note numérique à chaque évaluation. Pour se faire il a été décidé d'utiliser la table de conversion _symétrique_ suivante :
| Sigle | Valeur associée |
|-------|-----------------|
| ++ | +3 |
| + | +1 |
| - | -1 |
| -- | -3 |
%% Cell type:code id: tags:
``` python
list_answers = ["--","-","+","++"]
list_coeffs = [-3,-1,1,3]
def get_nb_eval(row):
return sum([row["stats"]["1"][i] for i in list_answers])
def get_mark_q(row, q):
if type(q) is int:
q = str(q)
return sum([row["stats"][q][list_answers[i]] * list_coeffs[i] for i in range(len(list_coeffs))]) / get_nb_eval(row)
```
%% Cell type:code id: tags:
``` python
tmp = EVALS_MAIN_SEM.apply((lambda r : get_mark_q(r,10)), axis=1)
graph_data = [
go.Scatter(
x=tmp.sort_values(ascending = False).index,
y=tmp.sort_values(ascending = False),
)
]
layout = go.Layout(
title="Visualisation de l'appréciation globale de chaque UV (question 10) sous forme de note moyenne"
)
fig = go.Figure(data=graph_data, layout=layout)
iplot(fig)
```
%% Cell type:code id: tags:
``` python
# Tweak css for displaying data
from IPython.core.display import HTML
style = open('./src/style.css').read()
HTML("<style>"+style+"</style>")
```
%% Cell type:code id: tags:
``` python
```
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment