from flask import Flask, request, url_for from scipy.optimize import bisect import math import numpy as np app = Flask(__name__) app.secret_key = 'Whatever' @app.route('/') def index(): return """A Bayesian calculator for Down Syndrome odds in twin pregnancies.""" % (url_for('twin_calculator_input'),) def mathjax(): return """""" def introduction(): return """ This calculator might be of interest to you if:
Computing the probability of both your twins having Down Syndrome (idealized model)
Because the blood tests cannot distinguish between the two foetuses, the results are difficult to interpret. Yet they tend to be communicated in the same manner as results for single foetus pregnancies - which I personally find confusing. What is intended here is a calculation to estimate the probability of giving birth to zero, one or two Down Syndrome children in a twin pregnancy after a positive blood test for Down Syndrome has been received. There are many caveats, but let's proceed... """ def input_form(): return """
User inputs
""" % (url_for('twin_calculator_output'),) @app.route('/twins_calculator_input') def twin_calculator_input(): return mathjax() + introduction() + input_form() def priorOdds( age ): # Taken from http://downsyndrome.about.com/od/diagnosingdownsyndrome/a/Matagechart.htm table = { 20:1667, 21:1429,22:1429, 23:1429, 24:1250, 25:1250, 26:1176,27:1111, 28:1053, 29:1000, 30: 952, 31:909, 32:769, 33:625, 34: 500, 35: 385, 36:294, 37:227, 38:175, 39:137, 40: 106, 41:82, 43:50, 44:38, 45:30, 46: 23, 47:18, 48:18, 49:11 } return table[age] muNormal = 0.0 muDown = 1.0 def implyMarker( odds_ratio ): """ Given odds of n:1 , interpret as a marker measurement """ def markerRatio(x ): return math.exp( -(x-muDown)*(x-muDown)/2. ) / math.exp( -(x-muNormal)*(x-muNormal)/2. ) def markerRatioError( x ): return markerRatio(x) - odds_ratio a = muNormal - 10.0 b = muDown + 10.0 marker = bisect( markerRatioError, a, b) return marker @app.route('/twins_calculator_output', methods = ['POST']) def twin_calculator_output(): # Interpret the odds ratio supplied as a lab result age = int(request.form["age"]) odds_down = float(request.form["odds_down"]) odds_prior = priorOdds( age ) odds_ratio = odds_prior / (2*odds_down) prob_identical = float(request.form["prob_identical"]) marker = implyMarker( odds_ratio ) # Posterior probabilities for the four possibilities prob_down = 1/(2*odds_down) pDD = prob_identical*prob_down + (1-prob_identical)*prob_down*prob_down pDU = (1-prob_identical)*prob_down*(1-prob_down) pUD = (1-prob_identical)*prob_down*(1-prob_down) pUU = (1-prob_identical)*(1-prob_down)*(1-prob_down) + prob_identical*(1-prob_down) probs = [ pDD, pDU, pUD, pUU ] markers = [ (muDown,muDown), (muDown,muNormal), (muNormal,muDown), (muNormal,muNormal) ] posteriors = list() for (marker1, marker2),prior in zip( markers, probs ): meanMarker = 0.5*marker1 + 0.5*marker2 prob = math.exp( -( marker-(meanMarker )*( marker-meanMarker )/2. ) ) posterior = prior*prob posteriors.append( posterior ) p0 = posteriors[3] / np.sum( posteriors ) p1 = (posteriors[1]+posteriors[2]) / np.sum( posteriors ) p2 = posteriors[0] / np.sum( posteriors ) return """ ResultsBased on an age of %s the prior odds of Down Syndrome are %s:1.
So the updated odds of %s:1 might (I presume) be interpreted as odds of roughly %s:1 for each foetus and thus as an odds ratio of %s and, furthermore, as a marker measurement of %s in standardized terms.
However, assuming a fifty/fifty mix in the blood test (i.e. half coming from each foetus, which is admittedly quite an assumption) the twin probabilities may actually be as follows.
| Outcome | Probability |
|---|---|
| Neither has Down Syndrome | %s percent |
| One has Down Syndrome | %s percent |
| Both have Down Syndrome | 1 in %s |
These probabilites should be taken with a grain of salt. There are many assumptions, including a prior probability of identical twins equal to %s percent. Perhaps the biggest simplification, however, is that the test result is from a single marker. In reality it is from multiple markers.
As a final caveat, I may well have misinterpreted the odds ratio supplied to patients who have twins. Here I have assumed that the same scale is used for single pregnancies in converting from marker levels to an odds ratio. I'd be happy to be corrected on that one.
Please provide feedback at github where the code for this little app is posted. """ % ( age, odds_prior, odds_down, 2*odds_down, int(10*odds_ratio)/10., int(100*marker)/100., int(p0*1000)/10., int(p1*1000)/10., int(1/p2), int(prob_identical*100) )