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

APN-Pucky / smpl / 17467129012

04 Sep 2025 02:24PM UTC coverage: 90.252% (+1.2%) from 89.04%
17467129012

push

github

APN-Pucky
add append_chi2

5 of 21 new or added lines in 1 file covered. (23.81%)

4 existing lines in 1 file now uncovered.

1611 of 1785 relevant lines covered (90.25%)

4.51 hits per line

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

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

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

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

17

18
def set_plot_style():
5✔
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
5✔
37
usd = unp.std_devs
5✔
38

39
default = {
5✔
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
    "show": [
138
        False,
139
        "Call plt.show()",
140
    ],
141
    "size": [
142
        None,
143
        "Size of the plot as a tuple (x,y). Only has an effect if ``init`` is True",
144
    ],
145
    "number_format": [
146
        io.gf(4),
147
        "Format to display numbers.",
148
    ],
149
    # ,          '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.",],
150
    # ,          'fixed_params'  :[ True     ,"Enable fixing parameters by choosing the same-named variables from ``kwargs``.",],
151
    # ,          'sortbyx'       :[ True     , "Enable sorting the x and y data so that x is sorted.",],
152
    "interpolate": [False, "Enable interpolation of the data."],
153
    "interpolate_fmt": [
154
        "-",
155
        "Either format string or linestyle tuple.",
156
    ],
157
    "interpolate_label": [
158
        None,
159
        "Label for the interpolation.",
160
    ],
161
    "extrapolate": [
162
        True,
163
        "Enable extrapolation of whole data if fit range is limited by ``frange`` or ``fselector``.",
164
    ],
165
    "extrapolate_min": [
166
        None,
167
        "Lower extrapolation bound",
168
    ],
169
    "extrapolate_max": [
170
        None,
171
        "Higher extrapolation bound",
172
    ],
173
    "extrapolate_fmt": [
174
        "--",
175
        "Format of the extrapolation line",
176
    ],
177
    "extrapolate_hatch": [
178
        r"||",
179
        "Extrapolation shape/hatch for filled area in case of ``sigmas``>0. See https://matplotlib.org/stable/gallery/shapes_and_collections/hatch_style_reference.html",
180
    ],
181
    "bbox_to_anchor": [
182
        None,
183
        "Position in a tuple (x,y),Shift position of the legend out of the main pane. ",
184
    ],
185
    "ncol": [
186
        None,
187
        "Columns in the legend if used with ``bbox_to_anchor``.",
188
    ],
189
    "steps": [
190
        1000,
191
        "resolution of the plotted function",
192
    ],
193
    "fitinline": [
194
        False,
195
        "No newlines for each fit parameter",
196
    ],
197
    "grid": [
198
        True,
199
        "Enable grid for the plot",
200
    ],
201
    "hist": [
202
        False,
203
        "Enable histogram plot",
204
    ],
205
    "stairs": [
206
        False,
207
        "Enable stair plot",
208
    ],
209
    "capsize": [5, "size of cap on error bar plot"],
210
    "axes": [None, "set current axis"],
211
    "linestyle": [None, "linestyle, only active if `fmt`=None"],
212
    "xspace": [
213
        np.linspace,
214
        "xspace gets called with xspace(xmin,xmax,steps) in :func:`function` to get the points of the function that will be drawn.",
215
    ],
216
    "alpha": [0.2, "alpha value for the fill_between plot"],
217
    "append_chi2": [False, "Append chi2 to legend"],
218
    "append_r2": [False, "Append r2 to legend"],
219
}
220

221

222
@doc.append_doc(ffit.fit_kwargs)
5✔
223
@doc.append_str("\t")
5✔
224
@doc.append_str(doc.array_table(default, init=False))
5✔
225
@doc.append_str(
5✔
226
    doc.array_table({"plot_kwargs        ": ["default", "description"]}, bottom=False)
227
)
228
@doc.append_str("\n\n")
5✔
229
def plot_kwargs(kwargs):
5✔
230
    """Set default :func:`plot_kwargs` if not set."""
231
    kwargs = ffit.fit_kwargs(kwargs)
5✔
232
    for k, v in default.items():
5✔
233
        if k not in kwargs:
5✔
234
            kwargs[k] = v[0]
5✔
235
    return kwargs
5✔
236

237

238
# TODO optimize to minimize number of calls to function
239
# @append_doc(default_kwargs)
240

241

242
def fit(func, *adata, **kwargs):
5✔
243
    """
244
    Fit and plot function to datax and datay.
245

246
    Parameters
247
    ----------
248
    datax : array_like
249
        X data either as ``unp.uarray`` or ``np.array`` or ``list``
250
    datay : array_like
251
        Y data either as ``unp.uarray`` or ``np.array`` or ``list``
252
    function : func
253
        Fit function with parameters: ``x``, ``params``
254
    **kwargs : optional
255
        see :func:`plot_kwargs`.
256
    Fit parameters can be fixed via ``kwargs`` eg. ``a=5``.
257

258
    Returns
259
    -------
260
    array_like
261
        Optimized fit parameters of ``function`` to ``datax`` and ``datay``.
262
        If ``datay`` is complex, both the real and imaginary part are returned.
263

264
    Examples
265
    --------
266

267
    .. plot::
268
        :include-source:
269

270
        >>> from smpl import functions as f
271
        >>> from smpl import plot
272
        >>> param = plot.fit([0,1,2],[0,1,2],f.line)
273
        >>> float(plot.unv(param).round()[0])
274
        1.0
275

276
    """
277
    if "xaxis" in kwargs and ("xlabel" not in kwargs or not kwargs["xlabel"]):
5✔
278
        # warnings.warn("xaxis is deprecated. Use xlabel instead.", DeprecationWarning, 2)
279
        kwargs["xlabel"] = kwargs["xaxis"]
5✔
280
        # TODO maybe pop
281
    if "yaxis" in kwargs and ("ylabel" not in kwargs or not kwargs["ylabel"]):
5✔
282
        # warnings.warn("yaxis is deprecated. Use ylabel instead.", DeprecationWarning, 2)
283
        kwargs["ylabel"] = kwargs["yaxis"]
5✔
284
        # TODO maybe pop
285

286
    function = func
5✔
287
    if "function" in kwargs:
5✔
288
        function = kwargs["function"]
5✔
289
        del kwargs["function"]
5✔
290
        adata = [func, *adata]
5✔
291
    # Fix parameter order if necessary
292
    elif isinstance(function, (list, tuple, np.ndarray)):
5✔
293
        adata = [adata[-1], function, *adata[:-1]]
5✔
294
        function = adata[0]
5✔
295
        adata = adata[1:]
5✔
296
    if util.true("bins", kwargs):
5✔
297
        # yvalue will be overwritten
298
        ndata = [*adata, *adata]
5✔
299
        for i, o in enumerate(adata):
5✔
300
            ndata[2 * i] = o
5✔
301
            ndata[2 * i + 1] = o * 0
5✔
302
        adata = ndata
5✔
303

304
    assert len(adata) % 2 == 0, "data must be pairs of x and y data"
5✔
305
    if len(adata) == 2:
5✔
306
        datax, datay = adata
5✔
307
    else:
308
        rs = []
5✔
309
        for i in range(0, len(adata), 2):
5✔
310
            datax, datay = adata[i], adata[i + 1]
5✔
311
            if util.true("bins", kwargs):
5✔
312
                rs.append(fit(function, datax, **kwargs))
5✔
313
            else:
314
                rs.append(fit(function, datax, datay, **kwargs))
5✔
315
        return rs
5✔
316

317
    kwargs = plot_kwargs(kwargs)
5✔
318

319
    if np.any(np.iscomplex(datay)):
5✔
320
        label = util.get("label", kwargs, "")
5✔
321
        kwargs["label"] = label + "(real)"
5✔
322
        r = fit(datax, datay.real, function=function, **kwargs)
5✔
323
        kwargs["label"] = label + "(imag)"
5✔
324
        i = fit(datax, datay.imag, function=function, **kwargs)
5✔
325
        return r, i
5✔
326
    if kwargs["auto_fit"]:
5✔
327
        best_f, best_ff, lambda_f = ffit.auto(datax, datay, function, **kwargs)
5✔
328
        if best_f is not None:
5✔
329
            del kwargs["auto_fit"]
5✔
330
            fit(datax, datay, best_f, **kwargs)
5✔
331
        return best_f, best_ff, lambda_f
5✔
332
    if kwargs["also_fit"] == False and kwargs["label"] is None and kwargs["lpos"] == 0:
5✔
333
        kwargs["lpos"] = -1
5✔
334
    return _fit_impl(datax, datay, function, **kwargs)
5✔
335

336

337
def _fit_impl(datax, datay, function, **kwargs):
5✔
338
    x = None
5✔
339
    y = None
5✔
340
    rfit = None
5✔
341
    ifit = None
5✔
342
    fig = None
5✔
343
    fig = init_plot(kwargs)
5✔
344
    ll = None
5✔
345
    if kwargs["also_data"]:
5✔
346
        ll = plt_data(datax, datay, **kwargs).get_color()
5✔
347
    if kwargs["interpolate"]:
5✔
348
        ifit, _, x, y = plt_interpolate(datax, datay, icolor=ll, **kwargs)
5✔
349
    if kwargs["also_fit"]:
5✔
350
        assert function is not None, "function must be given"
5✔
351
        rfit, kwargs["fit_color"], _, _ = plt_fit(datax, datay, function, **kwargs)
5✔
352
    if kwargs["ss"]:
5✔
353
        kwargs["oldshow"] = kwargs["show"]
5✔
354
        kwargs["show"] = kwargs["show"] and not kwargs["residue"]
5✔
355
        save_plot(**kwargs)
5✔
356
        kwargs["show"] = kwargs["oldshow"]
5✔
357
    if kwargs["residue"] and fig is not None:
5✔
358
        plt_residue(datax, datay, function, rfit, fig, **kwargs)
5✔
359
    if not kwargs["also_fit"] and kwargs["interpolate"]:
5✔
360
        return (ifit, x, y)
5✔
361
        # return ifit
362
    return rfit
5✔
363

364

365
# @append_doc(default_kwargs)
366

367

368
def data(*data, function=None, **kwargs):
5✔
369
    """
370
    Plot datay against datax via :func:`fit`
371

372
    Parameters
373
    ----------
374
    datax : array_like
375
        X data either as ``unp.uarray`` or ``np.array`` or ``list``
376
    datay : array_like
377
        Y data either as ``unp.uarray`` or ``np.array`` or ``list``
378
    function : func,optional
379
        Fit function with parameters: ``x``, ``params``
380
    **kwargs : optional
381
        see :func:`plot_kwargs`.
382
    Returns
383
    -------
384
    array_like
385
        Optimized fit parameters of ``function`` to ``datax`` and ``datay``
386
    """
387
    if "also_fit" not in kwargs:
5✔
388
        kwargs["also_fit"] = False
5✔
389
    kwargs = plot_kwargs(kwargs)
5✔
390
    return fit(function=function, *data, **kwargs)
5✔
391

392

393
# @append_doc(default_kwargs)
394

395

396
def auto(*adata, funcs=None, **kwargs):
5✔
397
    """
398
    Automatically loop over functions and fit the best one.
399

400
    Parameters
401
    ----------
402
    funcs : function array
403
        functions to consider as fit. Default all ``smpl.functions``.
404
    **kwargs : optional
405
        see :func:`plot_kwargs`.
406

407
    Returns
408
    -------
409
    The best fit function and it's parameters. Also a lambda function where the parameters are already applied.
410

411

412

413
    """
414
    if "auto_fit" not in kwargs:
5✔
415
        kwargs["auto_fit"] = True
5✔
416
    kwargs = plot_kwargs(kwargs)
5✔
417
    return fit(function=funcs, *adata, **kwargs)
5✔
418

419

420
def _function(
5✔
421
    func,
422
    xfit,
423
    fmt="-",
424
    label=None,
425
    function_color=None,
426
    sigmas=0.0,
427
    alpha=0.4,
428
    **kwargs,
429
):
430
    # kargs = {}
431
    # if util.has("fmt", kwargs):
432
    #    kargs["fmt"] = kwargs["fmt"]
433
    # if util.has("label", kwargs) and kwargs["label"] != "":
434
    #    kargs["label"] = kwargs["label"]
435
    # if util.has("function_color", kwargs) and kwargs["function_color"] != "":
436
    #    kargs["color"] = kwargs["function_color"]
437
    # if util.has("sigmas", kwargs) and kwargs["sigmas"] != "":
438
    #    kargs["sigmas"] = kwargs["sigmas"]
439
    # if util.has("alpha", kwargs) and kwargs["alpha"] != "":
440
    #    kargs["alpha"] = kwargs["alpha"]
441
    # __function(func, xfit,  **kargs)
442

443
    if label == "":
5✔
444
        label = None
5✔
445
    if function_color == "":
5✔
446
        function_color = None
×
447
    if sigmas == "":
5✔
448
        sigmas = 0.0
×
449
    if alpha == "":
5✔
450
        alpha = 0.4
×
451
    __function(
5✔
452
        func,
453
        xfit,
454
        fmt=fmt,
455
        label=label,
456
        color=function_color,
457
        sigmas=sigmas,
458
        alpha=alpha,
459
        **kwargs,
460
    )
461

462

463
def plt_plt(x, y, fmt, color, label, linestyle, **kwargs):
5✔
464
    if linestyle is None and fmt is not None:
5✔
465
        return plt.plot(x, y, fmt, label=label, color=color, **kwargs)
5✔
466
    if linestyle is not None and fmt is None:
5✔
467
        return plt.plot(x, y, label=label, color=color, linestyle=linestyle, **kwargs)
×
468
    if linestyle is None and fmt is None:
5✔
469
        return plt.plot(x, y, label=label, color=color, **kwargs)
5✔
470
    # should not reach here
471
    raise ValueError(
×
472
        "Either fmt or linestyle must be given, but not both. fmt=%s, linestyle=%s"
473
        % (fmt, linestyle)
474
    )
475

476

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

568
    if isinstance(func(x)[0], uncertainties.UFloat):
5✔
569
        if sigmas > 0:
5✔
570
            (ll,) = plt_plt(
5✔
571
                x,
572
                unv(func(x)),
573
                fmt,
574
                label=None,
575
                color=color,
576
                linestyle=linestyle,
577
                **kwargs,
578
            )
579
            y = func(x)
5✔
580
            plt.fill_between(
5✔
581
                x,
582
                unv(y) - sigmas * usd(y),
583
                unv(y) + sigmas * usd(y),
584
                alpha=alpha,
585
                label=l,
586
                color=ll.get_color(),
587
                hatch=hatch,
588
                **kwargs,
589
            )
590
        else:
591
            (ll,) = plt_plt(
5✔
592
                x,
593
                unv(func(x)),
594
                fmt,
595
                label=l,
596
                color=color,
597
                linestyle=linestyle,
598
                **kwargs,
599
            )
600
    else:
601
        (ll,) = plt_plt(
5✔
602
            x, func(x), fmt, label=l, color=color, linestyle=linestyle, **kwargs
603
        )
604
    return ll
5✔
605

606

607
def function(func, *args, fmt="-", **kwargs):
5✔
608
    """
609
    Plot function ``func`` between ``xmin`` and ``xmax``
610

611
    Parameters
612
    ----------
613
    func : function
614
        Function to be plotted between ``xmin`` and ``xmax``, only taking `array_like` ``x`` as parameter
615
    *args : optional
616
        arguments for ``func``
617
    **kwargs : optional
618
        see :func:`plot_kwargs`.
619
    """
620
    kwargs["fmt"] = fmt
5✔
621
    if not util.has("xmin", kwargs) or not util.has("xmax", kwargs):
5✔
622
        kwargs["xmin"], kwargs["xmax"] = stat.get_interesting_domain(func)
5✔
623
        # raise Exception("xmin or xmax missing.")
624

625
    # if not util.has('lpos', kwargs) and not util.has('label', kwargs):
626
    #    kwargs['lpos'] = -1
627

628
    if "label" not in kwargs:
5✔
629
        kwargs = plot_kwargs(kwargs)
5✔
630
        kwargs["label"] = get_fnc_legend(func, args, **kwargs)
5✔
631
    else:
632
        kwargs = plot_kwargs(kwargs)
5✔
633

634
    xlin = kwargs["xspace"](kwargs["xmin"], kwargs["xmax"], kwargs["steps"])
5✔
635
    init_plot(kwargs)
5✔
636

637
    # kwargs['lpos'] = 0
638
    # _plot(xfit, func(xfit, *args), **kwargs)
639
    _function(wrap.get_lambda_argd(func, kwargs["xvar"], *args), xlin, **kwargs)
5✔
640
    if kwargs["ss"]:
5✔
641
        save_plot(**kwargs)
5✔
642

643

644
# xaxis="",yaxis="",fit_color=None,save = None,residue_err=True,show=False):
645
def plt_residue(datax, datay, gfunction, rfit, fig, **kwargs):
5✔
646
    function = wrap.get_lambda(gfunction, kwargs["xvar"])
5✔
647
    fig.add_axes((0.1, 0.1, 0.8, 0.2))
5✔
648
    kwargs["ylabel"] = "$\\Delta$" + kwargs["ylabel"]
5✔
649
    kwargs["data_color"] = kwargs["fit_color"]
5✔
650

651
    if kwargs["residue_err"]:
5✔
652
        plt_data(datax, datay - function(datax, *rfit), **kwargs)
5✔
653
    else:
654
        plt_data(unv(datax), unv(datay - function(datax, *rfit)), **kwargs)
5✔
655
    kwargs["lpos"] = -1
5✔
656
    save_plot(**kwargs)
5✔
657

658

659
def data_split(datax, datay, **kwargs):
5✔
660
    return ffit.data_split(datax, datay, **kwargs)
5✔
661

662

663
def _fit(datax, datay, function, **kwargs):
5✔
664
    """
665
    Returns a fit like :func:`fit` but does no plotting.
666
    """
667
    return ffit.fit(datax, datay, function, **kwargs)
5✔
668

669

670
def plt_data(datax, datay, **kwargs):
5✔
671
    """
672
    Plot datay vs datax
673
    """
674
    x, y, xerr, yerr = data_split(datax, datay, **kwargs)
5✔
675
    if xerr is not None:
5✔
676
        xerr = xerr * kwargs["data_sigmas"]
5✔
677
    if yerr is not None:
5✔
678
        yerr = yerr * kwargs["data_sigmas"]
5✔
679

680
    ll = None
5✔
681
    if xerr is None and yerr is None:
5✔
682
        if kwargs["fmt"] is None:
5✔
683
            if kwargs["linestyle"] is None:
×
684
                (ll,) = plt.plot(
×
685
                    x, y, label=kwargs["label"], color=kwargs["data_color"]
686
                )
687
            else:
688
                (ll,) = plt.plot(
×
689
                    x,
690
                    y,
691
                    label=kwargs["label"],
692
                    color=kwargs["data_color"],
693
                    linestyle=kwargs["linestyle"],
694
                )
695
        elif kwargs["fmt"] == "step":
5✔
696
            (ll,) = plt.step(
×
697
                x,
698
                y,
699
                where=kwargs["where"],
700
                label=kwargs["label"],
701
                color=kwargs["data_color"],
702
            )
703
        elif kwargs["fmt"] == "hist":
5✔
704
            (ll,) = plt.step(
5✔
705
                x,
706
                y,
707
                where=kwargs["where"],
708
                label=kwargs["label"],
709
                color=kwargs["data_color"],
710
            )
711
            plt.fill_between(x, y, step="mid", color=ll.get_color())
5✔
712
        else:
713
            (ll,) = plt.plot(
5✔
714
                x, y, kwargs["fmt"], label=kwargs["label"], color=kwargs["data_color"]
715
            )
716
    elif kwargs["fmt"] is None:
5✔
717
        if kwargs["linestyle"] is None:
5✔
718
            (
5✔
719
                ll,
720
                _,
721
                _,
722
            ) = plt.errorbar(
723
                x,
724
                y,
725
                yerr=yerr,
726
                xerr=xerr,
727
                fmt=" ",
728
                capsize=kwargs["capsize"],
729
                label=kwargs["label"],
730
                color=kwargs["data_color"],
731
            )
732
        else:
733
            (
1✔
734
                ll,
735
                _,
736
                _,
737
            ) = plt.errorbar(
738
                x,
739
                y,
740
                yerr=yerr,
741
                xerr=xerr,
742
                fmt=" ",
743
                capsize=kwargs["capsize"],
744
                label=kwargs["label"],
745
                color=kwargs["data_color"],
746
                linestyle=kwargs["linestyle"],
747
            )
748
    elif kwargs["fmt"] == "step":
5✔
749
        (ll,) = plt.step(x, y, where=kwargs["where"], color=kwargs["data_color"])
5✔
750
        if xerr is not None:
5✔
751
            for ix, xv in enumerate(x):
5✔
752
                dx = xerr[ix]
5✔
753
                tx = [xv - dx, xv + dx]
5✔
754
                plt.fill_between(
5✔
755
                    tx,
756
                    y[ix] - yerr[ix],
757
                    y[ix] + yerr[ix],
758
                    label=kwargs["label"] if ix == 1 else None,
759
                    alpha=kwargs["alpha"],
760
                    step="pre",
761
                    color=ll.get_color(),
762
                )
763
        else:
764
            plt.fill_between(
5✔
765
                x,
766
                y - yerr,
767
                y + yerr,
768
                label=kwargs["label"],
769
                alpha=kwargs["alpha"],
770
                step="mid",
771
                color=ll.get_color(),
772
            )
773
    elif kwargs["fmt"] == "hist":
5✔
774
        (
5✔
775
            ll,
776
            _,
777
            _,
778
        ) = plt.errorbar(
779
            x,
780
            y,
781
            yerr=yerr,
782
            xerr=xerr,
783
            fmt=" ",
784
            capsize=kwargs["capsize"],
785
            color="black",
786
        )
787
        plt.fill_between(x, y, step="mid", label=kwargs["label"], color=ll.get_color())
5✔
788
    else:
789
        (
5✔
790
            ll,
791
            _,
792
            _,
793
        ) = plt.errorbar(
794
            x,
795
            y,
796
            yerr=yerr,
797
            xerr=xerr,
798
            fmt=kwargs["fmt"],
799
            capsize=kwargs["capsize"],
800
            label=kwargs["label"],
801
            color=kwargs["data_color"],
802
        )
803
    return ll
5✔
804

805

806
def get_fnc_legend(function, rfit, datax=None, datay=None, **kwargs):
5✔
807
    l = wrap.get_latex(function)
5✔
808

809
    vnames = wrap.get_varnames(function, kwargs["xvar"])
5✔
810
    for i in range(1, len(vnames)):
5✔
811
        l = l + ("\n" if not kwargs["fitinline"] or i == 1 else " ")
5✔
812
        l = l + "$" + sympy.latex(sympy.symbols(str(vnames[i]))) + "$="
5✔
813
        if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
5✔
814
            l = l + "("
5✔
815
        if "number_format" in kwargs:
5✔
816
            l = l + kwargs["number_format"].format(rfit[i - 1])
5✔
817
        else:
818
            l = l + "%s" % (rfit[i - 1])
×
819

820
        if kwargs["units"] is not None and usd(rfit[i - 1]) > 0:
5✔
821
            l = l + ")"
5✔
822
        if kwargs["units"] is not None:
5✔
823
            l = l + " " + kwargs["units"][i - 1]
5✔
824
    
825
    # Append Chi2 and R2 if requested and data is available
826
    if datax is not None and datay is not None:
5✔
827
        if kwargs.get("append_chi2", False):
5✔
NEW
828
            try:
×
NEW
829
                chi2_val = ffit.Chi2(datax, datay, function, rfit, **kwargs)
×
NEW
830
                l = l + ("\n" if not kwargs["fitinline"] else " ")
×
NEW
831
                if "number_format" in kwargs:
×
NEW
832
                    l = l + "$\\chi^2$=" + kwargs["number_format"].format(chi2_val)
×
833
                else:
NEW
834
                    l = l + "$\\chi^2$=%s" % (chi2_val)
×
NEW
835
            except Exception:
×
NEW
836
                pass  # Ignore errors in Chi2 calculation
×
837
        
838
        if kwargs.get("append_r2", False):
5✔
NEW
839
            try:
×
NEW
840
                r2_val = ffit.R2(datax, datay, function, rfit, **kwargs)
×
NEW
841
                l = l + ("\n" if not kwargs["fitinline"] else " ")
×
NEW
842
                if "number_format" in kwargs:
×
NEW
843
                    l = l + "$R^2$=" + kwargs["number_format"].format(r2_val)
×
844
                else:
NEW
845
                    l = l + "$R^2$=%s" % (r2_val)
×
NEW
846
            except Exception:
×
NEW
847
                pass  # Ignore errors in R2 calculation
×
848
    
849
    return l
5✔
850

851

852
def plt_fit_or_interpolate(
5✔
853
    datax, datay, fitted, l=None, c=None, f=None, ls=None, **kwargs
854
):
855
    # just filter these kwargs out, so they dont get passed down and are replaced by above args
856
    # TODO why not pass label=XXX directly to this?
857
    #      -> probably since there are cases where both e.g. color and replace color are needed
858
    for key in ["color", "label", "fmt", "linestyle", "hatch"]:
5✔
859
        kwargs.pop(key, None)
5✔
860
    if kwargs["prange"] is None:
5✔
861
        x, _, _, _ = ffit.fit_split(datax, datay, **kwargs)
5✔
862
        xfit = kwargs["xspace"](np.min(unv(x)), np.max(unv(x)), kwargs["steps"])
5✔
863
    else:
864
        xfit = kwargs["xspace"](
×
865
            kwargs["prange"][0], kwargs["prange"][1], kwargs["steps"]
866
        )
867
    ll = __function(
5✔
868
        fitted,
869
        xfit,
870
        kwargs["fit_fmt"] if f is not None and ls is None else f,
871
        label=l,
872
        color=kwargs["fit_color"] if c is None else c,
873
        linestyle=ls,
874
        **kwargs,
875
    )
876

877
    if (
5✔
878
        (
879
            (kwargs["frange"] is not None or kwargs["fselector"] is not None)
880
            and util.true("extrapolate", kwargs)
881
        )
882
        or util.has("extrapolate_max", kwargs)
883
        or util.has("extrapolate_min", kwargs)
884
    ):
885
        xxfit = kwargs["xspace"](
5✔
886
            util.get("extrapolate_min", kwargs, np.min(unv(datax))),
887
            util.get("extrapolate_max", kwargs, np.max(unv(datax))),
888
            kwargs["steps"],
889
        )
890
        for pmin, pmax in [
5✔
891
            (np.min(xxfit), np.min(xfit)),
892
            (np.max(xfit), np.max(xxfit)),
893
        ]:
894
            __function(
5✔
895
                fitted,
896
                kwargs["xspace"](pmin, pmax, kwargs["steps"]),
897
                util.get("extrapolate_fmt", kwargs, "--"),
898
                color=ll.get_color(),
899
                hatch=util.get("extrapolate_hatch", kwargs, r"||"),
900
                **kwargs,
901
            )
902
    return ll.get_color(), xfit, fitted(xfit)
5✔
903

904

905
def plt_interpolate(datax, datay, icolor=None, **kwargs):
5✔
906
    """
907
    Interpolate and Plot that Interpolation.
908
    """
909
    inter = interpolate.interpolate(datax, datay, **kwargs)
5✔
910
    kargs = {}
5✔
911
    if isinstance(kwargs["interpolate_fmt"], tuple):
5✔
912
        kargs["ls"] = kwargs["interpolate_fmt"]
×
913
    else:
914
        kargs["f"] = kwargs["interpolate_fmt"]
5✔
915
    if kwargs["interpolate_label"] is not None:
5✔
916
        kargs["l"] = kwargs["interpolate_label"]
5✔
917
    # l = None so that no label
918
    return (
5✔
919
        inter,
920
        *plt_fit_or_interpolate(datax, datay, inter, c=icolor, **kargs, **kwargs),
921
    )
922

923

924
def plt_fit(datax, datay, gfunction, **kwargs):
5✔
925
    """
926
    Fit and Plot that Fit.
927
    """
928
    func = wrap.get_lambda(gfunction, kwargs["xvar"])
5✔
929
    rfit = _fit(datax, datay, gfunction, **kwargs)
5✔
930

931
    def fitted(x):
5✔
932
        return func(x, *rfit)
5✔
933

934
    vnames = wrap.get_varnames(gfunction, kwargs["xvar"])
5✔
935
    for v in vnames[1:]:  # remove fixed parameters from kwargs
5✔
936
        kwargs.pop(v, None)
5✔
937

938
    l = get_fnc_legend(gfunction, rfit, datax, datay, **kwargs)
5✔
939
    return (rfit, *plt_fit_or_interpolate(datax, datay, fitted, l, **kwargs))
5✔
940

941

942
def init_plot(kwargs):
5✔
943
    fig = None
5✔
944
    if util.has("axes", kwargs) and kwargs["axes"] is not None:
5✔
945
        plt.sca(kwargs["axes"])
5✔
946
        fig = kwargs["axes"].get_figure()
5✔
947
    if kwargs["init"] or util.true("residue", kwargs):
5✔
948
        if kwargs["size"] is None:
5✔
949
            fig = plt.figure()
5✔
950
        else:
951
            fig = plt.figure(figsize=kwargs["size"])
×
952
        if kwargs["residue"]:
5✔
953
            fig.add_axes((0.1, 0.3, 0.8, 0.6))
5✔
954
    if util.has("next_color", kwargs) and not kwargs["next_color"]:
5✔
955
        lines = plt.gca()._get_lines
5✔
956
        tmp_color = lines._cycler_items[lines._idx]["color"]
5✔
957

958
        if kwargs["data_color"] is None:
5✔
959
            kwargs["data_color"] = tmp_color
5✔
960
        if kwargs["fit_color"] is None:
5✔
961
            kwargs["fit_color"] = tmp_color
5✔
962
        if kwargs["function_color"] is None:
5✔
963
            kwargs["function_color"] = tmp_color
5✔
964
    return fig
5✔
965

966

967
def save_plot(**kwargs):
5✔
968
    """
969
    save plot
970
    """
971
    smplr.style_plot1d(**kwargs)
5✔
972
    if "lpos" in kwargs and kwargs["lpos"] >= 0:
5✔
973
        if util.has("bbox_to_anchor", kwargs):
5✔
974
            if util.has("ncol", kwargs):
5✔
975
                plt.legend(
5✔
976
                    loc=kwargs["lpos"],
977
                    bbox_to_anchor=kwargs["bbox_to_anchor"],
978
                    ncol=kwargs["ncol"],
979
                    borderaxespad=0,
980
                )
981
            else:
982
                plt.legend(loc=kwargs["lpos"], bbox_to_anchor=kwargs["bbox_to_anchor"])
5✔
983
        else:
984
            plt.legend(loc=kwargs["lpos"])
5✔
985
    if "save" in kwargs and kwargs["save"] is not None:
5✔
986
        io.mkdirs(kwargs["save"])
×
987
        plt.savefig(kwargs["save"] + ".pdf")
×
988
    if kwargs.get("show"):
5✔
989
        show(**kwargs)
5✔
990

991

992
def show(**kwargs):
5✔
993
    kwargs = plot_kwargs(kwargs)
5✔
994

995
    smplr.style_plot1d(**kwargs)
5✔
996
    plt.show()
5✔
997

998

999
from smpl.plot2d import plot2d as _plot2d
5✔
1000
from smpl.plot2d import plot2d_kwargs as _plot2d_kwargs
5✔
1001

1002

1003
def plot2d(*args, **kwargs):
5✔
1004
    _plot2d(*args, **kwargs)
5✔
1005

1006

1007
def plot2d_kwargs(*args, **kwargs):
5✔
1008
    _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