Skip to Content

PPOLY

Overview

The PPOLY function constructs a piecewise polynomial in the power basis, defined by a set of polynomial coefficients and breakpoints. This is useful for representing and evaluating polynomials that are defined piecewise over different intervals, such as in interpolation, curve fitting, and numerical analysis. The polynomial between each pair of breakpoints x[i]x[i] and x[i+1]x[i+1] is given by:

S(x)=m=0kc[m,i](xx[i])kmS(x) = \sum_{m=0}^{k} c[m, i] \cdot (x - x[i])^{k-m}

where kk is the degree of the polynomial, c[m,i]c[m, i] are the coefficients for the ii-th interval, and x[i]x[i] are the breakpoints. For more details, see the SciPy PPoly documentation .

This example function is provided as-is without any representation of accuracy.

Usage

To use the function in Excel:

=PPOLY(c, x, [eval_points], [extrapolate])
  • c (2D list, required): Coefficient matrix of shape (degree+1, n_intervals). Each column contains the coefficients for one interval, ordered from highest to lowest degree. Must be a 2D list. 1D lists are not supported.
  • x (2D list, required): Column vector of breakpoints of length n_intervals+1. Must be a 2D list with exactly one column and at least two rows.
  • eval_points (2D list, optional, default=x): Points at which to evaluate the piecewise polynomial. Scalars are treated as single-cell tables. Must be a 2D list when provided.
  • extrapolate (bool, optional, default=True): Whether to extrapolate outside the breakpoints. String values such as "TRUE"/"FALSE" are coerced to booleans.

The function returns a 2D list of evaluated values at the specified points, or an error message (string) if the input is invalid. When extrapolate is FALSE, out-of-range evaluations return None in the corresponding cells.

Examples

Example 1: Quadratic Piecewise Polynomial

This example constructs a quadratic piecewise polynomial with two intervals and evaluates it at the breakpoints.

Inputs:

cxeval_pointsextrapolate
1000True
0110.5
-1121

Excel formula:

=PPOLY({1,0;0,1;-1,1}, {0;1;2}, {0;0.5;1}, TRUE)

Expected output:

Result
-1.000
-0.750
1.000

This means the polynomial evaluates to -1 at the left endpoint, -0.75 at 0.5, and 1 at the right endpoint.

Example 2: Linear Piecewise Polynomial

This example constructs a linear piecewise polynomial and evaluates it at several points.

Inputs:

cxeval_pointsextrapolate
2100False
0110.5
21

Excel formula:

=PPOLY({2,1;0,1}, {0;1;2}, {0;0.5;1}, FALSE)

Expected output:

Result
0.000
1.000
1.000

Example 3: Default Evaluation at Breakpoints

This example omits the eval_points argument, so the polynomial is evaluated at the breakpoints.

Inputs:

cxextrapolate
120True
011
2

Excel formula:

=PPOLY({1,2;0,1}, {0;1;2}, , TRUE)

Expected output:

Result
0.000
1.000
3.000

This example illustrates the default evaluation when no additional eval_points are supplied and uses a blank placeholder for the optional argument in Excel.

Example 4: Quadratic Evaluation Without Extrapolation

This example evaluates the quadratic piecewise polynomial at two points while disabling extrapolation.

Inputs:

cxeval_pointsextrapolate
1000False
0111
-112

Excel formula:

=PPOLY({1,0;0,1;-1,1}, {0;1;2}, {0;1}, FALSE)

Expected output:

Result
-1.000
1.000

Python Code

from typing import List, Optional, Union import numpy as np from scipy.interpolate import PPoly as scipy_ppoly ScalarInput = Union[int, float, bool, str, None] MatrixInput = Union[ScalarInput, List[List[ScalarInput]]] MatrixOutput = List[List[Union[float, bool, str, None]]] ReturnType = Union[str, MatrixOutput] def ppoly( c: MatrixInput, x: MatrixInput, eval_points: Optional[MatrixInput] = None, extrapolate: Union[bool, int, float, str] = True, ) -> ReturnType: """ Construct and evaluate a piecewise polynomial in the power basis. Args: c: 2D list of coefficients (degree+1, n_intervals). Must be a 2D list. x: 2D list of breakpoints (n_intervals+1, 1). Must be a 2D list (column vector) with at least two rows. eval_points: 2D list of points to evaluate at (default: x). Must be a 2D list. extrapolate: Whether to extrapolate outside breakpoints (default: True). Returns: 2D list of evaluated values, or error message (str) if input is invalid. This example function is provided as-is without any representation of accuracy. """ def _wrap_scalar(value: ScalarInput) -> List[List[ScalarInput]]: return [[value]] def _normalize_matrix(arg: MatrixInput, name: str) -> Union[str, List[List[ScalarInput]]]: # Convert scalars to single-cell 2D lists and ensure structural validity. candidate: List[List[ScalarInput]] if isinstance(arg, list): if not arg: return f"Invalid input: {name} must be a 2D list with at least one row." if any(not isinstance(row, list) or not row for row in arg): return f"Invalid input: {name} must be a 2D list with no empty rows." if any( any(isinstance(cell, list) for cell in row) for row in arg ): return f"Invalid input: {name} must contain scalar values." candidate = arg else: candidate = _wrap_scalar(arg) return candidate def _coerce_floats(matrix: List[List[ScalarInput]], name: str) -> Union[str, List[List[float]]]: # Convert all values to floats while validating numeric content. converted: List[List[float]] = [] for row in matrix: converted_row: List[float] = [] for value in row: try: converted_row.append(float(value)) except Exception: return f"Invalid input: {name} must contain numeric values." converted.append(converted_row) array_view = np.asarray(converted, dtype=float) if not np.all(np.isfinite(array_view)): return f"Invalid input: {name} must contain finite numeric values." return converted def _coerce_bool(value: Union[bool, int, float, str], name: str) -> Union[str, bool]: # Interpret Excel-style boolean representations. if isinstance(value, bool): return value if isinstance(value, (int, float)) and value in (0, 1): return bool(value) if isinstance(value, str): lowered = value.strip().lower() if lowered in {"true", "1", "yes"}: return True if lowered in {"false", "0", "no"}: return False return f"Invalid input: {name} must be a boolean value." normalized_c = _normalize_matrix(c, "c") if isinstance(normalized_c, str): return normalized_c if len(normalized_c) < 1: return "Invalid input: c must contain at least one row." row_lengths = {len(row) for row in normalized_c} if len(row_lengths) != 1: return "Invalid input: all rows in c must have the same length." n_intervals = next(iter(row_lengths)) if n_intervals < 1: return "Invalid input: c must define at least one interval." normalized_x = _normalize_matrix(x, "x") if isinstance(normalized_x, str): return normalized_x if len(normalized_x) != n_intervals + 1: return "Invalid input: x must be a column vector of length n_intervals + 1." if any(len(row) != 1 for row in normalized_x): return "Invalid input: x must be a 2D column vector." coerce_c = _coerce_floats(normalized_c, "c") if isinstance(coerce_c, str): return coerce_c coerce_x = _coerce_floats(normalized_x, "x") if isinstance(coerce_x, str): return coerce_x x_values = [row[0] for row in coerce_x] diffs = np.diff(x_values) if not (np.all(diffs > 0) or np.all(diffs < 0)): return "Invalid input: x must be strictly monotonic." if eval_points is None: eval_points_matrix = [[row[0]] for row in coerce_x] else: normalized_eval = _normalize_matrix(eval_points, "eval_points") if isinstance(normalized_eval, str): return normalized_eval eval_points_matrix = normalized_eval if len(eval_points_matrix) < 1 or any(len(row) < 1 for row in eval_points_matrix): return "Invalid input: eval_points must be a 2D list with no empty rows." coerced_eval = _coerce_floats(eval_points_matrix, "eval_points") if isinstance(coerced_eval, str): return coerced_eval extrapolate_value = _coerce_bool(extrapolate, "extrapolate") if isinstance(extrapolate_value, str): return extrapolate_value # Prepare arrays for SciPy evaluation. coefficients = np.asarray(coerce_c, dtype=float) breakpoints = np.asarray(x_values, dtype=float) eval_flat: List[float] = [] eval_shape: List[int] = [] for row in coerced_eval: eval_shape.append(len(row)) eval_flat.extend(row) eval_array = np.asarray(eval_flat, dtype=float) try: piecewise = scipy_ppoly(coefficients, breakpoints, extrapolate=extrapolate_value) evaluations = piecewise(eval_array) except Exception as exc: return f"scipy.PPoly error: {exc}" evaluations = np.asarray(evaluations, dtype=float) output: MatrixOutput = [] start_index = 0 for length in eval_shape: row_values: List[Union[float, bool, str, None]] = [] for offset in range(length): value = evaluations[start_index + offset] if not np.isfinite(value): row_values.append(None) else: row_values.append(float(value)) start_index += length output.append(row_values) return output

Example Workbook

Link to Workbook 

Last updated on