• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

morganjwilliams / pyrolite / 18810542081

29 Oct 2024 02:20AM UTC coverage: 91.571% (-0.07%) from 91.64%
18810542081

push

github

morganjwilliams
Merge branch 'release/0.3.6' into main

53 of 62 new or added lines in 12 files covered. (85.48%)

3 existing lines in 2 files now uncovered.

6225 of 6798 relevant lines covered (91.57%)

10.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.69
/pyrolite/plot/density/grid.py
1
import numpy as np
12✔
2

3
from ...util.distributions import sample_kde
12✔
4
from ...util.log import Handle
12✔
5
from ...util.math import flattengrid, linrng_, linspc_, logrng_, logspc_
12✔
6
from ...util.plot.grid import bin_centres_to_edges
12✔
7

8
logger = Handle(__name__)
12✔
9

10

11
class DensityGrid(object):
12✔
12
    def __init__(
12✔
13
        self, x, y, extent=None, bins=50, logx=False, logy=False, coverage_scale=1.2
14
    ):
15
        """
16
        Build a grid of x-y coordinates for use in evaluating KDE functions.
17

18
        Parameters
19
        -----------
20
        x : :class:`np.ndarray`
21
        y : :class:`np.ndarray`
22
        extent : :class:`list`
23
            Optionally-specified extent for the grid in the form (xmin, xmax, ymin, ymax).
24
        bins : :class:`int` | :class:`tuple`
25
            Number of bins for the grid. Can optionally specify
26
            a tuple with (xbins, ybins).
27
        logx : :class:`bool`
28
            Whether to use a log-spaced index for the x dimension of the grid.
29
        logy: :class:`bool`
30
            Whether to use a log-spaced index for the y dimension of the grid.
31
        coverage_scale : :class:`float`
32
            Multiplier for the range of the grid relative to the data. If >1, the grid
33
            will extend beyond the data.
34
        """
35
        # limits
36
        self.logx = logx
12✔
37
        self.logy = logy
12✔
38
        if not isinstance(bins, int):
12✔
39
            assert len(bins) == 2  # x-y bins
×
40
            self.xbins, self.ybins = bins
×
41
        else:
42
            self.xbins, self.ybins = bins, bins
12✔
43
        self.coverage_scale = coverage_scale
12✔
44

45
        if extent is None:
12✔
46
            self.xmin, self.xmax, self.ymin, self.ymax = self.extent_from_xy(x, y)
12✔
47
        else:
48
            self.xmin, self.xmax, self.ymin, self.ymax = extent
×
49
            # validation
NEW
50
            self.xmin, self.xmax = (
×
51
                min([self.xmin, self.xmax]),
52
                max([self.xmin, self.xmax]),
53
            )
NEW
54
            self.ymin, self.ymax = (
×
55
                min([self.ymin, self.ymax]),
56
                max([self.ymin, self.ymax]),
57
            )
58

59
        self.xstep = self.get_xstep()
12✔
60
        self.ystep = self.get_ystep()
12✔
61

62
        if self.logx:
12✔
63
            assert self.xmin > 0.0
12✔
64
            assert (self.xmin / self.xstep) > 0.0
12✔
65
        if self.logy:
12✔
66
            assert self.ymin > 0.0
12✔
67
            assert (self.ymin / self.ystep) > 0.0
12✔
68

69
        self.calculate_grid()
12✔
70

71
    def calculate_grid(self):
12✔
72
        self.get_centre_grid()
12✔
73
        self.get_edge_grid()
12✔
74

75
    def get_ystep(self):
12✔
76
        if self.logy:
12✔
77
            step = (self.ymax / self.ymin) / self.ybins
12✔
78
        else:
79
            step = (self.ymax - self.ymin) / self.ybins
12✔
80
        return step
12✔
81

82
    def get_xstep(self):
12✔
83
        if self.logx:
12✔
84
            step = (self.xmax / self.xmin) / self.xbins
12✔
85
        else:
86
            step = (self.xmax - self.xmin) / self.xbins
12✔
87
        return step
12✔
88

89
    def extent_from_xy(self, x, y, coverage_scale=None):
12✔
90
        cov = coverage_scale or self.coverage_scale
12✔
91
        expand_grid = (cov - 1.0) / 2
12✔
92
        return [
12✔
93
            *[linrng_, logrng_][self.logx](x, exp=expand_grid),
94
            *[linrng_, logrng_][self.logy](y, exp=expand_grid),
95
        ]
96

97
    def get_xrange(self):
12✔
98
        return self.xmin, self.xmax
12✔
99

100
    def get_yrange(self):
12✔
101
        return self.ymin, self.ymax
12✔
102

103
    def get_extent(self):
12✔
104
        return [*self.get_xrange(), *self.get_yrange()]
×
105

106
    def get_range(self):
12✔
107
        return [[*self.get_xrange()], [*self.get_yrange()]]
12✔
108

109
    def update_grid_centre_ticks(self):
12✔
110
        if self.logx:
12✔
111
            self.grid_xc = logspc_(self.xmin, self.xmax, 1.0, self.xbins)
12✔
112
        else:
113
            self.grid_xc = linspc_(self.xmin, self.xmax, self.xstep, self.xbins)
12✔
114

115
        if self.logy:
12✔
116
            self.grid_yc = logspc_(self.ymin, self.ymax, 1.0, self.ybins)
12✔
117
        else:
118
            self.grid_yc = linspc_(self.ymin, self.ymax, self.ystep, self.ybins)
12✔
119

120
    def update_grid_edge_ticks(self):
12✔
121
        self.update_grid_centre_ticks()
12✔
122
        if self.logx:
12✔
123
            self.grid_xe = np.exp(bin_centres_to_edges(np.log(np.sort(self.grid_xc))))
12✔
124
        else:
125
            self.grid_xe = bin_centres_to_edges(np.sort(self.grid_xc))
12✔
126

127
        if self.logy:
12✔
128
            self.grid_ye = np.exp(bin_centres_to_edges(np.log(np.sort(self.grid_yc))))
12✔
129
        else:
130
            self.grid_ye = bin_centres_to_edges(np.sort(self.grid_yc))
12✔
131

132
    def get_centre_grid(self):
12✔
133
        self.update_grid_centre_ticks()
12✔
134
        self.grid_xci, self.grid_yci = np.meshgrid(self.grid_xc, self.grid_yc)
12✔
135

136
    def get_edge_grid(self):
12✔
137
        self.update_grid_edge_ticks()
12✔
138
        self.grid_xei, self.grid_yei = np.meshgrid(self.grid_xe, self.grid_ye)
12✔
139

140
    def get_hex_extent(self):
12✔
141
        if self.logx:
12✔
142
            xex = [np.log(self.xmin / self.xstep), np.log(self.xmax * self.xstep)]
12✔
143
        else:
144
            xex = [self.xmin - self.xstep, self.xmax + self.xstep]
12✔
145

146
        if self.logy:
12✔
147
            yex = [np.log(self.ymin / self.ystep), np.log(self.ymax * self.ystep)]
12✔
148
        else:
149
            yex = [self.ymin - self.ystep, self.ymax + self.ystep]
12✔
150
        return xex + yex
12✔
151

152
    def kdefrom(
12✔
153
        self,
154
        xy,
155
        xtransform=lambda x: x,
156
        ytransform=lambda x: x,
157
        mode="centres",
158
        bw_method=None,
159
    ):
160
        """
161
        Take an x-y array and sample a KDE on the grid.
162
        """
163
        arr = xy.copy()
12✔
164
        # generate x grid over range spanned by log(x)
165
        arr[:, 0] = xtransform(xy[:, 0])
12✔
166
        # generate y grid over range spanned by log(y)
167
        arr[:, 1] = ytransform(xy[:, 1])
12✔
168
        if mode == "centres":
12✔
169
            assert np.isfinite(self.grid_xc).all() and np.isfinite(self.grid_yc).all()
×
170
            zi = sample_kde(
×
171
                arr,
172
                flattengrid(
173
                    np.meshgrid(xtransform(self.grid_xc), ytransform(self.grid_yc))
174
                ),
175
                bw_method=bw_method,
176
            )
177
            zi = zi.reshape(self.grid_xci.shape)
×
178
        elif mode == "edges":
12✔
179
            assert np.isfinite(self.grid_xe).all() and np.isfinite(self.grid_ye).all()
12✔
180
            zi = sample_kde(
12✔
181
                arr,
182
                flattengrid(
183
                    np.meshgrid(xtransform(self.grid_xe), ytransform(self.grid_ye))
184
                ),
185
                bw_method=bw_method,
186
            )
187
            zi = zi.reshape(self.grid_xei.shape)
12✔
188
        else:
189
            raise NotImplementedError("Valid modes are 'centres' and 'edges'.")
×
190
        return zi
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc