dmsaylor commited on
Commit
f86b8d6
·
1 Parent(s): 3764c54

working version of equivalency module

Browse files
CHRIS.py CHANGED
@@ -1,7 +1,7 @@
1
  from flask import Flask, render_template
2
 
3
  app = Flask(__name__)
4
- app.debug = True
5
 
6
  from color_module import blueprint
7
  app.register_blueprint(blueprint)
 
1
  from flask import Flask, render_template
2
 
3
  app = Flask(__name__)
4
+ app.debug = False
5
 
6
  from color_module import blueprint
7
  app.register_blueprint(blueprint)
equivalency_module/equivalency.py CHANGED
@@ -5,42 +5,44 @@ from scipy.special import erf
5
  from scipy import integrate
6
  from lmfit import minimize, Parameters, printfuncs, fit_report
7
 
8
- def max_CDF(x, mu, sigma, N):
 
9
 
10
- return (0.5 * (1. + erf((x - mu) / sigma / sqrt(2)))) ** N
 
 
 
11
 
12
- def max_PDF(x, mu, sigma, N):
13
 
14
- return (1. + erf((x - mu) / sigma / sqrt(2))) ** (N - 1) * N * 2. ** (0.5 - N) * exp(
15
- -(-mu + x) ** 2. / (2. * sigma ** 2.)) / sqrt(pi) / sigma
16
-
17
- def max_PDFCDF(y, lamb, mu_x, mu_y, sigma_x, sigma_y, N):
18
-
19
- return max_PDF(y, mu_y, sigma_y, N) * max_CDF(y * lamb, mu_x, sigma_x, N)
20
-
21
- def alpha_residual(params, prob, sigma, N):
22
 
23
  alpha = params['alpha'].value
24
- tmp, err = integrate.quad(max_PDFCDF, -inf, inf, args=(alpha, 1.0, 1., sigma, sigma, N))
 
25
 
26
- return prob - (1. - tmp)
27
 
28
- def beta_residual(params, prob, alpha, sigma, N):
29
 
30
  beta = params['beta'].value
31
- tmp, err = integrate.quad(max_PDFCDF, -inf, inf, args=(alpha, beta, 1., beta * sigma, sigma, N))
 
 
 
32
 
33
- return prob - tmp
34
 
35
- def FitParams(FPR, FNR, RSD, N):
36
 
 
37
  alpha_params = Parameters()
38
  alpha_params.add('alpha', value=1.)
39
- alpha_out = minimize(alpha_residual, alpha_params, args=(FPR/100., RSD, N))
40
 
41
  beta_params = Parameters()
42
- beta_params.add('beta', value=4.)
43
- beta_out = minimize(beta_residual, beta_params, args=(FNR/100., alpha_out.params['alpha'].value, RSD, N))
44
 
45
  return alpha_out.params['alpha'].value, beta_out.params['beta'].value
46
 
@@ -52,18 +54,27 @@ def equivalency():
52
 
53
  alpha = 0.
54
  beta = 0.
55
- RSD = 0.2
56
- N = 3
57
- FPR = 0.3
58
- FNR = 0.3
 
 
 
 
59
 
60
  if request.method == 'POST':
61
- RSD = float(request.form["RSD"])
62
- N = int(request.form["N"])
 
 
 
 
63
  FPR = float(request.form["FPR"])
64
  FNR = float(request.form["FNR"])
65
 
66
- alpha, beta = FitParams(FPR, FNR, RSD, N)
67
 
68
 
69
- return render_template('equivalency_index.html', RSD=RSD, N=N, FPR=FPR, FNR=FNR, alpha=alpha, beta=beta)
 
 
5
  from scipy import integrate
6
  from lmfit import minimize, Parameters, printfuncs, fit_report
7
 
8
+ def MeanIntegrad(y, RSD, BN, CN, alpha, beta):
9
+ result = exp(-BN * (1. - y) ** 2. / 2. / RSD ** 2.) * (1. + erf(sqrt(CN) * (alpha * y - beta) / sqrt(2) / RSD / beta))
10
 
11
+ return result
12
+ def Means(RSD, BN, CN, alpha, beta):
13
+ tmp, err = integrate.quad(MeanIntegrad, -inf, inf, args=(RSD, BN, CN, alpha, beta))
14
+ prob = 1. - tmp * sqrt(BN) / sqrt(2. * pi) / RSD / 2.
15
 
16
+ return prob
17
 
18
+ def FPR_alpha_residual(params, prob, RSD, BN, CN, beta):
 
 
 
 
 
 
 
19
 
20
  alpha = params['alpha'].value
21
+ tmp = Means(RSD, BN, CN, alpha, beta)
22
+ FPR = tmp
23
 
24
+ return prob - FPR
25
 
26
+ def FNR_beta_residual(params, prob, RSD, BN, CN, alpha):
27
 
28
  beta = params['beta'].value
29
+ tmp = Means(RSD, BN, CN, alpha, beta)
30
+ FNR = 1. - tmp
31
+
32
+ return prob - FNR
33
 
34
+ def FitParams(FPR, FNR, Bmean, Bstd, BN, Cmean, Cstd, CN):
35
 
36
+ RSD = max(Bstd/Bmean,Cstd/Cmean)
37
 
38
+ beta = 1.
39
  alpha_params = Parameters()
40
  alpha_params.add('alpha', value=1.)
41
+ alpha_out = minimize(FPR_alpha_residual, alpha_params, args=(FPR/100., RSD, BN, CN, beta))
42
 
43
  beta_params = Parameters()
44
+ beta_params.add('beta', value=2.)
45
+ beta_out = minimize(FNR_beta_residual, beta_params, args=(FNR/100., RSD, BN, CN, alpha_out.params['alpha'].value))
46
 
47
  return alpha_out.params['alpha'].value, beta_out.params['beta'].value
48
 
 
54
 
55
  alpha = 0.
56
  beta = 0.
57
+ Bmean = 100.
58
+ Cmean = 100.
59
+ Bstd = 20.
60
+ Cstd = 20.
61
+ BN = 3
62
+ CN = 3
63
+ FPR = 5.0
64
+ FNR = 5.0
65
 
66
  if request.method == 'POST':
67
+ Bmean = float(request.form["Bmean"])
68
+ Cmean = float(request.form["Cmean"])
69
+ Bstd = float(request.form["Bstd"])
70
+ Cstd = float(request.form["Cstd"])
71
+ BN = int(request.form["BN"])
72
+ CN = int(request.form["CN"])
73
  FPR = float(request.form["FPR"])
74
  FNR = float(request.form["FNR"])
75
 
76
+ alpha, beta = FitParams(FPR, FNR, Bmean, Bstd, BN, Cmean, Cstd, CN)
77
 
78
 
79
+ return render_template('equivalency_index.html', Bmean=Bmean, Cmean=Cmean, Bstd=Bstd, Cstd=Cstd, \
80
+ BN=BN, CN=CN, FPR=FPR, FNR=FNR, alpha=alpha, beta=beta)
equivalency_module/templates/equivalency_index.html CHANGED
@@ -8,13 +8,6 @@
8
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
9
  <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
10
 
11
- <style>
12
- table, th, td {
13
- border: 1px solid black;
14
- border-collapse: collapse;
15
- }
16
- </style>
17
-
18
  </head>
19
 
20
  <img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
@@ -37,19 +30,30 @@ table, th, td {
37
 
38
  <form method="POST" id="theForm" align="left">
39
 
40
- RSD: <input name="RSD" id="RSD" value="{{RSD}}" step="any" min="0.01" type="number" required> <br>
41
- N: <input name="N" id="N" value="{{N}}" step="1" min="1" type="number" onchange="this.value = parseInt(this.value);" required> <br>
 
 
 
 
 
 
 
42
  FPR (%): <input name="FPR" id="FPR" value="{{FPR}}" step="any" min="0.01" type="number" required> <br>
43
- FNR (%): <input name="FNR" id="FNR" value="{{FNR}}" step="any" min="0.01" type="number" required> <br> <br>
44
 
45
- Acceptance criterion factor:
46
  {% if alpha != 0 %}
47
- {{'%0.4f'%alpha|float}}
48
- {% endif %} <br>
49
- Minimum MOS:
50
- {% if beta != 0 %}
51
- {{'%0.4f'%beta|float}}
52
- {% endif %} <br>
 
 
 
 
 
53
 
54
  <br> <button type="submit">Calculate</button>
55
 
 
8
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
9
  <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles.css') }}">
10
 
 
 
 
 
 
 
 
11
  </head>
12
 
13
  <img src="{{ url_for('static',filename='images/FDAlogo.png') }}" style="float: left;" height="100"/>
 
30
 
31
  <form method="POST" id="theForm" align="left">
32
 
33
+ <h3> Baseline </h3>
34
+ Mean: <input name="Bmean" id="Bmean" value="{{Bmean}}" step="any" min="0.01" type="number" required> <br>
35
+ Stdev: <input name="Bstd" id="Bstd" value="{{Bstd}}" step="any" min="0.01" type="number" required> <br>
36
+ N: <input name="BN" id="BN" value="{{BN}}" step="1" min="1" type="number" onchange="this.value = parseInt(this.value);" required> <br>
37
+ <h3> Comparator </h3>
38
+ Mean: <input name="Cmean" id="Cmean" value="{{Cmean}}" step="any" min="0.01" type="number" required> <br>
39
+ Stdev: <input name="Cstd" id="Cstd" value="{{Cstd}}" step="any" min="0.01" type="number" required> <br>
40
+ N: <input name="CN" id="CN" value="{{CN}}" step="1" min="1" type="number" onchange="this.value = parseInt(this.value);" required> <br>
41
+ <h3> Statistical limits </h3>
42
  FPR (%): <input name="FPR" id="FPR" value="{{FPR}}" step="any" min="0.01" type="number" required> <br>
43
+ FNR (%): <input name="FNR" id="FNR" value="{{FNR}}" step="any" min="0.01" type="number" required> <br>
44
 
 
45
  {% if alpha != 0 %}
46
+ <h3> Result: </h3>
47
+ {% set accept = alpha*Bmean %}
48
+ The acceptance criterion factor is {{'%0.2f'%alpha|float}}. Therefore, your mean comparator results must be less than
49
+ {{'%0.2f'%accept|float}}. <br>
50
+ {% if accept >= Cmean %}
51
+ The acceptance criteria has been met. However, this only applies if the analyte of interest has a margin of safety (MOS)
52
+ greater than {{'%0.1f'%beta|float}}. <br>
53
+ {% else %}
54
+ The acceptance criteria has not been met. Additional justification and/or analyses will be required. <br>
55
+ {% endif %}
56
+ {% endif %}
57
 
58
  <br> <button type="submit">Calculate</button>
59