• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

morganjwilliams / pyrolite / 11624563836

01 Nov 2024 04:58AM UTC coverage: 91.367% (-0.2%) from 91.538%
11624563836

push

github

morganjwilliams
Update get_ionic_radii test

6223 of 6811 relevant lines covered (91.37%)

10.95 hits per line

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

93.1
/pyrolite/util/plot/style.py
1
"""
2
Functions for automated plot styling and argument handling.
3

4
Attributes
5
----------
6
DEFAULT_CONT_COLORMAP : :class:`matplotlib.colors.ScalarMappable`
7
    Default continuous colormap.
8
DEFAULT_DISC_COLORMAP : :class:`matplotlib.colors.ScalarMappable`
9
    Default discrete colormap.
10
"""
11

12
import itertools
12✔
13
from pathlib import Path
12✔
14

15
import matplotlib
12✔
16
import matplotlib.axes
12✔
17
import matplotlib.collections
12✔
18
import matplotlib.colors
12✔
19
import matplotlib.lines
12✔
20
import matplotlib.patches
12✔
21
import matplotlib.pyplot as plt
12✔
22
import numpy as np
12✔
23
import pandas as pd
12✔
24
from matplotlib.legend_handler import HandlerTuple
12✔
25

26
from ...comp.codata import close
12✔
27
from ..general import copy_file
12✔
28
from ..log import Handle
12✔
29
from ..meta import pyrolite_datafolder, subkwargs
12✔
30
from .helpers import get_centroid
12✔
31
from .transform import xy_to_tlr
12✔
32

33
logger = Handle(__name__)
12✔
34

35
DEFAULT_CONT_COLORMAP = plt.cm.viridis
12✔
36
DEFAULT_DISC_COLORMAP = plt.cm.tab10
12✔
37

38

39
def _export_mplstyle(
12✔
40
    src=pyrolite_datafolder("_config") / "pyrolite.mplstyle", refresh=False
41
):
42
    """
43
    Export a matplotlib style file to the matplotlib style library such that one can
44
    use e.g. `matplotlib.style.use("pyrolite")`.
45

46
    Parameters
47
    -----------
48
    src : :class:`str` | :class:`pathlib.Path`
49
        File path for the style file to be exported.
50
    refresh : :class:`bool`
51
        Whether to re-export a style file (e.g. after updating) even if it
52
        already exists in the matplotlib style libary.
53
    """
54
    src_fn = Path(src)
12✔
55
    dest_dir = Path(matplotlib.get_configdir()) / "stylelib"
12✔
56
    dest_fn = dest_dir / src.name
12✔
57
    if (not dest_fn.exists()) or refresh:
12✔
58
        logger.debug("Exporting pyrolite.mplstyle to matplotlib config folder.")
12✔
59
        if not dest_dir.exists():
12✔
60
            dest_dir.mkdir(parents=True)
12✔
61
        copy_file(src_fn, dest_dir)  # copy to the destination DIR
12✔
62
        logger.debug("Reloading matplotlib")
12✔
63
    matplotlib.style.reload_library()  # needed to load in pyrolite style NOW
12✔
64

65

66
def _restyle(f, **_style):
12✔
67
    """
68
    A decorator to set the default keyword arguments for :mod:`matplotlib`
69
    functions and classes which are not contained in the `matplotlibrc` file.
70
    """
71

72
    def wrapped(*args, **kwargs):
12✔
73
        return f(*args, **{**_style, **kwargs})
12✔
74

75
    wrapped.__name__ = f.__name__
12✔
76
    wrapped.__doc__ = f.__doc__
12✔
77
    return wrapped
12✔
78

79

80
def _export_nonRCstyles(**kwargs):
12✔
81
    """
82
    Export default options for parameters not in rcParams using :func:`_restyle`.
83
    """
84
    matplotlib.axes.Axes.legend = _restyle(
12✔
85
        matplotlib.axes.Axes.legend, **{"bbox_to_anchor": (1, 1), **kwargs}
86
    )
87
    matplotlib.figure.Figure.legend = _restyle(
12✔
88
        matplotlib.figure.Figure.legend, bbox_to_anchor=(1, 1)
89
    )
90

91

92
_export_mplstyle()
12✔
93
_export_nonRCstyles(handler_map={tuple: HandlerTuple(ndivide=None)})
12✔
94
matplotlib.style.use("pyrolite")
12✔
95

96

97
def linekwargs(kwargs):
12✔
98
    """
99
    Get a subset of keyword arguments to pass to a matplotlib line-plot call.
100

101
    Parameters
102
    -----------
103
    kwargs : :class:`dict`
104
        Dictionary of keyword arguments to subset.
105

106
    Returns
107
    --------
108
    :class:`dict`
109
    """
110
    kw = subkwargs(
12✔
111
        kwargs,
112
        plt.plot,
113
        matplotlib.axes.Axes.plot,
114
        matplotlib.lines.Line2D,
115
        matplotlib.collections.Collection,
116
    )
117
    # could trim cmap and norm here, in case they get passed accidentally
118
    kw.update(
12✔
119
        **dict(
120
            alpha=kwargs.get("alpha"),
121
            label=kwargs.get("label"),
122
            clip_on=kwargs.get("clip_on", True),
123
        )
124
    )  # issues with introspection for alpha
125
    return kw
12✔
126

127

128
def scatterkwargs(kwargs):
12✔
129
    """
130
    Get a subset of keyword arguments to pass to a matplotlib scatter call.
131

132
    Parameters
133
    -----------
134
    kwargs : :class:`dict`
135
        Dictionary of keyword arguments to subset.
136

137
    Returns
138
    --------
139
    :class:`dict`
140
    """
141
    kw = subkwargs(
12✔
142
        kwargs,
143
        plt.scatter,
144
        matplotlib.axes.Axes.scatter,
145
        matplotlib.collections.Collection,
146
    )
147
    kw.update(
12✔
148
        **dict(
149
            alpha=kwargs.get("alpha"),
150
            label=kwargs.get("label"),
151
            clip_on=kwargs.get("clip_on", True),
152
        )
153
    )  # issues with introspection for alpha
154
    return kw
12✔
155

156

157
def patchkwargs(kwargs):
12✔
158
    kw = subkwargs(
12✔
159
        kwargs,
160
        matplotlib.axes.Axes.fill_between,
161
        matplotlib.collections.PolyCollection,
162
        matplotlib.patches.Patch,
163
    )
164
    kw.update(
12✔
165
        **dict(
166
            alpha=kwargs.get("alpha"),
167
            label=kwargs.get("label"),
168
            clip_on=kwargs.get("clip_on", True),
169
        )
170
    )  # issues with introspection for alpha
171
    return kw
12✔
172

173

174
def _mpl_sp_kw_split(kwargs):
12✔
175
    """
176
    Process keyword arguments supplied to a matplotlib plot function.
177

178
    Returns
179
    --------
180
    :class:`tuple` ( :class:`dict`, :class:`dict` )
181
    """
182
    sctr_kwargs = scatterkwargs(kwargs)
12✔
183
    # c kwarg is first priority, if it isn't present, use the color arg
184
    if sctr_kwargs.get("c") is None:
12✔
185
        sctr_kwargs = {**sctr_kwargs, **{"c": kwargs.get("color")}}
×
186

187
    line_kwargs = linekwargs(kwargs)
12✔
188
    return sctr_kwargs, line_kwargs
12✔
189

190

191
def marker_cycle(markers=["D", "s", "o", "+", "*"]):
12✔
192
    """
193
    Cycle through a set of markers.
194

195
    Parameters
196
    ----------
197
    markers : :class:`list`
198
        List of markers to provide to matplotlib.
199
    """
200
    return itertools.cycle(markers)
12✔
201

202

203
def mappable_from_values(values, cmap=DEFAULT_CONT_COLORMAP, norm=None, **kwargs):
12✔
204
    """
205
    Create a scalar mappable object from an array of values.
206

207
    Returns
208
    -------
209
    :class:`matplotlib.cm.ScalarMappable`
210
    """
211
    if isinstance(values, pd.Series):
12✔
212
        values = values.values
12✔
213
    sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
12✔
214
    sm.set_array(values[np.isfinite(values)])
12✔
215
    return sm
12✔
216

217

218
def ternary_color(
12✔
219
    tlr,
220
    alpha=1.0,
221
    colors=([1, 0, 0], [0, 1, 0], [0, 0, 1]),
222
    coefficients=(1, 1, 1),
223
):
224
    """
225
    Color a set of points by their ternary combinations of three specified colors.
226

227
    Parameters
228
    ----------
229
    tlr : :class:`numpy.ndarray`
230

231
    alpha : :class:`float`
232
        Alpha coefficient for the colors; to be applied *multiplicatively* with
233
        any existing alpha value (for RGBA colors specified).
234
    colors : :class:`tuple`
235
        Set of colours corresponding to the top, left and right verticies,
236
        respectively.
237
    coefficients : :class:`tuple`
238
        Coefficients for the ternary data to adjust the centre.
239

240
    Returns
241
    -------
242
    :class:`numpy.ndarray`
243
        Color array for the ternary points.
244
    """
245
    colors = np.array([matplotlib.colors.to_rgba(c) for c in colors], dtype=float)
12✔
246
    _tlr = close(np.array(tlr) * np.array(coefficients))
12✔
247
    color = np.atleast_2d(_tlr @ colors)
12✔
248
    color[:, -1] *= alpha * (1 - 10e-7)  # avoid 'greater than 1' issues
12✔
249
    return color
12✔
250

251

252
def color_ternary_polygons_by_centroid(
12✔
253
    ax=None,
254
    patches=None,
255
    alpha=1.0,
256
    colors=([1, 0, 0], [0, 1, 0], [0, 0, 1]),
257
    coefficients=(1, 1, 1),
258
):
259
    """
260
    Color a set of polygons within a ternary diagram by their centroid colors.
261

262
    Parameters
263
    ----------
264
    ax : :class:`matplotlib.axes.Axes`
265
        Ternary axes to check for patches, if patches is not supplied.
266
    patches : :class:`list`
267
        List of ternary-hosted patches to apply color to.
268
    alpha : :class:`float`
269
        Alpha coefficient for the colors; to be applied *multiplicatively* with
270
        any existing alpha value (for RGBA colors specified).
271
    colors : :class:`tuple`
272
        Set of colours corresponding to the top, left and right verticies,
273
        respectively.
274
    coefficients : :class:`tuple`
275
        Coefficients for the ternary data to adjust the centre.
276

277
    Returns
278
    -------
279
    patches : :class:`list`
280
        List of patches, with updated facecolors.
281
    """
282

283
    if patches is None:
12✔
284
        if ax is None:
12✔
285
            raise NotImplementedError("Either an axis or patches need to be supplied.")
×
286
        patches = ax.patches
12✔
287

288
    for poly in patches:
12✔
289
        xy = get_centroid(poly)
×
290
        tlr = xy_to_tlr(np.array([xy]))[0]
×
291
        color = ternary_color(
×
292
            tlr, alpha=alpha, colors=colors, coefficients=coefficients
293
        )
294
        poly.set_facecolor(color)
×
295

296
    return patches
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