Skip to content

Rank-based change (CostRank)#

Bases: BaseCost

Rank-based cost function.

Source code in ruptures/costs/costrank.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class CostRank(BaseCost):
    r"""Rank-based cost function."""

    model = "rank"

    def __init__(self):
        """Initialize the object."""
        self.inv_cov = None
        self.ranks = None
        self.min_size = 2

    def fit(self, signal) -> "CostRank":
        """Set parameters of the instance.

        Args:
            signal (array): signal. Shape (n_samples,) or (n_samples, n_features)

        Returns:
            self
        """
        if signal.ndim == 1:
            signal = signal.reshape(-1, 1)

        obs, vars = signal.shape

        # Convert signal data into ranks in the range [1, n]
        ranks = rankdata(signal, axis=0)
        # Center the ranks into the range [-(n+1)/2, (n+1)/2]
        centered_ranks = ranks - ((obs + 1) / 2)
        # Sigma is the covariance of these ranks.
        # If it's a scalar, reshape it into a 1x1 matrix
        cov = np.cov(centered_ranks, rowvar=False, bias=True).reshape(vars, vars)

        # Use the pseudoinverse to handle linear dependencies
        # see Lung-Yut-Fong, A., Lévy-Leduc, C., & Cappé, O. (2015)
        try:
            self.inv_cov = pinv(cov)
        except LinAlgError as e:
            raise LinAlgError(
                "The covariance matrix of the rank signal is not invertible and the "
                "pseudo-inverse computation did not converge."
            ) from e
        self.ranks = centered_ranks
        self.signal = signal

        return self

    def error(self, start, end):
        """Return the approximation cost on the segment [start:end].

        Args:
            start (int): start of the segment
            end (int): end of the segment

        Returns:
            float: segment cost

        Raises:
            NotEnoughPoints: when the segment is too short (less than `min_size` samples).
        """
        if end - start < self.min_size:
            raise NotEnoughPoints

        mean = np.reshape(np.mean(self.ranks[start:end], axis=0), (-1, 1))

        return -(end - start) * mean.T @ self.inv_cov @ mean

__init__() #

Initialize the object.

Source code in ruptures/costs/costrank.py
15
16
17
18
19
def __init__(self):
    """Initialize the object."""
    self.inv_cov = None
    self.ranks = None
    self.min_size = 2

error(start, end) #

Return the approximation cost on the segment [start:end].

Parameters:

Name Type Description Default
start int

start of the segment

required
end int

end of the segment

required

Returns:

Name Type Description
float

segment cost

Raises:

Type Description
NotEnoughPoints

when the segment is too short (less than min_size samples).

Source code in ruptures/costs/costrank.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def error(self, start, end):
    """Return the approximation cost on the segment [start:end].

    Args:
        start (int): start of the segment
        end (int): end of the segment

    Returns:
        float: segment cost

    Raises:
        NotEnoughPoints: when the segment is too short (less than `min_size` samples).
    """
    if end - start < self.min_size:
        raise NotEnoughPoints

    mean = np.reshape(np.mean(self.ranks[start:end], axis=0), (-1, 1))

    return -(end - start) * mean.T @ self.inv_cov @ mean

fit(signal) #

Set parameters of the instance.

Parameters:

Name Type Description Default
signal array

signal. Shape (n_samples,) or (n_samples, n_features)

required

Returns:

Type Description
CostRank

self

Source code in ruptures/costs/costrank.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def fit(self, signal) -> "CostRank":
    """Set parameters of the instance.

    Args:
        signal (array): signal. Shape (n_samples,) or (n_samples, n_features)

    Returns:
        self
    """
    if signal.ndim == 1:
        signal = signal.reshape(-1, 1)

    obs, vars = signal.shape

    # Convert signal data into ranks in the range [1, n]
    ranks = rankdata(signal, axis=0)
    # Center the ranks into the range [-(n+1)/2, (n+1)/2]
    centered_ranks = ranks - ((obs + 1) / 2)
    # Sigma is the covariance of these ranks.
    # If it's a scalar, reshape it into a 1x1 matrix
    cov = np.cov(centered_ranks, rowvar=False, bias=True).reshape(vars, vars)

    # Use the pseudoinverse to handle linear dependencies
    # see Lung-Yut-Fong, A., Lévy-Leduc, C., & Cappé, O. (2015)
    try:
        self.inv_cov = pinv(cov)
    except LinAlgError as e:
        raise LinAlgError(
            "The covariance matrix of the rank signal is not invertible and the "
            "pseudo-inverse computation did not converge."
        ) from e
    self.ranks = centered_ranks
    self.signal = signal

    return self