CLOUGH_TOCHER

This function wraps SciPy’s Clough-Tocher interpolator to estimate values on a smooth surface from scattered points in two dimensions. The method triangulates the input points and builds a continuously differentiable piecewise-cubic interpolant.

Given data points (x_i, y_i) with values z_i, the interpolant returns an estimate \hat{z}(x, y) for each query point while approximately minimizing surface curvature.

Excel Usage

=CLOUGH_TOCHER(points, values, xi, fill_value, tol, maxiter, rescale)
  • points (list[list], required): Input point coordinates as (n_points, 2).
  • values (list[list], required): Function values at input points as (n_points, 1).
  • xi (list[list], required): Query coordinates as (n_query, 2).
  • fill_value (float, optional, default: 0): Value used outside the convex hull.
  • tol (float, optional, default: 0.000001): Tolerance used in gradient estimation.
  • maxiter (int, optional, default: 400): Maximum iterations for gradient estimation.
  • rescale (bool, optional, default: false): Rescale points to unit cube before interpolation.

Returns (list[list]): Interpolated values as a 2D list (column vector), or an error message string.

Example 1: Interpolate center point on planar data

Inputs:

points values xi
0 0 0 0.5 0.5
1 0 1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5})

Expected output:

1

Example 2: Interpolate two interior points

Inputs:

points values xi
0 0 0 0.25 0.75
1 0 1 0.8 0.1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.25,0.75;0.8,0.1})

Expected output:

Result
1
0.9
Example 3: Use fill value outside convex hull

Inputs:

points values xi fill_value
0 0 0 2 2 -999
1 0 1
0 1 1
1 1 2

Excel formula:

=CLOUGH_TOCHER({0,0;1,0;0,1;1,1}, {0;1;1;2}, {2,2}, -999)

Expected output:

-999

Example 4: Interpolate with rescaling enabled

Inputs:

points values xi rescale
0 0 0 5 0.05 true
10 0 10
0 0.1 0.1
10 0.1 10.1

Excel formula:

=CLOUGH_TOCHER({0,0;10,0;0,0.1;10,0.1}, {0;10;0.1;10.1}, {5,0.05}, TRUE)

Expected output:

5.05

Python Code

import math
import numpy as np
from scipy.interpolate import CloughTocher2DInterpolator as scipy_CloughTocher2DInterpolator

def clough_tocher(points, values, xi, fill_value=0, tol=1e-06, maxiter=400, rescale=False):
    """
    Piecewise cubic C1 interpolation for scattered 2D data.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.CloughTocher2DInterpolator.html

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

    Args:
        points (list[list]): Input point coordinates as (n_points, 2).
        values (list[list]): Function values at input points as (n_points, 1).
        xi (list[list]): Query coordinates as (n_query, 2).
        fill_value (float, optional): Value used outside the convex hull. Default is 0.
        tol (float, optional): Tolerance used in gradient estimation. Default is 1e-06.
        maxiter (int, optional): Maximum iterations for gradient estimation. Default is 400.
        rescale (bool, optional): Rescale points to unit cube before interpolation. Default is False.

    Returns:
        list[list]: Interpolated values as a 2D list (column vector), or an error message string.
    """
    try:
        def to2d(x):
            return [[x]] if not isinstance(x, list) else x

        def flatten_2d(arr):
            out = []
            for row in arr:
                for val in row:
                    out.append(val)
            return out

        points = to2d(points)
        values = to2d(values)
        xi = to2d(xi)

        if not isinstance(points, list) or not all(isinstance(row, list) for row in points):
            return "Error: Invalid input: points must be a 2D list."
        if not isinstance(values, list) or not all(isinstance(row, list) for row in values):
            return "Error: Invalid input: values must be a 2D list."
        if not isinstance(xi, list) or not all(isinstance(row, list) for row in xi):
            return "Error: Invalid input: xi must be a 2D list."

        if len(points) == 0 or len(values) == 0 or len(xi) == 0:
            return "Error: Invalid input: points, values, and xi must not be empty."

        if any(len(row) != 2 for row in points):
            return "Error: Invalid input: points must have exactly 2 columns."
        if any(len(row) != 2 for row in xi):
            return "Error: Invalid input: xi must have exactly 2 columns."

        values_flat = flatten_2d(values)
        if len(values_flat) != len(points):
            return "Error: Invalid input: values must contain one value per point."

        for i, row in enumerate(points):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: points[{i}][{j}] must be numeric."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: points[{i}][{j}] must be finite."

        for i, val in enumerate(values_flat):
            if not isinstance(val, (int, float)):
                return f"Error: Invalid input: values element {i} must be numeric."
            if math.isnan(val) or math.isinf(val):
                return f"Error: Invalid input: values element {i} must be finite."

        for i, row in enumerate(xi):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Error: Invalid input: xi[{i}][{j}] must be numeric."
                if math.isnan(val) or math.isinf(val):
                    return f"Error: Invalid input: xi[{i}][{j}] must be finite."

        if not isinstance(fill_value, (int, float)):
            return "Error: Invalid input: fill_value must be numeric."
        if not isinstance(tol, (int, float)):
            return "Error: Invalid input: tol must be numeric."
        if not isinstance(maxiter, int):
            return "Error: Invalid input: maxiter must be an integer."
        if maxiter <= 0:
            return "Error: Invalid input: maxiter must be positive."
        if not isinstance(rescale, bool):
            return "Error: Invalid input: rescale must be boolean."

        points_arr = np.array(points, dtype=float)
        values_arr = np.array(values_flat, dtype=float)
        xi_arr = np.array(xi, dtype=float)

        interp = scipy_CloughTocher2DInterpolator(
            points_arr,
            values_arr,
            fill_value=float(fill_value),
            tol=float(tol),
            maxiter=maxiter,
            rescale=rescale
        )

        result = interp(xi_arr)
        result_arr = np.asarray(result, dtype=float).reshape(-1)
        return [[float(v)] for v in result_arr]
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

Input point coordinates as (n_points, 2).
Function values at input points as (n_points, 1).
Query coordinates as (n_query, 2).
Value used outside the convex hull.
Tolerance used in gradient estimation.
Maximum iterations for gradient estimation.
Rescale points to unit cube before interpolation.