Regression#
Backwards-compatible re-exporter for process_improve.regression.
The implementation now lives in
process_improve.regression._robust_regression (ENG-23 / #305): the
renamed file makes filename-ranked tooling (Jump-to-File, fuzzy search,
codecov reports) less ambiguous about which methods.py is being shown.
Every public name remains importable as before:
from process_improve.regression.methods import OLS, robust_regression, repeated_median_slope
- class process_improve.regression.methods.OLS(fit_intercept=True, na_rm=True, conflevel=0.95, pi_resolution=50)[source]#
Bases:
RegressorMixin,BaseEstimatorOrdinary Least Squares regression with statistical diagnostics.
A scikit-learn-compatible estimator that fits an OLS model and exposes inferential statistics (standard errors, t-values, p-values, confidence intervals, F-statistic) and influence diagnostics (leverage, Cook’s distance). Calling
print(model)after fitting renders a summary similar to R’ssummary(lm(...)).- Parameters:
fit_intercept (bool, default=True) – If True, fits an intercept term. If False, the regression is forced through the origin.
na_rm (bool, default=True) – If True, drops rows with one or more missing values before fitting.
conflevel (float, default=0.95) – Confidence level for confidence and prediction intervals.
pi_resolution (int, default=50) – Number of grid points at which to compute prediction intervals over the range of x. Only used when X has a single column and an intercept is fitted.
- coefficients_#
Fitted slope coefficients (excludes the intercept).
- Type:
np.ndarray of shape (K,)
- standard_errors_#
Standard errors of
coefficients_.- Type:
np.ndarray of shape (K,)
- t_values_#
t-statistics for each coefficient.
- Type:
np.ndarray of shape (K,)
- p_values_#
Two-sided p-values for each coefficient.
- Type:
np.ndarray of shape (K,)
- conf_intervals_#
Lower and upper bounds of the coefficient confidence intervals.
- Type:
np.ndarray of shape (K, 2)
- conf_interval_intercept_#
Lower and upper bounds of the intercept confidence interval.
- Type:
np.ndarray of shape (2,)
- fitted_values_#
In-sample predictions.
- Type:
np.ndarray of shape (N,)
- residuals_#
In-sample residuals (NaN at rows removed by
na_rm).- Type:
np.ndarray of shape (N_original,)
- leverage_#
Hat-matrix diagonal \(h_i = \mathbf{x}_i^T (X^T X)^{-1} \mathbf{x}_i\), over the rows that were fitted. Matches
statsmodelshat_matrix_diag.- Type:
np.ndarray of shape (N,)
- influence_#
Cook’s distance per fitted row. Matches
statsmodelscooks_distance.- Type:
np.ndarray of shape (N,)
- pi_range_#
Columns are x-grid, lower bound, upper bound of the prediction interval.
np.nanif not applicable.- Type:
np.ndarray of shape (pi_resolution, 3) or float
Examples
>>> import numpy as np >>> from process_improve.regression.methods import OLS >>> rng = np.random.default_rng(0) >>> X = rng.standard_normal((50, 2)) >>> y = X @ [1.5, -2.0] + 0.5 + 0.1 * rng.standard_normal(50) >>> model = OLS().fit(X, y) >>> print(model) Call: OLS(fit_intercept=True, na_rm=True, conflevel=0.95) ...
See also
multiple_linear_regressionBackwards-compatible function returning a dict.
robust_regressionRobust regression via repeated-median slope.
- fit(X, y)[source]#
Fit the OLS model.
- Parameters:
X (array-like of shape (N, K)) – Feature matrix. Pandas and NumPy inputs are both accepted.
y (array-like of shape (N,) or (N, 1)) – Target vector.
- Returns:
self – Fitted estimator.
- Return type:
- predict(X)[source]#
Predict target values for
X.- Parameters:
X (array-like of shape (N, K))
- Returns:
y_pred
- Return type:
np.ndarray of shape (N,)
- prediction_interval(X, conflevel=None)[source]#
Prediction interval for new observations at arbitrary
X.Unlike the
pi_range_attribute - which is evaluated on a fixed grid spanning the training data - this method evaluates the prediction interval at any predictor value(s) supplied by the caller, including points outside the training range.- Parameters:
X (array-like of shape (M, K), (K,) or scalar) – New predictor value(s). A scalar or 1-D array is interpreted as a list of points when the model has a single feature, or as a single multi-feature point otherwise.
conflevel (float or None, default=None) – Confidence level for the interval. Defaults to the model’s own
conflevel.
- Returns:
A bunch with three length-M arrays:
predicted(the point prediction), andlower/upper(the prediction-interval bounds).- Return type:
- to_dict()[source]#
Return the legacy dictionary representation used by
multiple_linear_regression.- Return type:
- set_score_request(*, sample_weight='$UNCHANGED$')#
Configure whether metadata should be requested to be passed to the
scoremethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed toscoreif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it toscore.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
- process_improve.regression.methods.fit_robust_lm(x, y)[source]#
Fits a robust linear model between Numpy vectors x and y, with an intercept. Returns a length-2 array
[intercept, slope](theparamsattribute returned bystatsmodels.RLM); no extra checking on data consistency is done.See also: regression.repeated_median_slope
- process_improve.regression.methods.multiple_linear_regression(X, y, fit_intercept=True, na_rm=True, conflevel=0.95, pi_resolution=50)[source]#
Linear regression of the N rows and K columns of matrix X onto the single column ‘y’.
Backwards-compatible wrapper around
OLS. New code should use theOLSestimator directly, which exposes the same statistics as sklearn-style attributes and prints an R-likesummary(lm(...)).- Notes and limitations:
does not handle weighting
N >= K at least as many rows as columns in X
Returns a dictionary of outputs. Keys always present:
N: number of observations actually used to fit coefficients: a vector of K coefficients, one for each column in X; shape ``[np.nan]`` on the degenerate/unfitted path intercept: the fitted intercept; ``np.nan`` when ``fit_intercept=False`` or on the degenerate/unfitted path standard_errors: a vector of K standard errors, one per column in X standard_error_intercept: standard error for the intercept R2: the R^2 value R2_regression_based: R^2 computed as ``RegSS / TSS`` (added post-fit) R2_residual_based: R^2 computed as ``1 - RSS / TSS`` (added post-fit) k: the number of model parameters (added post-fit) SE: the model's standard error fitted_values: the N predicted values, one per row in y residuals: the N residuals t_value: the t-values for the standard errors conf_intervals: K rows x 2 columns (lower, upper) confidence intervals; shape ``[np.nan, np.nan]`` on the degenerate/unfitted path conf_interval_intercept: (lower, upper) confidence interval for the intercept (added post-fit)Keys present whenever the fit succeeds (populated for any K):
leverage: hat-matrix diagonal influence: Cook-style influence values
Keys present only for single-feature
X(and only whenfit_interceptis True and there is enough non-degenerate data):x_ssq: sum of squares of the centred predictor pi_range: prediction interval above and below, over the range of the predictor (``pi_resolution`` points)
- process_improve.regression.methods.repeated_median_slope(x, y, nowarn=False)[source]#
Robust slope calculation via Siegel’s repeated-median estimator.
https://en.wikipedia.org/wiki/Repeated_median_regression
An elegant (simple) method to compute the robust slope between a vector
xandy. For each pointithe median of the pairwise slopes(y[j] - y[i]) / (x[j] - x[i])over allj != iis computed; the returned slope is the median of those per-point medians.- Parameters:
x (np.ndarray or sequence) – Independent variable. Coerced to a 1-D numpy array. Must have at least 3 elements (unless
nowarn=True).y (np.ndarray or sequence) – Dependent variable. Must have the same length as
x.nowarn (bool, optional) – If
True, skip the minimum-length check and the SEC-19 cap on the number of points. DefaultFalse. The equal-length check and the finite-data check are always applied.
- Returns:
The repeated-median estimate of the slope. Returns
np.nanif all inner medians are undefined (e.g. allxvalues are equal).- Return type:
- Raises:
ValueError – If
xandydiffer in length; if, unlessnowarn=True, there are two points or fewer or more than the SEC-19 cap; or if fewer than three finite(x, y)pairs remain after non-finite pairs are dropped, so no repeated median can be formed.
Notes
Missing data: a pair is used only when both
x[i]andy[i]are finite. Pairs holding aNaNor an infinity in either vector are dropped before any slope is formed, and the estimate is computed from the remainder. The dropping is pairwise and silent, so check the inputs first if the count of usable observations matters to you.Cost: the pairwise slopes are formed at once as an N x N matrix, so time and memory grow as N squared. At the SEC-19 cap of 5,000 points the call needs about 425 MB of memory; at 1,000 points, 17 MB. Faster algorithms exist (Matousek, Mount, Netanyahu, 1998, https://link.springer.com/article/10.1007/PL00009190), but they are not used here.
- process_improve.regression.methods.robust_regression(x, y, fit_intercept=True, na_rm=True, conflevel=0.95, nowarn=False, pi_resolution=50, *, intervals=None, n_boot=999, random_state=0)[source]#
Perform the Simple robust regression analysis between x and y variables.
Simple robust regression between an x and a y using the repeated_median_slope method to calculate the slope. The intercept is the median intercept, when using that slope and the provided x and y values, or forced to zero if fit_intercept=False.
- Parameters:
x (np.ndarray, pd.DataFrame or pd.Series) – Sequences of numerical values, paired by position.
y (np.ndarray, pd.DataFrame or pd.Series) – Sequences of numerical values, paired by position.
fit_intercept (bool, default True) – If True, fits an intercept term. If False, forces regression through origin.
na_rm (bool, default True) – If True, removes all observations with one or more missing values.
conflevel (float, default 0.95) – Confidence level for the confidence and prediction intervals, strictly between 0 and 1.
nowarn (bool, default False) – If True, skips the minimum-size check and the SEC-19 cap on the number of points. Users should ensure data validity beforehand.
pi_resolution (int, default 50) – The number of x values, evenly spaced over the range of the data, at which
pi_rangegives the prediction interval.intervals ({"bootstrap", "least_squares"} or None, default None) – How
conf_intervals,conf_interval_interceptandpi_rangeare found."bootstrap"takes percentiles overn_bootresamples of the(x, y)pairs, each refitted with the repeated median, so the intervals resist outliers as the slope does."least_squares"applies the least-squares formulas to the residuals around the robust line: one gross outlier widens those intervals even when it leaves the slope where it was.Nonemeans"least_squares"until 3.0 and"bootstrap"from 3.0. Until then, a call whose intervals the change would move issues aFutureWarningand returns its text asintervals_note(see Notes). Passing either value silences it.n_boot (int, default 999) – The number of bootstrap resamples. Used only with
intervals="bootstrap".random_state (int, np.random.Generator or None, default 0) – Seeds the bootstrap resamples; see
process_improve._random.check_random_state(). The default seed makes a repeated call return the same intervals. Used only withintervals="bootstrap".
- Returns:
The fit, with these keys:
N: the number of observations used to fit the model coefficients: a length-1 list containing the regression slope intercept: the fitted intercept on the full-fit path (0 when ``fit_intercept=False``); ``np.nan`` on the degenerate early-return path (see below), regardless of ``fit_intercept`` standard_errors: a length-1 list containing the standard error of the slope standard_error_intercept: standard error for the intercept (np.nan if fit_intercept=False) R2: RegSS / (RegSS + RSS), around the robust line. Around a line that is not the least-squares one, RegSS + RSS is not the total sum of squares, so this is not 1 - RSS / TSS (the R2 of ``multiple_linear_regression``) SE: the model's standard error x_ssq: the sum of squares of (x - mean(x)) k: the number of model parameters (2 if fit_intercept else 1) fitted_values: the N predicted values, one per row in y residuals: the N residuals t_value: the t-values for the standard errors, one per coefficient (same meaning as in ``multiple_linear_regression``) conf_intervals: 1 row x 2 columns (lower, upper) confidence interval for the single slope coefficient, found as ``intervals`` says conf_interval_intercept: (lower, upper) confidence interval for the intercept, found as ``intervals`` says (np.nan if fit_intercept=False) pi_range: pi_resolution rows x 3 columns: x, then the lower and upper prediction limits for one new observation at that x, found as ``intervals`` says leverage: the hat-matrix diagonal (leverage) for each observation influence: Cook-style influence values for each observation intervals_note: only when ``intervals`` is not given and the default's change in 3.0 would change the intervals: the text of the ``FutureWarning`` that says soOn the degenerate early-return path (fewer than three usable observations in
xory), the returned dictionary is the initialization stub:NisNone,x_ssqis absent, andleverage,influence,pi_range,t_value,fitted_values,residuals, and the coefficient/interval arrays holdnp.nan.- Return type:
Notes
By default the three interval keys are least-squares intervals, as they were up to 2.2.2. 2.3.0 made the bootstrap the default without the announcement that the deprecation policy requires for a changed default, so code that kept running got other numbers; 2.3.1 restored least squares. The default becomes
"bootstrap"in 3.0, because its intervals resist outliers as the slope does. Until then a call that the change would affect (one with spread in x, a finiteSEand residuals that are more than rounding) warns and carriesintervals_note. A perfect fit stays quiet: every resample lies on its line, so both kinds of interval have zero width.A
confleveloutside (0, 1), NaN included, raisesValueErrorbefore any work is done. Up to 2.4.0 a level between -1 and 0 gave a reversed interval, and one with no t value gave NaN intervals.With
intervals="bootstrap"the three interval keys are percentile intervals over the resamples, each refitted as above. A resample whose x values are all equal has no slope and is left out. The prediction limits add to each resampled line a normal error scaled by 1.4826 times that resample’s median absolute residual, so an outlier widens them no more than it moves the line. Without outliers the slope interval is a little wider than the least-squares one, because it measures the spread of the repeated-median slope, which uses the data less efficiently than least squares does.SE, the standard errors,t_value,R2,leverageandinfluenceuse the least-squares formulas, applied to the residuals around the robust line, whicheverintervalsis: one gross outlier, which leaves the slope almost where it was, inflates the residual sum of squares and so raisesSEand lowersR2.The bootstrap refits the line
n_boottimes, and each fit grows as N squared (seerepeated_median_slope()). The default 999 resamples take about 0.1 s for 100 points, 3 s for 500 and over 10 s for 1,000; for more points, lowern_bootor useintervals="least_squares".
- process_improve.regression.methods.t_value(p, v)[source]#
Return the value on the x-axis if you plot the cumulative t-distribution with a fractional area of p (p is therefore a fractional value between 0 and 1 on the y-axis) and v is the degrees of freedom.
Examples
Since the cumulative distribution passes symmetrically through the x-axis at 0.0 for any number of degrees of freedom
>>> t_value(0.5, v=10) 0.0
Zero fractional area under the curve is always at \(-\infty\):
>>> t_value(0.0, v=10) -inf
100% fractional area is always at \(+\infty\):
>>> t_value(1.0, v=10) inf
See also
t_value_cdfdoes the inverse of this function.
Power of a regressor#
Box and Tidwell’s estimate of the power of a regressor.
A response that is curved in a regressor \(x\) can be straight in a power of it, \(x^\alpha\), with \(\ln x\) in the place of \(\alpha = 0\). Box and Tidwell (Technometrics, 1962) estimate the powers of the model
with ordinary regressions only. Near a trial power \(\alpha\),
so adding the constructed variable \(x^{\alpha}\ln x\) to a regression on \(x^{\alpha}\) gives it the coefficient \(\gamma = \beta(\alpha' - \alpha)\). Each step:
regresses \(y\) on the regressors at the trial powers, which gives each \(\hat\beta\);
adds the constructed variables, which gives each \(\hat\gamma\);
moves each power to \(\alpha + \hat\gamma / \hat\beta\).
From \(\alpha = 1\), the regressor as it is, the first step is Box and Tidwell’s \(\alpha_1 = 1 + \hat\gamma/\hat\beta\). Repeated, the steps are the Gauss-Newton method for the least-squares estimate of the powers; here a step is halved whenever it would raise the residual sum of squares. The standard error of each power comes from the same linearization at the estimate, as for any nonlinear least-squares parameter.
The constructed variable’s t value at \(\alpha = 1\) tests whether the regressor needs a power other than 1 at all: it is the score test of a straight-line relation in that regressor.
Examples
A response that is straight in \(\sqrt{x}\):
>>> import numpy as np
>>> x = np.arange(1.0, 11.0)
>>> found = box_tidwell(x, 2 + 3 * np.sqrt(x))
>>> round(found["power"]["x"], 6), found["df_resid"]
(0.5, 7)
- process_improve.regression.box_tidwell.SERIES = 1e-06#
Below this size a power’s columns are summed from their series in it, exact to rounding there.
- process_improve.regression.box_tidwell.HALVINGS = 40#
The most times a step is halved in search of a lower residual sum of squares.
- process_improve.regression.box_tidwell.FLAT = 1.4901161193847656e-08#
A regressor whose straight-line part of the fit is below this share of the spread of y has no slope at its trial power: its power cannot be moved, whatever rounding leaves in its coefficient.
- process_improve.regression.box_tidwell.box_tidwell(X, y, columns=None, max_iter=100, tol=1e-08)[source]#
Estimate the power of one or more regressors by Box and Tidwell’s method.
- Parameters:
X (pandas.DataFrame, numpy.ndarray or Sequence[float]) – The regressors, a column each. A one-dimensional array is one regressor, named
"x"; the columns of a two-dimensional array are named"x1","x2", and so on.y (numpy.ndarray or Sequence[float]) – The response.
columns (Sequence[str] or None) – The regressors whose power is estimated; each must be positive and take three or more different values. None estimates a power for every column. The other columns enter the model as they are, beside the intercept.
max_iter (int) – The most steps to take.
tol (float) – The steps stop when no power moves by more than this.
- Returns:
power: the least-squares estimate of each power, by column name;std_error: its standard error, \(s\) times the square root of the power’s diagonal element of \((J^\top J)^{-1}\), with \(J\) the derivatives of the fitted values in every parameter at the estimate;linearity_tandlinearity_p_value: the t value of each constructed variable in the regression with every power at 1, and its two-sided p value; a small p value says the relation is not a straight line in that regressor;sse, the residual sum of squares at the estimate, andsse_linear, with every power at 1;df_resid, the residual degrees of freedom: the rows less the intercept, the regressors’ coefficients and the powers;n, the rows;iterations, the powers at the start (all 1) and after each step: the first step, unless it had to be halved, is Box and Tidwell’s \(\alpha_1\).
- Return type:
- Raises:
ValueError – If the data or the arguments cannot give a power: a name that is not a column, a column to transform with a value that is not positive or fewer than three different values, a value that is not a finite number, a response of another length or with no variation, too few rows to leave a residual degree of freedom, a regressor with no clear straight-line effect on the response, or powers that do not settle in
max_itersteps. Powers that keep growing in size mean that no finite power fits best: the fit improves the more a regressor is reduced to its largest or smallest value.
Nonlinear least squares#
Least squares for a model that is nonlinear in its parameters, with the usual inference.
The model \(y = f(x; \theta) + \varepsilon\) is fitted by minimizing the residual sum of squares \(S(\theta) = \sum_u (y_u - f(x_u; \theta))^2\). Inference rests on the linearization of \(f\) at the estimate \(\hat\theta\), the matrix \(J\) of the derivatives of the fitted values in the \(p\) parameters (Box, Hunter and Hunter, section 14.6; Bates and Watts):
the covariance of the estimates is \(s^2 (J^\top J)^{-1}\), with \(s^2 = S(\hat\theta)/(n - p)\), which gives standard errors, t values and intervals;
the approximate joint confidence region for all the parameters is \(\{\theta : S(\theta) \le S(\hat\theta)\,[1 + \tfrac{p}{n - p} F_{p,\,n-p}]\}\). The region follows the contours of \(S\) itself, so it keeps the curved shape of the valley that the linearized intervals miss.
Examples
Box, Hunter and Hunter’s biochemical oxygen demand against incubation time, with \(\eta = \beta_1 (1 - e^{-\beta_2 x})\):
>>> import numpy as np
>>> days = np.array([1, 2, 3, 5, 7, 10])
>>> demand = np.array([109, 149, 149, 191, 213, 224])
>>> fit = nonlinear_regression(lambda x, b1, b2: b1 * (1 - np.exp(-b2 * x)), days, demand, {"b1": 200, "b2": 0.5})
>>> round(fit["estimates"]["b1"], 1), round(fit["estimates"]["b2"], 4), round(fit["sse"], 2)
(213.8, 0.5472, 1168.01)
- process_improve.regression.nonlinear.nonlinear_regression(model, x, y, start, conf_level=0.95)[source]#
Fit
y = model(x, *parameters)by least squares, and give the inference of the linearized model.- Parameters:
model (Callable[..., Any]) – Called as
model(x, theta_1, ..., theta_p), with the parameters in the order ofstart; returns the fitted value of each row ofx.x (numpy.ndarray, pandas.DataFrame or Sequence[float]) – The regressors, passed to
modelas they are.y (numpy.ndarray or Sequence[float]) – The response, one value per row of
x.start (Mapping[str, float]) – The parameters’ names and their starting values, from which the search begins. A nonlinear model can have more than one minimum: start near the expected values.
conf_level (float) – The confidence level of the intervals and of the joint region.
- Returns:
estimates,std_errors,t_values,p_values(two-sided, of each parameter against 0) andintervals(the estimate plus or minus \(t_{n-p}\) standard errors), each by parameter name;correlation: the correlations of the estimates, a nested dict;sse(\(S(\hat\theta)\)),df_resid(\(n - p\)),s2,n;fittedandresiduals, in the order of the rows;region_sse: \(S(\hat\theta)\,[1 + \tfrac{p}{n - p} F_{p,\,n-p}]\), the contour of the residual sum of squares that bounds the joint confidence region.
- Return type:
- Raises:
ValueError – If
startnames no parameter or gives one that is not a finite number,yhas a value that is not a finite number, there are no more rows than parameters,modeldoes not return a finite value for each row at the start,conf_levelis not between 0 and 1, the search does not converge, or the estimates cannot be told apart (the derivatives of the fitted values in the parameters are linearly dependent at the estimate).