LSTSQ
Overview
The LSTSQ function computes the least-squares solution to the linear matrix equation , minimizing the 2-norm . This wrapper around scipy.linalg.lstsq exposes the most frequently used arguments (A, B, cond, lapack_driver, and return_type) while keeping advanced options such as overwrite_a, overwrite_b, and check_finite at their SciPy defaults for simplicity. Least-squares fitting is widely used for solving overdetermined systems, linear regression, and model calibration.
The solution minimizes the squared error:
This example function is provided as-is without any representation of accuracy.
Usage
To use the function in Excel:
=LSTSQ(A, B, [cond], [lapack_driver], [return_type])A(2D list, required): Coefficient matrix with at least one row and column. Single scalars are treated as a 1×1 matrix.B(2D list, required): Right-hand side matrix with the same number of rows asA. Single scalars are treated as a 1×1 matrix.cond(float, optional, default=None): Cutoff for small singular values; values belowcond * largest_singular_valueare treated as zero.lapack_driver(str, optional, default="gelsd"): LAPACK driver used by SciPy. Accepts"gelsd","gelsy", or"gelss".return_type(str, optional, default="solution"): Specifies the value to return. Options are"solution","residuals","rank", or"singular_values".
The function returns a 2D list containing the requested value or a 2D list with an error message string if the input is invalid.
Examples
Example 1: Solution Vector
Inputs:
| A | B | cond | lapack_driver | return_type | ||
|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 2 | 1e-10 | gelsd | solution |
| 1 | 0 | 1 | 2 | |||
| 1 | 1 | 1 | 3 | |||
| 1 | 0 | 0 | 1 |
Excel formula:
=LSTSQ({1,1,0;1,0,1;1,1,1;1,0,0}, {2;2;3;1}, 1e-10, "gelsd", "solution")Expected output:
| Result |
|---|
| 1.000 |
| 1.000 |
| 1.000 |
Example 2: Residual Sum of Squares
Inputs:
| A | B | cond | lapack_driver | return_type | ||
|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 2 | 1e-10 | gelsd | residuals |
| 1 | 0 | 1 | 2 | |||
| 1 | 1 | 1 | 3 | |||
| 1 | 0 | 0 | 1 |
Excel formula:
=LSTSQ({1,1,0;1,0,1;1,1,1;1,0,0}, {2;2;3;1}, 1e-10, "gelsd", "residuals")Expected output:
| Result |
|---|
| 0.000 |
Example 3: Matrix Rank
Inputs:
| A | B | cond | lapack_driver | return_type | ||
|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 2 | 1e-10 | gelsd | rank |
| 1 | 0 | 1 | 2 | |||
| 1 | 1 | 1 | 3 | |||
| 1 | 0 | 0 | 1 |
Excel formula:
=LSTSQ({1,1,0;1,0,1;1,1,1;1,0,0}, {2;2;3;1}, 1e-10, "gelsd", "rank")Expected output:
| Result |
|---|
| 3.000 |
Example 4: Singular Values
Inputs:
| A | B | cond | lapack_driver | return_type | ||
|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 2 | 1e-10 | gelsd | singular_values |
| 1 | 0 | 1 | 2 | |||
| 1 | 1 | 1 | 3 | |||
| 1 | 0 | 0 | 1 |
Excel formula:
=LSTSQ({1,1,0;1,0,1;1,1,1;1,0,0}, {2;2;3;1}, 1e-10, "gelsd", "singular_values")Expected output:
| Result | ||
|---|---|---|
| 2.524 | 1.000 | 0.792 |
Python Code
from typing import List, Optional, Union
import numpy as np
from scipy.linalg import lstsq as scipy_lstsq
MatrixScalar = Union[float, int, bool, str, None]
MatrixInput = Union[List[List[MatrixScalar]], MatrixScalar]
MatrixReturn = List[List[Union[float, int, str, None]]]
def lstsq(A: MatrixInput, B: MatrixInput, cond: Optional[float] = None, lapack_driver: str = "gelsd", return_type: str = "solution") -> MatrixReturn:
"""
Compute the least-squares solution to Ax = B using scipy.linalg.lstsq.
Args:
A: 2D list (M x N) coefficient matrix. Single scalars are treated as 1x1 matrices.
B: 2D list (M x K) right-hand side matrix. Single scalars are treated as 1x1 matrices.
cond: Optional float cutoff for small singular values. Defaults to None.
lapack_driver: String identifying the LAPACK driver ("gelsd", "gelsy", "gelss"). Defaults to "gelsd".
return_type: String specifying the value to return: "solution", "residuals", "rank", or "singular_values". Defaults to "solution".
Returns:
2D list containing the requested result, or a 2D list with an error message string if the input is invalid.
This example function is provided as-is without any representation of accuracy.
"""
# Wrap scalar inputs coming from Excel into 2D lists
if not isinstance(A, list):
A = [[A]]
if not isinstance(B, list):
B = [[B]]
# Validate matrix structure for A and B
for name, matrix in (("A", A), ("B", B)):
if not isinstance(matrix, list) or len(matrix) == 0:
return [[f"Invalid input: {name} must be a 2D list with at least one row."]]
row_length = len(matrix[0]) if isinstance(matrix[0], list) else 0
if row_length == 0:
return [[f"Invalid input: {name} must be a 2D list with at least one column."]]
for row in matrix:
if not isinstance(row, list):
return [[f"Invalid input: {name} must be a 2D list with rows represented as lists."]]
if len(row) != row_length:
return [[f"Invalid input: each row in {name} must have the same length."]]
if len(A) != len(B):
return [["Invalid input: A and B must have the same number of rows."]]
# Convert matrices to numeric numpy arrays
try:
A_arr = np.array([[float(value) for value in row] for row in A], dtype=float)
B_arr = np.array([[float(value) for value in row] for row in B], dtype=float)
except Exception:
return [["Invalid input: A and B must contain numeric values."]]
# Validate optional parameters
if cond is not None:
try:
cond = float(cond)
except Exception:
return [["Invalid input: cond must be a float or None."]]
driver = str(lapack_driver).strip().lower()
if driver not in {"gelsd", "gelsy", "gelss"}:
return [["Invalid input: lapack_driver must be 'gelsd', 'gelsy', or 'gelss'."]]
result_type = str(return_type).strip().lower()
if result_type not in {"solution", "residuals", "rank", "singular_values"}:
return [["Invalid input: return_type must be one of 'solution', 'residuals', 'rank', 'singular_values'."]]
# Execute SciPy least-squares solver
try:
solution, residuals, effective_rank, singular_values = scipy_lstsq(
A_arr,
B_arr,
cond=cond,
lapack_driver=driver,
)
except Exception as exc:
return [[f"scipy.linalg.lstsq error: {exc}"]]
# Prepare helper to ensure outputs are finite and 2D
def finite_matrix(values: Union[np.ndarray, float, int]) -> MatrixReturn:
array = np.asarray(values, dtype=float)
if array.ndim == 0:
array = array.reshape(1, 1)
elif array.ndim == 1:
array = array.reshape(1, -1)
if array.size == 0:
return [[]]
if not np.isfinite(array).all():
return [["scipy.linalg.lstsq error: non-finite result encountered."]]
return array.tolist()
if result_type == "solution":
return finite_matrix(solution)
if result_type == "residuals":
return finite_matrix(residuals)
if result_type == "rank":
rank_matrix = finite_matrix(float(effective_rank))
return rank_matrix
if singular_values is None:
return [["Invalid input: singular values are unavailable for the selected LAPACK driver."]]
singular_values_matrix = finite_matrix(singular_values)
return singular_values_matrix