Degradation Analysis
Models for measurements of a unit’s condition over time – wear, crack
length, capacity loss – from which the time to failure is inferred as
the time the degradation crosses a threshold. Import everything on this
page from surpyval.degradation. There are three approaches: a
path model fitted to each unit’s measurements (DegradationAnalysis,
which returns a DegradationModel); a stochastic process (the
Wiener and gamma processes); and destructive degradation, where each
unit is measured once (DestructiveDegradation). The theory is in
Degradation Analysis and worked examples are in
Degradation Modelling with SurPyval.
Degradation Analysis Fitter
DegradationAnalysis is an instance of the class below.
- class surpyval.degradation.degradation_analysis.DegradationAnalysis_
Bases:
objectPseudo-failure-time degradation analysis.
Fits a degradation path model to each unit’s measurements, extrapolates each fitted path to the failure
thresholdto obtain per-unit pseudo failure times, and fits a lifetime distribution to those times. Units whose fitted path never reaches the threshold at a positive finite time are right censored at their last observed time (with a warning); units already past the threshold at their first measurement – the path crossed at or before time zero – have failed by then and are left censored at their first measurement time (with a warning). The side of the threshold that counts as failed is read from the units whose paths cross it.Examples
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> # 4 units measured every 100 hours; degradation grows linearly >>> # at a different rate per unit; failure is defined at level 150. >>> x = np.tile(np.arange(100, 1100, 100), 4) >>> slopes = np.repeat([0.31, 0.28, 0.44, 0.37], 10) >>> i = np.repeat([1, 2, 3, 4], 10) >>> y = 10 + slopes * x >>> model = DegradationAnalysis.fit(x, y, i, threshold=150) >>> print(model) Degradation Analysis SurPyval Model =================================== Path Model : Linear Threshold : 150.0 Number of Units : 4 Censored Units : 0 Life Distribution : Weibull Parameters : alpha: 441.47809611105606 beta: 6.987078889297555 >>> model.pseudo_failure_times array([451.61290323, 500. , 318.18181818, 378.37837838])
- fit(x: ArrayLike, y: ArrayLike, i: ArrayLike, threshold: float, path: str | ~surpyval.degradation.path_models.PathModel = 'linear', distribution: ~typing.Any = <surpyval.univariate.parametric.distributions.weibull.Weibull_ object>, how: str = 'MLE', population_method: str = 'moments', Z: ArrayLike | None = None, links: dict[str, str] | None = None, acceleration: str | None = None, stress_ref: ArrayLike | None = None) DegradationModel
Fit a degradation analysis model.
- Parameters:
x (array like) – Times at which the degradation measurements were taken.
y (array like) – The degradation measurements.
i (array like) – The unit each measurement belongs to. Must have the same length as
xandy.threshold (float) – The degradation level at which a unit is defined to have failed.
path (str or PathModel, optional) – The degradation path model fitted to each unit: one of
"linear"(default),"quadratic","exponential","offset-exponential","power","logarithmic","lloyd-lipow","gompertz","michaelis-menten", aPathModelinstance, or"best"to fit every registered model to all units and select the one with the smallest AICc (the per-candidate scores are exposed aspath_selectionon the returned model; candidates that cannot be fitted to every unit are excluded).distribution (ParametricFitter, optional) – The lifetime distribution fitted to the pseudo failure times. Defaults to
Weibull.how (str, optional) – The method used to fit the lifetime distribution (passed to
distribution.fit). Defaults to"MLE".population_method (str, optional) – How the population path-parameter distribution (
path_param_mean,path_param_cov,measurement_var) is estimated."moments"(default) uses the two-stage noise-corrected sample moments;"reml"maximises the restricted marginal likelihood of the mixed model, which never needs clipping and is preferable with few units; both warn when the estimated covariance is singular (on the boundary: a variance of zero or a correlation of +-1). Linear-in-parameter paths (linear, quadratic, logarithmic, lloyd-lipow) are fitted as an exact linear mixed model; nonlinear paths (exponential, power, gompertz, …) are fitted by the Lindstrom-Bates FOCE linearisation. Either way REML requires measurement noise (some unit with more measurements than path parameters).Z (array like, optional) – Stress covariates for accelerated degradation testing (ADT). When given, the life model is fitted as a regression on the pseudo failure times –
log(pseudo failure time) = f(Z) + noise– so that life can be predicted at any stress.Zis aligned tox/y/i(one row per measurement) and must be constant within each unit (a unit is tested at a single stress); it is reduced to one covariate row per unit. Ifdistributionis already a regression fitter (e.g.AFT(Weibull),WeibullPH,CoxPH) it is used directly; a plain distribution (e.g.Weibull) is wrapped in an accelerated failure time model,AFT(distribution). The returned model’s prediction methods (sf,ff,qf,random…) then take the stress vectorZat which to evaluate life.links (dict, optional) – Model the degradation mechanism against stress as well (requires
Z): the path parameters named here depend on the unit’s stress, the rest do not. Each value is the link the parameter is modelled on –"identity"(the parameter is linear inZ) or"log"(its log is linear inZ, so a rate withZ = 1/Tfollows an Arrhenius relationship and the parameter stays positive). Per unit,eta_i = D(z_i) gamma + u_ion the link scale with a between-unit random effectu_i ~ MVN(0, Sigma);gammaandSigmaare estimated by the same two-stage or REML route as the plain population, and stored aspath_param_fixed(labelled bypath_param_fixed_names) andpath_param_link_cov. The life model is still the covariate regression on the pseudo failure times, so every prediction method works as withoutlinks. For examplelinks={"b": "log"}with the linear path lets the degradation ratebaccelerate log-linearly with stress while the intercepta(the initial state) is common.acceleration ({None, "clock"}, optional) –
"clock"models stress as speeding up the clock of every unit’s path, which allowsZto change during a unit’s test (a step-stress test) as well as between units. A unit at stresszagesAF(z) = exp(gamma' (z - stress_ref))times faster than at the reference stress, and its path is the path model evaluated on the reference-stress time it has aged,tau(t) = integral of AF(z(s)) ds.Zis then one row per measurement giving the stress applied over the interval that ends at that measurement (the first interval starts at time zero, so times must be non-negative). The path parameters, their population and the pseudo failure times are all on the reference-stress clock;distributionis fitted to those reference-stress lifetimes, and the prediction methods take the stress asZ(one stress row or aStepSchedule) to give life under any stress history,F(t) = F0(tau(t)). The stress coefficients are stored asgamma. Withpopulation_method="moments"they are estimated by profile least squares, which needs units whose stress changes during the test (a unit held at one stress can absorb any acceleration into its own path parameters); with"reml"by the mixed model, which also uses the differences between units at different stresses. Cannot be combined withlinksorpath="best".stress_ref (array like, optional) – The reference stress for
acceleration="clock"(usually the use condition), one row. Defaults to the mean stress over the measurement intervals.
- Returns:
The fitted degradation model, with the per-unit paths, pseudo failure times, and the fitted life model.
- Return type:
- fit_from_df(df: DataFrame, x_col: str = 'x', y_col: str = 'y', i_col: str = 'i', Z_cols: str | list[str] | None = None, **fit_kwargs: Any) DegradationModel
Fit a degradation analysis model from a DataFrame.
The column arguments end in
_col(_colsfor a list), as in everyfit_from_df(principle 21); their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23.- Parameters:
df (DataFrame) – DataFrame with the degradation data.
x_col (str, optional) – Column of the measurement times. Defaults to
"x".y_col (str, optional) – Column of the degradation measurements. Defaults to
"y".i_col (str, optional) – Column of the unit identifiers. Defaults to
"i".Z_cols (str or list of str, optional) – Column(s) of the stress covariates for accelerated degradation testing. When given, the selected columns are passed as
Ztofit(), fitting a covariate (ADT) life model. Their names are recorded on the model asZ_cols(and kept byto_dict), so every method that takesZalso takes a DataFrame and selects these columns by name.**fit_kwargs – Remaining arguments passed to
fit():threshold(required), and optionallypath,distribution,how,population_method,links,accelerationandstress_ref.
- Returns:
The fitted degradation model.
- Return type:
Degradation Model
- class surpyval.degradation.degradation_analysis.DegradationModel(x: NDArray, y: NDArray, i: NDArray, units: NDArray, threshold: float, path_model: Any, path_params: NDArray, pseudo_failure_times: NDArray, c: NDArray, life_model: Any, measurement_var: float, path_param_mean: NDArray, path_param_cov: NDArray, path_param_sample_cov: NDArray, population_method: str, path_selection: dict | None = None, Z: NDArray | None = None, links: dict[str, str] | None = None, path_param_fixed: NDArray | None = None, path_param_fixed_names: list[str] | None = None, path_param_link_cov: NDArray | None = None, acceleration: str | None = None, gamma: ArrayLike | None = None, stress_ref: ArrayLike | None = None, Z_cols: list[str] | None = None)
Bases:
SerialisableMixinA fitted degradation analysis model.
This is the model object returned by
DegradationAnalysis.fit(). It holds the per-unit fitted degradation paths, the pseudo failure times extrapolated from them, and the lifetime distribution fitted to those pseudo failure times. The usual lifetime functions (sf,ff,df,hf,Hf,qf,mean,random) are forwarded to the fitted life model, and the failure time of a new, partially observed unit can be estimated from its trajectory withpredict_failure_time()/predict_remaining_life().- Parameters:
x (ndarray) – The degradation data: measurement times, measurements, and the unit each measurement belongs to.
y (ndarray) – The degradation data: measurement times, measurements, and the unit each measurement belongs to.
i (ndarray) – The degradation data: measurement times, measurements, and the unit each measurement belongs to.
units (ndarray) – The distinct unit identifiers.
threshold (float) – The degradation level at which a unit is considered failed.
path_model (PathModel) – The degradation path model fitted to each unit.
path_params (ndarray) – Per-unit fitted path parameters, one row per entry of
units.pseudo_failure_times (ndarray) – Per-unit pseudo failure time: the extrapolated threshold crossing time; the unit’s last observed time for a unit whose path never reaches the threshold; its first (positive) measurement time for a unit already past the threshold there.
c (ndarray) – Per-unit censor flags: 0 where the fitted path crosses the threshold at a positive time, 1 (right censored) where it never reaches it, -1 (left censored: failed before its first measurement) where the path is already past the threshold at the first measurement, having crossed at or before time zero.
life_model (Parametric) – The lifetime distribution fitted to the pseudo failure times.
measurement_var (float) – Pooled estimate of the measurement-error variance around the per-unit paths (the per-unit residual sums of squares over the total residual degrees of freedom). Zero when every unit has exactly as many measurements as path parameters.
path_param_mean (ndarray) – Mean of the per-unit fitted path parameters: the estimated population mean path.
path_param_cov (ndarray) – Noise-corrected estimate of the between-unit covariance of the true path parameters (Lu-Meeker two-stage): the sample covariance of the per-unit estimates minus the average least-squares estimation covariance, projected onto the positive semi-definite cone.
path_param_sample_cov (ndarray) – The raw (uncorrected) sample covariance of the per-unit fitted path parameters. This overstates the between-unit variability because each per-unit estimate also carries least-squares estimation noise.
population_method (str) – How the population estimates (
measurement_var,path_param_mean,path_param_cov) were obtained:"moments"(two-stage correction) or"reml".path_selection (dict or None) – When fitted with
path="best", the AICc score of every candidate path model (nanfor candidates that could not be fitted to every unit);Noneotherwise. The fittedpath_modelis the candidate with the smallest score. The keys are the models’ display names ("Offset Exponential"), not thepath=strings.Z (ndarray or None) – The stresses of an accelerated model: one row per unit (aligned to
units) for a model fitted withZalone or withlinks; for a step-stress (acceleration="clock") model the stress rows as given, one per measurement (aligned tox).Nonefor a model fitted without stress.links (dict or None) – When the path parameters were modelled against stress (
linksgiven toDegradationAnalysis.fit()), the stress-dependent parameters and their links;Noneotherwise. Withlinksthe population of path parameters is stress-conditional, on the link scale:eta_i = D(z_i) gamma + u_iwithu_i ~ MVN(0, Sigma)andtheta_i = h(eta_i).path_param_fixed (ndarray or None) – The fixed effects
gammaof the stress-conditional population model, labelled bypath_param_fixed_names: for every path parameter its link-scale intercept, followed (for the stress-dependent ones) by its coefficient on each covariate.path_param_fixed_names (list of str or None) – Labels for
path_param_fixed: the link-scale parameter name ("log(b)"for a log link) and"<name>:Z<j>"for the coefficient on covariatej.path_param_link_cov (ndarray or None) – The between-unit covariance
Sigmaof the link-scale path parameters given the stress – the scatter left after the stress effect is removed, unlike the pooledpath_param_covwhich mixes the stress levels.acceleration (str or None) –
"clock"when stress was modelled as speeding up the clock of every unit’s path (acceleration="clock"inDegradationAnalysis.fit());Noneotherwise. The path parameters, their population, the pseudo failure times and the life model are then all on the reference-stress clock, andZholds the stress rows aligned tox.gamma (ndarray or None) – The stress coefficients of the clock: a unit at stress
zagesexp(gamma' (z - stress_ref))times faster than at the reference stress.stress_ref (ndarray or None) – The reference stress of the clock.
Z_cols (list of str or None) – The covariate columns of a model fitted with
DegradationAnalysis.fit_from_df(): every method that takesZthen also takes a DataFrame and selects these columns by name.Nonefor a model fitted from arrays, which refuses a DataFrame.
Examples
Eight units, each degrading linearly at its own rate and measured ten times, fail when the measurement reaches 450:
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> rng = np.random.default_rng(1) >>> x = np.tile(np.arange(100.0, 1100.0, 100.0), 8) >>> i = np.repeat(np.arange(8), 10) >>> a = np.repeat(rng.normal(10.0, 3.0, 8), 10) >>> b = np.repeat(rng.normal(0.3, 0.05, 8), 10) >>> y = a + b * x + rng.normal(0, 3.0, x.size) >>> model = DegradationAnalysis.fit(x, y, i, threshold=450) >>> model.pseudo_failure_times.round(1) array([1393.1, 1377.5, 1455.4, 1358.9, 1673.9, 1495.6, 1592.4, 1334. ])
A Weibull is fitted to those, and the lifetime functions use it:
>>> model.life_model.params.round(3) array([1515.035, 12.785]) >>> model.sf([1200, 1500]).round(4) array([0.9505, 0.4147])
- Hf(x: ArrayLike, Z: Any = None) NDArray
Cumulative hazard of the life model (
Zfor accelerated).
- Z: NDArray | None
Per-unit covariates when fitted as an accelerated-degradation model (
Zgiven toDegradationAnalysis.fit());Noneotherwise.
- acceleration_factor(Z: Any) float
How much faster a unit ages at stress
Zthan at the reference stress:exp(gamma' (z - stress_ref)), for a model fitted withacceleration="clock".A unit held at
Zdegrades along its path this many times faster, and a life at the reference stress divides by it to give the life atZ.- Parameters:
Z (array like or DataFrame) – One stress row (
nanfor a missing value givesnan); a one-row DataFrame for a model fitted withfit_from_df.
- cb(x: ArrayLike, Z: Any = None, on: str = 'sf', alpha_ci: float = 0.05, bound: str = 'two-sided', method: str = 'analytic', n_boot: int = 200, random_state: int | None = None) NDArray
Confidence bounds on the reliability of the fitted life model that account for the degradation analysis being a two-stage estimator.
The pseudo failure times are extrapolated per-unit path fits, not observed failures, so
life_model.cb– which treats them as exact – gives intervals that are too narrow. These bounds fold the first-stage (measurement + extrapolation) uncertainty back in.For an accelerated-degradation (covariate) model the bound is evaluated at a stress vector
Zand onlymethod='bootstrap'is available: the generated-regressor delta-method correction is not derived for the regression life fit, so the first-stage uncertainty is folded in by resampling units (each carrying its stress) and rerunning the whole accelerated pipeline.- Parameters:
x (array like) – Times at which to evaluate the bound(s).
Z (array like, optional) – Stress vector at which to evaluate the bound; required for an accelerated model, rejected for a plain one. For a step-stress (
acceleration="clock") model it is one stress row or aStepSchedule, and onlymethod='bootstrap'is available: units are resampled with their stress histories and the clock is re-estimated on each resample (with the model’spopulation_method, so a"reml"model’s bootstrap takes correspondingly longer).on ({'sf', 'ff', 'Hf'}, optional) – The function to bound (
'R'and'F'are accepted as aliases of'sf'and'ff'). Default'sf'.alpha_ci (float, optional) – Total tail probability of the bound(s): a two-sided band has
alpha_ci / 2in each tail. Default 0.05 (a 95% band).bound ({'two-sided', 'lower', 'upper'}, optional) – Two-sided bounds put
[lower, upper]on the last axis.method ({'analytic', 'bootstrap'}, optional) –
'analytic'(default) is a fast delta-method correction;'bootstrap'resamples units and reruns the whole pipeline (a slower, assumption-light cross-check). Accelerated (covariate) models support'bootstrap'only.n_boot (int, optional) – Bootstrap resamples (
method='bootstrap'only). Default 200.random_state (int or numpy.random.Generator, optional) – Seed or generator for the bootstrap resampling.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- Returns:
The confidence bound(s) on
onat eachx.- Return type:
numpy array
- df(x: ArrayLike, Z: Any = None) NDArray
Density of the fitted life model (
Zfor accelerated models).
- ff(x: ArrayLike, Z: Any = None) NDArray
CDF of the fitted life model (pass
Zfor accelerated models).
- classmethod from_dict(model_dict: dict) DegradationModel
Rebuild a degradation model from a
to_dict()dictionary.The path model is resolved by name (its
PATH_MODELSkey, or the display name that older dictionaries stored) and the life model by its ownfrom_dict; both are restricted to the known types.
- classmethod from_json(fp: str | PathLike) Any
Load a model from a JSON file written by
to_json(), or from the JSON text it returned (a string starting with{).
- hf(x: ArrayLike, Z: Any = None) NDArray
Hazard rate of the fitted life model (
Zfor accelerated).
- induced_life(n_samples: int = 10000, *, Z: Any = None, random_state: int | None = None) InducedFailureDistribution
The population failure-time distribution induced by the path model (the Lu-Meeker approach), as a Monte-Carlo diagnostic complement to the pseudo-failure-time
life_model.Path parameters are drawn from the fitted population distribution
theta ~ N(path_param_mean, path_param_cov)and each draw is pushed through the path model’sinv_path(threshold)to a failure time. This derives the population life directly from the path model, rather than via each unit’s noisy extrapolated failure time. Overlaying the returned distribution’sffon this model’s ownffis a check that the two agree.- Parameters:
n_samples (int, optional) – Number of Monte-Carlo path-parameter draws. Default 10000.
Z (array like, optional) – The stress to induce the life at. Required for a model whose path parameters were modelled against stress (fitted with
links): the draws are theneta ~ N(D(z) gamma, Sigma)on the link scale, mapped through the links to path parameters. Refused for a model withoutlinks, unless it is a step-stress (acceleration="clock") model: thenZis required, as one stress row or aStepSchedule, and each draw’s reference-stress failure time is read along that stress’s clock. The returned distribution records a constant stress row as itsstress; under a profile it records none.random_state (int or numpy.random.Generator, optional) – Seed or generator for a reproducible result.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- Returns:
The Monte-Carlo induced failure-time distribution.
- Return type:
Examples
The induced median next to the pseudo-failure fit’s:
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> rng = np.random.default_rng(1) >>> x = np.tile(np.arange(100.0, 1100.0, 100.0), 8) >>> i = np.repeat(np.arange(8), 10) >>> a = np.repeat(rng.normal(10.0, 3.0, 8), 10) >>> b = np.repeat(rng.normal(0.3, 0.05, 8), 10) >>> y = a + b * x + rng.normal(0, 3.0, x.size) >>> model = DegradationAnalysis.fit(x, y, i, threshold=450) >>> induced = model.induced_life(random_state=0) >>> round(induced.median()), round(float(model.qf(0.5))) (1452, 1472) >>> induced.prob_never_fails 0.0
- property is_accelerated: bool
the life model is a covariate (ADT) regression model, or stress accelerates the clock (
acceleration="clock").- Type:
True when life depends on stress
- life_model: Any
A plain
Parametriclife model, or the regression model for an accelerated (covariate) fit.
- life_parameter_covariance(method: str = 'analytic') NDArray
Covariance of the fitted life-model parameters, corrected for the first-stage (path-fit and extrapolation) uncertainty that the plain life-model MLE ignores.
See
cb()for the two-stage rationale;method='analytic'is the delta-method / generated-regressor correctionH^{-1} + sum_i v_i (dphi/dt_i)(dphi/dt_i)'.
- mean(Z: Any = None) float
Mean of the fitted life model.
For an accelerated model the mean life at stress
Zis obtained by integrating the survival function (the regression model has no closedmean). For a step-stress model it is the reference-stress mean divided by the acceleration factor at a constant stress, and the integral of the survival function under aStepSchedule.
- path(x: ArrayLike, unit: Any) NDArray
Evaluate the fitted degradation path of
unitatx.For a step-stress (
acceleration="clock") modelxis calendar time: the path is evaluated on the unit’s reference-stress clock, from its recorded stress history (the last stress held beyond its last measurement).
- path_param_link_mean(Z: Any) NDArray
Mean of the path parameters at stress
Z, on the link scale.For a model fitted with
linksthis is \(D(z)\,\gamma\) – the population mean of the link-scale path parametersetafor a unit tested at stressZ. The parameters are in path order, named like the intercepts inpath_param_fixed_names("log(b)"for a log-linkedb). The between-unit covariance around it ispath_param_link_cov, the same at every stress.- Parameters:
Z (array like or DataFrame) – One stress row, with as many covariates as the model was fitted with (a one-row DataFrame for a model fitted with
fit_from_df). A missing (nan) covariate makes the parameters that depend on itnan.- Returns:
The link-scale mean path parameters at
Z.- Return type:
ndarray
- path_param_median(Z: Any) NDArray
Median path parameters at stress
Z, on their natural scale.Each link is monotone and each link-scale parameter is normal, so mapping the link-scale mean through the links gives every parameter’s population median exactly: \(h(D(z)\,\gamma)\). For a log-linked rate that is the geometric-mean rate at
Z(the rate’s population mean is larger, by the log-normal factor). Evaluate the typical path at a stress withmodel.path_model.path(t, *model.path_param_median(Z)).- Parameters:
Z (array like or DataFrame) – One stress row, with as many covariates as the model was fitted with (a one-row DataFrame for a model fitted with
fit_from_df). A missing (nan) covariate makes the parameters that depend on itnan.- Returns:
The median path parameters at
Z, in path order.- Return type:
ndarray
- plot(ax: Any = None) Any
Plot the degradation data, the fitted per-unit paths (extended to each unit’s pseudo failure time), and the failure threshold.
- Parameters:
ax (matplotlib axes, optional) – An axes object to draw the plot on. Creates a new one if not provided.
- Returns:
An axes object with the plot.
- Return type:
matplotlib axes
- predict_failure_time(x: ArrayLike, y: ArrayLike, Z: Any = None, Z_future: Any = None) float
Estimate the failure time of a new unit from its (partial) degradation trajectory.
Fits this model’s path model to the new unit’s measurements and extrapolates the fitted path to this model’s failure threshold, exactly as was done for each unit during fitting.
- Parameters:
x (array like) – Times at which the new unit’s measurements were taken.
y (array like) – The new unit’s degradation measurements.
Z (array like, optional) – For a step-stress (
acceleration="clock") model, required: the new unit’s stress history, one row per measurement (the stress over the interval ending at it), or one row for a constant stress. The path is fitted on the unit’s clock.Z_future (array like or StepSchedule, optional) – For a step-stress model, the stress from the last measurement on: one row, or a
StepSchedulewhose time zero is the last measurement. Defaults to holding the last stress.
- Returns:
The time at which the new unit’s fitted path reaches the threshold. This can be smaller than the last observed time if the trajectory has already crossed the threshold. A trajectory already past the threshold at its first measurement returns the non-positive time at which its fitted path crossed (
0if the path is past the threshold throughout, and for a step-stress model, whose clock starts at zero). Returnsnan(with a warning) if the fitted path never reaches the threshold.- Return type:
float
- predict_remaining_life(x: ArrayLike, y: ArrayLike, Z: Any = None, Z_future: Any = None) float
Estimate the remaining life of a new unit from its (partial) degradation trajectory.
This is
predict_failure_time()minus the new unit’s last observed time. A negative value means the fitted path crossed the threshold before the last observation (the unit is predicted to have already failed);nan(with a warning) means the fitted path never reaches the threshold.ZandZ_futureare as forpredict_failure_time().
- predict_rul(x: ArrayLike, y: ArrayLike, *, Z: Any = None, Z_future: Any = None, alpha_ci: float = 0.05, n_samples: int = 10000, random_state: int | None = None) RULPrediction
Bayesian remaining-useful-life prediction for a new unit.
The population distribution of path parameters estimated at fit time (
path_param_mean,path_param_cov) is used as a prior, the new unit’s measurements as the likelihood (with the pooledmeasurement_varas the noise variance), and the Gaussian posterior of the unit’s path parameters is pushed through the threshold crossing by Monte Carlo. The posterior is exact (conjugate) for path models that are linear in their parameters, and an iterated-linearisation (Laplace) approximation otherwise.Compared to
predict_failure_time(), this shrinks short or noisy trajectories toward the population instead of trusting the raw extrapolation, works from a single measurement, and returns credible intervals. With many measurements the posterior concentrates on the least-squares fit and the two agree.- Parameters:
x (array like) – Times at which the new unit’s measurements were taken. One or more measurements are required.
y (array like) – The new unit’s degradation measurements.
Z (array like, optional) – The stress the new unit runs at. Required for a model whose path parameters were modelled against stress (fitted with
links): the prior is then the stress-conditional population,eta ~ N(D(z) gamma, Sigma)on the link scale, rather than the pooled population that mixes the stress levels. The posterior is taken on the link scale (so a log-linked rate stays positive) and pushed through the threshold crossing in the same way. Refused for a model withoutlinks. For a step-stress (acceleration="clock") model, required: the new unit’s stress history, one row per measurement (the stress over the interval ending at it, as at fit), or one row for a constant stress. The posterior is then taken on the unit’s clock against the reference-stress population, and each sampled reference-stress failure time is mapped back to calendar time along the unit’s history andZ_future.Z_future (array like or StepSchedule, optional) – For a step-stress model, the stress from the last measurement on: one row, or a
StepSchedulewhose time zero is the last measurement. Defaults to holding the last stress. Refused for other models.alpha_ci (float, optional) – Significance level for the equal-tailed credible intervals, between 0 and 1. Defaults to 0.05 (95% intervals).
n_samples (int, optional) – Number of Monte Carlo posterior samples (at least one). Defaults to 10,000.
random_state (int or numpy.random.Generator, optional) – Seed or generator for reproducible sampling.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- Returns:
Posterior medians, credible intervals, failure probabilities, and the parameter posterior.
- Return type:
Examples
Eight units with their own start and rate, then a new unit seen three times:
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> rng = np.random.default_rng(1) >>> x = np.tile(np.arange(100.0, 1100.0, 100.0), 8) >>> i = np.repeat(np.arange(8), 10) >>> a = np.repeat(rng.normal(10.0, 3.0, 8), 10) >>> b = np.repeat(rng.normal(0.3, 0.05, 8), 10) >>> y = a + b * x + rng.normal(0, 3.0, x.size) >>> model = DegradationAnalysis.fit(x, y, i, threshold=450) >>> pred = model.predict_rul( ... [100.0, 200.0, 300.0], [42.0, 71.0, 99.0], random_state=0 ... ) >>> round(pred.failure_time), round(pred.rul) (1473, 1173) >>> [round(v) for v in pred.failure_time_interval] [1395, 1562]
- qf(p: ArrayLike, Z: Any = None) NDArray
Quantile function of the fitted life model.
Plain life models expose their own
qf; accelerated regression models do not, so the quantile at stressZis obtained by numerically inverting the survival function, pairing eachpwith a row ofZassf()pairs eachx(a single row, or a singlep, is broadcast). For a step-stress model it is the calendar time at which the clock ofZreaches the reference-stress quantile, \(\tau^{-1}(F_0^{-1}(p))\). A missing (nan) probability or covariate givesnan.
- random(size: int, Z: Any = None, random_state: int | None = None) NDArray
Random pseudo failure times from the fitted life model.
For an accelerated model,
sizesamples are drawn at stressZby inverse-transform sampling of the fitted survival function (the regression models do not all exposerandomdirectly). For a step-stress model the reference-stress quantiles are carried to calendar time along the clock ofZ.- Parameters:
size (int) – Number of draws.
Z (array like or StepSchedule, optional) – The stress, as for
sf(); required for an accelerated or step-stress model, refused otherwise.random_state (int or numpy.random.Generator, optional) – Seed or generator for reproducible draws.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- sf(x: ArrayLike, Z: Any = None) NDArray
Survival function of the fitted life model.
For an accelerated-degradation model (fitted with covariates) the stress vector
Zat which to evaluate life is required. For a step-stress model (acceleration="clock")Zis one stress row or aStepSchedulestress profile, and life is the reference-stress life at the clock time,S(t) = S0(tau(t)); the same holds for every life method below.
- to_dict() dict
Serialise this fitted degradation model to a plain, JSON-serialisable dict.
Everything needed to rebuild the model is stored: the raw measurement data, the path model (by name) and its per-unit fitted parameters, the pseudo failure times and censor flags, the fitted life model (its own
to_dict), and the population summaries. The restored model reproduces the life predictions and per-unit paths, and (because the raw data is kept) its bootstrap confidence bounds too.
- to_json(fp: str | PathLike | None = None, with_data: bool = False) str | None
Write
to_dict()tofpas strict JSON, or return it.- Parameters:
fp (str or os.PathLike, optional) – The file to write. Without it the JSON is returned as a string (as
pandas.DataFrame.to_jsondoes), whichfrom_jsonalso reads.with_data (bool, optional) – Write
to_dict(with_data=True), which also stores the fitted data, for the models whoseto_dicttakeswith_data(the univariateParametricandNonParametric); aTypeErrorfor any other model. Defaults toFalse.
- class surpyval.degradation.degradation_analysis.RULPrediction(failure_time: float, failure_time_interval: tuple[float, float], rul: float, rul_interval: tuple[float, float], prob_failed: float, prob_never_fails: float, posterior_mean: NDArray, posterior_cov: NDArray, alpha_ci: float, samples: NDArray)
Bases:
objectPosterior failure-time / remaining-useful-life prediction for a new unit, returned by
DegradationModel.predict_rul().All summaries come from Monte Carlo samples of the new unit’s path parameters drawn from their Gaussian posterior and pushed through the path model’s threshold crossing. Samples whose path never reaches the threshold contribute
inffailure times, so the median and interval endpoints areinfwhen that much of the posterior mass never fails. Samples whose path is already past the threshold at the unit’s first measurement (it crossed at or before time zero) have failed: they contribute a failure time of0, so a trajectory that starts past the threshold hasprob_failed = 1,failure_time = 0and a remaining life of minus its age.- Parameters:
failure_time (float) – Posterior median of the unit’s failure time (measured from the unit’s time zero, like the fitted life model).
failure_time_interval (tuple of float) – Equal-tailed
1 - alpha_cicredible interval for the failure time.rul (float) – Posterior median remaining useful life: failure time minus the unit’s last observed time. Negative means the unit has most likely already crossed the threshold.
rul_interval (tuple of float) – Equal-tailed
1 - alpha_cicredible interval for the remaining useful life.prob_failed (float) – Posterior probability that the unit’s path has already crossed the threshold (failure time at or before its last observed time).
prob_never_fails (float) – Posterior probability that the unit’s path never reaches the threshold (a path already past it has failed, not “never fails”).
posterior_mean (ndarray) – The Gaussian posterior of the unit’s path parameters. For a model whose path parameters were modelled against stress (
links) these are on the link scale, in the order of the model’spath_param_fixed_namesintercepts ("log(b)"for a log-linkedb); otherwise on the natural scale.posterior_cov (ndarray) – The Gaussian posterior of the unit’s path parameters. For a model whose path parameters were modelled against stress (
links) these are on the link scale, in the order of the model’spath_param_fixed_namesintercepts ("log(b)"for a log-linkedb); otherwise on the natural scale.alpha_ci (float) – The interval significance level used.
samples (ndarray) – The Monte Carlo failure-time samples (
infwhere the sampled path never reaches the threshold,0where it is already past the threshold at the first measurement).
Examples
A new unit, measured three times, of a population of eight fitted units:
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> rng = np.random.default_rng(1) >>> x = np.tile(np.arange(100.0, 1100.0, 100.0), 8) >>> i = np.repeat(np.arange(8), 10) >>> a = np.repeat(rng.normal(10.0, 3.0, 8), 10) >>> b = np.repeat(rng.normal(0.3, 0.05, 8), 10) >>> y = a + b * x + rng.normal(0, 3.0, x.size) >>> model = DegradationAnalysis.fit(x, y, i, threshold=450) >>> pred = model.predict_rul( ... [100.0, 200.0, 300.0], [42.0, 71.0, 99.0], random_state=0 ... ) >>> round(pred.rul), [round(v) for v in pred.rul_interval] (1173, [1095, 1262]) >>> pred.prob_failed 0.0
- class surpyval.degradation.degradation_analysis.InducedFailureDistribution(samples: NDArray, threshold: float, path_name: str, stress: list[float] | None = None)
Bases:
SerialisableMixinThe population failure-time distribution induced by the degradation path model – the Lu-Meeker approach.
Where the fitted
life_modelfits a lifetime distribution to each unit’s (noisy) extrapolated pseudo failure time, this instead derives the population life directly from the fitted path-parameter distribution: path parameters are drawntheta ~ N(path_param_mean, path_param_cov)and each draw is pushed through the path model’sinv_path(threshold)to a failure time by Monte Carlo. It is produced byDegradationModel.induced_life().Draws whose path never reaches the threshold are recorded as
inf– a defective (“never fails”) mass exposed asprob_never_fails– so the quantiles and the mean areinfonce they reach into that mass. Draws whose path is already past the threshold at the earliest measurement time (it crossed at or before time zero) have failed from the start and are recorded as0, an atom of failures at time zero.Use it as a diagnostic: overlay
induced.ff(t)on the model’s ownff(t)(the pseudo-failure fit); close agreement is evidence that the path model and its population summary are consistent with the pseudo-failure lifetime fit.- Parameters:
samples (numpy array) – The Monte-Carlo failure-time draws (
infwhere the path never reaches the threshold,0where it is past it from the start).threshold (float) – The degradation failure threshold used.
path_name (str) – Name of the degradation path model.
stress (list of float, optional) – The stress row the distribution was induced at, for a model whose path parameters depend on stress;
Nonefor the plain population.
Examples
The induced life of a fitted population of eight units, next to the Weibull fitted to their pseudo failure times:
>>> import numpy as np >>> from surpyval.degradation import DegradationAnalysis >>> rng = np.random.default_rng(1) >>> x = np.tile(np.arange(100.0, 1100.0, 100.0), 8) >>> i = np.repeat(np.arange(8), 10) >>> a = np.repeat(rng.normal(10.0, 3.0, 8), 10) >>> b = np.repeat(rng.normal(0.3, 0.05, 8), 10) >>> y = a + b * x + rng.normal(0, 3.0, x.size) >>> model = DegradationAnalysis.fit(x, y, i, threshold=450) >>> induced = model.induced_life(random_state=0) >>> induced InducedFailureDistribution(Linear path, threshold=450, median=1451.95, prob_never_fails=0) >>> induced.sf([1200, 1500]).round(4) array([0.9949, 0.3489]) >>> model.sf([1200, 1500]).round(4) array([0.9505, 0.4147])
- ff(x: ArrayLike) NDArray
Failure probability
P(T <= x)from the Monte-Carlo draws (nanat a missing time).
- classmethod from_dict(model_dict: dict) InducedFailureDistribution
Rebuild an induced failure-time distribution from a dict.
- classmethod from_json(fp: str | PathLike) Any
Load a model from a JSON file written by
to_json(), or from the JSON text it returned (a string starting with{).
- mean() float
Mean failure time (
infif any draw never fails).
- median() float
Median failure time.
- qf(p: ArrayLike) NDArray
Quantile of the induced distribution (
infin the never-fails mass,nanfor a missing probability).
- random(size: int, random_state: int | None = None) NDArray
Draw failure times by resampling the Monte-Carlo population.
- sf(x: ArrayLike) NDArray
Survival function
P(T > x).
- to_dict() dict
Serialise this induced failure-time distribution to a plain dict.
Stores the Monte-Carlo samples (the
infnever-fails draws are written asnullso the result is valid JSON), the threshold and the path model’s name.
- to_json(fp: str | PathLike | None = None, with_data: bool = False) str | None
Write
to_dict()tofpas strict JSON, or return it.- Parameters:
fp (str or os.PathLike, optional) – The file to write. Without it the JSON is returned as a string (as
pandas.DataFrame.to_jsondoes), whichfrom_jsonalso reads.with_data (bool, optional) – Write
to_dict(with_data=True), which also stores the fitted data, for the models whoseto_dicttakeswith_data(the univariateParametricandNonParametric); aTypeErrorfor any other model. Defaults toFalse.
Path Models
The shape of each unit’s degradation over time. Pass one to
DegradationAnalysis.fit by name (path="linear") or as an
instance (path=LinearPath); the instances LinearPath,
QuadraticPath, ExponentialPath, … are of the classes below,
and PATH_MODELS maps each accepted name ("linear",
"quadratic", "exponential", "offset-exponential",
"power", "logarithmic", "lloyd-lipow", "gompertz",
"michaelis-menten") to its instance. Subclass PathModel for a
new shape.
- class surpyval.degradation.path_models.PathModel
Bases:
ABCBase class for degradation path models.
A path model is a deterministic function of time with a small number of parameters that is fitted, per unit, to that unit’s degradation measurements. Subclass this (implementing
path,inv_pathand thename/parameter_namesattributes, and either a_initial_guess(x, y)starting point for the default least-squaresfitorfititself) to use a custom degradation path withDegradationAnalysis. A subclass that still names its parametersparam_names(before v0.22) works until v0.23, with aDeprecationWarning.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- abstractmethod inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- abstractmethod path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.LinearPath_
Bases:
PathModelLinear degradation path:
y = a + b * x.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = True
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.QuadraticPath_
Bases:
PathModelQuadratic degradation path:
y = a + b * x + c * x**2.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
First positive time at which the parabola reaches
y.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = True
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.ExponentialPath_
Bases:
PathModelExponential degradation path:
y = a * exp(b * x).- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.OffsetExponentialPath_
Bases:
PathModelOffset exponential degradation path:
y = a + b * exp(c * x).Covers exponential growth or decay toward/away from the asymptote
a; witha = 0it reduces to the exponential path.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.PowerPath_
Bases:
PathModelPower degradation path:
y = a * x**b.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.LogarithmicPath_
Bases:
PathModelLogarithmic degradation path:
y = a + b * ln(x).- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = True
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.LloydLipowPath_
Bases:
PathModelLloyd-Lipow degradation path:
y = a - b / x.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = True
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.GompertzPath_
Bases:
PathModelGompertz degradation path:
y = a * exp(-b * exp(-c * x)).An S-shaped path approaching the asymptote
a.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- class surpyval.degradation.path_models.MichaelisMentenPath_
Bases:
PathModelMichaelis-Menten degradation path:
y = a * x / (b + x).A saturating path rising from zero toward the asymptote
a, reaching half of it atx = b.- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
Fit the path parameters to one unit’s measurements by (nonlinear) least squares.
- Parameters:
x (array_like) – The unit’s measurement times.
y (array_like) – Its degradation measurements.
- Returns:
The fitted parameters, in the order of
parameter_names.- Return type:
numpy array
Examples
>>> from surpyval.degradation import LinearPath >>> LinearPath.fit([1, 2, 3, 4], [10.5, 12.1, 13.4, 15.2]).round(4) array([8.95, 1.54])
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- surpyval.degradation.path_models.get_path_model(path: str | PathModel) PathModel
Resolve
pathto aPathModelinstance.Accepts a
PathModelinstance (returned unchanged) or one of the registered names inPATH_MODELS(case-insensitive):"linear","quadratic","exponential","offset-exponential","power","logarithmic","lloyd-lipow","gompertz","michaelis-menten". A built-in model’s displayname(e.g."Offset Exponential") is accepted too. ("best"— automatic selection — is handled byDegradationAnalysis.fit, not here.)Examples
>>> import numpy as np >>> from surpyval.degradation import get_path_model >>> power = get_path_model("power") >>> power.name, power.parameter_names ('Power', ['a', 'b']) >>> power.path(np.array([1.0, 4.0]), 2.0, 0.5) array([2., 4.]) >>> get_path_model("Offset Exponential").name 'Offset Exponential'
Stress-Dependent Path Parameters
For accelerated degradation tests whose mechanism depends on stress
(links in DegradationAnalysis.fit): the path parameters are
modelled on a link scale, eta_i = D(z_i) gamma + u_i, so a
log-linked rate with Z = 1/T follows the Arrhenius relationship.
- class surpyval.degradation.stress.LinkedPathModel(base: PathModel, links: dict[str, str])
Bases:
PathModelA path model reparameterised onto a link scale.
Wraps
baseso that its parametersthetaare replaced byetawiththeta = h(eta)elementwise:his the identity for most parameters andexpfor those given a"log"link.path,inv_path,jacobianandfitall take and return link-scale parameters, so the wrapped model is a drop-in path model for the per-unit fits and the population (REML) machinery, which then estimate the population ofeta.- Parameters:
base (PathModel) – The path model on its natural scale.
links (dict) –
{parameter name: "identity" | "log"}for the parameters whose link is not the identity (a parameter left out gets the identity link).
Examples
The linear path
a + b twith its slope on a log link, so thatbstays positive:>>> import numpy as np >>> from surpyval.degradation import LinkedPathModel, get_path_model >>> linked = LinkedPathModel(get_path_model("linear"), {"b": "log"}) >>> linked.parameter_names ['a', 'log(b)'] >>> eta = linked.to_link([10.0, 0.3]) >>> eta.round(4) array([10. , -1.204]) >>> linked.path(np.array([0.0, 10.0]), *eta) array([10., 13.])
- check_data(x: NDArray, y: NDArray) None
Raise
ValueErrorif the data is outside the model domain.
- fit(x: ArrayLike, y: ArrayLike) NDArray
The base model’s per-unit fit, returned on the link scale.
- inv_path(y: ArrayLike, *params: float) NDArray
Time at which the path reaches level
y.Returns a non-finite value (
nanorinf) or a non-positive value when the path never reachesyat a positive finite time.
- jacobian(x: ArrayLike, *params: float) NDArray
Partial derivatives of
pathwith respect to the parameters, evaluated at time(s)x: a(len(x), n_params)matrix.Used to estimate the least-squares estimation covariance of per-unit fitted parameters. The base implementation uses central finite differences; the built-in models override it with the analytic derivatives.
- linear_in_parameters: bool = False
True when
pathis linear in its parameters, i.e.path(x, *theta) == jacobian(x) @ thetawith a Jacobian that does not depend ontheta. Enables exact conjugate posterior updates and REML population estimation.
- path(x: ArrayLike, *params: float) NDArray
Evaluate the degradation path at time(s)
x.
- to_link(theta: ArrayLike) NDArray
Link-scale parameters
eta = h^-1(theta); the parameters run along the last axis, as forto_natural().
- to_natural(eta: ArrayLike) NDArray
Natural-scale parameters
theta = h(eta).etais one parameter vector, or an array of them with the parameters along the last axis (one row per Monte-Carlo draw).
- surpyval.degradation.stress.stress_design(z: ArrayLike, links: dict[str, str], parameter_names: list[str]) NDArray
The fixed-effects design
D(z)of one unit: a(p, m)matrix mapping the fixed effectsgammato the unit’s link-scale path parameter means,eta_mean = D(z) gamma.The columns are laid out parameter by parameter, in path order: an intercept column for every parameter, followed – for the stress-dependent parameters named in
links– by one column per covariate carrying that unit’s stress values. Som = p + q * len(links)withqcovariates.
- surpyval.degradation.stress.fixed_effect_names(linked_parameter_names: list[str], parameter_names: list[str], links: dict[str, str], n_cov: int) list[str]
Labels for
gammamatchingstress_design()’s columns: the link-scale parameter name for each intercept and"<name>:Z<j>"for each stress coefficient.
Step-Stress: the Accelerated Clock
For tests whose stress changes during a unit’s test
(acceleration="clock" in DegradationAnalysis.fit): stress
speeds up the clock of every unit’s path, AF(z) = exp(gamma' (z -
stress_ref)), and the path is the ordinary path model on the
reference-stress time the unit has aged. The fitted
DegradationModel then
carries gamma and stress_ref, its life methods take the stress as
one row or a StepSchedule, and its trajectory methods
take the unit’s stress history Z and a planned Z_future. How the
stress coefficients are estimated:
Step-stress general-path degradation: estimating the accelerated clock.
With acceleration="clock" in DegradationAnalysis.fit() stress
speeds up the clock of every unit’s degradation path, so unit i’s
measurements are
y_ij = g(tau_i(t_ij); theta_i) + eps_ij, theta_i ~ MVN(mu, Sigma),
with tau_i(t) = sum_k AF(z_ik) dt_ik the reference-stress time the unit
has aged by t and AF(z) = exp(gamma' (z - z_ref)). Z has one row
per measurement: the stress applied over the interval that ends at that
measurement (the first interval starts at time zero).
Once gamma is known the model is the ordinary general-path model on
tau, so everything downstream reuses the existing pipeline. This module
estimates gamma:
profile_least_squares()– the two-stage route. For a trialgammaevery unit’s path is refitted on itstauand the residual sums of squares are pooled;gammaminimises the total. A unit held at one stress can absorb any acceleration into its own path parameters (every built-in path family is closed under rescaling time), so only units whose stress changes during the test carry information here.mixed_model_estimate()– the mixed-model route. The units share one population of path parameters, so units at different constant stresses identifygammatoo. A Lindstrom-Bates FOCE iteration withgammaamong the fixed effects (the path linearised intheta_iandgamma) finds the neighbourhood, and the FOCE-approximate profile likelihood ofgammais then maximised directly.
- class surpyval.degradation.step_stress.ClockUnit(x: NDArray, y: NDArray, dt: NDArray, s: NDArray)
Bases:
objectOne unit’s time-ordered data with the stress over each interval.
- tau(g: NDArray) NDArray
Reference-stress time at each measurement.
- tau_gradient(g: NDArray) NDArray
d tau / d gat each measurement,(n, q).
- surpyval.degradation.step_stress.clock_units(x: NDArray, y: NDArray, i: NDArray, units: NDArray, Z: NDArray, z_ref: NDArray, scale: NDArray) list[ClockUnit]
Split the data by unit, time-ordered, with scaled interval stresses.
- surpyval.degradation.step_stress.mixed_model_estimate(units: list[ClockUnit], path_model: Any, g_init: NDArray, theta_init: NDArray, mean_init: NDArray, cov_init: NDArray, sigma2_init: float) tuple[NDArray, bool, tuple]
Mixed-model estimate of the scaled stress coefficients.
The joint FOCE iteration (
gamong the fixed effects) moves quickly to the neighbourhood of the estimate, but crawls along the ridge a unit held at one stress creates – its rate andgnearly trade off, and the linearisation follows that curved ridge in small steps. So it is followed by a direct maximisation of the profile likelihood ofg: for each trialgthe population is refitted by FOCE withgheld fixed, and the approximate marginal likelihood compared.gchanges the fixed-effects design, so the profile uses the plain likelihood rather than REML, whose adjustment term is not comparable across designs.Returns
(g, converged, population)withpopulationthe REML estimate(mu, Sigma, sigma2, converged)of the reference-stress population of path parameters at thatg.
- surpyval.degradation.step_stress.path_time_derivative(path_model: Any, tau: NDArray, theta: NDArray) NDArray
d g(tau; theta) / d tauby finite differences (one-sided at 0).
- surpyval.degradation.step_stress.profile_least_squares(units: list[ClockUnit], path_model: Any, q: int) NDArray
Two-stage estimate of the scaled stress coefficients: minimise the pooled per-unit residual sum of squares of the paths refitted on
tau(g). For one covariate a grid locates the basin before a bounded scalar search; otherwise a local search starts fromg = 0.
Stochastic Process Models
Where a path model treats degradation as a deterministic curve with
noise, these treat it as a stochastic process in its own right: the
Wiener process for degradation that can go down as well as up, and the
gamma process for monotone accumulation such as wear or crack growth.
Both give a first-passage distribution to the threshold in closed form,
and so a remaining-useful-life prediction with bounds. Both also take a
stress Z (one row per measurement), which may change between or
during units’ tests: stress accelerates the process clock, and the life
under any stress profile stays in closed form.
- class surpyval.degradation.process_models.WienerProcess
Bases:
objectFitter for the Wiener-process degradation model (see
WienerProcessModel).- classmethod fit(x: ArrayLike, y: ArrayLike, i: ArrayLike, threshold: float, Z: ArrayLike | None = None, stress_ref: ArrayLike | None = None) WienerProcessModel
Fit a Wiener-process degradation model by maximum likelihood.
- Parameters:
x (array_like) – Measurement times.
y (array_like) – Degradation measurements.
i (array_like) – Unit identifier for each measurement.
threshold (float) – The degradation level defining failure.
Z (array_like, optional) – Stress covariates, one row per measurement, for an accelerated or step-stress test. The stress may differ between units and change during a unit’s test: the stress on a measurement is the stress applied since the previous one. Stress speeds up the process clock by
exp(gamma' (z - stress_ref)), scaling the drift and the variance per unit time together (the time-scale transformation of Whitmore and Schenkelberg, 1997);gammais estimated withmuandsigmaby maximum likelihood. At least two distinct stress levels are needed.stress_ref (array_like, optional) – The reference stress at which the fitted
muandsigmaapply. Defaults to the mean stress over the measurement intervals; pass the use conditions to read the model at them.
- Returns:
The fitted model, whose life-distribution methods (
sf,ff,mean, …) give the first-passage time tothreshold.- Return type:
Examples
Five units whose degradation drifts upwards at 0.5 per unit time with Brownian noise:
>>> import numpy as np >>> from surpyval.degradation import WienerProcess >>> rng = np.random.default_rng(1) >>> t = np.tile(np.arange(0, 110, 10.0), 5) # 5 units, 11 readings >>> i = np.repeat(np.arange(5), 11) >>> steps = rng.normal(0.5 * 10, 1.0 * np.sqrt(10), size=(5, 10)) >>> y = np.hstack([np.r_[0.0, np.cumsum(s)] for s in steps]) >>> model = WienerProcess.fit(t, y, i, threshold=100) >>> model Wiener Process Degradation Model ================================ Drift (mu) : 0.488591 Diffusion (sigma) : 0.88113 Threshold : 100 Mean time to failure: 204.67 >>> model.sf([150, 200]).round(4) array([0.9922, 0.548 ])
- classmethod fit_from_df(df: DataFrame, x_col: str = 'x', y_col: str = 'y', i_col: str = 'i', Z_cols: str | list[str] | None = None, **fit_kwargs: Any) WienerProcessModel
Fit a Wiener-process degradation model from the columns of a DataFrame.
- Parameters:
df (DataFrame) – The degradation data, one row per measurement.
x_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).y_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).i_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).Z_cols (str or list of str, optional) – The stress column(s), passed to
fit()asZ. Their names are recorded on the model (asZ_cols, kept byto_dict), so its methods also take the stress as a one-row DataFrame and select these columns by name.**fit_kwargs – The remaining arguments of
fit():threshold(required), and optionallystress_refand the others.
- Returns:
The fitted model.
- Return type:
- class surpyval.degradation.process_models.WienerProcessModel(mu: float, sigma: float, threshold: float, gamma: Any = None, stress_ref: Any = None)
Bases:
FirstPassageProcessModelA fitted Wiener-process degradation model,
W(t) = mu*t + sigma*B(t).The first passage of the process to the failure
threshold(from an assumed degradation of zero att = 0) is Inverse-Gaussian distributed with meanthreshold / muand shapethreshold**2 / sigma**2, and the failure-time methods below evaluate that distribution. A positive driftmuis required for a proper (non-defective) life distribution.- Parameters:
mu (float) – Fitted drift (mean degradation rate).
sigma (float) – Fitted diffusion (volatility) coefficient.
threshold (float) – The degradation level defining failure.
gamma (array like, optional) – For a model fitted with stress
Z: the stress coefficients and the reference stress at whichmuandsigmaapply. At stresszthe process clock runsexp(gamma' (z - stress_ref))times faster, scaling both the drift and the variance per unit time.stress_ref (array like, optional) – For a model fitted with stress
Z: the stress coefficients and the reference stress at whichmuandsigmaapply. At stresszthe process clock runsexp(gamma' (z - stress_ref))times faster, scaling both the drift and the variance per unit time.
Examples
WienerProcess.fitreturns one; it can also be built from known parameters:>>> from surpyval.degradation import WienerProcessModel >>> model = WienerProcessModel(mu=0.5, sigma=1.0, threshold=100) >>> model Wiener Process Degradation Model ================================ Drift (mu) : 0.5 Diffusion (sigma) : 1 Threshold : 100 Mean time to failure: 200 >>> model.sf([150, 200, 250]).round(4) array([0.9759, 0.4719, 0.0489])
- Hf(x: ArrayLike, Z: Any = None) NDArray
Cumulative hazard of the first-passage time.
- Z_cols: list[str] | None = None
The covariate columns of a model fitted with
fit_from_df, by which a DataFrameZis read;Nonefor a model fitted from arrays.
- acceleration_factor(Z: Any) float
How much faster the process runs at stress
Zthan at the reference stress:exp(gamma' (z - stress_ref)).A life at the reference stress divides by this to give the life at
Z, and a unit atZaccumulates degradation this many times faster.- Parameters:
Z (array like or DataFrame) – One stress row (
nanfor a missing value givesnan). A model fitted withfit_from_dfalso takes a one-row DataFrame, read by column name.
- df(x: ArrayLike, Z: Any = None) NDArray
Density of the first-passage time. Under a stress path it is the reference-stress density at the clock time
tau(x)times the clock’s rate,AFof the stress in force atx.
- ff(x: ArrayLike, Z: Any = None) NDArray
Failure (CDF) of the first-passage time to the threshold.
For a model fitted with stress,
Zis required: one stress row for a constant stress, or aStepSchedulefor a stress profile; a model fitted withfit_from_dfalso takes a one-row DataFrame, read by column name. The same applies to every method below. A missing (nan) time or stress givesnan.
- classmethod from_dict(model_dict: dict) FirstPassageProcessModel
Rebuild a fitted process model from a
to_dict()dict.
- classmethod from_json(fp: str | PathLike) Any
Load a model from a JSON file written by
to_json(), or from the JSON text it returned (a string starting with{).
- gamma: NDArray | None
Stress coefficients and the reference stress, for a model fitted with
Z; bothNoneotherwise.
- hf(x: ArrayLike, Z: Any = None) NDArray
Hazard function of the first-passage time.
- property is_accelerated: bool
True for a model fitted with a stress
Z.
- mean(Z: Any = None) float
Mean time to failure. At a constant stress it is the reference-stress mean divided by the acceleration factor; under a stress profile it is the integral of the survival function.
- predict_rul(current_degradation: float, *, Z: Any = None, alpha_ci: float = 0.05) ProcessRUL
Remaining useful life given the current degradation level.
Increments are independent for both processes, so the remaining first passage over the residual distance
threshold - current_degradationfollows the same law as a fresh process; its median and equal-tailed interval are returned.- Parameters:
current_degradation (float) – The unit’s current degradation level (not
nan).Z (array like or StepSchedule, optional) – For a model fitted with stress: the stress the unit will run at from now on – one row for a constant stress, or a
StepSchedulewhose time zero is now (or, for a model fitted withfit_from_df, a one-row DataFrame). It describes this one unit, so a missing value is refused.alpha_ci (float, optional) – Tail probability of the returned interval, between 0 and 1. Default
0.05.
- Returns:
The median remaining life and its equal-tailed interval.
- Return type:
- qf(p: ArrayLike, Z: Any = None) NDArray
Quantile (inverse CDF) of the first-passage time (
nanfor a missing probability or stress).
- random(size: int, Z: Any = None, random_state: int | None = None) NDArray
Draw first-passage (failure) times from the fitted model.
- Parameters:
size (int) – Number of draws.
Z (array like or StepSchedule, optional) – The stress, for a model fitted with
Z(required then); each reference-stress draw is carried to calendar time along its clock.random_state (int or numpy.random.Generator, optional) – Seed or generator for reproducible draws.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- sf(x: ArrayLike, Z: Any = None) NDArray
Survival function of the first-passage time.
- to_dict() dict
Serialise this fitted process model to a plain dict.
- to_json(fp: str | PathLike | None = None, with_data: bool = False) str | None
Write
to_dict()tofpas strict JSON, or return it.- Parameters:
fp (str or os.PathLike, optional) – The file to write. Without it the JSON is returned as a string (as
pandas.DataFrame.to_jsondoes), whichfrom_jsonalso reads.with_data (bool, optional) – Write
to_dict(with_data=True), which also stores the fitted data, for the models whoseto_dicttakeswith_data(the univariateParametricandNonParametric); aTypeErrorfor any other model. Defaults toFalse.
- class surpyval.degradation.process_models.GammaProcess
Bases:
objectFitter for the Gamma-process degradation model (see
GammaProcessModel).- classmethod fit(x: ArrayLike, y: ArrayLike, i: ArrayLike, threshold: float, Z: ArrayLike | None = None, stress_ref: ArrayLike | None = None, resolution: float | None = None, gauge: float | None = None, rounding: str = 'nearest', exact_start: bool = False, gauge_method: str = 'exact') GammaProcessModel
Fit a Gamma-process degradation model by maximum likelihood.
The degradation must be monotone increasing (all increments non-negative); an increment that decreases raises an error pointing to the Wiener model for non-monotone signals.
A gamma increment is zero with probability zero, so an increment recorded as exactly zero means the change was below the measurement resolution: it enters the likelihood as censored,
P(increment <= resolution). Data without zero increments is fitted by the ordinary likelihood.That treats every non-zero increment as exact, which is fine when the readings are much finer than the increments. When they come from a coarse gauge – a step comparable to the increments – every increment is rounded, and the fit is biased (
alphacan come out at double or half its value, see the example below). Pass the gauge step asgaugeto fit the quantised likelihood instead: a readingrthen means the true level lies somewhere in the gauge bin aroundr, and the likelihood of a unit is the probability that its whole path passes through its recorded bins. The increments of a path are not independent once rounded (two consecutive increments share the rounding error of the reading between them), sogauge_method="exact"evaluates that path probability by a forward recursion over each unit’s readings, carrying the distribution of the true level across its bin (on a grid of cells, integrated exactly within each cell).gauge_method="independent"is a cheaper approximation that multiplies the exact probabilities of the single recorded increments, ignoring their dependence; it is nearly unbiased too, but less efficient. The true level at a unit’s first reading is taken to be uniform over its bin, unlessexact_startsays it is known.A gauge coarser than the scatter a unit’s degradation builds up over the whole test leaves the readings unable to tell a random path from a straight line:
alphaandbetathen run off to large values together, while their ratio (the mean rate) and the mean life stay well estimated.- Parameters:
x (array_like) – Measurement times.
y (array_like) – Degradation measurements.
i (array_like) – Unit identifier for each measurement.
threshold (float) – The degradation level defining failure.
Z (array_like, optional) – Stress covariates, one row per measurement, for an accelerated or step-stress test. The stress may differ between units and change during a unit’s test: the stress on a measurement is the stress applied since the previous one. Stress speeds up the process clock, so the shape accrues at
alpha * exp(gamma' (z - stress_ref))per unit time withbetaunchanged;gammais estimated withalphaandbetaby maximum likelihood. At least two distinct stress levels are needed.stress_ref (array_like, optional) – The reference stress at which the fitted
alphaapplies. Defaults to the mean stress over the measurement intervals.resolution (float, optional) – The measurement resolution: an increment recorded as zero is taken to be somewhere in
[0, resolution]. Defaults to the smallest positive increment in the data (for readings rounded to a grid, the grid step). Only used when some increment is zero, and not withgauge, which models the zeros itself.gauge (float, optional) – The step of the gauge the readings were rounded to. Given, the fit uses the quantised likelihood described above; the differences between readings of a unit must then be whole multiples of it. The default,
None, keeps the likelihood above (exact non-zero increments, censored zeros).rounding ({"nearest", "floor"}, optional) – How the gauge rounds, with
gauge:"nearest"(the default) means a readingrstands for a true level in[r - gauge/2, r + gauge/2);"floor"(a gauge that truncates, or a counter that ticks once a whole step is complete) means[r, r + gauge). Only differences between bins enter the likelihood, so the convention changes the fit only together withexact_start.exact_start (bool, optional) – With
gauge:Trueif each unit’s first reading is its exact true level (for instance new units at zero wear, not read off the gauge), so that later bins are placed relative to it byrounding. The default,False, treats the first reading as a gauge reading like the rest.gauge_method ({"exact", "independent"}, optional) – With
gauge:"exact"(the default) for the path likelihood,"independent"for the cheaper approximation that treats the rounded increments as independent. The exact method costs several times as much: a few hundred increments take a fraction of a second, ten thousand a few seconds.
- Returns:
The fitted model, whose life-distribution methods (
sf,ff,mean, …) give the first-passage time tothreshold.- Return type:
- Warns:
UserWarning – “No finite maximum” when every increment is proportional to its time step (noise-free readings), so the likelihood keeps increasing with
alpha: the returnedalphaandbetaare meaningless. (WienerProcessrefuses such data.)
Examples
Five units whose wear accumulates in non-negative gamma-distributed increments (mean 0.5 per unit time):
>>> import numpy as np >>> from surpyval.degradation import GammaProcess >>> rng = np.random.default_rng(1) >>> t = np.tile(np.arange(0, 110, 10.0), 5) # 5 units, 11 readings >>> i = np.repeat(np.arange(5), 11) >>> steps = rng.gamma(shape=2.0 * 10, scale=0.25, size=(5, 10)) >>> y = np.hstack([np.r_[0.0, np.cumsum(s)] for s in steps]) >>> model = GammaProcess.fit(t, y, i, threshold=100) >>> model Gamma Process Degradation Model =============================== Shape rate (alpha) : 2.61045 Rate (beta) : 5.45561 Threshold : 100 Mean time to failure: 209.183 >>> model.sf([150, 200]).round(4) array([1. , 0.8477])
The same wear read off a gauge with a step of 2. Rounding turns increments of about 5 into 4s and 6s, so fitted as exact increments they look far more variable than they are and
alphahalves; the quantised likelihood puts it back near the 2.61 of the unrounded readings:>>> y_gauge = np.round(y / 2.0) * 2.0 >>> naive = GammaProcess.fit(t, y_gauge, i, threshold=100) >>> quantised = GammaProcess.fit(t, y_gauge, i, 100, gauge=2.0) >>> print(round(naive.alpha, 2), round(quantised.alpha, 2)) 1.07 2.16
- classmethod fit_from_df(df: DataFrame, x_col: str = 'x', y_col: str = 'y', i_col: str = 'i', Z_cols: str | list[str] | None = None, **fit_kwargs: Any) GammaProcessModel
Fit a Gamma-process degradation model from the columns of a DataFrame.
- Parameters:
df (DataFrame) – The degradation data, one row per measurement.
x_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).y_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).i_col (str, optional) – The columns of the measurement times, the measurements and the unit identifiers. Default
"x","y"and"i". Their v0.21 namesx,yandistill work, with aDeprecationWarning, until v0.23 (every DataFrame entry point names its columns with a_colsuffix, principle 21).Z_cols (str or list of str, optional) – The stress column(s), passed to
fit()asZ. Their names are recorded on the model (asZ_cols, kept byto_dict), so its methods also take the stress as a one-row DataFrame and select these columns by name.**fit_kwargs – The remaining arguments of
fit():threshold(required), and optionallystress_refand the others.
- Returns:
The fitted model.
- Return type:
- class surpyval.degradation.process_models.GammaProcessModel(alpha: float, beta: float, threshold: float, gamma: Any = None, stress_ref: Any = None)
Bases:
FirstPassageProcessModelA fitted Gamma-process degradation model with stationary independent increments: over an interval
dtthe degradation increment isGamma(shape = alpha * dt, rate = beta). The path is monotone increasing.Because the path is monotone, the first passage to the failure
thresholdhas failure CDFP(W(t) >= threshold), evaluated with the regularised upper incomplete gamma function.- Parameters:
alpha (float) – Fitted shape rate (shape accrues as
alpha * t).beta (float) – Fitted rate parameter of the increments.
threshold (float) – The degradation level defining failure.
gamma (array like, optional) – For a model fitted with stress
Z: the stress coefficients and the reference stress at whichalphaapplies. At stresszthe shape accrues atalpha * exp(gamma' (z - stress_ref)).stress_ref (array like, optional) – For a model fitted with stress
Z: the stress coefficients and the reference stress at whichalphaapplies. At stresszthe shape accrues atalpha * exp(gamma' (z - stress_ref)).
Examples
GammaProcess.fitreturns one; it can also be built from known parameters. Wear accruing at a meanalpha / beta = 0.5per unit time, with failure at 100:>>> from surpyval.degradation import GammaProcessModel >>> model = GammaProcessModel(alpha=2.0, beta=4.0, threshold=100) >>> model.sf([150, 200, 250]).round(4) array([1. , 0.5066, 0. ]) >>> round(model.mean(), 2) 200.25
- Hf(x: ArrayLike, Z: Any = None) NDArray
Cumulative hazard of the first-passage time.
- Z_cols: list[str] | None = None
The covariate columns of a model fitted with
fit_from_df, by which a DataFrameZis read;Nonefor a model fitted from arrays.
- acceleration_factor(Z: Any) float
How much faster the process runs at stress
Zthan at the reference stress:exp(gamma' (z - stress_ref)).A life at the reference stress divides by this to give the life at
Z, and a unit atZaccumulates degradation this many times faster.- Parameters:
Z (array like or DataFrame) – One stress row (
nanfor a missing value givesnan). A model fitted withfit_from_dfalso takes a one-row DataFrame, read by column name.
- df(x: ArrayLike, Z: Any = None) NDArray
Density of the first-passage time. Under a stress path it is the reference-stress density at the clock time
tau(x)times the clock’s rate,AFof the stress in force atx.
- ff(x: ArrayLike, Z: Any = None) NDArray
Failure (CDF) of the first-passage time to the threshold.
For a model fitted with stress,
Zis required: one stress row for a constant stress, or aStepSchedulefor a stress profile; a model fitted withfit_from_dfalso takes a one-row DataFrame, read by column name. The same applies to every method below. A missing (nan) time or stress givesnan.
- classmethod from_dict(model_dict: dict) FirstPassageProcessModel
Rebuild a fitted process model from a
to_dict()dict.
- classmethod from_json(fp: str | PathLike) Any
Load a model from a JSON file written by
to_json(), or from the JSON text it returned (a string starting with{).
- gamma: NDArray | None
Stress coefficients and the reference stress, for a model fitted with
Z; bothNoneotherwise.
- hf(x: ArrayLike, Z: Any = None) NDArray
Hazard function of the first-passage time.
- property is_accelerated: bool
True for a model fitted with a stress
Z.
- mean(Z: Any = None) float
Mean time to failure. At a constant stress it is the reference-stress mean divided by the acceleration factor; under a stress profile it is the integral of the survival function.
- predict_rul(current_degradation: float, *, Z: Any = None, alpha_ci: float = 0.05) ProcessRUL
Remaining useful life given the current degradation level.
Increments are independent for both processes, so the remaining first passage over the residual distance
threshold - current_degradationfollows the same law as a fresh process; its median and equal-tailed interval are returned.- Parameters:
current_degradation (float) – The unit’s current degradation level (not
nan).Z (array like or StepSchedule, optional) – For a model fitted with stress: the stress the unit will run at from now on – one row for a constant stress, or a
StepSchedulewhose time zero is now (or, for a model fitted withfit_from_df, a one-row DataFrame). It describes this one unit, so a missing value is refused.alpha_ci (float, optional) – Tail probability of the returned interval, between 0 and 1. Default
0.05.
- Returns:
The median remaining life and its equal-tailed interval.
- Return type:
- qf(p: ArrayLike, Z: Any = None) NDArray
Quantile (inverse CDF) of the first-passage time (
nanfor a missing probability or stress).
- random(size: int, Z: Any = None, random_state: int | None = None) NDArray
Draw first-passage (failure) times from the fitted model.
- Parameters:
size (int) – Number of draws.
Z (array like or StepSchedule, optional) – The stress, for a model fitted with
Z(required then); each reference-stress draw is carried to calendar time along its clock.random_state (int or numpy.random.Generator, optional) – Seed or generator for reproducible draws.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- sf(x: ArrayLike, Z: Any = None) NDArray
Survival function of the first-passage time.
- to_dict() dict
Serialise this fitted process model to a plain dict.
- to_json(fp: str | PathLike | None = None, with_data: bool = False) str | None
Write
to_dict()tofpas strict JSON, or return it.- Parameters:
fp (str or os.PathLike, optional) – The file to write. Without it the JSON is returned as a string (as
pandas.DataFrame.to_jsondoes), whichfrom_jsonalso reads.with_data (bool, optional) – Write
to_dict(with_data=True), which also stores the fitted data, for the models whoseto_dicttakeswith_data(the univariateParametricandNonParametric); aTypeErrorfor any other model. Defaults toFalse.
- class surpyval.degradation.process_models.ProcessRUL(rul: float, rul_interval: tuple, prob_already_failed: float, alpha_ci: float)
Bases:
objectRemaining-useful-life summary from a fitted process model.
- rul
Median remaining useful life from the current state.
- Type:
float
- rul_interval
Equal-tailed
1 - alpha_ciinterval for the remaining life.- Type:
tuple of float
- prob_already_failed
1.0when the current degradation is at or beyond the threshold (the remaining life and its interval are then0), else0.0.- Type:
float
- alpha_ci
The tail probability of
rul_interval.- Type:
float
Examples
predict_rulof a process model returns one. A unit that has degraded to 60 of a failure threshold of 100, drifting at 0.5 per unit time:>>> from surpyval.degradation import WienerProcessModel >>> model = WienerProcessModel(mu=0.5, sigma=1.0, threshold=100) >>> rul = model.predict_rul(60.0) >>> rul ProcessRUL(rul=78.06, interval=(50.7, 120.3), prob_already_failed=0) >>> round(rul.rul, 2) 78.06
Destructive Degradation
For tests that destroy the unit being measured, so each unit yields one observation at one time rather than a path. The degradation distribution at each time is modelled directly, and the failure distribution follows from the threshold crossing.
- class surpyval.degradation.destructive.DestructiveDegradation_
Bases:
objectFitter for destructive degradation data (one destructive measurement per unit). Use the module-level singleton
DestructiveDegradation.- fit(x: ArrayLike, y: ArrayLike, threshold: float, c: ArrayLike | None = None, distribution: ~typing.Any = <surpyval.univariate.parametric.distributions.lognormal.LogNormal_ object>, transform: str = 'linear', direction: str = 'auto') DestructiveDegradationModel
Fit a destructive degradation model.
- Parameters:
x (array_like) – Measurement time of each unit (one value per unit).
y (array_like) – The destructive degradation measurement of each unit.
threshold (float) – The degradation level
D_fat which a unit is deemed failed.c (array_like, optional) – Censoring of each measurement (not the time):
0observed,1right-censored (e.g. did not break at the maximum load),-1left-censored (below the test floor). Default all observed.distribution (Parametric or str, optional) – Location-scale response distribution –
LogNormal(default, positive response),Normal, or another such asLogisticorLogLogistic– as the object or its name. A distribution with positive support needs every measurement positive.transform (str, optional) – Time transform \(\varphi(t)\) for the location:
"linear","log","sqrt","reciprocal", or"best"to pick the transform with the lowest AICc.direction ({'auto', 'increasing', 'decreasing'}, optional) – Whether degradation moves up toward the threshold (wear) or down toward it (strength loss).
"auto"infers it from the sign of the time-degradation trend.
- Returns:
The fitted model, whose life-distribution methods (
sf,ff, …) give the probability of having crossedthresholdby each time.- Return type:
- Warns:
UserWarning – “No finite maximum” when every measurement lies on the fitted path (noise-free readings): the fitted spread is then 0 to the precision of the fit, and
sigmais meaningless.
Examples
Six units broken at each of four ages; strength falls log-linearly with age, and a unit has failed once its strength is below 20:
>>> import numpy as np >>> from surpyval.degradation import DestructiveDegradation >>> rng = np.random.default_rng(1) >>> x = np.repeat([10.0, 20.0, 30.0, 40.0], 6) >>> y = np.exp(4.0 - 0.02 * x + rng.normal(0, 0.1, 24)) >>> model = DestructiveDegradation.fit(x, y, threshold=20) >>> model Destructive Degradation Model ============================= Response distribution : LogNormal Time transform : t Direction : decreasing Threshold : 20 Location : 4.02814 + -0.0206616*t Scale (sigma) : 0.0612395 >>> model.sf([50, 80]).round(4) array([0.4956, 0. ])
- fit_from_df(df: Any, x_col: str = 'x', y_col: str = 'y', c_col: str | None = None, **fit_kwargs: Any) DestructiveDegradationModel
Fit a destructive degradation model from the columns of a
pandas.DataFrame, with the argument names ofDegradationAnalysis.fit_from_df.- Parameters:
df (pandas.DataFrame) – One row per unit tested.
x_col (str, optional) – Column of the measurement times. Defaults to
"x".y_col (str, optional) – Column of the measurements. Defaults to
"y".c_col (str, optional) – Column of the measurements’ censoring flags. Default all observed.
**fit_kwargs – Remaining arguments passed to
fit():threshold(required), and optionallydistribution,transformanddirection.
- Returns:
The model
fit()returns for the same arrays.- Return type:
Examples
>>> import numpy as np >>> import pandas as pd >>> from surpyval.degradation import DestructiveDegradation >>> rng = np.random.default_rng(1) >>> age = np.repeat([10.0, 20.0, 30.0, 40.0], 6) >>> df = pd.DataFrame({ ... "age": age, ... "strength": np.exp(4.0 - 0.02 * age + rng.normal(0, 0.1, 24)), ... }) >>> model = DestructiveDegradation.fit_from_df( ... df, x_col="age", y_col="strength", threshold=20 ... ) >>> model.sf([50, 80]).round(4) array([0.4956, 0. ])
- class surpyval.degradation.destructive.DestructiveDegradationModel(distribution: Any, transform: str, direction: str, beta: NDArray, sigma: float, threshold: float, data: dict | None, neg_ll: float, transform_scores: dict | None = None)
Bases:
SerialisableMixinResult of
DestructiveDegradation.fit().Exposes the induced lifetime distribution at the failure threshold (
sf/ff/Hf/df) plus the fitted degradation distribution over time (degradation_quantile). The fitted parameters are the location intercept and slopebetaand the scalesigma.Examples
Six units destroyed in a strength test at each of four ages; a unit has failed once its strength is below 20:
>>> import numpy as np >>> from surpyval.degradation import DestructiveDegradation >>> rng = np.random.default_rng(1) >>> x = np.repeat([10.0, 20.0, 30.0, 40.0], 6) >>> y = np.exp(4.0 - 0.02 * x + rng.normal(0, 0.1, 24)) >>> model = DestructiveDegradation.fit(x, y, threshold=20)
The median strength at ages 10 and 50, and the probability a unit is still above the threshold at 50 and 80:
>>> model.degradation_quantile(0.5, [10, 50]).round(3) array([45.674, 19.987]) >>> model.sf([50, 80]).round(4) array([0.4956, 0. ])
- Hf(x: ArrayLike) NDArray
Cumulative hazard of the induced lifetime distribution.
- cb(x: ArrayLike, on: str = 'sf', alpha_ci: float = 0.05, bound: str = 'two-sided', n_boot: int = 200, random_state: int | None = None) NDArray
Bootstrap confidence bounds on the induced lifetime function
on.Units are resampled with replacement (each carrying its own
(x, y, c)) and the whole fit is rerun, folding the estimation uncertainty into the band. Percentile bounds are returned.- Parameters:
x (array_like) – Times at which to evaluate the bound(s).
on ({'sf', 'ff', 'Hf'}, optional) – The lifetime function to bound (
'R'and'F'are accepted for'sf'and'ff'). Default'sf'.alpha_ci (float, optional) – Total tail probability. Default 0.05.
bound ({'two-sided', 'lower', 'upper'}, optional) – Two-sided bounds put
[lower, upper]on the last axis, withalpha_ci / 2in each tail. Default'two-sided'.n_boot (int, optional) – Number of bootstrap resamples. Default 200.
random_state (int or numpy.random.Generator, optional) – Seed or generator for the resampling.
None(the default) seeds from numpy’s global RNG, sonp.random.seedcontrols it.
- degradation_quantile(p: ArrayLike, x: ArrayLike) NDArray
The
p-quantile of the destructive measurement at timex(the fitted degradation distributiondist(loc(x), sigma)).- Parameters:
p (float or array_like) – Probability (or probabilities) in
(0, 1).x (float or array_like) – Time(s) at which to read the degradation distribution; a scalar
xgives a scalar result.
- df(x: ArrayLike) NDArray
Density of the induced lifetime distribution (finite-difference of the CDF; the closed form depends on the time transform).
- ff(x: ArrayLike) NDArray
Failure (CDF) of the lifetime induced by crossing the threshold.
- classmethod from_dict(d: dict) DestructiveDegradationModel
Rebuild a model from a
to_dict()dictionary.Dictionaries written before the fit data was stored still load; the model they give predicts, but its
cb()raises because there is no data to resample.
- classmethod from_json(fp: str | PathLike) Any
Load a model from a JSON file written by
to_json(), or from the JSON text it returned (a string starting with{).
- median_degradation(x: ArrayLike) NDArray
Median destructive measurement at time
x.
- sf(x: ArrayLike) NDArray
Reliability of the induced lifetime distribution.
- to_dict() dict
Serialise this fitted model to a plain, JSON-serialisable dict.
The fit data
(x, y, c)is stored along with the fitted parameters – asDegradationModelstores its raw data – so the restored model reproduces the original’s predictions and its bootstrapcb()(with the samerandom_state, exactly).
- to_json(fp: str | PathLike | None = None, with_data: bool = False) str | None
Write
to_dict()tofpas strict JSON, or return it.- Parameters:
fp (str or os.PathLike, optional) – The file to write. Without it the JSON is returned as a string (as
pandas.DataFrame.to_jsondoes), whichfrom_jsonalso reads.with_data (bool, optional) – Write
to_dict(with_data=True), which also stores the fitted data, for the models whoseto_dicttakeswith_data(the univariateParametricandNonParametric); aTypeErrorfor any other model. Defaults toFalse.