Analýza průběžného kvízu

In [1]:
# Výchozí parametry notebooku pro Papermill
FILENAME          = 'bizma1.json'
START             = '2020-09-24 20:00:00+0100'
DEADLINE          = '2020-10-11 23:59:00+0100'
SCORE_TO_PASS     = 15  # MARAST bodů
CLUSTER_THRESHOLD = 120 # minut
In [2]:
# Parameters
FILENAME = "bizma4.json"
START = "2020-11-12 12:00:00+0100"
DEADLINE = "2020-11-22 23:59:00+0100"
SCORE_TO_PASS = 15
In [3]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import fclusterdata

0. Načtení a předzpracování

Načtení dat z JSON formátu poskytovaného MARAST API, upravení datových typů sloupců a zpracování počátku/deadline.

In [4]:
df             = pd.read_json(FILENAME)
df.created_at  = pd.to_datetime(df.created_at, utc=True)
df.updated_at  = pd.to_datetime(df.updated_at, utc=True)
df.answered_at = pd.to_datetime(df.answered_at, utc=True)
df.score       = df.score.fillna(0).astype(np.int64)

start    = pd.to_datetime(START, utc=True)
deadline = pd.to_datetime(DEADLINE, utc=True)

Dopočtení dalších základních užitečných údajů:

  • doba od vytvoření otázky do zodpovězení,
  • doba zbývající do deadline.

Zahození dat, která pravděpodobně vznikla experimentováním nebo řešením singularit.

In [5]:
for idx, row in df.iterrows():
    # doba od vytvoření otázky do zodpovězení (v minutách)
    if row['answered_at']:
        df.at[idx, 'duration'] = (row['answered_at'] - row['created_at']).seconds / 60
        # doba od zodpovězení otázky do deadline (v minutách)
        df.at[idx, 'to_deadline_answer'] = (deadline - row['answered_at']).total_seconds() / 60

    # doba od vytvoření otázky do deadline (v minutách)
    df.at[idx, 'to_deadline'] = (deadline - row['created_at']).total_seconds() / 60

df = df[(df.created_at >= start) & (df.created_at <= deadline)]

Souhrn sloupců, pro kontrolu.

In [6]:
df.columns
Out[6]:
Index(['problem_id', 'anonymous_user_id', 'is_open', 'is_correctly_answered',
       'score', 'state', 'kind', 'solution', 'created_at', 'updated_at',
       'answered_at', 'is_modified', 'duration', 'to_deadline_answer',
       'to_deadline'],
      dtype='object')

Přehled datových typů sloupců.

In [7]:
df.dtypes
Out[7]:
problem_id                             int64
anonymous_user_id                     object
is_open                                 bool
is_correctly_answered                   bool
score                                  int64
state                                 object
kind                                  object
solution                              object
created_at               datetime64[ns, UTC]
updated_at               datetime64[ns, UTC]
answered_at              datetime64[ns, UTC]
is_modified                             bool
duration                             float64
to_deadline_answer                   float64
to_deadline                          float64
dtype: object

Extrakce studentů a příkladů vyskytujících se v datech.

In [8]:
students    = df.anonymous_user_id.drop_duplicates().values
problem_ids = df.problem_id.drop_duplicates().values

1. Celkový přehled odpovědí, studentů a příkladů

Celkové počty studentů, otázek a příkladů.

In [9]:
print('Počet studentů:              ', len(students))
print('Počet vygenerovaných otázek: ', len(df))
print('Počet zodpovězených otázek:  ', len(df[~df.is_open]))
print('Počet příkladů:              ', len(problem_ids))
print('Počet záporně ohodnocených:  ', len(df[df.score < 0]))
Počet studentů:               555
Počet vygenerovaných otázek:  11204
Počet zodpovězených otázek:   10997
Počet příkladů:               59
Počet záporně ohodnocených:   519

Další "globální" statistiky.

In [10]:
print(
    'Celková úspěšnost (správně zodpovězené / celkový počet odpovědí): ',
    len(df[(~df.is_open) & (df.is_correctly_answered)]) / len(df[~df.is_open])
)
print('Průměrný počet minut potřebaný k odeslání odpovědi:', df.duration.mean())
print('Medián minut potřebaných k odeslání odpovědi:', df.duration.median())
Celková úspěšnost (správně zodpovězené / celkový počet odpovědí):  0.7221060289169774
Průměrný počet minut potřebaný k odeslání odpovědi: 38.4385998365568
Medián minut potřebaných k odeslání odpovědi: 3.6166666666666667

Kdy jsou studenti nejaktivnější co se otevírání nových otázek, resp. odesílání odpovědí, a denní hodiny týče?

In [11]:
ax = df.created_at.apply(lambda t: t.hour + t.minute / 60).hist(bins=24, figsize=(10, 5))
ax.set_xlabel('Hodina dne')
ax.set_ylabel('Počet vytvořených otázek')
ax.set_title('Otevírání nových otázek')

plt.xticks(range(25))
plt.show()
In [12]:
ax = df.answered_at.apply(lambda t: t.hour + t.minute / 60).hist(bins=24, figsize=(10, 5))
ax.set_xlabel('Hodina dne')
ax.set_ylabel('Počet odeslaných odpovědí')
ax.set_title('Zodpovídání otázek')

plt.xticks(range(25))
plt.show()

Jak je vytváření otázek závislé na blížícím se deadline?

In [13]:
data = df.to_deadline.apply(lambda t: t / 60)
maxh = int(np.ceil(data.max()))

ax = data.hist(bins=maxh, figsize=(12, 5))
ax.set_xlabel('Počet hodin zbývajících do deadline')
ax.set_ylabel('Počet vytvořených otázek')
ax.set_title('Aktivita studentů během doby přístupnosti kvízu')

plt.xticks(range(0, maxh, 24), rotation=90)
plt.show()

Jak dlouho studentům trvá na otázku odpovědět? Následující graf je ořezaný o několik outlierů (velmi dlouho otevřené otázky).

In [14]:
data = df[df.duration <= 10*df.duration.median()].duration
maxh = int(np.ceil(data.max()))

ax = data.hist(bins=maxh, figsize=(12, 5))
ax.set_xlabel('Počet minut na zodpovězení')
ax.set_ylabel('Počet otázek')
ax.set_title('Doba potřebaná k zodpovězení otázky')

plt.xticks(range(0, maxh, 1), rotation=90)
plt.show()

2. Statistiky příkladů a úspěšnost

Předpočtení statistik jednotlivých příkladů.

In [15]:
# Nový DataFrame
dp = pd.DataFrame(index=problem_ids, columns=[
    'displayed', 'kind', 'answers', 'correct', 'wrong', 'success_rate', 'median_time'
])

# Příklad po příkladu...
for p in problem_ids:
    answers = df[(df.problem_id == p) & (df.is_open == False)]
    
    if pd.isnull(dp.at[p, 'kind']):
        dp.at[p, 'kind'] = answers.iloc[0]['kind']
    
    dp.at[p, 'displayed'] = len(df[df.problem_id == p])
    dp.at[p, 'answers'] = len(answers)
    dp.at[p, 'correct'] = len(df[(df.problem_id == p) & (df.is_open == False) & (df.is_correctly_answered == True)])
    dp.at[p, 'wrong'] = dp.at[p, 'answers'] - dp.at[p, 'correct']
    dp.at[p, 'success_rate'] = dp.at[p, 'correct'] / dp.at[p, 'answers']
    dp.at[p, 'median_time'] = answers.duration.median()

Zobrazení výsledků, seřazeno podle míry úspěšnosti (od "nejtěžšího" příkladu k "nejlehčímu"). Index (první sloupec) je MARAST ID příkladu. V posledním sloupci je uveden medián počtu minut potřebných na zodpovězení otázky.

In [16]:
with pd.option_context('display.max_rows', None, 'display.max_columns', None):
    display(dp.sort_values(by=['success_rate']))
displayed kind answers correct wrong success_rate median_time
2593 189 multichoice 184 47 137 0.255435 7.88333
2573 185 multichoice 179 66 113 0.368715 2.73333
2574 195 multichoice 187 69 118 0.368984 6.41667
2558 207 multichoice 203 83 120 0.408867 4.91667
991 195 multichoice 192 91 101 0.473958 4.13333
1248 182 multichoice 179 85 94 0.47486 6.88333
2592 181 multichoice 178 86 92 0.483146 4.50833
1243 190 multichoice 186 91 95 0.489247 4.96667
990 183 multichoice 180 92 88 0.511111 4.325
1245 159 multichoice 159 82 77 0.515723 6.36667
2594 189 multichoice 186 98 88 0.526882 5.84167
2572 199 multichoice 193 104 89 0.53886 3.83333
2570 200 multichoice 197 109 88 0.553299 3.88333
2580 182 multichoice 179 103 76 0.575419 4.66667
7010 201 text_field 198 115 83 0.580808 5.45833
2577 184 multichoice 180 106 74 0.588889 7.09167
988 185 multichoice 177 105 72 0.59322 5.4
2488 193 multichoice 190 114 76 0.6 4.04167
2481 200 multichoice 195 118 77 0.605128 3.46667
2492 179 multichoice 174 106 68 0.609195 3.6
2603 189 multichoice 184 117 67 0.63587 6.36667
2575 189 multichoice 188 125 63 0.664894 4.49167
2487 183 multichoice 180 122 58 0.677778 4.75833
2576 199 multichoice 197 135 62 0.685279 3.45
2571 184 multichoice 181 130 51 0.718232 3.68333
2493 191 multichoice 188 136 52 0.723404 5.375
7017 191 text_field 184 135 49 0.733696 7.91667
985 176 multichoice 172 127 45 0.738372 4.25833
1246 198 multichoice 196 146 50 0.744898 4.40833
1242 196 multichoice 194 145 49 0.747423 3.125
2489 171 multichoice 166 126 40 0.759036 3.80833
1247 205 multichoice 198 151 47 0.762626 4.55833
7016 201 text_field 196 151 45 0.770408 7.86667
7015 188 text_field 186 144 42 0.774194 7.09167
1251 182 multichoice 178 139 39 0.780899 3.9
7018 187 text_field 180 145 35 0.805556 4.84167
2486 175 multichoice 173 142 31 0.820809 2.61667
2581 192 multichoice 185 152 33 0.821622 3.55
2596 194 multichoice 192 159 33 0.828125 4.675
2579 186 multichoice 186 156 30 0.83871 3.26667
2483 195 multichoice 195 164 31 0.841026 2.58333
2480 169 multichoice 166 140 26 0.843373 3.29167
2578 210 multichoice 207 176 31 0.850242 2.08333
2479 175 multichoice 174 149 25 0.856322 3.68333
7023 195 text_field 191 166 25 0.86911 4.23333
2559 190 multichoice 189 168 21 0.888889 1.96667
984 200 multichoice 191 170 21 0.890052 2.48333
7024 198 text_field 192 172 20 0.895833 4.5
992 179 multichoice 176 159 17 0.903409 1.675
1244 180 multichoice 178 161 17 0.904494 2.69167
989 189 multichoice 187 170 17 0.909091 2.41667
1249 190 multichoice 188 176 12 0.93617 2
7019 197 text_field 195 184 11 0.94359 2.98333
2482 184 multichoice 182 174 8 0.956044 1.775
7021 195 text_field 193 185 8 0.958549 3.46667
2491 192 multichoice 190 183 7 0.963158 1.49167
2485 191 multichoice 188 183 5 0.973404 1.1
2484 206 multichoice 203 198 5 0.975369 1.3
1250 214 multichoice 212 210 2 0.990566 1.79167

Závisí celková úspěšnost odpovídání na blízkosti deadline?

In [17]:
step = 6 * 60 # 3 hours
frame = pd.DataFrame(columns=['hours_to_deadline','success_ratio'])

for k in range(1, 8 * 7): # one week
    dsnap = df[(df.to_deadline_answer >= (k-1)*step) & (k*step > df.to_deadline_answer)]
    if len(dsnap) > 0:
        frame.at[k, 'hours_to_deadline'] = k*step
        frame.at[k, 'success_ratio'] = len(dsnap[dsnap.is_correctly_answered]) / len(dsnap)

maxt = frame.hours_to_deadline.max()
        
ax = frame.plot(
    x="hours_to_deadline", y="success_ratio",
    kind="bar", grid=True, legend=False
)

ax.set_xlabel('Počet minut na zodpovězení')
ax.set_ylabel('Počet otázek')
ax.set_title('Doba potřebaná k zodpovězení otázky')

plt.xticks(range(0, 8*7, 24), rotation=90)
plt.show()

4. Podrobnější analýza chování

Nejprve musíme extrahovat potřebná data. Zaznamenáme kolik odpovědí student vytvořil, kolik jich potřeboval na splnění kvízu, jak dlouho mu to od otevření kvízu trvalo, kolik času do deadline mu v okamžiku splnění kvízu zbývalo, úspěšnost jeho odpovědí a medián doby potřebné na odeslání jedné odpovědi.

In [18]:
ds = pd.DataFrame(index=students, columns=[
    'total_questions', 'total_score', 'questions_to_success',
    'minutes_to_success', 'minutes_left', 'success_rate', 'median_minutes_to_answer'
])

Nyní samotný výpočet.

In [19]:
for s in students:
    questions = df[df.anonymous_user_id == s].copy()
    
    ds.at[s, 'total_questions'] = len(questions)
    ds.at[s, 'total_score'] = questions.score.sum()
    
    score   = 0.0
    counter = 0
    success = 0
        
    for idx, q in questions.iterrows():
        counter += 1
        score   += q['score']
        
        if q['is_correctly_answered']:
            success += 1
    
        if q['answered_at']:
            questions.at[idx, 'minutes_to_answer'] = (q['answered_at'] - q['created_at']).total_seconds() / 60
    
        if score >= SCORE_TO_PASS:
            ds.at[s, 'questions_to_success'] = counter
            ds.at[s, 'minutes_to_success'] = (q['answered_at'] - questions.iloc[0]['answered_at']).total_seconds() / 60
            ds.at[s, 'minutes_left'] = (deadline - q['answered_at']).total_seconds() / 60
            break
    
    ds.at[s, 'success_rate'] = success / len(questions)
    ds.at[s, 'median_minutes_to_answer'] = questions.minutes_to_answer.dropna().median()

ds.questions_to_success = ds.questions_to_success.fillna(0).astype(np.int64)

Kontrola datových typů.

In [20]:
ds = ds.infer_objects()
ds.dtypes
Out[20]:
total_questions               int64
total_score                   int64
questions_to_success          int64
minutes_to_success          float64
minutes_left                float64
success_rate                float64
median_minutes_to_answer    float64
dtype: object

Náhled dat, z očividných důvodů nezobrazujeme všechny.

In [21]:
ds
Out[21]:
total_questions total_score questions_to_success minutes_to_success minutes_left success_rate median_minutes_to_answer
78f77e979c7858268fa9d1a7d6d3fbea 23 14 0 NaN NaN 0.695652 2.375000
58b2fb7c6c464b5091c7edadeca5e4ab 6 4 0 NaN NaN 0.666667 4.200000
b955f10d3ffc5d6cf5c7bd26f59bc518 35 16 33 316.733333 8764.433333 0.514286 2.266667
50381df917fd5456ccb5399e12641f41 24 15 24 485.300000 4670.866667 0.708333 3.558333
04ffe297e862ba3e4a25b62b6a06105e 9 8 0 NaN NaN 0.888889 4.791667
... ... ... ... ... ... ... ...
3a7a8620dd8cd4c8a539d50fcd971e42 6 3 0 NaN NaN 0.500000 4.283333
13ffcb43cdd94fe2e4ef8217e28baed8 1 0 0 NaN NaN 0.000000 22.083333
a53e071480ff6d6332869375dcbc7464 8 2 0 NaN NaN 0.500000 1.691667
fe2c6b729c9f956cb4376313977f2380 16 13 0 NaN NaN 0.812500 1.183333
4fd62aadb3fc4299c4cec07ce80bdd8d 7 5 0 NaN NaN 0.714286 1.983333

555 rows × 7 columns

Zcela základní ukazatale.

In [22]:
print("Počet studentů:      ", len(ds))
print("Z nich kvíz splnilo: ", len(ds[ds.total_score >= SCORE_TO_PASS]))
Počet studentů:       555
Z nich kvíz splnilo:  332

Studenti s nejvíce otázkami.

In [23]:
ds.sort_values(by='total_questions', ascending=False).head(10)
Out[23]:
total_questions total_score questions_to_success minutes_to_success minutes_left success_rate median_minutes_to_answer
a131005bc99e0505cc970be992378a4f 60 15 59 1983.683333 10092.316667 0.350000 1.166667
e2a8daa5eb919905dadd795593084c22 57 25 35 5600.000000 2181.550000 0.315789 5.600000
55f983f1fbb534ed35222d66a0052e00 54 28 35 4492.233333 8713.383333 0.351852 2.466667
0b93f1f04fb10bfbd341bce90c5c04a2 50 17 44 1720.633333 457.416667 0.440000 2.516667
3230fa44b91997878d450b940b4ac848 50 30 24 118.850000 8922.216667 0.300000 1.816667
77265183b141be78bab5487b9403ad84 49 31 26 481.550000 8641.666667 0.346939 4.233333
7d35406dc4780516573e05300e0be62e 48 29 26 167.333333 3213.100000 0.333333 2.625000
9bbf286a9986939ec7510941af522904 48 26 20 -11630.883333 13413.700000 0.333333 6.616667
1ecd20d3ec3e3e5e89ad9b87f90d0db7 46 30 16 156.150000 12879.250000 0.326087 4.841667
e60ddfb6ee3b1b48d70acc13d5be99e3 44 30 26 387.933333 5683.233333 0.409091 2.325000

Kolik otázek studenti potřeboval ke splnění kvízu?

In [24]:
data = ds[ds.questions_to_success > 0].questions_to_success
maxo = int(data.max())

ax = data.hist(bins=(maxo+1), figsize=(10, 5))
ax.set_xlabel('Počet otázek')
ax.set_ylabel('Počet studentů')
ax.set_title('Počet otázek potřebných ke splnění kvízu')

plt.xticks(range(0, maxo, SCORE_TO_PASS), rotation=90)
plt.show()

Další statistiky počtu otázek potřebných ke splnění kvízu.

In [25]:
print(
    'Průměrný počet otázek potřebných ke splnění kvízu:',
    ds[ds.questions_to_success > 0].questions_to_success.mean()
)
print(
    'Medián počtu otázek potřebných ke splnění kvízu:',
    ds[ds.questions_to_success > 0].questions_to_success.median()
)
print(
    'Maximum počtu otázek potřebných ke splnění kvízu:',
    ds[ds.questions_to_success > 0].questions_to_success.max()
)
print(
    'Minimum počtu otázek potřebných ke splnění kvízu:',
    ds[ds.questions_to_success > 0].questions_to_success.min()
)
Průměrný počet otázek potřebných ke splnění kvízu: 21.662650602409638
Medián počtu otázek potřebných ke splnění kvízu: 20.0
Maximum počtu otázek potřebných ke splnění kvízu: 59
Minimum počtu otázek potřebných ke splnění kvízu: 15

Kolik času (od prvního otevření kvízu po jeho splnění) studentům řešení kvízu zabralo? Toto je hrubý nástřel, typicky bude obsahovat i noční dobu atp. I tak je ale výsledek zajímavý.

In [26]:
data = ds[ds.minutes_to_success > 0].minutes_to_success.apply(lambda t: t / 60)
maxd = int(np.ceil(data.max()) / 2)

ax = data.hist(bins=maxd, figsize=(18, 5))
ax.set_xlabel('Čas v hodinách')
ax.set_ylabel('Počet studentů')
ax.set_title('Čas potřebný ke splnění kvízu')

plt.xticks(range(0, 2*maxd, 4), rotation=90)
plt.show()

Kolik času studentům zbývalo do deadline v okamžik splnění kvízu?

In [27]:
data = ds[ds.minutes_to_success > 0].minutes_left.apply(lambda t: t / 60)
maxh = int(np.ceil(data.max()) / 2)

ax = data.hist(bins=maxh, figsize=(12, 5))
ax.set_xlabel('Počet hodin zbývajících do deadline')
ax.set_ylabel('Počet studentů')
ax.set_title('Kolik času zbývalo do deadline v okamžik splnění kvízu?')

plt.xticks(range(0, 2*maxh, 24), rotation=90)
plt.show()

Histogram úspěšnosti odpovídání.

In [28]:
ax = ds.success_rate.hist(bins=50, figsize=(10, 5))
ax.set_xlabel('Úspěšnost')
ax.set_ylabel('Počet studentů')
ax.set_title('Rozložení úspěšnosti odpovídání studentů')

plt.show()

Histogram mediánu času potřebného k odeslání odpovědi od vygenerování otázky.

In [29]:
ax = ds[ds.median_minutes_to_answer <= 60 * 6].median_minutes_to_answer.hist(bins=48, figsize=(10, 5))

ax.set_xlabel('Medián doby potřebné na zodpovězení (minuty)')
ax.set_ylabel('Počet studentů')
ax.set_title('Rozložení doby potřebné na zodpovězení')

plt.show()

Porovnání úspěšnosti odpovídání a času zbývajícího do deadline při splnění kvízu.

In [30]:
ax = ds.dropna(how='any', subset=['success_rate','minutes_left']).plot.hexbin(
    x='success_rate', y='minutes_left', gridsize=25, figsize=(10,7), sharex=False
)
ax.set_xlabel('Úspěšnost')
ax.set_ylabel('Čas zbývající do deadline (minuty)')
ax.set_title('Úspěšnost vs. čas zbývající do deadline')

plt.show()

Porovnání úspěšnosti odpovídání a mediánu doby potřebné k odeslání jedné odpovědi.

In [31]:
ax = ds[ds.median_minutes_to_answer <= 15].dropna(how='any', subset=['success_rate','median_minutes_to_answer']).plot.hexbin(
    x='success_rate', y='median_minutes_to_answer', gridsize=25, figsize=(10,7), sharex=False
)
ax.set_xlabel('Úspěšnost')
ax.set_ylabel('Medián doby na odpověď (minuty)')
ax.set_title('Úspěšnost vs. medián doby potřebné k zodpovězení')

plt.show()

5. Analýza počtu "sezení"

Rozšíříme tabulku studentů o další sloupce, pomocí clusterování se snažíme zjistit na kolik "sezení" student kvíz splnil.

In [32]:
for idx, row in ds.iterrows():
    answers = df[df.anonymous_user_id == idx].copy()
    
    if len(answers) <= 1:
        continue
    
    data = np.reshape(answers.to_deadline.values, (len(answers), 1))
    clusters = fclusterdata(data, t=CLUSTER_THRESHOLD, criterion='distance')
    answers['clusters'] = clusters
    
    ds.at[idx, 'nclusters'] = np.max(clusters)

ds.nclusters = ds.nclusters.fillna(0).astype(np.int64)

Hrubý náhled.

In [33]:
ds
Out[33]:
total_questions total_score questions_to_success minutes_to_success minutes_left success_rate median_minutes_to_answer nclusters
78f77e979c7858268fa9d1a7d6d3fbea 23 14 0 NaN NaN 0.695652 2.375000 2
58b2fb7c6c464b5091c7edadeca5e4ab 6 4 0 NaN NaN 0.666667 4.200000 1
b955f10d3ffc5d6cf5c7bd26f59bc518 35 16 33 316.733333 8764.433333 0.514286 2.266667 1
50381df917fd5456ccb5399e12641f41 24 15 24 485.300000 4670.866667 0.708333 3.558333 4
04ffe297e862ba3e4a25b62b6a06105e 9 8 0 NaN NaN 0.888889 4.791667 1
... ... ... ... ... ... ... ... ...
3a7a8620dd8cd4c8a539d50fcd971e42 6 3 0 NaN NaN 0.500000 4.283333 1
13ffcb43cdd94fe2e4ef8217e28baed8 1 0 0 NaN NaN 0.000000 22.083333 0
a53e071480ff6d6332869375dcbc7464 8 2 0 NaN NaN 0.500000 1.691667 1
fe2c6b729c9f956cb4376313977f2380 16 13 0 NaN NaN 0.812500 1.183333 1
4fd62aadb3fc4299c4cec07ce80bdd8d 7 5 0 NaN NaN 0.714286 1.983333 1

555 rows × 8 columns

Studenti nejvíce se vracející.

In [34]:
ds.sort_values(by='nclusters', ascending=False).head(10)
Out[34]:
total_questions total_score questions_to_success minutes_to_success minutes_left success_rate median_minutes_to_answer nclusters
9bbf286a9986939ec7510941af522904 48 26 20 -11630.883333 13413.700000 0.333333 6.616667 12
e2a8daa5eb919905dadd795593084c22 57 25 35 5600.000000 2181.550000 0.315789 5.600000 9
14a07bc8ee7d825bbe35a2bb5a2d0514 26 14 0 NaN NaN 0.576923 10.700000 9
d809c01aa0e59aad19c87c7e229fc396 28 15 27 8565.850000 5881.700000 0.642857 31.066667 8
d6d1d2b50655a964810ba5592c9200a5 41 34 18 2744.950000 491.116667 0.365854 11.716667 8
fe858281188f10005605ea7b1dd0bf7f 24 8 0 NaN NaN 0.375000 11.550000 8
77265183b141be78bab5487b9403ad84 49 31 26 481.550000 8641.666667 0.346939 4.233333 8
6f70088e3dd9d4e355c43a3b9de2b4eb 28 15 28 7774.500000 7051.616667 0.607143 3.533333 8
c27b7f8c60c0fbdd0efa30fa0821fc3f 24 15 23 5830.616667 4241.200000 0.708333 4.383333 7
196b8221d2483c32cc76ca806d4884b4 18 4 0 NaN NaN 0.388889 3.258333 7
In [35]:
data = ds.nclusters
maxc = data.max()

ax = data.hist(bins=maxc, figsize=(10, 5))
ax.set_xlabel('Počet "sezení"')
ax.set_ylabel('Počet studentů')
ax.set_title('Na kolik "sezení" studenti kvíz vyplnili')

plt.show()

Porovnání ve vztahu k úspěšnosti.

In [36]:
ax = ds[ds.median_minutes_to_answer <= 15].dropna(how='any', subset=['success_rate','nclusters']).plot.hexbin(
    x='success_rate', y='nclusters', gridsize=25, figsize=(10,7), sharex=False
)
ax.set_xlabel('Úspěšnost')
ax.set_ylabel('Počet "sezení"')
ax.set_title('Úspěšnost vs. počet "sezení"')

plt.show()