File: pruning.py

package info (click to toggle)
astrodendro 0.2.0%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,928 kB
  • sloc: python: 5,911; makefile: 128; sh: 59
file content (156 lines) | stat: -rw-r--r-- 4,190 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# Licensed under an MIT open source license - see LICENSE
"""
The pruning module provides several functions to perform common
pruning via the ``is_independent`` keyword in the Dendrogram
:meth:`~astrodendro.dendrogram.Dendrogram.compute` method.

Examples::

    #prune unless leaf peak value >= 5
    Dendrogram.compute(data, is_independent=min_peak(5))

    #prune unless leaf contains 10 pixels
    Dendrogram.compute(data, is_independent=min_npix(10))

    #apply both criteria
    is_independent = all_true((min_peak(5), min_npix(10)))
    Dendrogram.compute(data, is_independent=is_independent)
"""

import numpy as np


def _ravel_multi_index(multi_index, dims, mode='raise'):
    # partial implementation of ravel_multi_index,
    # for compatibility with numpy <= 1.5
    # does not implement order kwarg
    ndim = len(dims)

    if len(multi_index) != len(dims):
        raise ValueError("parameter multi_index must be "
                         "a sequence of length %i" % ndim)

    indices = [np.asarray(m) for m in multi_index]
    if mode == 'raise':
        for i, d in zip(indices, dims):
            if ((i < 0) | (i >= d)).any():
                raise ValueError("invalid entry in coordinates array")
    elif mode == 'clip':
        indices = [np.clip(i, 0, d - 1) for i, d in zip(indices, dims)]
    else:  # mode == 'wrap'
        indices = [i % d for i, d in zip(indices, dims)]

    result = np.zeros(len(multi_index[0]), dtype=int)
    offset = 1
    for i, d in list(zip(indices, dims))[::-1]:
        result += (i * offset).ravel()
        offset *= d

    return result


if not hasattr(np, 'ravel_multi_index'):
    np.ravel_multi_index = _ravel_multi_index


def all_true(funcs):
    """Combine several ``is_independent`` functions into one

    Parameters
    ----------
    funcs : list-like
        A list of ``is_independent`` functions

    Returns
    -------
    combined_func : function
        A new function which returns true of all the input functions are true
    """
    def result(*args, **kwargs):
        return all(f(*args, **kwargs) for f in funcs)
    return result


def min_delta(delta):
    """
    Minimum delta criteria

    Parameters
    ----------
    delta : float
        The minimum height of a leaf above its merger level

    """
    def result(structure, index=None, value=None):
        if value is None:
            if structure.parent is not None:
                return (structure.height - structure.parent.height) >= delta

            return (structure.vmax - structure.vmin) >= delta
        return (structure.vmax - value) >= delta
    return result


def min_sum(sum):
    """
    Minimum sum criteria

    Parameters
    ----------
    sum : float
        The minimum sum of the pixel values in a leaf
    """
    def result(structure, index=None, value=None):
        return np.nansum(structure.values()) >= sum
    return result


def min_peak(peak):
    """
    Minimum peak criteria

    Parameters
    ----------
    peak : float
        The minimum peak pixel value in a leaf
    """
    def result(structure, index=None, value=None):
        return structure.vmax >= peak
    return result


def min_npix(npix):
    """
    Minimum npix criteria

    Parameters
    ----------
    npix : int
        The minimum number of pixels in a leaf
    """
    def result(structure, index=None, value=None):
        return len(structure.values()) >= npix
    return result


def contains_seeds(seeds):
    """
    Critieria that leaves contain at least one of a list of seed positions

    Parameters
    ----------
    seeds : tuple of array-like
        seed locations. The ith array in the tuple lists the ith coordinate
        for each seed. This is the format returned, e.g., by np.where
    """
    shp = [np.asarray(s).max() + 2 for s in seeds]
    rav = np.ravel_multi_index(seeds, shp)

    def result(structure, index=None, value=None):
        sid = structure.indices()
        if len(sid) != len(seeds):
            raise TypeError("Dimensions of seeds and data do not agree")
        rav2 = np.ravel_multi_index(sid, shp, mode='clip')
        return np.intersect1d(rav, rav2).size > 0

    return result