Source code for lpspline.spline.piecewise_linear
import numpy as np
import cvxpy as cp
from typing import List, Optional, Union
from .base import Spline
[docs]
class PiecewiseLinear(Spline):
"""
Piecewise Linear Spine framework built primarily around discrete ReLU knot bases.
"""
def __init__(self, term: str, knots: Union[int, np.ndarray], tag: Optional[str] = 'pwl', by: Optional[str] = None):
"""
Initialize the Piecewise Linear Spline.
Parameters
----------
term : str
The column name representing the continuous covariate.
knots : Union[int, np.ndarray]
An explicit array sequence of sorted breakpoint knot coordinates, or an integer specifying count.
tag : Optional[str], default='pwl'
The descriptive tag denoting spline implementation type.
by : Optional[str], default=None
The column representing interaction categorical groupings.
"""
super().__init__(term=term, tag=tag)
self._knots = knots
self._by = by
self._variables = []
@property
def knots(self) -> Union[int, np.ndarray]:
"""
Returns the knot breaking parameters natively tracked within.
Returns
-------
Union[int, np.ndarray]
The 1D locations sequentially mapping piecewise shift points.
"""
return self._knots
@property
def by(self) -> Optional[str]:
"""
Returns the grouping variable column name.
Returns
-------
Optional[str]
The mapping group attribute column name.
"""
return self._by
[docs]
def init_spline(self, x: np.ndarray, by: np.ndarray = None):
"""
Initializes parameter bounds dynamically assuming knots were expressed as integer lengths.
Parameters
----------
x : np.ndarray
The 1D input target observation set.
by : np.ndarray, default=None
Assigned category classes if grouping interactively.
"""
super().init_spline(x, by)
if isinstance(self._knots, int):
self._knots = np.linspace(np.min(x), np.max(x), self._knots)
else:
self._knots = np.sort(self._knots)
def _build_basis(self, x: np.ndarray, **kwargs) -> np.ndarray:
"""
Builds the localized Rectified Linear basis terms around internal knots.
Evaluates natively sequentially across features utilizing $max(0, x-k_i)$.
Parameters
----------
x : np.ndarray
Input dataset array points.
**kwargs : dict
Supplemental arguments.
Returns
-------
np.ndarray
Matrix containing structural features with shape `(n_samples, 2 + len(knots))`.
"""
x = np.array(x).flatten()
n = len(x)
basis_list = [np.ones(n), x]
for k in self.knots:
basis_list.append(np.maximum(0, x - k))
base_basis = np.vstack(basis_list).T
return base_basis
def _build_variables(self) -> cp.Variable:
"""
Creates optimized CVXPY configurations sequentially describing parameter weights.
Returns
-------
cp.Variable
Structured matrix matching length required for evaluating full sequence.
"""
if isinstance(self._variables, list) and not self._variables:
dim_base = 2 + len(self.knots)
if self._by is not None:
self._variables = cp.Variable(shape=(dim_base, len(self._by_classes)), name=f"{self.term}_pwl")
else:
self._variables = cp.Variable(shape=(dim_base,), name=f"{self.term}_pwl")
return self._variables
def __repr__(self):
return f"PiecewiseLinear(term='{self.term}', knots={self.knots}, by={self._by})"