Combine populations for weighted outcomes#
Use the patient population to combine the outcomes from the separate cohorts. This notebook combines the added utility, shift in mean mRS, and proportion with mRS<=2. The following notebook combines the mRS distributions.
Plain English summary#
In the previous notebook we calculated outcomes for patients across all of England and Wales. This assumed fixed times to treatment except for travel times to the stroke units. We calculated one set of times for the drip-and-ship scenario, where patients first go to their nearest stroke unit and are later transferred to the MT unit if they need thrombectomy, and a second set of times for the mothership scenario, where every patient goes directly to the MT unit.
The existing data has separate entries for each cohort of patients:
patients with an nLVO treated with IVT
patients with a LVO treated with IVT only
patients with a LVO treated with MT only
patients with a LVO treated with both IVT and MT
In this notebook we will combine the data of multiple groups of patients.
Aims#
To find the averaged outcome data for added utility, shift in mean mRS, and proportion with an mRS score of 2 or less for the following groups:
patients with a LVO and a mix of the available treatment types
patients with an nLVO and patients with a LVO and a mix of the available treatment types
Method#
Use the example patient proportions to calculate a weighted sum of the separate cohorts.
Notebook setup#
import pandas as pd
import numpy as np
import os
import stroke_outcome.outcome_utilities
dir_output = 'output'
Import data#
Patient 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 some additional proportions:
# Proportion of treated LVO patients:
prop_lvo_treated = 0.0
for key, value in proportions.items():
if (('lvo' in key) & ('nlvo' not in key) & ('no_treat' not in key)):
print(key)
prop_lvo_treated += value
prop_lvo_treated
lvo_ivt_only
lvo_ivt_mt
lvo_mt_only
0.10840000000000001
# Proportion of treated ischaemic patients:
prop_ischaemic_treated = 0.0
for key, value in proportions.items():
if (('lvo' in key) & ('no_treat' not in key)):
print(key)
prop_ischaemic_treated += value
prop_ischaemic_treated
lvo_ivt_only
lvo_ivt_mt
lvo_mt_only
nlvo_ivt
0.21500000000000002
# Proportion of ischaemic patients:
prop_ischaemic = 1.0 - proportions['haemorrhagic']
prop_ischaemic
0.864
Outcomes:
df_results = pd.read_csv(os.path.join(dir_output, 'cohort_outcomes.csv'), index_col=0)
df_results.info()
<class 'pandas.core.frame.DataFrame'>
Index: 34752 entries, Adur 001A to York 024F
Data columns (total 51 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 closest_ivt_time 34752 non-null float64
1 closest_ivt_unit 34752 non-null object
2 closest_mt_time 34752 non-null float64
3 closest_mt_unit 34752 non-null object
4 transfer_mt_time 34752 non-null float64
5 transfer_mt_unit 34752 non-null object
6 mt_transfer_required 34752 non-null bool
7 ivt_drip_ship 34752 non-null float64
8 mt_drip_ship 34752 non-null float64
9 ivt_mothership 34752 non-null float64
10 mt_mothership 34752 non-null float64
11 drip_ship_nlvo_ivt_added_utility 34752 non-null float64
12 drip_ship_nlvo_ivt_mean_mrs 34752 non-null float64
13 drip_ship_nlvo_ivt_mrs_less_equal_2 34752 non-null float64
14 drip_ship_nlvo_ivt_mrs_shift 34752 non-null float64
15 drip_ship_nlvo_ivt_added_mrs_less_equal_2 34752 non-null float64
16 drip_ship_lvo_ivt_added_utility 34752 non-null float64
17 drip_ship_lvo_ivt_mean_mrs 34752 non-null float64
18 drip_ship_lvo_ivt_mrs_less_equal_2 34752 non-null float64
19 drip_ship_lvo_ivt_mrs_shift 34752 non-null float64
20 drip_ship_lvo_ivt_added_mrs_less_equal_2 34752 non-null float64
21 drip_ship_lvo_ivt_mt_added_utility 34752 non-null float64
22 drip_ship_lvo_ivt_mt_mean_mrs 34752 non-null float64
23 drip_ship_lvo_ivt_mt_mrs_less_equal_2 34752 non-null float64
24 drip_ship_lvo_ivt_mt_mrs_shift 34752 non-null float64
25 drip_ship_lvo_ivt_mt_added_mrs_less_equal_2 34752 non-null float64
26 drip_ship_lvo_mt_added_utility 34752 non-null float64
27 drip_ship_lvo_mt_mean_mrs 34752 non-null float64
28 drip_ship_lvo_mt_mrs_less_equal_2 34752 non-null float64
29 drip_ship_lvo_mt_mrs_shift 34752 non-null float64
30 drip_ship_lvo_mt_added_mrs_less_equal_2 34752 non-null float64
31 mothership_nlvo_ivt_added_utility 34752 non-null float64
32 mothership_nlvo_ivt_mean_mrs 34752 non-null float64
33 mothership_nlvo_ivt_mrs_less_equal_2 34752 non-null float64
34 mothership_nlvo_ivt_mrs_shift 34752 non-null float64
35 mothership_nlvo_ivt_added_mrs_less_equal_2 34752 non-null float64
36 mothership_lvo_ivt_added_utility 34752 non-null float64
37 mothership_lvo_ivt_mean_mrs 34752 non-null float64
38 mothership_lvo_ivt_mrs_less_equal_2 34752 non-null float64
39 mothership_lvo_ivt_mrs_shift 34752 non-null float64
40 mothership_lvo_ivt_added_mrs_less_equal_2 34752 non-null float64
41 mothership_lvo_ivt_mt_added_utility 34752 non-null float64
42 mothership_lvo_ivt_mt_mean_mrs 34752 non-null float64
43 mothership_lvo_ivt_mt_mrs_less_equal_2 34752 non-null float64
44 mothership_lvo_ivt_mt_mrs_shift 34752 non-null float64
45 mothership_lvo_ivt_mt_added_mrs_less_equal_2 34752 non-null float64
46 mothership_lvo_mt_added_utility 34752 non-null float64
47 mothership_lvo_mt_mean_mrs 34752 non-null float64
48 mothership_lvo_mt_mrs_less_equal_2 34752 non-null float64
49 mothership_lvo_mt_mrs_shift 34752 non-null float64
50 mothership_lvo_mt_added_mrs_less_equal_2 34752 non-null float64
dtypes: bool(1), float64(47), object(3)
memory usage: 13.6+ MB
Mean outcomes#
Combine LVO results#
Combine the separate IVT, MT, and IVT and MT cohorts for LVO into one “LVO” group.
for model_name in ['drip_ship', 'mothership']:
for outcome_name in ['added_utility', 'mrs_less_equal_2', 'mrs_shift', 'added_mrs_less_equal_2']:
# New column for results:
col = f'{model_name}_lvo_mix_{outcome_name}'
# Combine results:
df_results[col] = (
(df_results[f'{model_name}_lvo_ivt_{outcome_name}'] * proportions['lvo_ivt_only']) +
(df_results[f'{model_name}_lvo_mt_{outcome_name}'] * proportions['lvo_mt_only']) +
(df_results[f'{model_name}_lvo_ivt_mt_{outcome_name}'] * proportions['lvo_ivt_mt'])
)
# Divide by proportion of LVO patients:
df_results[col] = df_results[col] / prop_lvo_treated
# Round the results:
df_results[col] = np.round(df_results[col], 5)
Calculate treatment rates based on expected proportions of LVO/nLVO#
Add weighted benefit based on proportions of treated LVO and nLVO: Add weighted utility, mrs<=2, and mean_shift based on stroke type and treatment rates.
The expected shift from the no-treatment outcomes is zero for the following groups:
Haemorrhagic
LVO no treatment
nLVO no treatment
so we can skip adding them in the following equation.
for model_name in ['drip_ship', 'mothership']:
for outcome_name in ['added_utility', 'mrs_less_equal_2', 'mrs_shift', 'added_mrs_less_equal_2']:
col = f'{model_name}_weighted_{outcome_name}'
df_results[col] = (
(df_results[f'{model_name}_nlvo_ivt_{outcome_name}'] * proportions['nlvo_ivt']) +
(df_results[f'{model_name}_lvo_ivt_{outcome_name}'] * proportions['lvo_ivt_only']) +
(df_results[f'{model_name}_lvo_mt_{outcome_name}'] * proportions['lvo_mt_only']) +
(df_results[f'{model_name}_lvo_ivt_mt_{outcome_name}'] * proportions['lvo_ivt_mt'])
)
# Remove proportion of non-ischaemic patients:
df_results[col] = df_results[col] / prop_ischaemic
# Round the results:
df_results[col] = np.round(df_results[col], 5)
for model_name in ['drip_ship', 'mothership']:
for outcome_name in ['added_utility', 'mrs_less_equal_2', 'mrs_shift', 'added_mrs_less_equal_2']:
col = f'{model_name}_weighted_treated_{outcome_name}'
df_results[col] = (
(df_results[f'{model_name}_nlvo_ivt_{outcome_name}'] * proportions['nlvo_ivt']) +
(df_results[f'{model_name}_lvo_ivt_{outcome_name}'] * proportions['lvo_ivt_only']) +
(df_results[f'{model_name}_lvo_mt_{outcome_name}'] * proportions['lvo_mt_only']) +
(df_results[f'{model_name}_lvo_ivt_mt_{outcome_name}'] * proportions['lvo_ivt_mt'])
)
# Remove proportion of non-ischaemic patients:
df_results[col] = df_results[col] / prop_ischaemic_treated
# Round the results:
df_results[col] = np.round(df_results[col], 5)
# Show results
df_results.head().T
LSOA | Adur 001A | Adur 001B | Adur 001C | Adur 001D | Adur 001E |
---|---|---|---|---|---|
closest_ivt_time | 17.6 | 18.7 | 17.6 | 17.6 | 16.5 |
closest_ivt_unit | BN25BE | BN25BE | BN112DH | BN112DH | BN112DH |
closest_mt_time | 17.6 | 18.7 | 19.8 | 19.8 | 19.8 |
closest_mt_unit | BN25BE | BN25BE | BN25BE | BN25BE | BN25BE |
transfer_mt_time | 0.0 | 0.0 | 31.6 | 31.6 | 31.6 |
... | ... | ... | ... | ... | ... |
drip_ship_weighted_treated_added_mrs_less_equal_2 | 0.14571 | 0.14512 | 0.11861 | 0.11861 | 0.11918 |
mothership_weighted_treated_added_utility | 0.13633 | 0.13579 | 0.13525 | 0.13525 | 0.13525 |
mothership_weighted_treated_mrs_less_equal_2 | 0.56689 | 0.5663 | 0.56571 | 0.56571 | 0.56571 |
mothership_weighted_treated_mrs_shift | -0.71592 | -0.7129 | -0.70988 | -0.70988 | -0.70988 |
mothership_weighted_treated_added_mrs_less_equal_2 | 0.14571 | 0.14512 | 0.14453 | 0.14453 | 0.14453 |
75 rows × 5 columns
Save to file:
# Save
df_results.to_csv(os.path.join(dir_output, 'cohort_outcomes_weighted.csv'), index_label='lsoa')
summary = df_results.mean(axis=0).T
summary.to_csv(os.path.join(dir_output, 'cohort_outcomes_weighted_summary.csv'), index_label='lsoa')
summary
/tmp/ipykernel_49202/164885061.py:1: FutureWarning: The default value of numeric_only in DataFrame.mean is deprecated. In a future version, it will default to False. In addition, specifying 'numeric_only=None' is deprecated. Select only valid columns or specify the value of numeric_only to silence this warning.
summary = df_results.mean(axis=0).T
closest_ivt_time 19.548181
closest_mt_time 34.939310
transfer_mt_time 27.509447
mt_transfer_required 0.675098
ivt_drip_ship 109.548181
...
drip_ship_weighted_treated_added_mrs_less_equal_2 0.124662
mothership_weighted_treated_added_utility 0.127779
mothership_weighted_treated_mrs_less_equal_2 0.557609
mothership_weighted_treated_mrs_shift -0.668417
mothership_weighted_treated_added_mrs_less_equal_2 0.136428
Length: 72, dtype: float64
Save separate added utility for sharing#
These saved files are pretty big (several 10s of megabytes). The stroke outcome paper shows only the added utility, so create a separate file that contains only the added utility information in the maps to save file space.
Keep the following columns, which are anything to do with treatment or travel times or added utility:
cols_to_keep = [
'closest_ivt_time', 'closest_ivt_unit',
'closest_mt_time', 'closest_mt_unit',
'transfer_mt_time', 'transfer_mt_unit', 'mt_transfer_required',
'ivt_drip_ship', 'mt_drip_ship',
'ivt_mothership', 'mt_mothership'
]
cols_to_keep += [c for c in df_results.columns if 'added_utility' in c]
df_added_utility = df_results[cols_to_keep].copy()
Save this data to file:
df_added_utility.to_csv(os.path.join(dir_output, 'fig_data_englandwales_maps_added_utility.csv'))
This file does not include the “Advantage of mothership” data because it takes up more file space and is simple enough to calculate later.
To find this information, take the difference between matching data in the mothership and the drip and ship columns:
outcome_names = ['added_utility']
cohort_names = ['nlvo_ivt', 'lvo_ivt', 'lvo_mt', 'lvo_mix', 'weighted', 'weighted_treated']
cols_diff = [f'mothership_minus_dripship_{c}_{o}' for c in cohort_names for o in outcome_names]
cols_moth = [f'mothership_{c}_{o}' for c in cohort_names for o in outcome_names]
cols_drip = [f'drip_ship_{c}_{o}' for c in cohort_names for o in outcome_names]
df_added_utility[cols_diff] = df_added_utility[cols_moth].values - df_added_utility[cols_drip].values
# Round the values again in case of floating point error:
df_added_utility[cols_diff] = np.round(df_added_utility[cols_diff], 5)
df_added_utility.info()
<class 'pandas.core.frame.DataFrame'>
Index: 34752 entries, Adur 001A to York 024F
Data columns (total 31 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 closest_ivt_time 34752 non-null float64
1 closest_ivt_unit 34752 non-null object
2 closest_mt_time 34752 non-null float64
3 closest_mt_unit 34752 non-null object
4 transfer_mt_time 34752 non-null float64
5 transfer_mt_unit 34752 non-null object
6 mt_transfer_required 34752 non-null bool
7 ivt_drip_ship 34752 non-null float64
8 mt_drip_ship 34752 non-null float64
9 ivt_mothership 34752 non-null float64
10 mt_mothership 34752 non-null float64
11 drip_ship_nlvo_ivt_added_utility 34752 non-null float64
12 drip_ship_lvo_ivt_added_utility 34752 non-null float64
13 drip_ship_lvo_ivt_mt_added_utility 34752 non-null float64
14 drip_ship_lvo_mt_added_utility 34752 non-null float64
15 mothership_nlvo_ivt_added_utility 34752 non-null float64
16 mothership_lvo_ivt_added_utility 34752 non-null float64
17 mothership_lvo_ivt_mt_added_utility 34752 non-null float64
18 mothership_lvo_mt_added_utility 34752 non-null float64
19 drip_ship_lvo_mix_added_utility 34752 non-null float64
20 mothership_lvo_mix_added_utility 34752 non-null float64
21 drip_ship_weighted_added_utility 34752 non-null float64
22 mothership_weighted_added_utility 34752 non-null float64
23 drip_ship_weighted_treated_added_utility 34752 non-null float64
24 mothership_weighted_treated_added_utility 34752 non-null float64
25 mothership_minus_dripship_nlvo_ivt_added_utility 34752 non-null float64
26 mothership_minus_dripship_lvo_ivt_added_utility 34752 non-null float64
27 mothership_minus_dripship_lvo_mt_added_utility 34752 non-null float64
28 mothership_minus_dripship_lvo_mix_added_utility 34752 non-null float64
29 mothership_minus_dripship_weighted_added_utility 34752 non-null float64
30 mothership_minus_dripship_weighted_treated_added_utility 34752 non-null float64
dtypes: bool(1), float64(27), object(3)
memory usage: 8.3+ MB
Conclusion#
We have calculated a weighted sum of the separate patient cohorts to find outcome data for a mix of LVO patients and for the treated ischaemic population.
We have also saved a file with just the added utility results from all of the patient cohorts. This file contains a copy of the data that will be used to create maps of added utility around England and Wales, and is saved separately so that this data can be shared without everyone needing to download a massive file of mostly irrelevant data.