processbehavior 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- processbehavior/__init__.py +133 -0
- processbehavior/analysis.py +2944 -0
- processbehavior/analysis_dataset.py +361 -0
- processbehavior/analysis_result.py +1276 -0
- processbehavior/calibration.py +70 -0
- processbehavior/capability.py +720 -0
- processbehavior/data_preparation.py +792 -0
- processbehavior/datasets/__init__.py +37 -0
- processbehavior/datasets/data/coffee_shop_demo_long.csv +385 -0
- processbehavior/datasets/loaders.py +66 -0
- processbehavior/datasets/synthetic.py +1711 -0
- processbehavior/derivations.py +602 -0
- processbehavior/effects_calculator.py +603 -0
- processbehavior/exceptions.py +216 -0
- processbehavior/exporters/__init__.py +5 -0
- processbehavior/exporters/excel_exporter.py +708 -0
- processbehavior/focused_result.py +329 -0
- processbehavior/formulation_spec.py +144 -0
- processbehavior/loss_function.py +517 -0
- processbehavior/maximum_information.py +285 -0
- processbehavior/plotting/__init__.py +46 -0
- processbehavior/plotting/_math.py +121 -0
- processbehavior/plotting/capability_chart.py +858 -0
- processbehavior/plotting/contracts.py +176 -0
- processbehavior/plotting/control_chart.py +206 -0
- processbehavior/plotting/effects_charts.py +640 -0
- processbehavior/plotting/lane_boundaries.py +78 -0
- processbehavior/plotting/limits.py +157 -0
- processbehavior/plotting/maximum_information_chart.py +395 -0
- processbehavior/plotting/plotter.py +1469 -0
- processbehavior/plotting/renderers.py +660 -0
- processbehavior/plotting/report.py +211 -0
- processbehavior/plotting/residuals.py +282 -0
- processbehavior/plotting/run_rules_viz.py +150 -0
- processbehavior/plotting/stats_box.py +183 -0
- processbehavior/plotting/themes.py +544 -0
- processbehavior/plotting/x_axis_layout.py +474 -0
- processbehavior/plotting/zones.py +127 -0
- processbehavior/process_behavior.py +1290 -0
- processbehavior/py.typed +0 -0
- processbehavior/residual_calculator.py +572 -0
- processbehavior/result_signals.py +149 -0
- processbehavior/result_tabular.py +296 -0
- processbehavior/sds_detector.py +1432 -0
- processbehavior/signals/__init__.py +14 -0
- processbehavior/signals/config.py +435 -0
- processbehavior/signals/detector.py +328 -0
- processbehavior/signals/detectors.py +368 -0
- processbehavior/signals/result.py +232 -0
- processbehavior/spc_constants.py +692 -0
- processbehavior/study.py +2733 -0
- processbehavior/types.py +154 -0
- processbehavior-0.2.0.dist-info/METADATA +279 -0
- processbehavior-0.2.0.dist-info/RECORD +56 -0
- processbehavior-0.2.0.dist-info/WHEEL +4 -0
- processbehavior-0.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Maximum Information Analysis — Bishop.
|
|
3
|
+
|
|
4
|
+
Examines R2 residuals (the noise floor / within-cell variation) via two views:
|
|
5
|
+
an XmR process behavior chart ("Is the noise floor stable?") and a percentage
|
|
6
|
+
histogram ("What does the noise floor distribution look like?").
|
|
7
|
+
|
|
8
|
+
Together they give the analyst full insight into irreducible variation.
|
|
9
|
+
|
|
10
|
+
References
|
|
11
|
+
----------
|
|
12
|
+
Bishop, T. *Variance Analysis System*.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import TYPE_CHECKING, Literal
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import pandas as pd
|
|
22
|
+
|
|
23
|
+
from .exceptions import ValidationError
|
|
24
|
+
from .spc_constants import R_UPPER_LIMIT_MULTIPLIER, XMR_LIMIT_MULTIPLIER
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from .analysis_dataset import AnalysisDataSet
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ============================================================================
|
|
31
|
+
# Data Class
|
|
32
|
+
# ============================================================================
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class MaximumInformationResult:
|
|
37
|
+
"""
|
|
38
|
+
Result of a Maximum Information Analysis on R2 residuals.
|
|
39
|
+
|
|
40
|
+
Attributes
|
|
41
|
+
----------
|
|
42
|
+
n : int
|
|
43
|
+
Number of R2 values.
|
|
44
|
+
r2_mean : float
|
|
45
|
+
Mean of R2 (~0).
|
|
46
|
+
r2_mR : float
|
|
47
|
+
Average moving range of R2.
|
|
48
|
+
sigma_hat : float
|
|
49
|
+
mR / d2 — noise floor sigma estimate.
|
|
50
|
+
upl : float
|
|
51
|
+
Upper natural process limit (R2 mean + 2.66 * mR).
|
|
52
|
+
lpl : float
|
|
53
|
+
Lower natural process limit (R2 mean - 2.66 * mR).
|
|
54
|
+
n_signals : int
|
|
55
|
+
Points beyond limits on XmR.
|
|
56
|
+
round_to : int
|
|
57
|
+
Decimal places for display.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
n: int
|
|
61
|
+
r2_mean: float
|
|
62
|
+
r2_mR: float
|
|
63
|
+
sigma_hat: float
|
|
64
|
+
upl: float
|
|
65
|
+
lpl: float
|
|
66
|
+
n_signals: int
|
|
67
|
+
|
|
68
|
+
# Internal — for plotting
|
|
69
|
+
_xmr_chart_info: dict
|
|
70
|
+
_histogram_chart_info: dict
|
|
71
|
+
_response_name: str
|
|
72
|
+
|
|
73
|
+
round_to: int = 3
|
|
74
|
+
|
|
75
|
+
# ------------------------------------------------------------------
|
|
76
|
+
# Visualization
|
|
77
|
+
# ------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
def plot(
|
|
80
|
+
self,
|
|
81
|
+
*,
|
|
82
|
+
view: Literal['combined', 'xmr', 'histogram'] = 'combined',
|
|
83
|
+
bins: int = 10,
|
|
84
|
+
theme=None,
|
|
85
|
+
width: int = 900,
|
|
86
|
+
height: int = 700,
|
|
87
|
+
title: str | None = None,
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
Plot the maximum information analysis.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
view : str, default ``'combined'``
|
|
95
|
+
``'combined'`` — XmR (top) + percentage histogram (bottom).
|
|
96
|
+
``'xmr'`` — XmR of R2 only.
|
|
97
|
+
``'histogram'`` — Percentage histogram only.
|
|
98
|
+
bins : int, default 10
|
|
99
|
+
Number of histogram bins.
|
|
100
|
+
theme : ChartTheme, optional
|
|
101
|
+
Visual theme (default: processbehavior theme).
|
|
102
|
+
width, height : int
|
|
103
|
+
Figure dimensions in pixels.
|
|
104
|
+
title : str, optional
|
|
105
|
+
Override chart title.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
plotly.graph_objects.Figure
|
|
110
|
+
"""
|
|
111
|
+
from .plotting.maximum_information_chart import create_maximum_information_chart
|
|
112
|
+
|
|
113
|
+
return create_maximum_information_chart(
|
|
114
|
+
self,
|
|
115
|
+
view=view,
|
|
116
|
+
bins=bins,
|
|
117
|
+
theme=theme,
|
|
118
|
+
width=width,
|
|
119
|
+
height=height,
|
|
120
|
+
title=title,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# ------------------------------------------------------------------
|
|
124
|
+
# Presentation helpers
|
|
125
|
+
# ------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
def as_dict(self, round_to: int | None = None) -> dict:
|
|
128
|
+
"""Return results as a dict with values rounded for display."""
|
|
129
|
+
r = round_to if round_to is not None else self.round_to
|
|
130
|
+
|
|
131
|
+
def _r(v: float) -> float:
|
|
132
|
+
if not np.isfinite(v):
|
|
133
|
+
return v
|
|
134
|
+
return round(v, r)
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
'n': self.n,
|
|
138
|
+
'r2_mean': _r(self.r2_mean),
|
|
139
|
+
'r2_mR': _r(self.r2_mR),
|
|
140
|
+
'sigma_hat': _r(self.sigma_hat),
|
|
141
|
+
'upl': _r(self.upl),
|
|
142
|
+
'lpl': _r(self.lpl),
|
|
143
|
+
'n_signals': self.n_signals,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def __repr__(self) -> str:
|
|
147
|
+
d = self.as_dict()
|
|
148
|
+
lines = [
|
|
149
|
+
'MaximumInformationResult:',
|
|
150
|
+
f' R2 residuals (noise floor): n={d["n"]}',
|
|
151
|
+
f' Mean={d["r2_mean"]}, mR={d["r2_mR"]}, sigma_hat={d["sigma_hat"]}',
|
|
152
|
+
f' NPL: [{d["lpl"]}, {d["upl"]}]',
|
|
153
|
+
f' Signals: {d["n_signals"]}',
|
|
154
|
+
]
|
|
155
|
+
return '\n'.join(lines)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ============================================================================
|
|
159
|
+
# Pure Function
|
|
160
|
+
# ============================================================================
|
|
161
|
+
|
|
162
|
+
# d2 for n=2 (moving range of consecutive pairs)
|
|
163
|
+
_D2 = 1.128
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def assess_maximum_information(
|
|
167
|
+
ads: AnalysisDataSet,
|
|
168
|
+
round_to: int = 3,
|
|
169
|
+
) -> MaximumInformationResult:
|
|
170
|
+
"""
|
|
171
|
+
Perform Maximum Information Analysis on R2 residuals.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
ads : AnalysisDataSet
|
|
176
|
+
Must have VAS residuals with R2 column.
|
|
177
|
+
round_to : int, default 3
|
|
178
|
+
Decimal places for presentation.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
MaximumInformationResult
|
|
183
|
+
|
|
184
|
+
Raises
|
|
185
|
+
------
|
|
186
|
+
ValidationError
|
|
187
|
+
If R2 residuals are not available.
|
|
188
|
+
"""
|
|
189
|
+
if not ads.has_vas_residuals or 'R2' not in ads.analysis_dataset.columns:
|
|
190
|
+
raise ValidationError(
|
|
191
|
+
'Maximum information analysis requires R2 residuals. '
|
|
192
|
+
'R2 is only available for designs with factors and time '
|
|
193
|
+
f'(current SDS: {ads.observed_design_state}).'
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
df = ads.analysis_dataset
|
|
197
|
+
r2_values = df['R2'].dropna().to_numpy(dtype=float)
|
|
198
|
+
n = len(r2_values)
|
|
199
|
+
|
|
200
|
+
if n < 2:
|
|
201
|
+
raise ValidationError(f'Maximum information analysis requires at least 2 R2 values, got {n}.')
|
|
202
|
+
|
|
203
|
+
# --- XmR statistics on R2 ---
|
|
204
|
+
r2_mean = float(np.mean(r2_values))
|
|
205
|
+
mr_values = np.abs(np.diff(r2_values))
|
|
206
|
+
r2_mR = float(np.mean(mr_values))
|
|
207
|
+
sigma_hat = r2_mR / _D2
|
|
208
|
+
|
|
209
|
+
# Limits: mean ± E2 * mR
|
|
210
|
+
upl = r2_mean + XMR_LIMIT_MULTIPLIER * r2_mR
|
|
211
|
+
lpl = r2_mean - XMR_LIMIT_MULTIPLIER * r2_mR
|
|
212
|
+
|
|
213
|
+
# Count signals (beyond limits)
|
|
214
|
+
beyond = (r2_values > upl) | (r2_values < lpl)
|
|
215
|
+
n_signals = int(np.sum(beyond))
|
|
216
|
+
|
|
217
|
+
# --- Build chart info dicts for renderers ---
|
|
218
|
+
|
|
219
|
+
# XmR chart info — matches the format render_control_chart expects via RenderContext
|
|
220
|
+
# We build a simplified dict for use by the maximum_information_chart module
|
|
221
|
+
mr_series = np.empty(n, dtype=float)
|
|
222
|
+
mr_series[0] = np.nan
|
|
223
|
+
mr_series[1:] = mr_values
|
|
224
|
+
|
|
225
|
+
# R chart limits
|
|
226
|
+
r_upl = R_UPPER_LIMIT_MULTIPLIER * r2_mR
|
|
227
|
+
|
|
228
|
+
xmr_data = pd.DataFrame(
|
|
229
|
+
{
|
|
230
|
+
'R2': r2_values,
|
|
231
|
+
'mr': mr_series,
|
|
232
|
+
'obs': np.arange(1, n + 1),
|
|
233
|
+
'beyond_limits': beyond.astype(int),
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Mark R beyond limits
|
|
238
|
+
r_beyond = np.zeros(n, dtype=int)
|
|
239
|
+
r_beyond[0] = 0 # first has NaN mR
|
|
240
|
+
r_beyond[1:] = (mr_values > r_upl).astype(int)
|
|
241
|
+
xmr_data['r_beyond_limits'] = r_beyond
|
|
242
|
+
|
|
243
|
+
xmr_chart_info = {
|
|
244
|
+
'data': xmr_data,
|
|
245
|
+
'statistics': {
|
|
246
|
+
'x_mean': r2_mean,
|
|
247
|
+
'x_upl': upl,
|
|
248
|
+
'x_lpl': lpl,
|
|
249
|
+
'mR': r2_mR,
|
|
250
|
+
'r_upl': r_upl,
|
|
251
|
+
},
|
|
252
|
+
'metadata': {
|
|
253
|
+
'value_col': 'R2',
|
|
254
|
+
'mr_col': 'mr',
|
|
255
|
+
'x_col': 'obs',
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# Histogram chart info
|
|
260
|
+
response_name = ads.spec.response_var
|
|
261
|
+
histogram_chart_info = {
|
|
262
|
+
'data': pd.DataFrame({'R2': r2_values}),
|
|
263
|
+
'statistics': {
|
|
264
|
+
'mean': r2_mean,
|
|
265
|
+
'n': n,
|
|
266
|
+
},
|
|
267
|
+
'metadata': {
|
|
268
|
+
'value_col': 'R2',
|
|
269
|
+
'chart_type': 'Histogram',
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return MaximumInformationResult(
|
|
274
|
+
n=n,
|
|
275
|
+
r2_mean=r2_mean,
|
|
276
|
+
r2_mR=r2_mR,
|
|
277
|
+
sigma_hat=sigma_hat,
|
|
278
|
+
upl=upl,
|
|
279
|
+
lpl=lpl,
|
|
280
|
+
n_signals=n_signals,
|
|
281
|
+
_xmr_chart_info=xmr_chart_info,
|
|
282
|
+
_histogram_chart_info=histogram_chart_info,
|
|
283
|
+
_response_name=response_name,
|
|
284
|
+
round_to=round_to,
|
|
285
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plotting framework for ProcessBehavior.
|
|
3
|
+
|
|
4
|
+
Provides an intuitive, extensible API for creating control charts
|
|
5
|
+
with built-in support for faceting, interactivity, and export.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .capability_chart import create_capability_chart
|
|
11
|
+
from .contracts import PlotError
|
|
12
|
+
from .control_chart import ControlChartFigure
|
|
13
|
+
from .effects_charts import (
|
|
14
|
+
create_factor_effects_chart,
|
|
15
|
+
create_factor_interaction_chart,
|
|
16
|
+
create_main_effects_chart,
|
|
17
|
+
create_time_effects_chart,
|
|
18
|
+
create_time_interaction_chart,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Redundant alias marks this as a deliberate re-export that is intentionally
|
|
22
|
+
# absent from __all__ — the convention both ruff and type checkers understand.
|
|
23
|
+
from .plotter import Plotter as Plotter
|
|
24
|
+
from .themes import ChartTheme, get_theme, list_themes, register_theme
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
'PlotError',
|
|
28
|
+
'ControlChartFigure',
|
|
29
|
+
'ChartTheme',
|
|
30
|
+
'get_theme',
|
|
31
|
+
'list_themes',
|
|
32
|
+
'register_theme',
|
|
33
|
+
'create_capability_chart',
|
|
34
|
+
'create_main_effects_chart',
|
|
35
|
+
'create_factor_effects_chart',
|
|
36
|
+
'create_time_effects_chart',
|
|
37
|
+
'create_time_interaction_chart',
|
|
38
|
+
'create_factor_interaction_chart',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# `Plotter` is imported above and stays importable for library internals
|
|
42
|
+
# (analysis_result, excel_exporter) and for anyone who reaches for it
|
|
43
|
+
# deliberately. It is deliberately absent from __all__: it was already labelled
|
|
44
|
+
# internal, and listing an internal class in the star-import surface is how it
|
|
45
|
+
# becomes a semver commitment by accident.
|
|
46
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Pure Python normal distribution functions (scipy replacement)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def normal_pdf(x: float, mu: float = 0.0, sigma: float = 1.0) -> float:
|
|
9
|
+
"""
|
|
10
|
+
Calculate normal probability density function.
|
|
11
|
+
|
|
12
|
+
Pure Python implementation to avoid scipy dependency.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
x : float
|
|
17
|
+
Point at which to evaluate the PDF
|
|
18
|
+
mu : float, default 0.0
|
|
19
|
+
Mean of the distribution
|
|
20
|
+
sigma : float, default 1.0
|
|
21
|
+
Standard deviation of the distribution
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
float
|
|
26
|
+
PDF value at x
|
|
27
|
+
"""
|
|
28
|
+
coefficient = 1.0 / (sigma * math.sqrt(2.0 * math.pi))
|
|
29
|
+
exponent = -0.5 * ((x - mu) / sigma) ** 2
|
|
30
|
+
return coefficient * math.exp(exponent)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def normal_ppf(p: float) -> float:
|
|
34
|
+
"""
|
|
35
|
+
Calculate normal percent point function (inverse CDF / quantile function).
|
|
36
|
+
|
|
37
|
+
Uses Acklam's algorithm for rational approximation to the
|
|
38
|
+
inverse cumulative normal distribution. Accurate to approximately
|
|
39
|
+
1.15e-9 in absolute error for p in (0, 1).
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
p : float
|
|
44
|
+
Probability value in (0, 1)
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
float
|
|
49
|
+
Quantile (z-score) corresponding to probability p
|
|
50
|
+
|
|
51
|
+
References
|
|
52
|
+
----------
|
|
53
|
+
Acklam, P. J. (2010). An algorithm for computing the inverse normal
|
|
54
|
+
cumulative distribution function.
|
|
55
|
+
https://web.archive.org/web/20151030215612/http://home.online.no/~pjacklam/notes/invnorm/
|
|
56
|
+
"""
|
|
57
|
+
# Coefficients for rational approximation
|
|
58
|
+
a = [
|
|
59
|
+
-3.969683028665376e01,
|
|
60
|
+
2.209460984245205e02,
|
|
61
|
+
-2.759285104469687e02,
|
|
62
|
+
1.383577518672690e02,
|
|
63
|
+
-3.066479806614716e01,
|
|
64
|
+
2.506628277459239e00,
|
|
65
|
+
]
|
|
66
|
+
b = [
|
|
67
|
+
-5.447609879822406e01,
|
|
68
|
+
1.615858368580409e02,
|
|
69
|
+
-1.556989798598866e02,
|
|
70
|
+
6.680131188771972e01,
|
|
71
|
+
-1.328068155288572e01,
|
|
72
|
+
]
|
|
73
|
+
c = [
|
|
74
|
+
-7.784894002430293e-03,
|
|
75
|
+
-3.223964580411365e-01,
|
|
76
|
+
-2.400758277161838e00,
|
|
77
|
+
-2.549732539343734e00,
|
|
78
|
+
4.374664141464968e00,
|
|
79
|
+
2.938163982698783e00,
|
|
80
|
+
]
|
|
81
|
+
d = [
|
|
82
|
+
7.784695709041462e-03,
|
|
83
|
+
3.224671290700398e-01,
|
|
84
|
+
2.445134137142996e00,
|
|
85
|
+
3.754408661907416e00,
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
# Define break-points
|
|
89
|
+
p_low = 0.02425
|
|
90
|
+
p_high = 1 - p_low
|
|
91
|
+
|
|
92
|
+
if p <= 0 or p >= 1:
|
|
93
|
+
if p == 0:
|
|
94
|
+
return float('-inf')
|
|
95
|
+
elif p == 1:
|
|
96
|
+
return float('inf')
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError(f'p must be in (0, 1), got {p}')
|
|
99
|
+
|
|
100
|
+
# Rational approximation for lower region
|
|
101
|
+
if p < p_low:
|
|
102
|
+
q = math.sqrt(-2 * math.log(p))
|
|
103
|
+
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / (
|
|
104
|
+
(((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Rational approximation for upper region
|
|
108
|
+
if p > p_high:
|
|
109
|
+
q = math.sqrt(-2 * math.log(1 - p))
|
|
110
|
+
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) / (
|
|
111
|
+
(((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Rational approximation for central region
|
|
115
|
+
q = p - 0.5
|
|
116
|
+
r = q * q
|
|
117
|
+
return (
|
|
118
|
+
(((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5])
|
|
119
|
+
* q
|
|
120
|
+
/ (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1)
|
|
121
|
+
)
|