Stroke pathway simulation - generation of results from alternative scenarios
Contents
Stroke pathway simulation - generation of results from alternative scenarios#
This notebook runs alternative pathway simulations, with adjusted pathway parameters. The scenarios are:
Base: Uses the hospitals’ recorded pathway statistics in SSNAP (same as validation notebook)
Speed: Sets 95% of patients having a scan within 4 hours of arrival, and all patients have 15 minutes arrival to scan and 15 minutes scan to needle.
Onset-known: Sets the proportion of patients with a known onset time of stroke to the national upper quartile if currently less than the national upper quartile (leave any greater than the upper national quartile at their current level).
Benchmark: The benchmark thrombolysis rate takes the likelihood to give thrombolysis for patients scanned within 4 hours of onset from the majority vote of the 30 hospitals with the highest predicted thrombolysis use in a standard 10k cohort set of patients. These are from Random Forests models.
Combine Speed and Onset-known
Combine Speed and Benchmark
Combine Onset-known and Benchmark
Combine Speed, Onset-known and Benchmark
Results are saved for each hospital and scenario. Detailed analysis will be performed in subsequent notebooks
Import libraries and data#
# Import libraries
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pathway import model_ssnap_pathway_scenarios
# load data from csv and store in pandas dataframe
filename = './hosp_performance_output/hospital_performance.csv'
hospital_performance_original = pd.read_csv(filename, index_col=0)
Get results of alternative scenarios#
Base scenario#
The base scenario uses the hospitals’ recorded pathway statistics in SSNAP (same as validation notebook)
results_all = model_ssnap_pathway_scenarios(hospital_performance_original)
results_all['scenario'] = 'base'
# Save pathway stats used
hospital_performance_original.to_csv('output/performance_base.csv')
Speed (30 minute arrival to needle)#
The adjusted speed scenario sets 95% of patients having a scan within 4 hours of arrival, and all patients have 15 minutes arrival to scan and 15 minutes scan to needle.
# Create scenarios
hospital_performance = hospital_performance_original.copy()
hospital_performance['scan_within_4_hrs'] = 0.95
hospital_performance['arrival_scan_arrival_mins_mu'] = np.log(15)
hospital_performance['arrival_scan_arrival_mins_sigma'] = 0
hospital_performance['scan_needle_mins_mu'] = np.log(15)
hospital_performance['scan_needle_mins_sigma'] = 0
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'speed'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_speed.csv')
Known onset#
Set the proportion of patients with a known onset time of stroke to the national upper quartile if currently less than the national upper quartile (leave any greater than the upper national quartile at their current level).
# Create scenarios
hospital_performance = hospital_performance_original.copy()
onset_known = hospital_performance_original['onset_known']
onset_known_upper_q = np.percentile(onset_known, 75)
adjusted_onset_known = []
for val in onset_known:
if val > onset_known_upper_q:
adjusted_onset_known.append(val)
else:
adjusted_onset_known.append(onset_known_upper_q)
hospital_performance['onset_known'] = adjusted_onset_known
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'onset'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_onset.csv')
Use benchmark thrombolysis#
The benchmark thrombolysis rate takes the likelihood to give thrombolysis for patients scanned within 4 hours of onset from the majority vote of the 30 hospitals with the highest predicted thrombolysis use in a standard 10k cohort set of patients. These are from Random Forests models.
See Random Forests notebooks: Benchmark hospitals and How would thrombolysis use change if clinical decisions were made by hospitals with the highest current thrombolysis rate?
# Load benchmark rates
filename = './hosp_performance_output/benchmark_4hr_scan.csv'
benchmark = pd.read_csv(filename, index_col=0)
# Convert from percentage to fraction
benchmark *= 0.01
# Merge in benchmark rates (to ensure order is correct)
hospital_performance = hospital_performance_original.copy()
hospital_performance = hospital_performance.merge(
benchmark, left_index=True, right_index=True, how='left')
hospital_performance['eligable'] = hospital_performance['benchmark']
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'benchmark'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_benchmark.csv')
Combine speed and onset known#
95% patients have scan within 4 hours of arrival
30 min arrival to needle (15 minute arrival-to-scan, 15 minute scan-to-needle)
Proportion of patients with known stroke onset time set to upper quartile if currently lower
# Create scenarios
hospital_performance = hospital_performance_original.copy()
# Speed
hospital_performance['scan_within_4_hrs'] = 0.95
hospital_performance['arrival_scan_arrival_mins_mu'] = np.log(15)
hospital_performance['arrival_scan_arrival_mins_sigma'] = 0
hospital_performance['scan_needle_mins_mu'] = np.log(15)
hospital_performance['scan_needle_mins_sigma'] = 0
# Onset known
onset_known = hospital_performance_original['onset_known']
onset_known_upper_q = np.percentile(onset_known, 75)
adjusted_onset_known = []
for val in onset_known:
if val > onset_known_upper_q:
adjusted_onset_known.append(val)
else:
adjusted_onset_known.append(onset_known_upper_q)
hospital_performance['onset_known'] = adjusted_onset_known
# Save pathway stats used
hospital_performance.to_csv('output/performance_speed_onset.csv')
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'speed_onset'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
Combine speed and benchmark#
95% patients have scan within 4 hours of arrival
30 min arrival to needle (15 minute arrival-to-scan, 15 minute scan-to-needle)
Decision to thrombolyse patients if scanned within 4 hours of known onset as predicted from majority vote of 30 benchmark hospitals
# Create scenarios
hospital_performance = hospital_performance_original.copy()
# Speed
hospital_performance['scan_within_4_hrs'] = 0.95
hospital_performance['arrival_scan_arrival_mins_mu'] = np.log(15)
hospital_performance['arrival_scan_arrival_mins_sigma'] = 0
hospital_performance['scan_needle_mins_mu'] = np.log(15)
hospital_performance['scan_needle_mins_sigma'] = 0
# Benchmark
hospital_performance = hospital_performance.merge(
benchmark, left_index=True, right_index=True, how='left')
hospital_performance['eligable'] = hospital_performance['benchmark']
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'speed_benchmark'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_speed_benchmark.csv')
Combine onset-known and benchmark#
Proportion of patients with known stroke onset time set to upper quartile if currently lower
Decision to thrombolyse patients if scanned within 4 hours of known onset as predicted from majority vote of 30 benchmark hospitals
# Create scenarios
hospital_performance = hospital_performance_original.copy()
# Onset known
onset_known = hospital_performance_original['onset_known']
onset_known_upper_q = np.percentile(onset_known, 75)
adjusted_onset_known = []
for val in onset_known:
if val > onset_known_upper_q:
adjusted_onset_known.append(val)
else:
adjusted_onset_known.append(onset_known_upper_q)
hospital_performance['onset_known'] = adjusted_onset_known
# Benchmark
hospital_performance = hospital_performance.merge(
benchmark, left_index=True, right_index=True, how='left')
hospital_performance['eligable'] = hospital_performance['benchmark']
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'onset_benchmark'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_onset_benchmark.csv')
Combine speed, onset-known, and benchmark#
95% patients have scan within 4 hours of arrival
30 min arrival to needle (15 minute arrival-to-scan, 15 minute scan-to-needle)
Proportion of patients with known stroke onset time set to upper quartile if currently lower
Decision to thrombolyse patients if scanned within 4 hours of known onset as predicted from majority vote of 30 benchmark hospitals
# Create scenarios
hospital_performance = hospital_performance_original.copy()
# Speed
hospital_performance['scan_within_4_hrs'] = 0.95
hospital_performance['arrival_scan_arrival_mins_mu'] = np.log(15)
hospital_performance['arrival_scan_arrival_mins_sigma'] = 0
hospital_performance['scan_needle_mins_mu'] = np.log(15)
hospital_performance['scan_needle_mins_sigma'] = 0
# Onset known
onset_known = hospital_performance_original['onset_known']
onset_known_upper_q = np.percentile(onset_known, 75)
adjusted_onset_known = []
for val in onset_known:
if val > onset_known_upper_q:
adjusted_onset_known.append(val)
else:
adjusted_onset_known.append(onset_known_upper_q)
hospital_performance['onset_known'] = adjusted_onset_known
# Benchmark
hospital_performance = hospital_performance.merge(
benchmark, left_index=True, right_index=True, how='left')
hospital_performance['eligable'] = hospital_performance['benchmark']
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'speed_onset_benchmark'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/performance_speed_onset_benchmark.csv')
Same patient characteristics to all hospitals#
# Get predicted cohort thrombolysis for patients arrived in 4 hours
cohort_rate = pd.read_csv(
'../random_forest/predictions/corhort_rates.csv', index_col='hospital')
cohort_rate['cohort_rate'] /= 100 # Convert from percent to fraction
# Create scenarios
hospital_performance = hospital_performance_original.copy()
hospital_performance = hospital_performance.merge(
cohort_rate['cohort_rate'], left_index=True, right_index=True)
# Set all hopsital to 1000 arrivals
hospital_performance['arrivals'] = 1000
# Set aged 80+ to national average for arrivaks within 4 hours of stroke onset
hospital_performance['80_plus'] = 0.423
# Set known arrival within 4 hours to 58% of onset_known
hospital_performance['known_arrival_within_4hrs'] = 0.58
# Set onet to arrival to national average
hospital_performance['onset_arrival_mins_mu'] = 4.58
hospital_performance['onset_arrival_mins_sigma'] = 0.56
# Set eligible to prop of 10k cohort predicted to receive thrombolysis
# Adjust for rate being just for those scanned in 4 hours from onset
correction = 1 / 0.827 # 82.7% in 10k cohort scanned in 4 hours from onset
hospital_performance['eligable'] = hospital_performance['cohort_rate'] * correction
# Get results
results = model_ssnap_pathway_scenarios(hospital_performance)
results['scenario'] = 'same_patient_characteristics'
# Add to results_all
results_all = pd.concat([results_all, results], axis=0)
# Save pathway stats used
hospital_performance.to_csv('output/same_patient_characteristics.csv')
results
Baseline_good_outcomes_(median) | Baseline_good_outcomes_per_1000_patients_(low_5%) | Baseline_good_outcomes_per_1000_patients_(high_95%) | Baseline_good_outcomes_per_1000_patients_(mean) | Baseline_good_outcomes_per_1000_patients_(stdev) | Baseline_good_outcomes_per_1000_patients_(95ci) | Percent_Thrombolysis_(median%) | Percent_Thrombolysis_(low_5%) | Percent_Thrombolysis_(high_95%) | Percent_Thrombolysis_(mean) | ... | Percent_Thrombolysis_(95ci) | Additional_good_outcomes_per_1000_patients_(median) | Additional_good_outcomes_per_1000_patients_(low_5%) | Additional_good_outcomes_per_1000_patients_(high_95%) | Additional_good_outcomes_per_1000_patients_(mean) | Additional_good_outcomes_per_1000_patients_(stdev) | Additional_good_outcomes_per_1000_patients_(95ci) | Onset_to_needle_(mean) | calibration | scenario | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
AGNOF1041H | 256.33 | 231.00 | 280.33 | 256.68 | 15.27 | 2.99 | 11.03 | 8.94 | 12.82 | 11.06 | ... | 0.22 | 9.21 | 7.32 | 11.08 | 9.21 | 1.12 | 0.22 | 164.15 | 1.0 | same_patient_characteristics |
AKCGO9726K | 256.34 | 236.79 | 280.84 | 256.40 | 13.15 | 2.58 | 22.48 | 20.03 | 24.50 | 22.39 | ... | 0.27 | 19.97 | 17.46 | 21.74 | 19.82 | 1.33 | 0.26 | 151.32 | 1.0 | same_patient_characteristics |
AOBTM3098N | 257.00 | 226.00 | 290.10 | 257.08 | 19.05 | 3.73 | 8.60 | 6.80 | 10.61 | 8.71 | ... | 0.24 | 6.47 | 4.86 | 8.17 | 6.62 | 0.97 | 0.19 | 182.50 | 1.0 | same_patient_characteristics |
APXEE8191H | 256.26 | 227.33 | 291.91 | 256.63 | 22.71 | 4.45 | 13.10 | 10.71 | 15.95 | 13.21 | ... | 0.31 | 10.16 | 8.20 | 12.67 | 10.30 | 1.31 | 0.26 | 177.40 | 1.0 | same_patient_characteristics |
ATDID5461S | 254.55 | 218.00 | 316.36 | 259.09 | 30.24 | 5.93 | 7.27 | 5.45 | 10.18 | 7.59 | ... | 0.30 | 5.23 | 3.33 | 7.26 | 5.29 | 1.19 | 0.23 | 197.94 | 1.0 | same_patient_characteristics |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
YPKYH1768F | 256.00 | 220.00 | 292.40 | 258.40 | 23.60 | 4.63 | 7.20 | 4.80 | 10.42 | 7.47 | ... | 0.34 | 4.98 | 3.26 | 7.24 | 4.98 | 1.25 | 0.24 | 206.28 | 1.0 | same_patient_characteristics |
YQMZV4284N | 256.98 | 220.39 | 293.44 | 256.98 | 22.35 | 4.38 | 16.20 | 12.53 | 18.72 | 15.92 | ... | 0.39 | 12.97 | 9.91 | 15.56 | 12.87 | 1.75 | 0.34 | 170.94 | 1.0 | same_patient_characteristics |
ZBVSO0975W | 258.35 | 229.40 | 289.76 | 258.08 | 17.57 | 3.44 | 7.80 | 5.35 | 10.02 | 7.66 | ... | 0.29 | 6.27 | 4.09 | 8.38 | 6.23 | 1.31 | 0.26 | 168.60 | 1.0 | same_patient_characteristics |
ZHCLE1578P | 256.28 | 228.64 | 281.60 | 256.71 | 16.16 | 3.17 | 10.87 | 9.05 | 12.33 | 10.76 | ... | 0.20 | 8.83 | 7.34 | 10.38 | 8.78 | 0.89 | 0.17 | 169.10 | 1.0 | same_patient_characteristics |
ZRRCV7012C | 256.28 | 229.48 | 286.43 | 257.04 | 16.69 | 3.27 | 11.22 | 9.36 | 13.07 | 11.19 | ... | 0.23 | 8.62 | 7.10 | 10.23 | 8.67 | 1.00 | 0.20 | 178.32 | 1.0 | same_patient_characteristics |
132 rows × 21 columns
results_all
Baseline_good_outcomes_(median) | Baseline_good_outcomes_per_1000_patients_(low_5%) | Baseline_good_outcomes_per_1000_patients_(high_95%) | Baseline_good_outcomes_per_1000_patients_(mean) | Baseline_good_outcomes_per_1000_patients_(stdev) | Baseline_good_outcomes_per_1000_patients_(95ci) | Percent_Thrombolysis_(median%) | Percent_Thrombolysis_(low_5%) | Percent_Thrombolysis_(high_95%) | Percent_Thrombolysis_(mean) | ... | Percent_Thrombolysis_(95ci) | Additional_good_outcomes_per_1000_patients_(median) | Additional_good_outcomes_per_1000_patients_(low_5%) | Additional_good_outcomes_per_1000_patients_(high_95%) | Additional_good_outcomes_per_1000_patients_(mean) | Additional_good_outcomes_per_1000_patients_(stdev) | Additional_good_outcomes_per_1000_patients_(95ci) | Onset_to_needle_(mean) | calibration | scenario | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
AGNOF1041H | 257.82 | 228.02 | 287.70 | 258.00 | 17.26 | 3.38 | 15.28 | 12.95 | 17.15 | 15.20 | ... | 0.28 | 12.78 | 10.72 | 14.81 | 12.76 | 1.27 | 0.25 | 163.02 | 1.0 | base |
AKCGO9726K | 259.84 | 241.38 | 286.26 | 260.74 | 14.08 | 2.76 | 14.79 | 13.29 | 16.89 | 14.91 | ... | 0.20 | 13.09 | 11.68 | 15.13 | 13.21 | 1.02 | 0.20 | 154.07 | 1.0 | base |
AOBTM3098N | 243.00 | 211.90 | 272.00 | 242.04 | 19.53 | 3.83 | 7.80 | 6.19 | 9.40 | 7.80 | ... | 0.22 | 5.61 | 4.27 | 7.34 | 5.67 | 0.98 | 0.19 | 183.15 | 1.0 | base |
APXEE8191H | 235.76 | 205.01 | 275.74 | 239.00 | 21.02 | 4.12 | 10.02 | 8.42 | 12.53 | 10.40 | ... | 0.26 | 7.46 | 5.89 | 9.53 | 7.59 | 1.20 | 0.24 | 180.86 | 1.0 | base |
ATDID5461S | 232.73 | 196.36 | 272.73 | 236.47 | 24.64 | 4.83 | 9.09 | 6.89 | 12.00 | 9.17 | ... | 0.33 | 6.12 | 4.62 | 8.61 | 6.28 | 1.20 | 0.24 | 188.62 | 1.0 | base |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
YPKYH1768F | 256.00 | 220.00 | 292.40 | 258.40 | 23.60 | 4.63 | 7.20 | 4.80 | 10.42 | 7.47 | ... | 0.34 | 4.98 | 3.26 | 7.24 | 4.98 | 1.25 | 0.24 | 206.28 | 1.0 | same_patient_characteristics |
YQMZV4284N | 256.98 | 220.39 | 293.44 | 256.98 | 22.35 | 4.38 | 16.20 | 12.53 | 18.72 | 15.92 | ... | 0.39 | 12.97 | 9.91 | 15.56 | 12.87 | 1.75 | 0.34 | 170.94 | 1.0 | same_patient_characteristics |
ZBVSO0975W | 258.35 | 229.40 | 289.76 | 258.08 | 17.57 | 3.44 | 7.80 | 5.35 | 10.02 | 7.66 | ... | 0.29 | 6.27 | 4.09 | 8.38 | 6.23 | 1.31 | 0.26 | 168.60 | 1.0 | same_patient_characteristics |
ZHCLE1578P | 256.28 | 228.64 | 281.60 | 256.71 | 16.16 | 3.17 | 10.87 | 9.05 | 12.33 | 10.76 | ... | 0.20 | 8.83 | 7.34 | 10.38 | 8.78 | 0.89 | 0.17 | 169.10 | 1.0 | same_patient_characteristics |
ZRRCV7012C | 256.28 | 229.48 | 286.43 | 257.04 | 16.69 | 3.27 | 11.22 | 9.36 | 13.07 | 11.19 | ... | 0.23 | 8.62 | 7.10 | 10.23 | 8.67 | 1.00 | 0.20 | 178.32 | 1.0 | same_patient_characteristics |
1188 rows × 21 columns
hospital_performance
thrombolysis_rate | admissions | 80_plus | onset_known | known_arrival_within_4hrs | onset_arrival_mins_mu | onset_arrival_mins_sigma | scan_within_4_hrs | arrival_scan_arrival_mins_mu | arrival_scan_arrival_mins_sigma | onset_scan_4_hrs | eligable | scan_needle_mins_mu | scan_needle_mins_sigma | cohort_rate | arrivals | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
AGNOF1041H | 0.154839 | 671.666667 | 0.423 | 0.635236 | 0.58 | 4.58 | 0.56 | 0.965596 | 1.665700 | 1.497966 | 0.935867 | 0.335671 | 3.669602 | 0.664462 | 0.2776 | 1000 |
AKCGO9726K | 0.158892 | 1143.333333 | 0.423 | 0.970845 | 0.58 | 4.58 | 0.56 | 0.955882 | 2.834183 | 0.999719 | 0.908425 | 0.452842 | 2.904479 | 0.874818 | 0.3745 | 1000 |
AOBTM3098N | 0.085885 | 500.666667 | 0.423 | 0.619174 | 0.58 | 4.58 | 0.56 | 0.935043 | 3.471419 | 1.254744 | 0.846435 | 0.314389 | 3.694918 | 0.518929 | 0.2600 | 1000 |
APXEE8191H | 0.098634 | 439.333333 | 0.423 | 0.716237 | 0.58 | 4.58 | 0.56 | 0.966899 | 3.312930 | 0.714465 | 0.904505 | 0.362394 | 3.585094 | 0.751204 | 0.2997 | 1000 |
ATDID5461S | 0.090689 | 275.666667 | 0.423 | 0.573156 | 0.58 | 4.58 | 0.56 | 0.878594 | 4.125690 | 0.549301 | 0.865455 | 0.313422 | 3.497262 | 0.608126 | 0.2592 | 1000 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
YPKYH1768F | 0.105193 | 250.333333 | 0.423 | 0.585885 | 0.58 | 4.58 | 0.56 | 0.952681 | 3.779215 | 0.872809 | 0.844371 | 0.269045 | 3.982100 | 0.683223 | 0.2225 | 1000 |
YQMZV4284N | 0.104186 | 358.333333 | 0.423 | 0.945116 | 0.58 | 4.58 | 0.56 | 0.948936 | 3.574735 | 0.912298 | 0.798206 | 0.361306 | 3.285165 | 0.463749 | 0.2988 | 1000 |
ZBVSO0975W | 0.081602 | 449.333333 | 0.423 | 0.465134 | 0.58 | 4.58 | 0.56 | 0.972222 | 2.860226 | 0.990966 | 0.930952 | 0.317170 | 3.606046 | 0.575788 | 0.2623 | 1000 |
ZHCLE1578P | 0.112647 | 796.000000 | 0.423 | 0.733668 | 0.58 | 4.58 | 0.56 | 0.949830 | 3.306916 | 0.842940 | 0.892569 | 0.291898 | 3.276043 | 0.795401 | 0.2414 | 1000 |
ZRRCV7012C | 0.063058 | 597.333333 | 0.423 | 0.779576 | 0.58 | 4.58 | 0.56 | 0.977305 | 3.743456 | 0.662710 | 0.851959 | 0.288755 | 3.261270 | 0.803624 | 0.2388 | 1000 |
132 rows × 16 columns
Save results#
results_all['stroke_team'] = results_all.index
results_all.to_csv('./output/scenario_results.csv', index=False)