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 = "bizma2.json"
START = "2020-10-05 12:00:00+0100"
DEADLINE = "2020-10-25 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ů:               697
Počet vygenerovaných otázek:  15327
Počet zodpovězených otázek:   15067
Počet příkladů:               33
Počet záporně ohodnocených:   791

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.705382624278224
Průměrný počet minut potřebaný k odeslání odpovědi: 65.93362158460893
Medián minut potřebaných k odeslání odpovědi: 4.333333333333333

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
964 452 multichoice 443 201 242 0.453725 5.61667
8750 486 multichoice 474 218 256 0.459916 8.01667
2383 481 multichoice 469 245 224 0.522388 2.7
3858 459 multichoice 449 238 211 0.530067 5.31667
3836 472 multichoice 464 262 202 0.564655 5.33333
3856 459 multichoice 446 257 189 0.576233 3.66667
11293 484 multichoice 474 278 196 0.586498 13.2333
11294 476 multichoice 466 290 176 0.622318 9.45833
963 484 multichoice 475 297 178 0.625263 3.9
11295 493 multichoice 485 304 181 0.626804 6.46667
3835 483 multichoice 480 318 162 0.6625 4.425
1223 472 multichoice 460 312 148 0.678261 4.81667
3834 471 multichoice 466 317 149 0.680258 3.66667
11296 475 multichoice 469 320 149 0.682303 4.68333
961 461 multichoice 455 317 138 0.696703 3.88333
1227 449 multichoice 444 313 131 0.704955 5.05
11297 463 multichoice 458 323 135 0.70524 4.78333
3842 483 multichoice 473 337 136 0.712474 2.86667
3829 442 multichoice 435 311 124 0.714943 2.81667
3832 462 multichoice 454 326 128 0.718062 2.54167
3833 472 multichoice 466 337 129 0.723176 3.46667
3830 444 multichoice 438 321 117 0.732877 3.94167
1228 458 multichoice 448 350 98 0.78125 6.20833
1230 458 multichoice 450 357 93 0.793333 5.16667
996 449 multichoice 441 353 88 0.800454 5.71667
3843 451 multichoice 447 365 82 0.816555 2.51667
4568 478 multichoice 472 392 80 0.830508 6.525
962 453 multichoice 449 373 76 0.830735 2.93333
966 462 multichoice 453 385 68 0.84989 4.76667
3845 444 multichoice 436 384 52 0.880734 4.35833
1226 466 multichoice 460 416 44 0.904348 2.05
3837 445 multichoice 438 408 30 0.931507 2.20833
3831 440 multichoice 430 403 27 0.937209 1.775

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
23cd0166e02eb43fb5c4323f8b1191f6 17 11 0 NaN NaN 0.705882 4.033333
8eee11ee7ca63d411e87ee6a74745788 19 15 19 28699.516667 130.466667 0.842105 1.433333
98a98e334db28b842d67e1308c1cf0db 18 14 0 NaN NaN 0.777778 1.733333
e55d1ae12b4ccd30016b03c3687ea33a 40 30 21 2699.133333 7831.533333 0.400000 5.966667
4903e8d4bf16fa448e138a0f4cec6b62 4 4 0 NaN NaN 1.000000 3.425000
... ... ... ... ... ... ... ...
a54e1325c917c695ea28f9dc6a36a478 1 0 0 NaN NaN 0.000000 NaN
68a0e2670e977096141663bb022c99c6 14 9 0 NaN NaN 0.642857 2.850000
eb44cc9c7546aa8b47c658fbfa298bb8 2 0 0 NaN NaN 0.500000 5.175000
7cba9f62f81d47ec24ff3301d19918f2 17 15 17 49.350000 16.466667 0.941176 2.300000
6d9a475a93a5d9b0438865ccdbea01c0 1 0 0 NaN NaN 0.000000 NaN

697 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ů:       697
Z nich kvíz splnilo:  587

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
4f40276a3393364efdd9a22257958dcd 123 12 0 NaN NaN 0.260163 1.366667
50d9f0bb1317b75360b16d93fe2d7254 121 10 0 NaN NaN 0.272727 1.033333
af55c14f4b08275b24f4cea2e7a4dd29 95 10 0 NaN NaN 0.347368 3.016667
4094af4f8b749541fac5d98c3e22e693 89 15 89 22810.116667 3781.350000 0.359551 1.966667
2ab439c02ddc48446d861937b4adc066 77 15 77 27883.916667 1365.783333 0.389610 5.616667
4bf555a622c5def722ea81e24c7080e6 63 46 25 2002.850000 15985.116667 0.253968 20.500000
ba85c6a65126f1eeb1c0cf0e4b637d8e 62 30 36 7482.100000 3331.216667 0.306452 2.483333
f3806e5ff186dc9a67518a780ebff675 58 15 58 10267.883333 18765.950000 0.448276 2.733333
d58002a1b2c0788168fc492bea8ead63 55 30 29 319.050000 29181.766667 0.290909 3.783333
928b8233f68225055204674eec24f8d6 50 30 19 4537.033333 20364.616667 0.300000 1.383333

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: 22.10732538330494
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: 89
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
23cd0166e02eb43fb5c4323f8b1191f6 17 11 0 NaN NaN 0.705882 4.033333 2
8eee11ee7ca63d411e87ee6a74745788 19 15 19 28699.516667 130.466667 0.842105 1.433333 2
98a98e334db28b842d67e1308c1cf0db 18 14 0 NaN NaN 0.777778 1.733333 1
e55d1ae12b4ccd30016b03c3687ea33a 40 30 21 2699.133333 7831.533333 0.400000 5.966667 12
4903e8d4bf16fa448e138a0f4cec6b62 4 4 0 NaN NaN 1.000000 3.425000 1
... ... ... ... ... ... ... ... ...
a54e1325c917c695ea28f9dc6a36a478 1 0 0 NaN NaN 0.000000 NaN 0
68a0e2670e977096141663bb022c99c6 14 9 0 NaN NaN 0.642857 2.850000 1
eb44cc9c7546aa8b47c658fbfa298bb8 2 0 0 NaN NaN 0.500000 5.175000 1
7cba9f62f81d47ec24ff3301d19918f2 17 15 17 49.350000 16.466667 0.941176 2.300000 1
6d9a475a93a5d9b0438865ccdbea01c0 1 0 0 NaN NaN 0.000000 NaN 0

697 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
4f40276a3393364efdd9a22257958dcd 123 12 0 NaN NaN 0.260163 1.366667 25
c251b8357e9567e6734fac5fc6e7e17b 34 15 34 26139.050000 546.900000 0.470588 18.383333 19
4094af4f8b749541fac5d98c3e22e693 89 15 89 22810.116667 3781.350000 0.359551 1.966667 17
cda6690a73656beb65710ac68296c980 37 10 0 NaN NaN 0.513514 8.808333 15
76aabd38809d6ad5141236abdb6d9563 48 30 23 6712.433333 773.750000 0.354167 22.166667 14
2ab439c02ddc48446d861937b4adc066 77 15 77 27883.916667 1365.783333 0.389610 5.616667 13
ba85c6a65126f1eeb1c0cf0e4b637d8e 62 30 36 7482.100000 3331.216667 0.306452 2.483333 13
e55d1ae12b4ccd30016b03c3687ea33a 40 30 21 2699.133333 7831.533333 0.400000 5.966667 12
87f9384a67677cc459f9f880407bef23 31 15 31 22911.700000 1553.283333 0.516129 2.433333 12
e38f8ec511a5d13b5f3f71a006b59bad 50 29 34 13878.083333 15167.266667 0.320000 3.358333 12
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()