National mRS distributions#

This notebook creates a plot of the mRS distributions averaged across England and Wales.

Plain English summary#

This notebook loads the data that we created in a previous notebook. It loads the mRS distributions averaged across all parts of England and Wales where redirection from the nearest stroke unit to an MT centre makes a difference to which stroke unit is attended first.

Aims#

Plot the national mRS distributions for these cohorts:

  • nLVO with IVT

  • LVO with mixed treatments

  • treated ischaemic population

Methods#

Load in the existing data.

Create new no-treatment mRS distributions for the weighted combination by combining the separate nLVO and LVO no-treatment distributions in the required proportions.

Plot the mRS distributions and the cumulative probabilities.

dir_output = 'output'
limit_to_england = False

Import packages#

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os

import stroke_outcome.outcome_utilities

pd.set_option('display.max_rows', 150)
stroke_outcome.__version__
'0.1.6'

Load data#

Results from outcome model:

df_mrs_national_cumsum = pd.read_csv(os.path.join(dir_output, 'cohort_mrs_dists_weighted_national.csv'), index_col=0)
df_mrs_national_cumsum
mRS<=0 mRS<=1 mRS<=2 mRS<=3 mRS<=4 mRS<=5 mRS<=6
drip_ship_lvo_ivt 0.10356 0.19544 0.32788 0.49012 0.70857 0.82316 1.0
drip_ship_lvo_ivt_mt 0.11472 0.22605 0.37176 0.54453 0.75047 0.85084 1.0
drip_ship_lvo_mix 0.11382 0.22365 0.36835 0.54031 0.74723 0.84869 1.0
drip_ship_lvo_mt 0.11449 0.22589 0.37170 0.54455 0.75052 0.85086 1.0
drip_ship_nlvo_ivt 0.36134 0.58906 0.70508 0.82416 0.92123 0.95515 1.0
drip_ship_weighted 0.18250 0.39015 0.51547 0.65399 0.81988 0.89575 1.0
drip_ship_weighted_treated 0.23655 0.40482 0.53531 0.68105 0.83350 0.90148 1.0
mothership_lvo_ivt 0.09705 0.18809 0.32101 0.48329 0.70384 0.81982 1.0
mothership_lvo_ivt_mt 0.15523 0.27670 0.42080 0.59378 0.78327 0.87247 1.0
mothership_lvo_mix 0.15072 0.26984 0.41306 0.58522 0.77711 0.86839 1.0
mothership_lvo_mt 0.15523 0.27670 0.42080 0.59378 0.78327 0.87247 1.0
mothership_nlvo_ivt 0.34442 0.57736 0.69431 0.81494 0.91617 0.95206 1.0
mothership_weighted 0.18504 0.39450 0.51975 0.65849 0.82301 0.89784 1.0
mothership_weighted_treated 0.24676 0.42231 0.55251 0.69911 0.84606 0.90988 1.0
df_mrs_national_noncum = pd.read_csv(os.path.join(dir_output, 'cohort_mrs_dists_weighted_national_noncum.csv'), index_col=0)
df_mrs_national_noncum
mRS=0 mRS=1 mRS=2 mRS=3 mRS=4 mRS=5 mRS=6
drip_ship_lvo_ivt 0.10356 0.09188 0.13244 0.16224 0.21845 0.11459 0.17684
drip_ship_lvo_ivt_mt 0.11472 0.11133 0.14571 0.17277 0.20594 0.10037 0.14916
drip_ship_lvo_mix 0.11382 0.10983 0.14470 0.17197 0.20692 0.10146 0.15131
drip_ship_lvo_mt 0.11449 0.11139 0.14582 0.17284 0.20597 0.10034 0.14914
drip_ship_nlvo_ivt 0.36134 0.22771 0.11603 0.11907 0.09707 0.03393 0.04485
drip_ship_weighted 0.18250 0.20765 0.12532 0.13852 0.16589 0.07586 0.10425
drip_ship_weighted_treated 0.23655 0.16828 0.13048 0.14574 0.15245 0.06798 0.09852
mothership_lvo_ivt 0.09705 0.09104 0.13293 0.16228 0.22054 0.11599 0.18018
mothership_lvo_ivt_mt 0.15523 0.12147 0.14409 0.17298 0.18949 0.08921 0.12753
mothership_lvo_mix 0.15072 0.11911 0.14323 0.17215 0.19189 0.09128 0.13161
mothership_lvo_mt 0.15523 0.12147 0.14409 0.17298 0.18949 0.08921 0.12753
mothership_nlvo_ivt 0.34442 0.23294 0.11695 0.12062 0.10124 0.03589 0.04794
mothership_weighted 0.18504 0.20946 0.12525 0.13873 0.16452 0.07483 0.10216
mothership_weighted_treated 0.24676 0.17555 0.13020 0.14660 0.14695 0.06382 0.09012
df_mrs_national_std = pd.read_csv(os.path.join(dir_output, 'cohort_mrs_dists_weighted_national_std.csv'), index_col=0)
df_mrs_national_std
mRS=0 mRS=1 mRS=2 mRS=3 mRS=4 mRS=5 mRS=6
drip_ship_lvo_ivt 0.00262 0.00029 0.00022 0.00003 0.00083 0.00055 0.00129
drip_ship_lvo_ivt_mt 0.00983 0.00399 0.00143 0.00129 0.00452 0.00355 0.00729
drip_ship_lvo_mix 0.00784 0.00317 0.00112 0.00102 0.00360 0.00283 0.00580
drip_ship_lvo_mt 0.01037 0.00373 0.00054 0.00080 0.00460 0.00347 0.00723
drip_ship_nlvo_ivt 0.00667 0.00215 0.00039 0.00064 0.00160 0.00074 0.00116
drip_ship_weighted 0.00128 0.00048 0.00015 0.00015 0.00049 0.00037 0.00074
drip_ship_weighted_treated 0.00515 0.00192 0.00060 0.00060 0.00198 0.00147 0.00298
mothership_lvo_ivt 0.00568 0.00090 0.00035 0.00004 0.00188 0.00128 0.00309
mothership_lvo_ivt_mt 0.01227 0.00250 0.00093 0.00032 0.00475 0.00307 0.00577
mothership_lvo_mix 0.00978 0.00199 0.00074 0.00025 0.00378 0.00245 0.00460
mothership_lvo_mt 0.01227 0.00250 0.00093 0.00032 0.00475 0.00307 0.00577
mothership_nlvo_ivt 0.01520 0.00444 0.00075 0.00131 0.00387 0.00187 0.00297
mothership_weighted 0.00224 0.00060 0.00013 0.00016 0.00067 0.00038 0.00068
mothership_weighted_treated 0.00901 0.00242 0.00053 0.00066 0.00271 0.00154 0.00275
cols_each_scen = df_mrs_national_noncum.index.values

cols_each_scen
array(['drip_ship_lvo_ivt', 'drip_ship_lvo_ivt_mt', 'drip_ship_lvo_mix',
       'drip_ship_lvo_mt', 'drip_ship_nlvo_ivt', 'drip_ship_weighted',
       'drip_ship_weighted_treated', 'mothership_lvo_ivt',
       'mothership_lvo_ivt_mt', 'mothership_lvo_mix', 'mothership_lvo_mt',
       'mothership_nlvo_ivt', 'mothership_weighted',
       'mothership_weighted_treated'], dtype=object)

Reference no-treatment mRS distributions:

mrs_dists, mrs_dists_notes = (
    stroke_outcome.outcome_utilities.import_mrs_dists_from_file())
mrs_dists
mRS<=0 mRS<=1 mRS<=2 mRS<=3 mRS<=4 mRS<=5 mRS<=6
Stroke type
pre_stroke_nlvo 0.583 0.746 0.850 0.951 0.993 1.000 1
pre_stroke_lvo 0.408 0.552 0.672 0.838 0.956 1.000 1
no_treatment_lvo 0.050 0.129 0.265 0.429 0.676 0.811 1
no_treatment_nlvo 0.198 0.460 0.580 0.708 0.856 0.918 1
no_effect_nlvo_ivt_deaths 0.196 0.455 0.574 0.701 0.847 0.908 1
no_effect_lvo_ivt_deaths 0.048 0.124 0.255 0.414 0.653 0.783 1
no_effect_lvo_mt_deaths 0.048 0.124 0.255 0.412 0.649 0.779 1
t0_treatment_nlvo_ivt 0.445 0.642 0.752 0.862 0.941 0.967 1
t0_treatment_lvo_ivt 0.140 0.233 0.361 0.522 0.730 0.838 1
t0_treatment_lvo_mt 0.306 0.429 0.548 0.707 0.851 0.915 1
mrs_dist_nlvo_no_treatment = mrs_dists.loc['no_treatment_nlvo'].values
mrs_dist_lvo_no_treatment = mrs_dists.loc['no_treatment_lvo'].values

mrs_dist_nlvo_no_treatment_noncum = np.diff(mrs_dist_nlvo_no_treatment, prepend=0.0)
mrs_dist_lvo_no_treatment_noncum = np.diff(mrs_dist_lvo_no_treatment, prepend=0.0)

Stroke type proportions:

proportions = pd.read_csv(
    os.path.join(dir_output, 'patient_proportions.csv'),
    index_col=0, header=None).squeeze()
proportions
0
haemorrhagic         0.13600
lvo_no_treatment     0.14648
lvo_ivt_only         0.00840
lvo_ivt_mt           0.08500
lvo_mt_only          0.01500
nlvo_no_treatment    0.50252
nlvo_ivt             0.10660
Name: 1, dtype: float64

Calculate weighted no treatment dist:#

prop_lvo_treated = 0.0
prop_nlvo_treated = 0.0

for key, value in proportions.items():
    if ('nlvo' in key) & ('no_treatment' not in key):
        prop_nlvo_treated += value
    elif ('lvo' in key) & ('no_treatment' not in key):
        prop_lvo_treated += value
prop_lvo_treated, prop_nlvo_treated
(0.10840000000000001, 0.1066)
prop_lvo_treated / (prop_nlvo_treated + prop_lvo_treated), prop_nlvo_treated / (prop_nlvo_treated + prop_lvo_treated)
(0.5041860465116279, 0.49581395348837204)
mrs_dist_weighted_no_treatment_noncum = (
    (mrs_dist_nlvo_no_treatment_noncum * prop_nlvo_treated) +
    (mrs_dist_lvo_no_treatment_noncum * prop_lvo_treated)
)

# Remove the non-treated patients:
mrs_dist_weighted_no_treatment_noncum = (
    mrs_dist_weighted_no_treatment_noncum / (prop_nlvo_treated + prop_lvo_treated)
)
np.sum(mrs_dist_weighted_no_treatment_noncum)
1.0
# Same structure as the other distribution dictionaries:
dict_no_treat = {
    'nlvo': {
        'values': mrs_dist_nlvo_no_treatment_noncum,
        'cumsum': np.cumsum(mrs_dist_nlvo_no_treatment_noncum)
    },
    'lvo': {
        'values': mrs_dist_lvo_no_treatment_noncum,
        'cumsum': np.cumsum(mrs_dist_lvo_no_treatment_noncum)
    },
    # 'weighted': {
    #     'values': mrs_dist_weighted_no_treatment_noncum,
    #     'cumsum': np.cumsum(mrs_dist_weighted_no_treatment_noncum)
    # },
    'weighted_treated': {
        'values': mrs_dist_weighted_no_treatment_noncum,
        'cumsum': np.cumsum(mrs_dist_weighted_no_treatment_noncum)
    },
}

Gather data for plots#

def gather_dists(scenario, df_mrs_national_noncum, df_mrs_national_std, df_mrs_national_cumsum):
    d = {}

    for cohort in ['nlvo_ivt', 'lvo_mix', 'weighted_treated']:
        ind = f'{scenario}_{cohort}'

        d[cohort] = {}
        d[cohort]['values'] = df_mrs_national_noncum.loc[ind]
        d[cohort]['std'] = df_mrs_national_std.loc[ind]
        d[cohort]['cumsum'] = df_mrs_national_cumsum.loc[ind]
    
    return d
dict_drip_ship = gather_dists('drip_ship', df_mrs_national_noncum, df_mrs_national_std, df_mrs_national_cumsum)
dict_mothership = gather_dists('mothership', df_mrs_national_noncum, df_mrs_national_std, df_mrs_national_cumsum)

Plot#

colour_drip = '#00517f'   # seaborn colorblind blue darker
colour_moth = '#0072b2'  # seaborn colorblind blue
colour_no_treat = 'DarkGray'
fig, axs = plt.subplots(2, 3, figsize=(15, 7), gridspec_kw={'wspace': 0.1})

bar_width = 0.25
offsets = [-bar_width, 0.0, bar_width]

legend_drawn = False
for c, cohort in enumerate(['nlvo_ivt', 'lvo_mix', 'weighted_treated']):
    dist_drip = dict_drip_ship[cohort]['values']
    std_drip = dict_drip_ship[cohort]['std']
    dist_cumsum_drip = dict_drip_ship[cohort]['cumsum']
    dist_moth = dict_mothership[cohort]['values']
    std_moth = dict_mothership[cohort]['std']
    dist_cumsum_moth = dict_mothership[cohort]['cumsum']
    
    if 'nlvo' in cohort:
        ax = axs[0, 0]
        ax_cumsum = axs[1, 0]
        dist_no_treat = dict_no_treat['nlvo']['values']
        dist_cumsum_no_treat = dict_no_treat['nlvo']['cumsum']
    elif 'lvo' in cohort:
        ax = axs[0, 1]
        ax_cumsum = axs[1, 1]
        dist_no_treat = dict_no_treat['lvo']['values']
        dist_cumsum_no_treat = dict_no_treat['lvo']['cumsum']
    elif 'treated' in cohort:
        ax = axs[0, 2]
        ax_cumsum = axs[1, 2]
        dist_no_treat = dict_no_treat['weighted_treated']['values']
        dist_cumsum_no_treat = dict_no_treat['weighted_treated']['cumsum']
    # else:
    #     ax = axs[0, -1]
    #     ax_cumsum = axs[1, -1]
    #     dist_no_treat = dict_no_treat['weighted']['values']
    #     dist_cumsum_no_treat = dict_no_treat['weighted']['cumsum']

    ax.bar(
        np.arange(7) + offsets[0], dist_drip, yerr=std_drip, capsize=2.0,
        facecolor=colour_drip, label='Drip & ship', width=bar_width
        )
    ax.bar(
        np.arange(7) + offsets[1], dist_moth, yerr=std_moth, capsize=2.0,
        facecolor=colour_moth, label='Mothership', width=bar_width
        )
    ax.bar(
        np.arange(7) + offsets[2], dist_no_treat,
        facecolor=colour_no_treat, label='No treatment', width=bar_width
        )

    ax_cumsum.plot(np.arange(7), dist_cumsum_drip, color=colour_drip, linestyle='-', label='Drip & ship')
    ax_cumsum.plot(np.arange(7), dist_cumsum_moth, color=colour_moth, linestyle='--', label='Mothership')
    ax_cumsum.plot(np.arange(7), dist_cumsum_no_treat, color=colour_no_treat, linestyle=':', label='No treatment')

    if c == 2:
        ax.legend(bbox_to_anchor=(1.0, 0.5), loc='center left')
        ax_cumsum.legend(bbox_to_anchor=(1.0, 0.5), loc='center left')
        legend_drawn = True

for i, ax_list in enumerate(axs):
    ylim = [0, 0.4] if i == 0 else [0.0, 1.05]
    for j, ax in enumerate(ax_list):
        ax.set_ylim(*ylim)
        ax.set_xticks(range(7))
        if (j > 0):  # & (j < len(ax_list) - 1):
            ax.set_yticklabels([])

axs[0, 0].set_title('nLVO')
axs[0, 1].set_title('LVO')
axs[0, 2].set_title('Treated ischaemic population')
# axs[0, -1].set_title('Total\nischaemic population')

axs[0, 0].set_ylabel('Probability')
# axs[0, -1].set_ylabel('Probability')
# axs[0, -1].yaxis.set_tick_params(labelleft=True)  # turn the tick labels back on
axs[0, 1].set_xlabel('mRS')
# axs[0, -1].set_xlabel('mRS')

axs[1, 0].set_ylabel('Cumulative probability')
# axs[1, -1].set_ylabel('Cumulative probability')
# axs[1, -1].yaxis.set_tick_params(labelleft=True)  # turn the tick labels back on
axs[1, 1].set_xlabel('mRS')
# axs[1, -1].set_xlabel('mRS')

# axs[0, 3].axis('off')
# axs[1, 3].axis('off')

plt.savefig(os.path.join(dir_output, 'mrs_dists_redirection_areas.png'), bbox_inches='tight')
plt.show()
../_images/c771d356203533615daa2426836a8492f772de19522a1dcbfbb0b207d1bc5b31.png

Find mean values#

Import utility scores

utility_dists, utility_dists_notes = (
    stroke_outcome.outcome_utilities.import_utility_dists_from_file())

utility_weights = utility_dists.loc['Wang2020'].values
dist_dicts = {
    'drip_ship': dict_drip_ship,
    'mothership': dict_mothership,
    'no_treat': dict_no_treat
}
df_means = pd.DataFrame()

for scenario, dist_dict in dist_dicts.items():
    for cohort, d_dict in dist_dict.items():
            
        if 'nlvo' in cohort:
            dist_no_treat = dict_no_treat['nlvo']['values']
        elif 'lvo' in cohort:
            dist_no_treat = dict_no_treat['lvo']['values']
        elif 'treated' in cohort:
            dist_no_treat = dict_no_treat['weighted_treated']['values']
        else:
            print('help')
            # dist_no_treat = dict_no_treat['weighted']['values']

        mean_mrs_no_treat = np.mean(dist_no_treat * np.arange(7))
        mean_util_no_treat = np.mean(dist_no_treat * utility_weights)
        mrsleq2_no_treat = np.sum(dist_no_treat[:3])
            
        dist = d_dict['values']
        ind = f'{scenario}_{cohort}'
        df_means.loc[ind, 'mean_mrs'] = np.mean(dist * np.arange(7))
        df_means.loc[ind, 'mean_util'] = np.mean(dist * utility_weights)
        df_means.loc[ind, 'mrsleq2'] = np.sum(dist[:3])
        df_means.loc[ind, 'mean_mrs_shift'] = (
            df_means.loc[ind, 'mean_mrs'] - mean_mrs_no_treat)
        df_means.loc[ind, 'mean_util_shift'] = (
            df_means.loc[ind, 'mean_util'] - mean_util_no_treat)
        df_means.loc[ind, 'mrsleq2_shift'] = (
            df_means.loc[ind, 'mrsleq2'] - mrsleq2_no_treat)
df_means
mean_mrs mean_util mrsleq2 mean_mrs_shift mean_util_shift mrsleq2_shift
drip_ship_nlvo_ivt 0.234859 0.102172 0.705080 -0.090856 0.016509 0.125080
drip_ship_lvo_mix 0.451140 0.061546 0.368350 -0.068860 0.014031 0.103350
drip_ship_weighted_treated 0.343897 0.081689 0.535310 -0.079773 0.015260 0.114129
mothership_nlvo_ivt 0.242964 0.100770 0.694310 -0.082750 0.015107 0.114310
mothership_lvo_mix 0.419377 0.067532 0.413060 -0.100623 0.020016 0.148060
mothership_weighted_treated 0.331910 0.084012 0.552510 -0.091760 0.017582 0.131329
no_treat_nlvo 0.325714 0.085663 0.580000 0.000000 0.000000 0.000000
no_treat_lvo 0.520000 0.047516 0.265000 0.000000 0.000000 0.000000
no_treat_weighted_treated 0.423670 0.066430 0.421181 0.000000 0.000000 0.000000

Save a copy of data behind the figure#

Keep a copy of the separate LVO cohorts even though the figure only shows the combined LVO cohort.

Add the no-treatment distributions into the starting data:

for key, value in dict_no_treat.items():
    ind = f'no_treatment_{key}'
    df_mrs_national_noncum.loc[ind] = value
    df_mrs_national_std.loc[ind] = pd.NA
# Save to file:
df_mrs_national_noncum.to_csv(os.path.join(dir_output, 'fig_data_mrs_dists.csv'))
df_mrs_national_std.to_csv(os.path.join(dir_output, 'fig_data_mrs_dists_std.csv'))
df_means.to_csv(os.path.join(dir_output, 'fig_data_mrs_dists_means.csv'))

Conclusions#

For nLVO, the mRS distributions are weighted slightly lower (less disability) for the drip-and-ship scenario. For LVO, they are weighted lower (less disability) for the mothership scenario. For the combined cohort, the mothership scenario just wins out. This is most easily seen by looking at which scenario is highest on the cumulative probability plots.