def decluster(coord, input_array, scale, min_samples=1, verbose=False):
    """
    Decluster a set of sparse data points by aggregating, that is, taking
    the median value of all values lying within a certain distance (i.e., a
    cluster).

    Parameters
    ----------
    coord: array_like
        Array of shape (n, d) containing the coordinates of the input data into
        a space of *d* dimensions.
    input_array: array_like
        Array of shape (n) or (n, m), where *n* is the number of samples and
        *m* the number of variables.
        All values in ``input_array`` are required to have finite values.
    scale: float or array_like
        The ``scale`` parameter in the same units of ``coord``.
        It can be a scalar or an array_like of shape (d).
        Data points within the declustering ``scale`` are aggregated.
    min_samples: int, optional
        The minimum number of samples for computing the median within a given
        cluster.
    verbose: bool, optional
        Print out information.

    Returns
    -------
    out: tuple of ndarrays
        A two-element tuple (``out_coord``, ``output_array``) containing the
        declustered coordinates (l, d) and input array (l, m), where *l* is
        the new number of samples with *l* <= *n*.
    """

    coord = np.copy(coord)
    input_array = np.copy(input_array)

    # check inputs
    if np.any(~np.isfinite(input_array)):
        raise ValueError("input_array contains non-finite values")

    if input_array.ndim == 1:
        nvar = 1
        input_array = input_array[:, None]
    elif input_array.ndim == 2:
        nvar = input_array.shape[1]
    else:
        raise ValueError(
            "input_array must have 1 (n) or 2 dimensions (n, m), but it has %i"
            % input_array.ndim
        )

    if coord.ndim != 2:
        raise ValueError(
            "coord must have 2 dimensions (n, d), but it has %i" % coord.ndim
        )
    if coord.shape[0] != input_array.shape[0]:
        raise ValueError(
            "the number of samples in the input_array does not match the "
            + "number of coordinates %i!=%i" % (input_array.shape[0], coord.shape[0])
        )

    if np.isscalar(scale):
        scale = float(scale)
    else:
        scale = np.copy(scale)
        if scale.ndim != 1:
            raise ValueError(
                "scale must have 1 dimension (d), but it has %i" % scale.ndim
            )
        if scale.shape[0] != coord.shape[1]:
            raise ValueError(
                "scale must have %i elements, but it has %i"
                % (coord.shape[1], scale.shape[0])
            )
        scale = scale[None, :]

    # reduce original coordinates
    coord_ = np.floor(coord / scale)

    # keep only unique pairs of the reduced coordinates
    ucoord_ = np.unique(coord_, axis=0)

    # loop through these unique values and average data points which belong to
    # the same cluster
    dinput = np.empty(shape=(0, nvar))
    dcoord = np.empty(shape=(0, coord.shape[1]))
    for i in range(ucoord_.shape[0]):
        idx = np.all(coord_ == ucoord_[i, :], axis=1)
        npoints = np.sum(idx)
        if npoints >= min_samples:
            dinput = np.append(
                dinput, np.median(input_array[idx, :], axis=0)[None, :], axis=0
            )
            dcoord = np.append(
                dcoord, np.median(coord[idx, :], axis=0)[None, :], axis=0
            )

    if verbose:
        print("--- %i samples left after declustering ---" % dinput.shape[0])

    return dcoord, dinput
Decluster a set of sparse data points by aggregating, that is, taking
the median value of all values lying within a certain distance (i.e., a
cluster).

Parameters
----------
coord: array_like
Array of shape (n, d) containing the coordinates of the input data into
a space of *d* dimensions.
input_array: array_like
Array of shape (n) or (n, m), where *n* is the number of samples and
*m* the number of variables.
All values in ``input_array`` are required to have finite values.
scale: float or array_like
The ``scale`` parameter in the same units of ``coord``.
It can be a scalar or an array_like of shape (d).
Data points within the declustering ``scale`` are aggregated.
min_samples: int, optional
The minimum number of samples for computing the median within a given
cluster.
verbose: bool, optional
Print out information.

Returns
-------
out: tuple of ndarrays
A two-element tuple (``out_coord``, ``output_array``) containing the
declustered coordinates (l, d) and input array (l, m), where *l* is
the new number of samples with *l* <= *n*.
pysteps/utils/cleansing.py