Extrapolating a known odds ratio back to time zero#
This document shows how to extrapolate a probability back to time zero using a reference data point. The main example uses a probability at time one hour from the Emberson meta-analysis.
The important results from this example are:
Occlusion type |
mRS<=1 Probability at time=0 |
---|---|
nLVO |
0.6416 |
LVO |
0.2329 |
Plain English summary#
We want to find information about the modified Rankin Scale (mRS) distribution for a population of patients treated with intravenous thrombolysis (IVT) at zero minutes after their stroke began. This data does not exist in real life because realistically nobody can be treated that quickly. Instead we have to use the available data and fit a model to it to find a formula for changing probability with time. Then we can plug time zero into this formula to find the probability at time zero.
The available data is from a meta-analysis of thrombolysis by Emberson et al. 2014. They provide:
The odds ratio of a good outcome (mRS
1) with thrombolysis at a treatment time of one hour.The odds ratio of a good outcome (mRS
1) with thrombolysis at the time of no effect, 6.3 hours.The fact that plotting log-odds against time gives a straight line graph.
We can take their data at times of 1 hour and 6.3 hours and recreate the straight-line fit to these points. By continuing to draw the straight line back to time zero, we can define the odds ratio of a good outcome at that time.
We then combine that result with the mRS probability distribution that we previously derived for thrombolysis at the time-of-no-effect TO DO: add link. This allows us to define a probability of a good outcome for treatment at time zero.
Limitations#
The exact results from this example are only valid when mRS
Method#
To set up the extrapolation generally, we will do the following steps:
Gather the odds ratio with time data from Emberson et al. 2014
Recreate the straight line fit in the form
Define the log(odds ratio) at time zero
Convert this to a formula for probability at time zero
To convert the new log(odds ratio) to probability at our chosen start time of zero hours, we have to provide an already-known probability at the time-of-no-effect.
Notebook admin#
# Keep notebook cleaner once finalised
import warnings
warnings.filterwarnings('ignore')
# Import packages
import numpy as np
import matplotlib.pyplot as plt
# Set up MatPlotLib
%matplotlib inline
# Change default colour scheme:
plt.style.use('seaborn-colorblind')
Gather data#
Weโre mainly using the plot of odds ratio with time from Emberson et al. 2014 (their Figure 1):
n.b. The y-axis is definitely โodds ratioโ. The dark blue line was calculated using โlog(odds ratio)โ and then transformed to โodds ratioโ for the graph.
We can define a few points from this figure:
The time-of-no-effect
(marked with white square).The odds ratio
(marked with white square).The odds ratio
(intersection of the main dark blue line with the y-axis).
t_ne = 6.3 # hours
OR_mRSleq1_tne = 1.0
OR_mRSleq1_t1 = 1.9
Plot the available data#
Weโll plot the data in terms of log(odds ratio) and draw a straight line connecting the points. This will match the data behind the main dark blue line in Figure 1 from Emberson et al. 2014.
Define x and y coordinates for the t=1hr and t=t_ne points:
x_t1 = 1 # hours
y_t1 = np.log(OR_mRSleq1_t1) # from the data
x_tne = t_ne # hours
y_tne = 0 # by definition, log(1)=0
Define some coordinates of the straight line connecting these two points:
# Find the step from the t=1 point to the t_ne point:
x_step = x_tne - x_t1
y_step = y_tne - y_t1
# Starting from the t=1hr point, step forwards and backwards in t:
x_line = [x_t1 - x_step, x_t1 + x_step]
y_line = [y_t1 - y_step, y_t1 + y_step]
Plot the data:
# Mark these two data points:
plt.scatter(x_t1, y_t1, label='Patient treated at 6.3hr', marker='D')
plt.scatter(x_tne, y_tne, label='Patient treated at 1hr', marker='o')
# Draw a straight line passing through the two points:
plt.plot(x_line, y_line, linestyle=':',color='k')
# แปther plot setup:
plt.ylabel('log(odds ratio)')
plt.xlabel('Onset to treatment time (hours)')
plt.xlim(0, 7)
plt.ylim(-0.02, 0.8)
plt.legend()
plt.title('log(odds ratio)')
plt.show()

Define a straight line model for log(odds ratio)#
Here, we want to define log(OR) for mRS
At
Then for a generic
At
This contains only known values and so we can calculate
Note that the units of
and must match here. In the equations above, we have used hours. If we had to use minutes instead, we would use: $ $
Extrapolate to t=0#
Now that weโve defined
At
Calculate
a = (
np.log(OR_mRSleq1_t1) /
(1.0 - (1.0/t_ne))
)
a
0.7629583929973748
Plot the new value#
We can compare the newly-calculated log(OR) value with the plot from before:
# Define x and y coordinates of the t=0 point:
x_t0 = 0
y_t0 = a
# Mark the data points:
plt.scatter(x_t1, y_t1, label='Patient treated at 6.3hr', marker='D')
plt.scatter(x_tne, y_tne, label='Patient treated at 1hr', marker='o')
plt.scatter(x_t0, y_t0, label='Extrapolated point', marker='s')
# Draw a straight line connecting the points:
plt.plot(x_line, y_line, linestyle=':', color='k')
# แปther plot setup:
plt.ylabel('log(odds ratio)')
plt.xlabel('Onset to treatment time (hours)')
plt.xlim(-0.1, 7)
plt.ylim(-0.02, 0.8)
plt.legend()
plt.title('log(odds ratio)')
plt.savefig('./images/extrapolate-emberson-fig1.jpg', dpi=300, bbox_inches='tight')
plt.show()

Convert this log(OR) to probability#
We can use the new value of log(odds ratio) at
By definition, the odds ratio:
Filling in the unknowns and rearranging:
To rearrange this equation to find probability, weโll set
This is ugly to write out in full, so we define
Calculate the probability at time zero#
We can take probabilities of mRS<=1 at the time-of-no-effect from the derived mRS distribution. TO DO - put link here
# Probability of mRS<=1 at t=t_ne (a.k.a. probability P_R):
p_mRSleq1_tne_nlvo = 0.455
p_mRSleq1_tne_lvo = 0.124
For each probability, we calculate
R = np.exp(a) * p_mRSleq1_tne_nlvo / (1 - p_mRSleq1_tne_nlvo)
p_mRSleq1_t0_nlvo = R / (1 + R)
R = np.exp(a) * p_mRSleq1_tne_lvo / (1 - p_mRSleq1_tne_lvo)
p_mRSleq1_t0_lvo = R / (1 + R)
Results#
print(f'nLVO mRS<=1 Probability at time=0: {p_mRSleq1_t0_nlvo:7.4f}')
print(f'LVO mRS<=1 Probability at time=0: {p_mRSleq1_t0_lvo:7.4f}')
nLVO mRS<=1 Probability at time=0: 0.6416
LVO mRS<=1 Probability at time=0: 0.2329
General extrapolation#
The previous sections gave results that were specific to the data from Emberson et al. 2014. The following sections redo the formulae for any general case.
This section isnโt essential for following the examples above.
In the current example we have chosen the reference data points at times
For
the time
, andodds ratio
.
For
the time
,odds ratio
, andprobability
for the conversion.
This is most convenient for
Important: Make sure the two odds ratios are defined in the same way. Are both using the same baseline at
?
Find #
To find
We can combine these by isolating
And then rearrange to find
To put this into the same form as Equation 3, the
When
Find #
It is straightforward to find
By definition, the gradient
So for our straight line of
General function#
We have combined the steps above into a function extrapolate_odds_ratio()
that is stored in the outcome_utilities
module. The function takes the known times, odds ratios and probabilities, and extrapolates the straight line of log(odds ratio) to a given time
from stroke_outcome.outcome_utilities import extrapolate_odds_ratio
extrapolate_odds_ratio??
Signature:
extrapolate_odds_ratio(
t_1: float,
OR_1: float,
t_2: float,
OR_2: float,
p_2: float,
t_e: float = 0,
)
Source:
def extrapolate_odds_ratio(
t_1: float,
OR_1: float,
t_2: float,
OR_2: float,
p_2: float,
t_e: float = 0
):
"""
Use two odds ratios to extrapolate the straight line fit and find
the odds ratio at a given time, then convert to probability.
The three time parameters MUST use the same units, e.g. hours.
Inputs:
t_1, t_2 - float. Times for data points 1 and 2.
OR_1, OR_2 - float. Odds ratios at times t_1 and t_2.
p_2 - float. Probability at time t_2.
t_e - float. Time to extrapolate the line to.
Returns:
OR_e - float. Extrapolated odds ratio at time t_e.
p_e - float. Extrapolated probability at time t_e.
a, b - float. Constants for the straight line fit a+bt.
"""
# Calculate "a", the log(odds ratio) at time t=0:
a = (
(np.log(OR_1) - (t_1/t_2)*np.log(OR_2)) /
(1.0 - (t_1/t_2))
)
# Calculate "b", the gradient of the log(odds ratio) straight line.
b = (np.log(OR_2) - np.log(OR_1)) / (t_2 - t_1)
# Use these to calculate the odds ratio at time t_e:
OR_e = np.exp(a + b * t_e)
# Rearrange odds ratio formula:
# ORe = {pe/(1-pe)} / {p2/(1-p2)}
# pe/(1-pe) = ORe * p2/(1-p2)
# Calculate R, the right-hand-side of this equation:
R = OR_e * p_2 / (1 - p_2)
# Rearrange pe/(1-pe)=R to find pe, probability at time t=t_e:
p_e = R / (1 + R)
return OR_e, p_e, a, b
File: ~/miniconda3/lib/python3.9/site-packages/stroke_outcome/outcome_utilities.py
Type: function
Check that this gives the same results as we found earlier in the notebook for the nLVO data:
OR_0, p_0, a_0, b_0 = extrapolate_odds_ratio(
t_1=1, OR_1=1.9, # t=1hr data
t_2=t_ne, OR_2=1, p_2=p_mRSleq1_tne_nlvo, # t=t_ne data
t_e=0 # Extrapolate to this time.
)
Define the gradient of the straight line we plotted initially, before calculating
gradient = y_step / x_step
print(' | From function | From notebook |')
print(f'P | {p_0:7.4f} | {p_mRSleq1_t0_nlvo:7.4f} |')
print(f'a | {a_0:7.4f} | {a:7.4f} |')
print(f'b | {b_0:7.4f} | {gradient:7.4f} |')
| From function | From notebook |
P | 0.6416 | 0.6416 |
a | 0.7630 | 0.7630 |
b | -0.1211 | -0.1211 |
References#
Emberson J, Lees KR, Lyden P, et al. Effect of treatment delay, age, and stroke severity on the effects of intravenous thrombolysis with alteplase for acute ischaemic stroke: A meta-analysis of individual patient data from randomised trials. The Lancet 2014;384:1929โ35. doi:10.1016/S0140-6736(14)60584-5