Statistiche gare Bebras italiano 2016

In [1]:
from IPython.display import HTML

HTML('''<script>
code_show=true; 
function code_toggle() {
 if (code_show){
 $('div.input').hide();
 } else {
 $('div.input').show();
 }
 code_show = !code_show
} 
$( document ).ready(code_toggle);
</script>
<input type="button" value="Clicca per vedere/nascondere il codice Python" onclick="code_toggle()">''')
Out[1]:

Distribuzione dei punteggi

In [2]:
import pandas as pd
import json, hashlib
In [3]:
score = None
with open("highscore.json") as hs:
    score = json.load(hs)
In [4]:
scoredf = pd.DataFrame(score['exams'])

# L'orario va corretto per il fuso orario
scoredf['server_start'] = pd.to_datetime(scoredf['exam_date'].astype('int64') + 60*60, unit='s')
scoredf['orainizio'] = pd.np.floor((scoredf['exam_date'].astype('int64') + 60*60) / (45*60)) # ore da 45', il tempo di gara
scoredf['punteggio'] = pd.to_numeric(scoredf['score'])
scoredf['anonid'] = scoredf['team_id'].str.cat(scoredf['exam_date']).map(lambda x: hashlib.md5(x).hexdigest())
scoredf['categoria'] = scoredf['category'].astype("category", categories=["Kilo","Mega","Giga","Tera","Peta"], ordered=True)
In [5]:
valid = scoredf[scoredf['exam_valid_score'] == 1]
valid.to_csv('anonris.csv', columns=['anonid', 'categoria', 'orainizio', 'punteggio', 'time'])
In [6]:
from IPython.display import display, Markdown

txt = '''<table>
<caption>Squadre partecipanti al Bebras 20156 con risultati validi, 
cioè ritenuti confrontabili con gli altri perché privi di anomalie tecniche o organizzative</caption>
<thead>
  <tr><th>Categoria</th>
  <th>squadre</th>
  <th> min </th>
  <th> max </th>
  <th> media </th>
  <th> std.dev. </th>
  <th>I quartile </th>
  <th>mediana </th>
  <th>III quartile</th>
  </tr>
<tbody>
'''
for k in valid['categoria'].unique():
    s = valid[valid['categoria'] == k]['punteggio'].describe()
    txt += "<tr><th>{}</th><td>{}</td><td>{}</td><td>{}</td><td>{:.1f}</td>\
<td>{:3.1f}</td><td>{}</td><td>{}</td><td>{}</td></tr>".format(k, 
                                                              int(s['count']),
                                                              int(s['min']),
                                                              int(s['max']),
                                                              float(s['mean']),
                                                              float(s['std']),
                                                              int(s['25%']), 
                                                              int(s['50%']), 
                                                              int(s['75%']))
txt += '<tfoot><tr><th>Totale</th><td>{}</td></tr>'.format(valid['punteggio'].count())
txt += '</table>'
display(Markdown(txt))
Squadre partecipanti al Bebras 20156 con risultati validi, cioè ritenuti confrontabili con gli altri perché privi di anomalie tecniche o organizzative
Categoria squadre min max media std.dev. I quartile mediana III quartile
Kilo265905819.810.1121926
Mega216505320.79.9132027
Giga104805018.48.5121824
Tera122704518.58.0131824
Peta106405421.89.9152128
Totale8163
In [7]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')

histograms = valid['punteggio'].hist(by=valid['categoria'], bins=30, figsize=(10,8))

Percentili per punteggio

In [8]:
for k in valid['categoria'].unique():
    tot = float(valid[(valid['categoria'] == k)]['punteggio'].count())
    pp = [100 * valid[(valid['categoria'] == k) & (valid['punteggio'] < i)]['punteggio'].count()/tot for i in xrange(1,61)]
    txt = '''<table>
    <caption>Percentili per la categoria {} (che percentuale di squadre si supera con un dato punteggio)</caption>
    <thead>'''.format(k)
    txt += ''.join(['<td>{}</td>'.format(i) for i in xrange(1,61)])
    txt += '<tbody>'
    txt += ''.join(['<td>{:.1f}</td>'.format(f) for f in pp])
    txt += '</table>'
    display(Markdown(txt))    
Percentili per la categoria Kilo (che percentuale di squadre si supera con un dato punteggio)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
0.71.01.72.53.65.27.510.012.414.717.621.925.829.733.737.641.745.749.653.156.960.764.067.470.773.376.078.981.583.385.186.888.289.791.292.193.093.995.095.696.497.197.998.198.498.899.299.299.499.499.599.599.699.799.799.899.899.8100.0100.0
Percentili per la categoria Mega (che percentuale di squadre si supera con un dato punteggio)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
0.10.40.91.82.73.75.27.39.512.014.618.222.426.129.733.937.641.044.948.452.156.059.863.166.870.374.277.279.281.883.985.587.389.190.491.592.993.994.795.495.896.497.197.598.298.799.099.299.499.799.799.9100.0100.0100.0100.0100.0100.0100.0100.0
Percentili per la categoria Giga (che percentuale di squadre si supera con un dato punteggio)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
0.50.81.32.43.75.46.99.011.415.818.422.726.631.436.940.944.749.253.757.762.966.169.673.176.178.481.184.486.688.890.992.494.095.796.697.497.998.698.799.099.399.499.599.599.899.999.999.999.999.9100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0
Percentili per la categoria Tera (che percentuale di squadre si supera con un dato punteggio)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
0.60.81.52.03.34.35.97.610.913.016.418.723.627.332.536.342.847.652.957.061.565.670.172.678.180.884.487.290.291.493.294.395.796.296.997.798.098.198.898.999.299.499.499.699.9100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0100.0
Percentili per la categoria Peta (che percentuale di squadre si supera con un dato punteggio)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
0.40.81.32.12.83.74.96.08.110.612.514.717.920.223.428.031.735.140.745.147.951.655.959.663.066.669.472.475.578.681.083.786.187.689.390.992.393.494.595.395.996.697.297.597.898.598.999.199.299.399.599.699.799.8100.0100.0100.0100.0100.0100.0

Analisi delle risposte

In [9]:
rr = []
for r in valid.itertuples():
    for q in r.questions:
        t = q.copy()
        t['anonid'] = r.anonid
        rr.append(t)
In [10]:
quiz = pd.DataFrame(rr)

MAPBEBRAS = {
    'Pennello': 'PK-03',
    'RicettaSegreta': 'HU-02',
    'MessaggiSegreti': 'UK-06',
    'Coccinelle': 'SK-10',
    'ColoraFiori': 'SK-04',
    'ConiBiglietti': 'FR-02',
    'PallaCastoro': 'JP-03',
    'Commissioni': 'LT-03',
    'Tappi': 'JP-06',
    'Soccer': 'US-07b',
    'Direzioni': 'IE-05',
    'Robot': 'FR-04',
    'Scanner': 'MY-02',
    'SacchiAscensore': 'CZ-02a',
    'Rafting': 'LT-02',
    'Insalata': 'DE-08',
    'Cannone': 'IT-06',
    'Mug': 'TW-05',
    'HealthCare': 'CH-03', 
    'Thief': 'BE-02',
    'FiltroMediano': 'RU-02',
    'Bolle': 'IT-03',
    'Tunnel': 'CH-04a',
    'Isole': 'FR-03',
    'Colori': 'UK-04',
    'Mapreduce': 'CA-08b', 
    'BandiereCompresse': 'CZ-04',
    'Smartphone': 'IT-04',
    'Albero': 'CA-05',
    'Pozioni': 'JP-01',
    'Legno': 'CA-01',
    'Avventura': 'FR-07',
    'Kix': 'NL-04', 
    'Biglie': 'IT-02b', 
    'Forme': 'CA-09'
}


MAPNAMES = {
    'Pennello': "Il rullo dell'imbianchino",
    'RicettaSegreta': 'La ricetta segreta',
    'MessaggiSegreti': 'Messaggi segreti',
    'Coccinelle': 'Coccinelle',
    'ColoraFiori': 'Colora i fiori',
    'ConiBiglietti': 'Coni e biglietti',
    'PallaCastoro': 'BeaverBall',
    'Commissioni': 'Commissioni',
    'Tappi': 'Tappi',
    'Soccer': 'Partita di calcio',
    'Direzioni': 'Direzioni concorrenti',
    'Robot': 'Fai uscire il robot',
    'Scanner': 'Scanner per immagini',
    'SacchiAscensore': "Sacchi nell'ascensore",
    'Rafting': 'Rafting',
    'Insalata': 'Una tartaruga sistematica',
    'Cannone': 'Artiglieria programmabile',
    'Mug': 'Una collezione di tazze',
    'HealthCare': 'Pronto soccorso', 
    'Thief': 'Caccia al ladro',
    'FiltroMediano': 'Filtro mediano',
    'Bolle': 'Bolle',
    'Tunnel': 'La galleria',
    'Isole': 'Isole',
    'Colori': 'Quanti colori?',
    'Mapreduce': 'Fra parentesi', 
    'BandiereCompresse': 'Bandiere',
    'Smartphone': 'In fila per tre',
    'Albero': "L'albero di Natale",
    'Pozioni': 'Pozioni magiche',
    'Legno': 'Il legno buono',
    'Avventura': 'Avventura',
    'Kix': 'Codice a barre', 
    'Biglie': 'Biglie', 
    'Forme': 'Gioco di forme'
}

quiz['nome'] = quiz['code'].str.extract('\d+_.+_(.+)', expand=False)
quiz['cat'] = quiz['code'].str.extract('\d+_(.+)_.+', expand=False)
quiz['edizione'] = quiz['code'].str.extract('(\d+)_.+_.+', expand=False)
quiz['bebras'] = quiz['nome'].map(lambda x: MAPBEBRAS[x])
quiz['completo'] = quiz['score'] == quiz['score_max']
quiz['parziale'] = (quiz['score'] > 0) & (quiz['score'] != quiz['score_max'])
quiz['voto'] = quiz['score'] / quiz['score_max'].astype('float64')
quiz['minuti'] = quiz['time'].map(lambda x: x/(1000*60) if x >= 0 else pd.np.NaN)

quiz.to_csv('quiz.csv', columns=['anonid', 'cat', 'edizione', 'nome', 'bebras', 'score', 'score_max', 'time'])
In [11]:
vquiz = pd.merge(valid[['anonid', 'categoria', 'punteggio','orainizio','teacher_id','school_cap']], quiz, on='anonid')
In [12]:
plt.figure(figsize=(16,20))

def bname(n):
    if n in MAPBEBRAS and n in MAPNAMES:
        return '{} ({})'.format(MAPNAMES[n], MAPBEBRAS[n])
    else:
        return n

for j, k in enumerate(valid['categoria'].unique()):
    plt.subplot(5,1, j+1)
    plt.ylim(0,1)
    m = vquiz[vquiz['categoria'] == k].groupby('nome', 
                                             sort=False)[['completo','voto', 'parziale', 'minuti','score_max']].mean()
    m['vparziale'] = m['voto'] - m['completo']

    c = plt.bar(pd.np.arange(m.index.size), m['completo'])
    p = plt.bar(pd.np.arange(m.index.size), m['parziale'], bottom=m['completo'], color='lightblue')
    plt.xticks(pd.np.arange(m.index.size) + 0.4, map(bname, m.index.tolist()), rotation=90)
    for i, y in enumerate(m['voto'].tolist()):
        plt.annotate(s='{:.0f}'.format(m['minuti'].iloc[i]), xy=(i+0.3, y+.08))
        plt.annotate(s='{}'.format(m['score_max'].iloc[i]), xy=(i+0.3, .02), color='red')
    plt.legend((c[0],p[0]), ('completo','parziale'))
    plt.title('{}: tassi di soluzione (il numero in nero indica i minuti spesi in media sul quesito, \
il numero in rosso il punteggio massimo ottenibile)'.format(k))

plt.tight_layout()
In [13]:
plt.figure(figsize=(16,20))

for j, k in enumerate(valid['categoria'].unique()):
    plt.subplot(5,1, j+1)
    plt.ylim(0,1)
    m = vquiz[vquiz['categoria'] == k].groupby('nome', 
                                             sort=False)[['completo','voto', 'parziale', 'minuti','score_max']].mean()
    m['vparziale'] = m['voto'] - m['completo']

    c = plt.bar(pd.np.arange(m.index.size), m['voto'], color='green')
    plt.xticks(pd.np.arange(m.index.size) + 0.4, map(bname, m.index.tolist()), rotation=90)
    for i, y in enumerate(m['voto'].tolist()):
        plt.annotate(s='{}'.format(m['score_max'].iloc[i]), xy=(i+0.3, y+.08), color='red')
    
    plt.title('{}: percentuale di punteggio attribuito in media (in rosso il punteggio massimo ottenibile)'.format(k))

plt.tight_layout()