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

APN-Pucky / smpl / 28609932887

02 Jul 2026 05:40PM UTC coverage: 86.835% (-2.0%) from 88.798%
28609932887

Pull #358

github

web-flow
Merge 245f6c3af into 6042d4ac0
Pull Request #358: Add py3.14

5 of 6 new or added lines in 2 files covered. (83.33%)

34 existing lines in 2 files now uncovered.

1583 of 1823 relevant lines covered (86.83%)

1.74 hits per line

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

81.53
/smpl/plot.py
1
"""Simplified plotting."""
2

3
import matplotlib
2✔
4
import matplotlib.pyplot as plt
2✔
5
import numpy as np
2✔
6
import smplr
2✔
7
import sympy
2✔
8
import uncertainties
2✔
9
import uncertainties.unumpy as unp
2✔
10
from matplotlib import pylab
2✔
11
from matplotlib.pyplot import *
2✔
12

13
# local imports
14
from smpl import doc, interpolate, io, stat, util, wrap
2✔
15
from smpl import fit as ffit
2✔
16

17

18
def set_plot_style():
2✔
19
    # fig_size = (8, 6)
20
    # fig_legendsize = 14
21
    # fig_labelsize = 12 # ‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’.
22
    params = {
×
23
        "legend.fontsize": "x-large",
24
        "figure.figsize": (8, 6),
25
        "axes.labelsize": "x-large",
26
        "axes.titlesize": "x-large",
27
        "xtick.labelsize": "x-large",
28
        "ytick.labelsize": "x-large",
29
    }
30
    pylab.rcParams.update(params)
×
31
    matplotlib.rcParams.update(params)
×
32
    # matplotlib.rcParams.update({'font.size': fig_labelsize})
33
    # colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
34

35

36
unv = unp.nominal_values
2✔
37
usd = unp.std_devs
2✔
38

39
default = {
2✔
40
    #    'params'        :[None      ,"Initial fit parameters",
41
    "title": [None, "Plot title"],
42
    "xlabel": [
43
        "",
44
        "X axis label",
45
    ],
46
    "ylabel": [
47
        "",
48
        "Y axis label",
49
    ],
50
    "label": [
51
        None,
52
        "Legend name of plotted ``data``",
53
    ],
54
    "fmt": [
55
        ".",
56
        "Format for plotting fit function (could be a line style, marker or a combination of both or 'step')",
57
    ],
58
    "where": [
59
        "mid",
60
        "Where to place the ticks. Possible values are 'pre', 'post', 'mid', 'edge'. Might be incompatible with uncertainties.",
61
    ],
62
    "units": [
63
        None,
64
        "Units of the fit parameters as strings. Displayed in the Legend",
65
    ],
66
    "save": [
67
        None,
68
        " File to save the plot",
69
    ],
70
    "lpos": [
71
        0,
72
        "Legend position",
73
    ],
74
    "tight": [
75
        True,
76
        "tight_layout",
77
    ],
78
    #          'frange'        :[None      ,"Limit the fit to given range. First integer is the lowest and second the highest index.",],
79
    "prange": [
80
        None,
81
        "Limit the plot of the fit to given range",
82
    ],
83
    "sigmas": [
84
        0,
85
        "Color the array of given ``sigma`` times uncertainty. Only works if the fit function is coded with ``unp``",
86
    ],
87
    "data_sigmas": [
88
        1,
89
        "Color the array of given ``sigma`` times uncertainty. Only works if the data has uncertainties",
90
    ],
91
    "init": [False, "Initialize a new plot"],
92
    "ss": [
93
        True,
94
        "save, add legends and grid to the plot",
95
    ],
96
    "also_data": [True, " also plot the data"],
97
    "also_fit": [
98
        True,
99
        "also plot the fit",
100
    ],
101
    "auto_fit": [
102
        False,
103
        "automatically fit",
104
    ],
105
    "logy": [
106
        False,
107
        "logarithmic x axis",
108
    ],
109
    "logx": [
110
        False,
111
        "logarithmic y axis",
112
    ],
113
    "function_color": [
114
        None,
115
        "Color of the function plot",
116
    ],
117
    "data_color": [
118
        None,
119
        "Color of the data plot",
120
    ],
121
    "fit_color": [
122
        None,
123
        "Color of the fit plot",
124
    ],
125
    "fit_fmt": [
126
        "-",
127
        "Format of the fit plot",
128
    ],
129
    "residue": [
130
        False,
131
        "Display difference between fit and data in a second plot",
132
    ],
133
    "residue_err": [
134
        True,
135
        "Differences between fit and data will have errorbars",
136
    ],
137
    "ratio": [
138
        False,
139
        "Display ratio between fit and data in a second plot",
140
    ],
141
    "ratio_err": [
142
        True,
143
        "Ratio between fit and data will have errorbars",
144
    ],
145
    "show": [
146
        False,
147
        "Call plt.show()",
148
    ],
149
    "size": [
150
        None,
151
        "Size of the plot as a tuple (x,y). Only has an effect if ``init`` is True",
152
    ],
153
    "number_format": [
154
        io.gf(4),
155
        "Format to display numbers.",
156
    ],
157
    # ,          'selector'      :[ None     ,"Function that takes ``x`` and ``y`` as parameters and returns an array mask in order to limit the data points for fitting. Alternatively a mask for selecting elements from datax and datay.",],
158
    # ,          'fixed_params'  :[ True     ,"Enable fixing parameters by choosing the same-named variables from ``kwargs``.",],
159
    # ,          'sortbyx'       :[ True     , "Enable sorting the x and y data so that x is sorted.",],
160
    "interpolate": [False, "Enable interpolation of the data."],
161
    "interpolate_fmt": [
162
        "-",
163
        "Either format string or linestyle tuple.",
164
    ],
165
    "interpolate_label": [
166
        None,
167
        "Label for the interpolation.",
168
    ],
169
    "extrapolate": [
170
        True,
171
        "Enable extrapolation of whole data if fit range is limited by ``frange`` or ``fselector``.",
172
    ],
173
    "extrapolate_min": [
174
        None,
175
        "Lower extrapolation bound",
176
    ],
177
    "extrapolate_max": [
178
        None,
179
        "Higher extrapolation bound",
180
    ],
181
    "extrapolate_fmt": [
182
        "--",
183
        "Format of the extrapolation line",
184
    ],
185
    "extrapolate_hatch": [
186
        r"||",
187
        "Extrapolation shape/hatch for filled area in case of ``sigmas``>0. See https://matplotlib.org/stable/gallery/shapes_and_collections/hatch_style_reference.html",
188
    ],
189
    "bbox_to_anchor": [
190
        None,
191
        "Position in a tuple (x,y),Shift position of the legend out of the main pane. ",
192
    ],
193
    "ncol": [
194
        None,
195
        "Columns in the legend if used with ``bbox_to_anchor``.",
196
    ],
197
    "steps": [
198
        1000,
199
        "resolution of the plotted function",
200
    ],
201
    "fitinline": [
202
        False,
203
        "No newlines for each fit parameter",
204
    ],
205
    "grid": [
206
        True,
207
        "Enable grid for the plot",
208
    ],
209
    "hist": [
210
        False,
211
        "Enable histogram plot",
212
    ],
213
    "stairs": [
214
        False,
215
        "Enable stair plot",
216
    ],
217
    "capsize": [5, "size of cap on error bar plot"],
218
    "axes": [None, "set current axis"],
219
    "linestyle": [None, "linestyle, only active if `fmt`=None"],
220
    "xspace": [
221
        np.linspace,
222
        "xspace gets called with xspace(xmin,xmax,steps) in :func:`function` to get the points of the function that will be drawn.",
223
    ],
224
    "alpha": [0.2, "alpha value for the fill_between plot"],
225
    "append_chi2": [False, "Append chi2 to legend"],
226
    "append_r2": [False, "Append r2 to legend"],
227
    "append_ndf": [False, "Append number of degrees of freedom to legend"],
228
}
229

230

231
@doc.append_doc(ffit.fit_kwargs)
2✔
232
@doc.append_str("\t")
2✔
233
@doc.append_str(doc.array_table(default, init=False))
2✔
234
@doc.append_str(
2✔
235
    doc.array_table({"plot_kwargs        ": ["default", "description"]}, bottom=False)
236
)
237
@doc.append_str("\n\n")
2✔
238
def plot_kwargs(kwargs):
2✔
239
    """Set default :func:`plot_kwargs` if not set."""
240
    kwargs = ffit.fit_kwargs(kwargs)
2✔
241
    for k, v in default.items():
2✔
242
        if k not in kwargs:
2✔
243
            kwargs[k] = v[0]
2✔
244
    return kwargs
2✔
245

246

247
# TODO optimize to minimize number of calls to function
248
# @append_doc(default_kwargs)
249

250

251
def fit(func, *adata, **kwargs):
2✔
252
    """
253
    Fit and plot function to datax and datay.
254

255
    Parameters
256
    ----------
257
    datax : array_like
258
        X data either as ``unp.uarray`` or ``np.array`` or ``list``
259
    datay : array_like
260
        Y data either as ``unp.uarray`` or ``np.array`` or ``list``
261
    function : func
262
        Fit function with parameters: ``x``, ``params``
263
    **kwargs : optional
264
        see :func:`plot_kwargs`.
265
    Fit parameters can be fixed via ``kwargs`` eg. ``a=5``.
266

267
    Returns
268
    -------
269
    array_like
270
        Optimized fit parameters of ``function`` to ``datax`` and ``datay``.
271
        If ``datay`` is complex, both the real and imaginary part are returned.
272

273
    Examples
274
    --------
275

276
    .. plot::
277
        :include-source:
278

279
        >>> from smpl import functions as f
280
        >>> from smpl import plot
281
        >>> param = plot.fit([0,1,2],[0,1,2],f.line)
282
        >>> float(plot.unv(param).round()[0])
283
        1.0
284

285
    """
286
    if "xaxis" in kwargs and ("xlabel" not in kwargs or not kwargs["xlabel"]):
2✔
287
        # warnings.warn("xaxis is deprecated. Use xlabel instead.", DeprecationWarning, 2)
288
        kwargs["xlabel"] = kwargs["xaxis"]
2✔
289
        # TODO maybe pop
290
    if "yaxis" in kwargs and ("ylabel" not in kwargs or not kwargs["ylabel"]):
2✔
291
        # warnings.warn("yaxis is deprecated. Use ylabel instead.", DeprecationWarning, 2)
292
        kwargs["ylabel"] = kwargs["yaxis"]
2✔
293
        # TODO maybe pop
294

295
    function = func
2✔
296
    if "function" in kwargs:
2✔
297
        function = kwargs["function"]
2✔
298
        del kwargs["function"]
2✔
299
        adata = [func, *adata]
2✔
300
    # Fix parameter order if necessary
301
    elif isinstance(function, (list, tuple, np.ndarray)):
2✔
302
        adata = [adata[-1], function, *adata[:-1]]
2✔
303
        function = adata[0]
2✔
304
        adata = adata[1:]
2✔
305
    if util.true("bins", kwargs):
2✔
306
        # yvalue will be overwritten
307
        ndata = [*adata, *adata]
2✔
308
        for i, o in enumerate(adata):
2✔
309
            ndata[2 * i] = o
2✔
310
            ndata[2 * i + 1] = o * 0
2✔
311
        adata = ndata
2✔
312

313
    assert len(adata) % 2 == 0, "data must be pairs of x and y data"
2✔
314
    if len(adata) == 2:
2✔
315
        datax, datay = adata
2✔
316
    else:
317
        rs = []
2✔
318
        for i in range(0, len(adata), 2):
2✔
319
            datax, datay = adata[i], adata[i + 1]
2✔
320
            if util.true("bins", kwargs):
2✔
321
                rs.append(fit(function, datax, **kwargs))
2✔
322
            else:
323
                rs.append(fit(function, datax, datay, **kwargs))
2✔
324
        return rs
2✔
325

326
    kwargs = plot_kwargs(kwargs)
2✔
327

328
    if np.any(np.iscomplex(datay)):
2✔
329
        label = util.get("label", kwargs, "")
2✔
330
        kwargs["label"] = label + "(real)"
2✔
331
        r = fit(datax, datay.real, function=function, **kwargs)
2✔
332
        kwargs["label"] = label + "(imag)"
2✔
333
        i = fit(datax, datay.imag, function=function, **kwargs)
2✔
334
        return r, i
2✔
335
    if kwargs["auto_fit"]:
2✔
336
        best_f, best_ff, lambda_f = ffit.auto(datax, datay, function, **kwargs)
2✔
337
        if best_f is not None:
2✔
338
            del kwargs["auto_fit"]
2✔
339
            fit(datax, datay, best_f, **kwargs)
2✔
340
        return best_f, best_ff, lambda_f
2✔
341
    if kwargs["also_fit"] == False and kwargs["label"] is None and kwargs["lpos"] == 0:
2✔
342
        kwargs["lpos"] = -1
2✔
343
    return _fit_impl(datax, datay, function, **kwargs)
2✔
344

345

346
def _fit_impl(datax, datay, function, **kwargs):
2✔
347
    x = None
2✔
348
    y = None
2✔
349
    rfit = None
2✔
350
    ifit = None
2✔
351
    fig = None
2✔
352
    fig = init_plot(kwargs)
2✔
353
    ll = None
2✔
354
    if kwargs["also_data"]:
2✔
355
        ll = plt_data(datax, datay, **kwargs).get_color()
2✔
356
    if kwargs["interpolate"]:
2✔
357
        ifit, _, x, y = plt_interpolate(datax, datay, icolor=ll, **kwargs)
2✔
358
    if kwargs["also_fit"]:
2✔
359
        assert function is not None, "function must be given"
2✔
360
        rfit, kwargs["fit_color"], _, _ = plt_fit(datax, datay, function, **kwargs)
2✔
361
    if kwargs["ss"]:
2✔
362
        kwargs["oldshow"] = kwargs["show"]
2✔
363
        kwargs["show"] = (
2✔
364
            kwargs["show"] and not kwargs["residue"] and not kwargs["ratio"]
365
        )
366
        save_plot(**kwargs)
2✔
367
        kwargs["show"] = kwargs["oldshow"]
2✔
368
    if kwargs["residue"] and fig is not None:
2✔
369
        plt_residue(datax, datay, function, rfit, fig, **kwargs)
2✔
370
    if kwargs["ratio"] and fig is not None:
2✔
371
        plt_ratio(datax, datay, function, rfit, fig, **kwargs)
×
372
    if not kwargs["also_fit"] and kwargs["interpolate"]:
2✔
373
        return (ifit, x, y)
2✔
374
        # return ifit
375
    return rfit
2✔
376

377

378
# @append_doc(default_kwargs)
379

380

381
def data(*data, function=None, **kwargs):
2✔
382
    """
383
    Plot datay against datax via :func:`fit`
384

385
    Parameters
386
    ----------
387
    datax : array_like
388
        X data either as ``unp.uarray`` or ``np.array`` or ``list``
389
    datay : array_like
390
        Y data either as ``unp.uarray`` or ``np.array`` or ``list``
391
    function : func,optional
392
        Fit function with parameters: ``x``, ``params``
393
    **kwargs : optional
394
        see :func:`plot_kwargs`.
395
    Returns
396
    -------
397
    array_like
398
        Optimized fit parameters of ``function`` to ``datax`` and ``datay``
399
    """
400
    if "also_fit" not in kwargs:
2✔
401
        kwargs["also_fit"] = False
2✔
402
    kwargs = plot_kwargs(kwargs)
2✔
403
    return fit(function=function, *data, **kwargs)
2✔
404

405

406
# @append_doc(default_kwargs)
407

408

409
def auto(*adata, funcs=None, **kwargs):
2✔
410
    """
411
    Automatically loop over functions and fit the best one.
412

413
    Parameters
414
    ----------
415
    funcs : function array
416
        functions to consider as fit. Default all ``smpl.functions``.
417
    **kwargs : optional
418
        see :func:`plot_kwargs`.
419

420
    Returns
421
    -------
422
    The best fit function and it's parameters. Also a lambda function where the parameters are already applied.
423

424

425

426
    """
427
    if "auto_fit" not in kwargs:
2✔
428
        kwargs["auto_fit"] = True
2✔
429
    kwargs = plot_kwargs(kwargs)
2✔
430
    return fit(function=funcs, *adata, **kwargs)
2✔
431

432

433
def _function(
2✔
434
    func,
435
    xfit,
436
    fmt="-",
437
    label=None,
438
    function_color=None,
439
    sigmas=0.0,
440
    alpha=0.4,
441
    **kwargs,
442
):
443
    # kargs = {}
444
    # if util.has("fmt", kwargs):
445
    #    kargs["fmt"] = kwargs["fmt"]
446
    # if util.has("label", kwargs) and kwargs["label"] != "":
447
    #    kargs["label"] = kwargs["label"]
448
    # if util.has("function_color", kwargs) and kwargs["function_color"] != "":
449
    #    kargs["color"] = kwargs["function_color"]
450
    # if util.has("sigmas", kwargs) and kwargs["sigmas"] != "":
451
    #    kargs["sigmas"] = kwargs["sigmas"]
452
    # if util.has("alpha", kwargs) and kwargs["alpha"] != "":
453
    #    kargs["alpha"] = kwargs["alpha"]
454
    # __function(func, xfit,  **kargs)
455

456
    if label == "":
2✔
457
        label = None
2✔
458
    if function_color == "":
2✔
459
        function_color = None
×
460
    if sigmas == "":
2✔
461
        sigmas = 0.0
×
462
    if alpha == "":
2✔
463
        alpha = 0.4
×
464
    __function(
2✔
465
        func,
466
        xfit,
467
        fmt=fmt,
468
        label=label,
469
        color=function_color,
470
        sigmas=sigmas,
471
        alpha=alpha,
472
        **kwargs,
473
    )
474

475

476
def plt_plt(x, y, fmt, color, label, linestyle, **kwargs):
2✔
477
    if linestyle is None and fmt is not None:
2✔
478
        return plt.plot(x, y, fmt, label=label, color=color, **kwargs)
2✔
479
    if linestyle is not None and fmt is None:
2✔
480
        return plt.plot(x, y, label=label, color=color, linestyle=linestyle, **kwargs)
×
481
    if linestyle is None and fmt is None:
2✔
482
        return plt.plot(x, y, label=label, color=color, **kwargs)
2✔
483
    # should not reach here
484
    raise ValueError(
×
485
        "Either fmt or linestyle must be given, but not both. fmt=%s, linestyle=%s"
486
        % (fmt, linestyle)
487
    )
488

489

490
def __function(
2✔
491
    gfunc,
492
    xlinspace,
493
    fmt="-",
494
    label=None,
495
    color=None,
496
    hatch=None,
497
    sigmas=0.0,
498
    linestyle=None,
499
    alpha=0.4,
500
    **kwargs,
501
):
502
    # filter unused bad kwargs here to avoid passing them down
503
    # TODO it would be better to not pass them down in the first place
504
    for key in [
2✔
505
        "pre",
506
        "post",
507
        "xaxis",
508
        "yaxis",
509
        "xvar",
510
        "xmin",
511
        "xmax",
512
        "xlabel",
513
        "ylabel",
514
        "bins",
515
        "binunc",
516
        "bbox_to_anchor",
517
        "tight",
518
        "residue",
519
        "ratio",
520
        "lpos",
521
        "interpolate",
522
        "params",
523
        "also_fit",
524
        "init",
525
        "frange",
526
        "epsfcn",
527
        "units",
528
        "fselector",
529
        "maxfev",
530
        "sortbyx",
531
        "xerror",
532
        "yerror",
533
        "fixed_params",
534
        "autotqdm",
535
        "fitter",
536
        "title",
537
        "where",
538
        "save",
539
        "prange",
540
        "ss",
541
        "also_data",
542
        "auto_fit",
543
        "data_sigmas",
544
        "logy",
545
        "logx",
546
        "data_color",
547
        "fit_color",
548
        "fit_fmt",
549
        "show",
550
        "size",
551
        "number_format",
552
        "selector",
553
        "fitinline",
554
        "grid",
555
        "hist",
556
        "stairs",
557
        "capsize",
558
        "axes",
559
        "xspace",
560
        "extrapolate",
561
        "extrapolate_min",
562
        "extrapolate_max",
563
        "extrapolate_fmt",
564
        "extrapolate_hatch",
565
        "function_color",
566
        "residue_err",
567
        "ratio_err",
568
        "interpolate_fmt",
569
        "interpolate_label",
570
        "interpolate_lower_uncertainty",
571
        "ncol",
572
        "steps",
573
        "interpolator",
574
        "next_color",
575
        "append_chi2",
576
        "append_r2",
577
        "append_ndf",
578
    ]:
579
        kwargs.pop(key, None)
2✔
580
    func = gfunc
2✔
581
    x = xlinspace
2✔
582
    l = label
2✔
583

584
    if isinstance(func(x)[0], uncertainties.UFloat):
2✔
585
        if sigmas > 0:
2✔
586
            (ll,) = plt_plt(
2✔
587
                x,
588
                unv(func(x)),
589
                fmt,
590
                label=None,
591
                color=color,
592
                linestyle=linestyle,
593
                **kwargs,
594
            )
595
            y = func(x)
2✔
596
            plt.fill_between(
2✔
597
                x,
598
                unv(y) - sigmas * usd(y),
599
                unv(y) + sigmas * usd(y),
600
                alpha=alpha,
601
                label=l,
602
                color=ll.get_color(),
603
                hatch=hatch,
604
                **kwargs,
605
            )
606
        else:
607
            (ll,) = plt_plt(
2✔
608
                x,
609
                unv(func(x)),
610
                fmt,
611
                label=l,
612
                color=color,
613
                linestyle=linestyle,
614
                **kwargs,
615
            )
616
    else:
617
        (ll,) = plt_plt(
2✔
618
            x, func(x), fmt, label=l, color=color, linestyle=linestyle, **kwargs
619
        )
620
    return ll
2✔
621

622

623
def function(func, *args, fmt="-", **kwargs):
2✔
624
    """
625
    Plot function ``func`` between ``xmin`` and ``xmax``
626

627
    Parameters
628
    ----------
629
    func : function
630
        Function to be plotted between ``xmin`` and ``xmax``, only taking `array_like` ``x`` as parameter
631
    *args : optional
632
        arguments for ``func``
633
    **kwargs : optional
634
        see :func:`plot_kwargs`.
635
    """
636
    kwargs["fmt"] = fmt
2✔
637
    if not util.has("xmin", kwargs) or not util.has("xmax", kwargs):
2✔
638
        kwargs["xmin"], kwargs["xmax"] = stat.get_interesting_domain(func)
2✔
639
        # raise Exception("xmin or xmax missing.")
640

641
    # if not util.has('lpos', kwargs) and not util.has('label', kwargs):
642
    #    kwargs['lpos'] = -1
643

644
    if "label" not in kwargs:
2✔
645
        kwargs = plot_kwargs(kwargs)
2✔
646
        kwargs["label"] = get_fnc_legend(func, args, **kwargs)
2✔
647
    else:
648
        kwargs = plot_kwargs(kwargs)
2✔
649

650
    xlin = kwargs["xspace"](kwargs["xmin"], kwargs["xmax"], kwargs["steps"])
2✔
651
    init_plot(kwargs)
2✔
652

653
    # kwargs['lpos'] = 0
654
    # _plot(xfit, func(xfit, *args), **kwargs)
655
    _function(wrap.get_lambda_argd(func, kwargs["xvar"], *args), xlin, **kwargs)
2✔
656
    if kwargs["ss"]:
2✔
657
        save_plot(**kwargs)
2✔
658

659

660
# xaxis="",yaxis="",fit_color=None,save = None,residue_err=True,show=False):
661
def plt_residue(datax, datay, gfunction, rfit, fig, **kwargs):
2✔
662
    function = wrap.get_lambda(gfunction, kwargs["xvar"])
2✔
663
    fig.add_axes((0.1, 0.1, 0.8, 0.2))
2✔
664
    kwargs["ylabel"] = "$\\Delta$" + kwargs["ylabel"]
2✔
665
    kwargs["data_color"] = kwargs["fit_color"]
2✔
666

667
    if kwargs["residue_err"]:
2✔
668
        plt_data(datax, datay - function(datax, *rfit), **kwargs)
2✔
669
    else:
670
        plt_data(unv(datax), unv(datay - function(datax, *rfit)), **kwargs)
2✔
671
    kwargs["lpos"] = -1
2✔
672
    save_plot(**kwargs)
2✔
673

674

675
def plt_ratio(datax, datay, gfunction, rfit, fig, **kwargs):
2✔
676
    function = wrap.get_lambda(gfunction, kwargs["xvar"])
×
677
    fig.add_axes((0.1, 0.1, 0.8, 0.2))
×
678
    kwargs["ylabel"] = "Ratio " + kwargs["ylabel"]
×
679
    kwargs["data_color"] = kwargs["fit_color"]
×
680

681
    fit_values = function(datax, *rfit)
×
682
    if kwargs["ratio_err"]:
×
683
        plt_data(datax, datay / fit_values, **kwargs)
×
684
    else:
685
        plt_data(unv(datax), unv(datay / fit_values), **kwargs)
×
686
    kwargs["lpos"] = -1
×
687
    save_plot(**kwargs)
×
688

689

690
def data_split(datax, datay, **kwargs):
2✔
691
    return ffit.data_split(datax, datay, **kwargs)
2✔
692

693

694
def _fit(datax, datay, function, **kwargs):
2✔
695
    """
696
    Returns a fit like :func:`fit` but does no plotting.
697
    """
698
    return ffit.fit(datax, datay, function, **kwargs)
2✔
699

700

701
def plt_data(datax, datay, **kwargs):
2✔
702
    """
703
    Plot datay vs datax
704
    """
705
    x, y, xerr, yerr = data_split(datax, datay, **kwargs)
2✔
706
    if xerr is not None:
2✔
707
        xerr = xerr * kwargs["data_sigmas"]
2✔
708
    if yerr is not None:
2✔
709
        yerr = yerr * kwargs["data_sigmas"]
2✔
710

711
    ll = None
2✔
712
    if xerr is None and yerr is None:
2✔
713
        if kwargs["fmt"] is None:
2✔
714
            if kwargs["linestyle"] is None:
×
715
                (ll,) = plt.plot(
×
716
                    x, y, label=kwargs["label"], color=kwargs["data_color"]
717
                )
718
            else:
719
                (ll,) = plt.plot(
×
720
                    x,
721
                    y,
722
                    label=kwargs["label"],
723
                    color=kwargs["data_color"],
724
                    linestyle=kwargs["linestyle"],
725
                )
726
        elif kwargs["fmt"] == "step":
2✔
727
            (ll,) = plt.step(
×
728
                x,
729
                y,
730
                where=kwargs["where"],
731
                label=kwargs["label"],
732
                color=kwargs["data_color"],
733
            )
734
        elif kwargs["fmt"] == "fill":
2✔
735
            # Create invisible line to get color, then use for fill_between
736
            (ll,) = plt.plot(x, y, " ", color=kwargs["data_color"])
×
737
            plt.fill_between(
×
738
                x,
739
                y,
740
                label=kwargs["label"],
741
                color=ll.get_color(),
742
                alpha=kwargs["alpha"],
743
            )
744
        elif kwargs["fmt"] == "hist":
2✔
745
            (ll,) = plt.step(
2✔
746
                x,
747
                y,
748
                where=kwargs["where"],
749
                label=kwargs["label"],
750
                color=kwargs["data_color"],
751
            )
752
            plt.fill_between(x, y, step="mid", color=ll.get_color())
2✔
753
        else:
754
            (ll,) = plt.plot(
2✔
755
                x, y, kwargs["fmt"], label=kwargs["label"], color=kwargs["data_color"]
756
            )
757
    elif kwargs["fmt"] is None:
2✔
758
        if kwargs["linestyle"] is None:
2✔
759
            (
2✔
760
                ll,
761
                _,
762
                _,
763
            ) = plt.errorbar(
764
                x,
765
                y,
766
                yerr=yerr,
767
                xerr=xerr,
768
                fmt=" ",
769
                capsize=kwargs["capsize"],
770
                label=kwargs["label"],
771
                color=kwargs["data_color"],
772
            )
773
        else:
774
            (
1✔
775
                ll,
776
                _,
777
                _,
778
            ) = plt.errorbar(
779
                x,
780
                y,
781
                yerr=yerr,
782
                xerr=xerr,
783
                fmt=" ",
784
                capsize=kwargs["capsize"],
785
                label=kwargs["label"],
786
                color=kwargs["data_color"],
787
                linestyle=kwargs["linestyle"],
788
            )
789
    elif kwargs["fmt"] == "step":
2✔
790
        (ll,) = plt.step(x, y, where=kwargs["where"], color=kwargs["data_color"])
2✔
791
        if xerr is not None:
2✔
792
            for ix, xv in enumerate(x):
2✔
793
                dx = xerr[ix]
2✔
794
                tx = [xv - dx, xv + dx]
2✔
795
                plt.fill_between(
2✔
796
                    tx,
797
                    y[ix] - yerr[ix],
798
                    y[ix] + yerr[ix],
799
                    label=kwargs["label"] if ix == 1 else None,
800
                    alpha=kwargs["alpha"],
801
                    step="pre",
802
                    color=ll.get_color(),
803
                )
804
        else:
805
            plt.fill_between(
2✔
806
                x,
807
                y - yerr,
808
                y + yerr,
809
                label=kwargs["label"],
810
                alpha=kwargs["alpha"],
811
                step="mid",
812
                color=ll.get_color(),
813
            )
814
    elif kwargs["fmt"] == "fill":
2✔
815
        if xerr is not None:
×
816
            # For multiple fill_between calls, create invisible line first
817
            (ll,) = plt.plot([x[0]], [y[0]], " ", color=kwargs["data_color"])
×
818
            for ix, xv in enumerate(x):
×
819
                dx = xerr[ix]
×
820
                tx = [xv - dx, xv + dx]
×
821
                plt.fill_between(
×
822
                    tx,
823
                    y[ix] - yerr[ix],
824
                    y[ix] + yerr[ix],
825
                    label=kwargs["label"] if ix == 1 else None,
826
                    alpha=kwargs["alpha"],
827
                    color=ll.get_color(),
828
                )
829
        else:
830
            # Create invisible line to get color, then use for fill_between
831
            (ll,) = plt.plot(x, y, " ", color=kwargs["data_color"])
×
832
            plt.fill_between(
×
833
                x,
834
                y - yerr,
835
                y + yerr,
836
                label=kwargs["label"],
837
                alpha=kwargs["alpha"],
838
                color=ll.get_color(),
839
            )
840
    elif kwargs["fmt"] == "hist":
2✔
841
        (
2✔
842
            ll,
843
            _,
844
            _,
845
        ) = plt.errorbar(
846
            x,
847
            y,
848
            yerr=yerr,
849
            xerr=xerr,
850
            fmt=" ",
851
            capsize=kwargs["capsize"],
852
            color="black",
853
        )
854
        plt.fill_between(x, y, step="mid", label=kwargs["label"], color=ll.get_color())
2✔
855
    else:
856
        (
2✔
857
            ll,
858
            _,
859
            _,
860
        ) = plt.errorbar(
861
            x,
862
            y,
863
            yerr=yerr,
864
            xerr=xerr,
865
            fmt=kwargs["fmt"],
866
            capsize=kwargs["capsize"],
867
            label=kwargs["label"],
868
            color=kwargs["data_color"],
869
        )
870
    return ll
2✔
871

872

873
def get_fnc_legend(function, rfit, datax=None, datay=None, **kwargs):
2✔
874
    l = wrap.get_latex(function)
2✔
875

876
    vnames = wrap.get_varnames(function, kwargs["xvar"])
2✔
877
    for i in range(1, len(vnames)):
2✔
878
        l = l + ("\n" if not kwargs["fitinline"] or i == 1 else " ")
2✔
879
        l = l + "$" + sympy.latex(sympy.symbols(str(vnames[i]))) + "$="
2✔
880
        if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
2✔
881
            l = l + "("
2✔
882
        if "number_format" in kwargs:
2✔
883
            l = l + kwargs["number_format"].format(rfit[i - 1])
2✔
884
        else:
885
            l = l + "%s" % (rfit[i - 1])
×
886

887
        if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
2✔
888
            l = l + ")"
2✔
889
        if kwargs["units"] is not None:
2✔
890
            l = l + " " + kwargs["units"][i - 1]
2✔
891

892
    # Append Chi2, R2, and NDF if requested and data is available
893
    if datax is not None and datay is not None:
2✔
894
        if kwargs.get("append_chi2", False):
2✔
895
            try:
×
896
                chi2_val = ffit.Chi2(datax, datay, function, rfit, **kwargs)
×
897
                l = l + ("\n" if not kwargs["fitinline"] else " ")
×
898
                if "number_format" in kwargs:
×
899
                    l = l + "$\\chi^2$=" + kwargs["number_format"].format(chi2_val)
×
900
                else:
901
                    l = l + "$\\chi^2$=%s" % (chi2_val)
×
902
            except Exception:
×
903
                pass  # Ignore errors in Chi2 calculation
×
904

905
        if kwargs.get("append_r2", False):
2✔
906
            try:
×
907
                r2_val = ffit.R2(datax, datay, function, rfit, **kwargs)
×
908
                l = l + ("\n" if not kwargs["fitinline"] else " ")
×
909
                if "number_format" in kwargs:
×
910
                    l = l + "$R^2$=" + kwargs["number_format"].format(r2_val)
×
911
                else:
912
                    l = l + "$R^2$=%s" % (r2_val)
×
913
            except Exception:
×
914
                pass  # Ignore errors in R2 calculation
×
915

916
        if kwargs.get("append_ndf", False):
2✔
917
            try:
×
918
                ndf_val = ffit.Ndf(datax, datay, function, rfit, **kwargs)
×
919
                l = l + ("\n" if not kwargs["fitinline"] else " ")
×
920
                if "number_format" in kwargs:
×
921
                    l = l + "NDF=" + kwargs["number_format"].format(ndf_val)
×
922
                else:
923
                    l = l + "NDF=%s" % (ndf_val)
×
924
            except Exception:
×
925
                pass  # Ignore errors in NDF calculation
×
926

927
    return l
2✔
928

929

930
def plt_fit_or_interpolate(
2✔
931
    datax, datay, fitted, l=None, c=None, f=None, ls=None, **kwargs
932
):
933
    # just filter these kwargs out, so they dont get passed down and are replaced by above args
934
    # TODO why not pass label=XXX directly to this?
935
    #      -> probably since there are cases where both e.g. color and replace color are needed
936
    for key in ["color", "label", "fmt", "linestyle", "hatch"]:
2✔
937
        kwargs.pop(key, None)
2✔
938
    if kwargs["prange"] is None:
2✔
939
        x, _, _, _ = ffit.fit_split(datax, datay, **kwargs)
2✔
940
        xfit = kwargs["xspace"](np.min(unv(x)), np.max(unv(x)), kwargs["steps"])
2✔
941
    else:
942
        xfit = kwargs["xspace"](
×
943
            kwargs["prange"][0], kwargs["prange"][1], kwargs["steps"]
944
        )
945
    ll = __function(
2✔
946
        fitted,
947
        xfit,
948
        kwargs["fit_fmt"] if f is not None and ls is None else f,
949
        label=l,
950
        color=kwargs["fit_color"] if c is None else c,
951
        linestyle=ls,
952
        **kwargs,
953
    )
954

955
    if (
2✔
956
        (
957
            (kwargs["frange"] is not None or kwargs["fselector"] is not None)
958
            and util.true("extrapolate", kwargs)
959
        )
960
        or util.has("extrapolate_max", kwargs)
961
        or util.has("extrapolate_min", kwargs)
962
    ):
963
        xxfit = kwargs["xspace"](
2✔
964
            util.get("extrapolate_min", kwargs, np.min(unv(datax))),
965
            util.get("extrapolate_max", kwargs, np.max(unv(datax))),
966
            kwargs["steps"],
967
        )
968
        for pmin, pmax in [
2✔
969
            (np.min(xxfit), np.min(xfit)),
970
            (np.max(xfit), np.max(xxfit)),
971
        ]:
972
            __function(
2✔
973
                fitted,
974
                kwargs["xspace"](pmin, pmax, kwargs["steps"]),
975
                util.get("extrapolate_fmt", kwargs, "--"),
976
                color=ll.get_color(),
977
                hatch=util.get("extrapolate_hatch", kwargs, r"||"),
978
                **kwargs,
979
            )
980
    return ll.get_color(), xfit, fitted(xfit)
2✔
981

982

983
def plt_interpolate(datax, datay, icolor=None, **kwargs):
2✔
984
    """
985
    Interpolate and Plot that Interpolation.
986
    """
987
    inter = interpolate.interpolate(datax, datay, **kwargs)
2✔
988
    kargs = {}
2✔
989
    if isinstance(kwargs["interpolate_fmt"], tuple):
2✔
990
        kargs["ls"] = kwargs["interpolate_fmt"]
×
991
    else:
992
        kargs["f"] = kwargs["interpolate_fmt"]
2✔
993
    if kwargs["interpolate_label"] is not None:
2✔
994
        kargs["l"] = kwargs["interpolate_label"]
2✔
995
    # l = None so that no label
996
    return (
2✔
997
        inter,
998
        *plt_fit_or_interpolate(datax, datay, inter, c=icolor, **kargs, **kwargs),
999
    )
1000

1001

1002
def plt_fit(datax, datay, gfunction, **kwargs):
2✔
1003
    """
1004
    Fit and Plot that Fit.
1005
    """
1006
    func = wrap.get_lambda(gfunction, kwargs["xvar"])
2✔
1007
    rfit = _fit(datax, datay, gfunction, **kwargs)
2✔
1008

1009
    def fitted(x):
2✔
1010
        return func(x, *rfit)
2✔
1011

1012
    vnames = wrap.get_varnames(gfunction, kwargs["xvar"])
2✔
1013

1014
    l = get_fnc_legend(gfunction, rfit, datax, datay, **kwargs)
2✔
1015
    for v in vnames[1:]:  # remove fixed parameters from kwargs # APN TODO why?
2✔
1016
        kwargs.pop(v, None)
2✔
1017

1018
    return (rfit, *plt_fit_or_interpolate(datax, datay, fitted, l, **kwargs))
2✔
1019

1020

1021
def init_plot(kwargs):
2✔
1022
    fig = None
2✔
1023
    if util.has("axes", kwargs) and kwargs["axes"] is not None:
2✔
1024
        plt.sca(kwargs["axes"])
2✔
1025
        fig = kwargs["axes"].get_figure()
2✔
1026
    if kwargs["init"] or util.true("residue", kwargs) or util.true("ratio", kwargs):
2✔
1027
        if kwargs["size"] is None:
2✔
1028
            fig = plt.figure()
2✔
1029
        else:
1030
            fig = plt.figure(figsize=kwargs["size"])
×
1031
        if kwargs["residue"] or kwargs["ratio"]:
2✔
1032
            fig.add_axes((0.1, 0.3, 0.8, 0.6))
2✔
1033
    if util.has("next_color", kwargs) and not kwargs["next_color"]:
2✔
1034
        ax = plt.gca()
2✔
1035
        if not ax.lines:
2✔
NEW
1036
            raise ValueError(
×
1037
                "next_color=False requires an existing Line2D on the axes. "
1038
                "Plot a line first or pass color=... explicitly."
1039
            )
1040
        tmp_color = ax.lines[-1].get_color()
2✔
1041

1042
        if kwargs["data_color"] is None:
2✔
1043
            kwargs["data_color"] = tmp_color
2✔
1044
        if kwargs["fit_color"] is None:
2✔
1045
            kwargs["fit_color"] = tmp_color
2✔
1046
        if kwargs["function_color"] is None:
2✔
1047
            kwargs["function_color"] = tmp_color
2✔
1048
    return fig
2✔
1049

1050

1051
def save_plot(**kwargs):
2✔
1052
    """
1053
    save plot
1054
    """
1055
    smplr.style_plot1d(**kwargs)
2✔
1056
    if "lpos" in kwargs and kwargs["lpos"] >= 0:
2✔
1057
        if util.has("bbox_to_anchor", kwargs):
2✔
1058
            if util.has("ncol", kwargs):
2✔
1059
                plt.legend(
2✔
1060
                    loc=kwargs["lpos"],
1061
                    bbox_to_anchor=kwargs["bbox_to_anchor"],
1062
                    ncol=kwargs["ncol"],
1063
                    borderaxespad=0,
1064
                )
1065
            else:
1066
                plt.legend(loc=kwargs["lpos"], bbox_to_anchor=kwargs["bbox_to_anchor"])
2✔
1067
        else:
1068
            plt.legend(loc=kwargs["lpos"])
2✔
1069
    if "save" in kwargs and kwargs["save"] is not None:
2✔
1070
        io.mkdirs(kwargs["save"])
×
1071
        plt.savefig(kwargs["save"] + ".pdf")
×
1072
    if kwargs.get("show"):
2✔
1073
        show(**kwargs)
2✔
1074

1075

1076
def show(**kwargs):
2✔
1077
    kwargs = plot_kwargs(kwargs)
2✔
1078

1079
    smplr.style_plot1d(**kwargs)
2✔
1080
    plt.show()
2✔
1081

1082

1083
from smpl.plot2d import plot2d as _plot2d
2✔
1084
from smpl.plot2d import plot2d_kwargs as _plot2d_kwargs
2✔
1085

1086

1087
def plot2d(*args, **kwargs):
2✔
1088
    _plot2d(*args, **kwargs)
2✔
1089

1090

1091
def plot2d_kwargs(*args, **kwargs):
2✔
1092
    _plot2d_kwargs(*args, **kwargs)
×
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