Ejemplo n.º 1
0
def display_photometry_model_magnitudes(analysis, data=(), **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    with linear interpolation between each bin
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = list(analysis.data_list.keys())

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in list(analysis.data_list.keys()):

            if isinstance(analysis.data_list[key], photolike.PhotometryLike):

                new_data_keys.append(key)

            else:

                custom_warnings.warn(
                    "Dataset %s is not of the Photometery kind. Cannot be plotted by "
                    "display_photometry_model_magnitudes" % key)

    if not new_data_keys:
        RuntimeError(
            "There were no valid Photometry data requested for plotting. Please use the detector names in the data list"
        )

    data_keys = new_data_keys

    # Default is to show the model with steps
    step = threeML_config.plugins.photo.fit_plot.step

    data_cmap = threeML_config.plugins.photo.fit_plot.data_cmap.value  # plt.cm.rainbow

    model_cmap = threeML_config.plugins.photo.fit_plot.model_cmap.value

    # Legend is on by default
    show_legend = True

    # Default colors

    data_colors = cmap_intervals(len(data_keys), data_cmap)
    model_colors = cmap_intervals(len(data_keys), model_cmap)

    # Now override defaults according to the optional keywords, if present

    if "show_legend" in kwargs:

        show_legend = bool(kwargs.pop("show_legend"))

    if "step" in kwargs:
        step = bool(kwargs.pop("step"))

    if "data_cmap" in kwargs:
        data_cmap = plt.get_cmap(kwargs.pop("data_cmap"))
        data_colors = cmap_intervals(len(data_keys), data_cmap)

    if "model_cmap" in kwargs:
        model_cmap = kwargs.pop("model_cmap")
        model_colors = cmap_intervals(len(data_keys), model_cmap)

    if "data_colors" in kwargs:
        data_colors = kwargs.pop("data_colors")

        if len(data_colors) < len(data_keys):
            log.error(
                "You need to provide at least a number of data colors equal to the "
                "number of datasets")
            raise ValueError()

    if "model_colors" in kwargs:
        model_colors = kwargs.pop("model_colors")

        if len(model_colors) < len(data_keys):
            log.error(
                "You need to provide at least a number of model colors equal to the "
                "number of datasets")
            raise ValueError()

    residual_plot = ResidualPlot(**kwargs)

    # go thru the detectors
    for key, data_color, model_color in zip(data_keys, data_colors,
                                            model_colors):

        data = analysis.data_list[key]  # type: photolike

        # get the expected counts

        avg_wave_length = (data._filter_set.effective_wavelength.value
                           )  # type: np.ndarray

        # need to sort because filters are not always in order

        sort_idx = avg_wave_length.argsort()

        expected_model_magnitudes = data._get_total_expectation()[sort_idx]
        magnitudes = data.magnitudes[sort_idx]
        mag_errors = data.magnitude_errors[sort_idx]
        avg_wave_length = avg_wave_length[sort_idx]

        residuals = old_div((expected_model_magnitudes - magnitudes),
                            mag_errors)

        widths = data._filter_set.wavelength_bounds.widths[sort_idx]

        residual_plot.add_data(
            x=avg_wave_length,
            y=magnitudes,
            xerr=widths,
            yerr=mag_errors,
            residuals=residuals,
            label=data._name,
            color=data_color,
        )

        residual_plot.add_model(
            avg_wave_length,
            expected_model_magnitudes,
            label="%s Model" % data._name,
            color=model_color,
        )

        return residual_plot.finalize(
            xlabel="Wavelength\n(%s)" % data._filter_set.waveunits,
            ylabel="Magnitudes",
            xscale="linear",
            yscale="linear",
            invert_y=True,
        )
def display_posterior_model_counts(bayesian_analysis,
                                   result=None,
                                   thin=100,
                                   shade=True,
                                   q_level=68,
                                   gradient=0.6,
                                   data=(),
                                   **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param data_color: (optional) color for all datasets
    :param model_color: (optional) color for all folded models
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    :param model_subplot: (optional) axe(s) to plot to for overplotting
    with linear interpolation between each bin
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = bayesian_analysis.data_list.keys()

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in bayesian_analysis.data_list.keys():

            if isinstance(bayesian_analysis.data_list[key],
                          threeML.plugins.SpectrumLike.SpectrumLike):

                new_data_keys.append(key)

            else:

                custom_warnings.warn(
                    "Dataset %s is not of the SpectrumLike kind. Cannot be plotted by "
                    "display_spectrum_model_counts" % key)

    if not new_data_keys:
        RuntimeError(
            'There were no valid SpectrumLike data requested for plotting. Please use the detector names in the data list'
        )

    data_keys = new_data_keys

    # default settings

    # Default is to show the model with steps
    step = True

    data_cmap = threeML_config['ogip']['data plot cmap']  # plt.cm.rainbow
    model_cmap = threeML_config['ogip'][
        'model plot cmap']  # plt.cm.nipy_spectral_r

    # Legend is on by default
    show_legend = False

    show_residuals = False

    # Default colors

    data_colors = cmap_intervals(len(data_keys), data_cmap)
    model_colors = cmap_intervals(len(data_keys), model_cmap)

    # Now override defaults according to the optional keywords, if present

    model_kwargs = dict(alpha=0.05, zorder=-5000)
    if 'model_kwargs' in kwargs:

        model_kwargs_tmp = kwargs.pop('model_kwargs')

        for k, v in model_kwargs_tmp.items():

            model_kwargs[k] = v

    data_kwargs = dict(
        alpha=1.,
        fmt=threeML_config['residual plot']['error marker'],
        markersize=threeML_config['residual plot']['error marker size'],
        linestyle='',
        elinewidth=threeML_config['residual plot']['error line width'],
        capsize=0,
        zorder=-1)

    if 'data_kwargs' in kwargs:

        data_kwargs_tmp = kwargs.pop('data_kwargs')

        for k, v in data_kwargs_tmp.items():

            data_kwargs[k] = v

    if 'show_data' in kwargs:

        show_data = bool(kwargs.pop('show_data'))

    else:

        show_data = True

    if 'show_legend' in kwargs:
        show_legend = bool(kwargs.pop('show_legend'))

    if 'show_residuals' in kwargs:
        show_residuals = bool(kwargs.pop('show_residuals'))

        residual_storage = []
        residual_err_storage = []

    if 'step' in kwargs:
        step = bool(kwargs.pop('step'))

    if 'min_rate' in kwargs:
        min_rate = kwargs.pop('min_rate')

        # If min_rate is a floating point, use the same for all datasets, otherwise use the provided ones

        try:

            min_rate = float(min_rate)

            min_rates = [min_rate] * len(data_keys)

        except TypeError:

            min_rates = list(min_rate)

            assert len(min_rates) >= len(
                data_keys), "If you provide different minimum rates for each data set, you need" \
                            "to provide an iterable of the same length of the number of datasets"

    else:

        # This is the default (no rebinning)

        min_rates = [NO_REBIN] * len(data_keys)

    if 'data_cmap' in kwargs:
        data_cmap = plt.get_cmap(kwargs.pop('data_cmap'))
        data_colors = cmap_intervals(len(data_keys), data_cmap)

    if 'model_cmap' in kwargs:
        model_cmap = kwargs.pop('model_cmap')
        model_colors = cmap_intervals(len(data_keys), model_cmap)

    if 'data_colors' in kwargs:
        data_colors = kwargs.pop('data_colors')

        assert len(data_colors) >= len(data_keys), "You need to provide at least a number of data colors equal to the " \
                                                   "number of datasets"

    elif 'data_color' in kwargs:

        data_colors = [kwargs.pop('data_color')] * len(data_keys)

    if 'model_colors' in kwargs:
        model_colors = kwargs.pop('model_colors')

        assert len(model_colors) >= len(
            data_keys), "You need to provide at least a number of model colors equal to the " \
                        "number of datasets"

    ratio_residuals = False
    if 'ratio_residuals' in kwargs:
        ratio_residuals = bool(kwargs['ratio_residuals'])

    elif 'model_color' in kwargs:

        model_colors = [kwargs.pop('model_color')] * len(data_keys)

    if 'model_labels' in kwargs:
        model_labels = kwargs.pop('model_labels')

        assert len(model_labels) == len(
            data_keys
        ), 'you must have the same number of model labels as data sets'

    else:

        model_labels = [
            '%s Model' % bayesian_analysis.data_list[key]._name
            for key in data_keys
        ]

    #fig, (ax, ax1) = plt.subplots(2, 1, sharex=True, gridspec_kw={'height_ratios': [2, 1]}, **kwargs)

    residual_plot = ResidualPlot(show_residuals=show_residuals, **kwargs)

    if show_residuals:

        axes = [residual_plot.data_axis, residual_plot.residual_axis]

    else:

        axes = residual_plot.data_axis

    # extract the samples
    if result is None:
        samples = bayesian_analysis.results.samples.T[::thin]

    else:

        samples = result.samples.T[::thin]

    if shade:

        color_config = Colors()

        shade_y = []
        shade_x = []

    for params in samples:

        for i, (k, v) in enumerate(
                bayesian_analysis.likelihood_model.free_parameters.items()):

            v.value = params[i]

        # first with no data
        if shade:
            per_det_y = []
            per_det_x = []

        for key, data_color, model_color, min_rate, model_label in zip(
                data_keys, data_colors, model_colors, min_rates, model_labels):

            # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

            data = bayesian_analysis.data_list[
                key]  # type: threeML.plugins.SpectrumLike.SpectrumLike

            if not shade:

                data.display_model(data_color=data_color,
                                   model_color=model_color,
                                   min_rate=min_rate,
                                   step=step,
                                   show_residuals=False,
                                   show_data=False,
                                   show_legend=show_legend,
                                   ratio_residuals=ratio_residuals,
                                   model_label=None,
                                   model_subplot=axes,
                                   data_kwargs=data_kwargs,
                                   model_kwargs=model_kwargs)
            else:

                # this is private for now
                rebinned_quantities = data._construct_counts_arrays(
                    min_rate, ratio_residuals)

                if step:

                    pass

                else:

                    y = (rebinned_quantities['expected_model_rate'] /
                         rebinned_quantities['chan_width'])[data.mask]

                    x = np.mean([
                        rebinned_quantities['energy_min'],
                        rebinned_quantities['energy_max']
                    ],
                                axis=0)[data.mask]

                    per_det_y.append(y)
                    per_det_x.append(x)

        if shade:
            shade_y.append(per_det_y)
            shade_x.append(per_det_x)

    if shade:

        # convert to per detector
        shade_y = np.array(shade_y).T
        shade_x = np.array(shade_x).T
        model_kwargs.pop('zorder')
        for key, data_color, model_color, min_rate, model_label, x, y, in zip(
                data_keys, data_colors, model_colors, min_rates, model_labels,
                shade_x, shade_y):

            # we have to do a little reshaping because... life

            y = np.array([yy.tolist() for yy in y])

            q_levels = np.atleast_1d(q_level)
            q_levels.sort()

            scale = 1.
            zorder = -100
            for level in q_levels:

                color = color_config.format(model_color)
                color_scale = color_config.scale_colour(color, scale)

                # first we need to get the quantiles along the energy axis
                low = np.percentile(y, 50 - level * 0.5, axis=0)
                high = np.percentile(y, 50 + level * 0.5, axis=0)

                residual_plot.data_axis.fill_between(x[0],
                                                     low,
                                                     high,
                                                     color=color_scale,
                                                     zorder=zorder,
                                                     **model_kwargs)

                scale *= gradient

                zorder -= 1

    for key, data_color, model_color, min_rate, model_label in zip(
            data_keys, data_colors, model_colors, min_rates, model_labels):

        # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

        data = bayesian_analysis.data_list[
            key]  # type: threeML.plugins.SpectrumLike.SpectrumLike

        data.display_model(
            data_color=data_color,
            model_color=model_color,
            min_rate=min_rate,
            step=step,
            show_residuals=False,
            show_data=show_data,
            show_legend=show_legend,
            ratio_residuals=ratio_residuals,
            model_label=model_label,
            model_subplot=axes,
            model_kwargs=dict(alpha=0.),  # no model
            data_kwargs=data_kwargs)

    return residual_plot.figure
Ejemplo n.º 3
0
def display_spectrum_model_counts(analysis, data=(), **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param data_color: (optional) color for all datasets
    :param model_color: (optional) color for all folded models
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    :param model_subplot: (optional) axe(s) to plot to for overplotting
    with linear interpolation between each bin
    :param data_per_plot: (optional) Can specify how many detectors should be plotted in one plot. If there
    are more detectors than this number it will split it up in several plots
    :param show_background: (optional) Also show the background
    :param source_only: (optional) Plot only source (total data - background)
    :param background_cmap: (str) (optional) the color map used to extract automatically the colors for the background
    :param background_colors: (optional) a tuple or list with the color for each background
    :param background_color: (optional) color for all backgrounds
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = list(analysis.data_list.keys())

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in list(analysis.data_list.keys()):

            if isinstance(analysis.data_list[key], speclike.SpectrumLike):

                new_data_keys.append(key)

            else:

                log.warning(
                    "Dataset %s is not of the SpectrumLike kind. Cannot be plotted by "
                    "display_spectrum_model_counts" % key)

    if not new_data_keys:
        RuntimeError(
            "There were no valid SpectrumLike data requested for plotting. Please use the detector names in the data list"
        )

    data_keys = new_data_keys

    # default settings

    _sub_menu: BinnedSpectrumPlot = threeML_config.plugins.ogip.fit_plot

    # Default is to show the model with steps
    step = _sub_menu.step

    data_cmap = _sub_menu.data_cmap.value
    model_cmap = _sub_menu.model_cmap.value
    background_cmap = _sub_menu.background_cmap.value

    # Legend is on by default
    show_legend = _sub_menu.show_legend

    show_residuals = _sub_menu.show_residuals

    show_background: bool = _sub_menu.show_background

    # Default colors

    _cmap_len = max(len(data_keys), _sub_menu.n_colors)

    data_colors = cmap_intervals(_cmap_len, data_cmap)
    model_colors = cmap_intervals(_cmap_len, model_cmap)
    background_colors = cmap_intervals(_cmap_len, background_cmap)

    # Now override defaults according to the optional keywords, if present

    if "show_data" in kwargs:

        show_data = bool(kwargs.pop("show_data"))

    else:

        show_data = True

    if "show_legend" in kwargs:
        show_legend = bool(kwargs.pop("show_legend"))

    if "show_residuals" in kwargs:
        show_residuals = bool(kwargs.pop("show_residuals"))

    if "step" in kwargs:
        step = bool(kwargs.pop("step"))

    if "min_rate" in kwargs:

        min_rate = kwargs.pop("min_rate")

        # If min_rate is a floating point, use the same for all datasets, otherwise use the provided ones

        try:

            min_rate = float(min_rate)

            min_rates = [min_rate] * len(data_keys)

        except TypeError:

            min_rates = list(min_rate)

            if len(min_rates) < len(data_keys):
                log.error(
                    "If you provide different minimum rates for each data set, you need"
                    "to provide an iterable of the same length of the number of datasets"
                )
                raise ValueError()

    else:

        # This is the default (no rebinning)

        min_rates = [NO_REBIN] * len(data_keys)

    if "data_per_plot" in kwargs:
        data_per_plot = int(kwargs.pop("data_per_plot"))
    else:
        data_per_plot = len(data_keys)

    if "data_cmap" in kwargs:
        if len(data_keys) <= data_per_plot:

            _cmap_len = max(len(data_keys), _sub_menu.n_colors)

            data_colors = cmap_intervals(_cmap_len, kwargs.pop("data_cmap"))
        else:

            _cmap_len = max(data_per_plot, _sub_menu.n_colors)

            data_colors_base = cmap_intervals(_cmap_len,
                                              kwargs.pop("data_cmap"))
            data_colors = []
            for i in range(len(data_keys)):
                data_colors.append(data_colors_base[i % data_per_plot])

    elif "data_colors" in kwargs:
        data_colors = kwargs.pop("data_colors")

        if len(data_colors) < len(data_keys):
            log.error(
                "You need to provide at least a number of data colors equal to the "
                "number of datasets")
            raise ValueError()

    elif _sub_menu.data_color is not None:

        data_colors = [_sub_menu.data_color] * len(data_keys)

    # always override
    if ("data_color" in kwargs):

        data_colors = [kwargs.pop("data_color")] * len(data_keys)

    if "model_cmap" in kwargs:
        if len(data_keys) <= data_per_plot:

            _cmap_len = max(len(data_keys), _sub_menu.n_colors)

            model_colors = cmap_intervals(_cmap_len, kwargs.pop("model_cmap"))
        else:

            _cmap_len = max(data_per_plot, _sub_menu.n_colors)

            model_colors_base = cmap_intervals(_cmap_len,
                                               kwargs.pop("model_cmap"))
            model_colors = []
            for i in range(len(data_keys)):
                model_colors.append(model_colors_base[i % data_per_plot])

    elif "model_colors" in kwargs:
        model_colors = kwargs.pop("model_colors")

        if len(model_colors) < len(data_keys):
            log.error(
                "You need to provide at least a number of model colors equal to the "
                "number of datasets")
            raise ValueError()

    elif _sub_menu.model_color is not None:

        model_colors = [_sub_menu.model_color] * len(data_keys)

    # always overide
    if "model_color" in kwargs:

        model_colors = [kwargs.pop("model_color")] * len(data_keys)

    if "background_cmap" in kwargs:
        if len(data_keys) <= data_per_plot:
            background_colors = cmap_intervals(len(data_keys),
                                               kwargs.pop("background_cmap"))
        else:
            background_colors_base = cmap_intervals(
                data_per_plot, kwargs.pop("background_cmap"))
            background_colors = []
            for i in range(len(data_keys)):
                background_colors.append(background_colors_base[i %
                                                                data_per_plot])

    elif "background_colors" in kwargs:
        background_colors = kwargs.pop("background_colors")

        if len(background_colors) < len(data_keys):
            log.error(
                "You need to provide at least a number of background colors equal to the "
                "number of datasets")
            raise ValueError()

    elif _sub_menu.background_color is not None:

        background_colors = [_sub_menu.background_color] * len(data_keys)

    # always override
    if "background_color" in kwargs:

        background_colors = [kwargs.pop("background_color")] * len(data_keys)

    ratio_residuals = False
    if "ratio_residuals" in kwargs:
        ratio_residuals = bool(kwargs["ratio_residuals"])

    if "model_labels" in kwargs:

        model_labels = kwargs.pop("model_labels")

        if len(model_labels) != len(data_keys):
            log.error(
                "You must have the same number of model labels as data sets")
            raise ValueError()
    else:

        model_labels = [
            "%s Model" % analysis.data_list[key]._name for key in data_keys
        ]

    if "background_labels" in kwargs:

        background_labels = kwargs.pop("background_labels")

        if len(background_labels) != len(data_keys):
            log.error(
                "You must have the same number of background labels as data sets"
            )
            raise ValueError()

    else:

        background_labels = [
            "%s Background" % analysis.data_list[key]._name
            for key in data_keys
        ]

    if "source_only" in kwargs:

        source_only = kwargs.pop("source_only")

        if type(source_only) != bool:
            log.error("source_only must be a boolean")
            raise TypeError()

    else:

        source_only = True

    if "show_background" in kwargs:

        show_background = kwargs.pop("show_background")

        if type(show_background) != bool:
            log.error("show_background must be a boolean")
            raise TypeError()

    if len(data_keys) <= data_per_plot:
        # If less than data_per_plot detectors need to be plotted,
        # just plot it in one plot
        residual_plot = ResidualPlot(show_residuals=show_residuals, **kwargs)

        if show_residuals:

            axes = [residual_plot.data_axis, residual_plot.residual_axis]

        else:

            axes = residual_plot.data_axis

        # go thru the detectors
        for key, data_color, model_color, background_color, min_rate, model_label, background_label in zip(
                data_keys, data_colors, model_colors, background_colors,
                min_rates, model_labels, background_labels):

            # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

            data = analysis.data_list[key]  # type: speclike

            data.display_model(data_color=data_color,
                               model_color=model_color,
                               min_rate=min_rate,
                               step=step,
                               show_residuals=show_residuals,
                               show_data=show_data,
                               show_legend=show_legend,
                               ratio_residuals=ratio_residuals,
                               model_label=model_label,
                               model_subplot=axes,
                               show_background=show_background,
                               source_only=source_only,
                               background_color=background_color,
                               background_label=background_label)

        return residual_plot.figure

    else:
        # Too many detectors to plot everything in one plot... Make indivi.
        # plots with data_per_plot dets per plot

        # How many plots do we need?
        n_plots = int(np.ceil(1. * len(data_keys) / data_per_plot))

        plots = []
        for i in range(n_plots):
            plots.append(ResidualPlot(show_residuals=show_residuals, **kwargs))

        # go thru the detectors
        for j, (key, data_color, model_color, background_color, min_rate,
                model_label, background_label) in enumerate(
                    zip(data_keys, data_colors, model_colors,
                        background_colors, min_rates, model_labels,
                        background_labels)):
            axes = [
                plots[int(j / data_per_plot)].data_axis,
                plots[int(j / data_per_plot)].residual_axis
            ]
            # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

            data = analysis.data_list[key]  # type: speclike

            data.display_model(data_color=data_color,
                               model_color=model_color,
                               min_rate=min_rate,
                               step=step,
                               show_residuals=show_residuals,
                               show_data=show_data,
                               show_legend=show_legend,
                               ratio_residuals=ratio_residuals,
                               model_label=model_label,
                               model_subplot=axes,
                               show_background=show_background,
                               source_only=source_only,
                               background_color=background_color,
                               background_label=background_label)

        figs = []
        for p in plots:
            figs.append(p.figure)

        return figs
Ejemplo n.º 4
0
def plot_spectra(*analysis_results, **kwargs):
    """

    plotting routine for fitted point source spectra


    :param analysis_results: fitted JointLikelihood or BayesianAnalysis objects
    :param sources_to_use: (optional) list of PointSource string names to plot from the analysis
    :param energy_unit: (optional) astropy energy unit in string form (can also be frequency)
    :param flux_unit: (optional) astropy flux unit in string form
    :param confidence_level: (optional) confidence level to use (default: 0.68)
    :param ene_min: (optional) minimum energy to plot
    :param ene_max: (optional) maximum energy to plot
    :param num_ene: (optional) number of energies to plot
    :param use_components: (optional) True or False to plot the spectral components
    :param components_to_use: (optional) list of string names of the components to plot: including 'total'
    will also plot the total spectrum
    :param sum_sources: (optional) some all the MLE and Bayesian sources
    :param show_contours: (optional) True or False to plot the contour region
    :param plot_style_kwargs: (optional) dictionary of MPL plot styling for the best fit curve
    :param contour_style_kwargs: (optional) dictionary of MPL plot styling for the contour regions
    :param fit_cmap: MPL color map to iterate over for plotting multiple analyses
    :param contour_cmap: MPL color map to iterate over for plotting contours for  multiple analyses
    :param subplot: subplot to use
    :param xscale: 'log' or 'linear'
    :param yscale: 'log' or 'linear'
    :param include_extended: True or False, also plot extended source spectra.
    :return:
    """

    # allow matplotlib to plot quantities to the access

    quantity_support()

    _defaults = {
        'fit_cmap':
        threeML_config['model plot']['point source plot']['fit cmap'],
        'contour_cmap':
        threeML_config['model plot']['point source plot']['contour cmap'],
        'contour_colors':
        None,
        'fit_colors':
        None,
        'confidence_level':
        0.68,
        'equal_tailed':
        True,
        'best_fit':
        'median',
        'energy_unit':
        'keV',
        'flux_unit':
        '1/(keV s cm2)',
        'ene_min':
        10.,
        'ene_max':
        1E4,
        'num_ene':
        100,
        'use_components':
        False,
        'components_to_use': [],
        'sources_to_use': [],
        'sum_sources':
        False,
        'show_contours':
        True,
        'plot_style_kwargs':
        threeML_config['model plot']['point source plot']['plot style'],
        'contour_style_kwargs':
        threeML_config['model plot']['point source plot']['contour style'],
        'show_legend':
        True,
        'legend_kwargs':
        threeML_config['model plot']['point source plot']['legend style'],
        'subplot':
        None,
        'xscale':
        'log',
        'yscale':
        'log',
        'include_extended':
        False
    }

    for key, value in kwargs.items():

        if key in _defaults:
            _defaults[key] = value

    if isinstance(_defaults['ene_min'], u.Quantity):
        assert isinstance(
            _defaults['ene_max'],
            u.Quantity), 'both energy arguments must be Quantities'

    if isinstance(_defaults['ene_max'], u.Quantity):
        assert isinstance(
            _defaults['ene_min'],
            u.Quantity), 'both energy arguments must be Quantities'

    if isinstance(_defaults['ene_max'], u.Quantity):

        energy_range = np.linspace(_defaults['ene_min'], _defaults['ene_max'],
                                   _defaults['num_ene'])  # type: u.Quantity

        _defaults['energy_unit'] = energy_range.unit

        if _defaults['xscale'] == 'log':
            energy_range = np.logspace(
                np.log10(energy_range.min().value),
                np.log10(energy_range.max().value),
                _defaults['num_ene']) * energy_range.unit

    else:

        energy_range = np.logspace(
            np.log10(_defaults['ene_min']), np.log10(_defaults['ene_max']),
            _defaults['num_ene']) * u.Unit(_defaults['energy_unit'])

    mle_analyses, bayesian_analyses, num_sources_to_plot, duplicate_keys = _setup_analysis_dictionaries(
        analysis_results,
        energy_range,
        _defaults['energy_unit'],
        _defaults['flux_unit'],
        _defaults['use_components'],
        _defaults['components_to_use'],
        _defaults['confidence_level'],
        _defaults['equal_tailed'],
        differential=True,
        sources_to_use=_defaults['sources_to_use'],
        include_extended=_defaults['include_extended'])

    # we are now ready to plot.
    # all calculations have been made.

    # if we are not going to sum sources

    if not _defaults['sum_sources']:

        if _defaults['fit_colors'] is None:

            color_fit = cmap_intervals(num_sources_to_plot + 1,
                                       _defaults['fit_cmap'])

        else:

            # duck typing
            if isinstance(_defaults['fit_colors'], (str, str)):

                color_fit = [_defaults['fit_colors']] * num_sources_to_plot

            elif isinstance(_defaults['fit_colors'], list):

                assert len(_defaults['fit_colors']) == num_sources_to_plot, \
                    'list of colors (%d) must be the same length as sources ot plot (%s)' % (
                    len(_defaults['fit_colors']), num_sources_to_plot)

                color_fit = _defaults['fit_colors']

            else:
                raise ValueError('Can not setup color, wrong type:',
                                 type(_defaults['fit_colors']))

        if _defaults['contour_colors'] is None:

            color_contour = cmap_intervals(num_sources_to_plot + 1,
                                           _defaults['contour_cmap'])

        else:

            # duck typing
            if isinstance(_defaults['contour_colors'], (str, str)):

                color_contour = [_defaults['contour_colors']
                                 ] * num_sources_to_plot

            elif isinstance(_defaults['contour_colors'], list):

                assert len(_defaults['contour_colors']) == num_sources_to_plot, \
                    'list of colors (%d) must be the same length as sources ot plot (%s)' % (
                        len(_defaults['contour_colors']), num_sources_to_plot)

                color_contour = _defaults['fit_colors']

            else:
                raise ValueError('Can not setup contour color, wrong type:',
                                 type(_defaults['contour_colors']))

        color_itr = 0

        # go thru the mle analysis and plot spectra

        plotter = SpectralContourPlot(
            num_sources_to_plot,
            xscale=_defaults['xscale'],
            yscale=_defaults['yscale'],
            show_legend=_defaults['show_legend'],
            plot_kwargs=_defaults['plot_style_kwargs'],
            contour_kwargs=_defaults['contour_style_kwargs'],
            legend_kwargs=_defaults['legend_kwargs'],
            emin=_defaults['ene_min'],
            emax=_defaults['ene_max'],
            subplot=_defaults['subplot'])

        for key in list(mle_analyses.keys()):

            # we won't assume to plot the total until the end

            plot_total = False

            if _defaults['use_components']:

                # if this source has no components or none that we wish to plot
                # then we will plot the total spectrum after this

                if (not list(mle_analyses[key]['components'].keys())) or (
                        'total' in _defaults['components_to_use']):
                    plot_total = True

                for component in list(mle_analyses[key]['components'].keys()):

                    positive_error = None
                    negative_error = None

                    # extract the information and plot it

                    if _defaults['best_fit'] == 'average':

                        best_fit = mle_analyses[key]['components'][
                            component].average

                    else:

                        best_fit = mle_analyses[key]['components'][
                            component].median

                    if _defaults['show_contours']:
                        positive_error = mle_analyses[key]['components'][
                            component].upper_error
                        negative_error = mle_analyses[key]['components'][
                            component].lower_error

                        neg_mask = negative_error <= 0

                        # replace with small number

                        negative_error[neg_mask] = min(best_fit) * 0.9

                    label = "%s: %s" % (key, component)

                    # this is where we keep track of duplicates

                    if key in duplicate_keys:
                        label = "%s: MLE" % label

                    if mle_analyses[key]['components'][
                            component].is_dimensionless:

                        plotter.add_dimensionless_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label)

                    else:

                        plotter.add_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label)

                    color_itr += 1

            else:

                plot_total = True

            if plot_total:

                # it ends up that we need to plot the total spectrum
                # which is just a repeat of the process

                if _defaults['best_fit'] == 'average':

                    best_fit = mle_analyses[key]['fitted point source'].average

                else:

                    best_fit = mle_analyses[key]['fitted point source'].median

                if _defaults['show_contours']:

                    positive_error = mle_analyses[key][
                        'fitted point source'].upper_error
                    negative_error = mle_analyses[key][
                        'fitted point source'].lower_error

                    neg_mask = negative_error <= 0

                    # replace with small number

                    negative_error[neg_mask] = min(best_fit) * 0.9

                else:

                    positive_error = None
                    negative_error = None

                label = "%s" % key

                if key in duplicate_keys:
                    label = "%s: MLE" % label

                plotter.add_model(energy_range=energy_range,
                                  best_fit=best_fit,
                                  color=color_fit[color_itr],
                                  upper_error=positive_error,
                                  lower_error=negative_error,
                                  contour_color=color_contour[color_itr],
                                  label=label)

                color_itr += 1

        # we will do the exact same thing for the bayesian analyses

        for key in list(bayesian_analyses.keys()):

            plot_total = False

            if _defaults['use_components']:

                if (not list(bayesian_analyses[key]['components'].keys())) or (
                        'total' in _defaults['components_to_use']):
                    plot_total = True

                for component in list(
                        bayesian_analyses[key]['components'].keys()):

                    positive_error = None
                    negative_error = None

                    if _defaults['best_fit'] == 'average':

                        best_fit = bayesian_analyses[key]['components'][
                            component].average

                    else:

                        best_fit = bayesian_analyses[key]['components'][
                            component].median

                    if _defaults['show_contours']:
                        positive_error = bayesian_analyses[key]['components'][
                            component].upper_error
                        negative_error = bayesian_analyses[key]['components'][
                            component].lower_error

                    label = "%s: %s" % (key, component)

                    if key in duplicate_keys:
                        label = "%s: Bayesian" % label

                    if bayesian_analyses[key]['components'][
                            component].is_dimensionless:

                        plotter.add_dimensionless_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label)

                    else:

                        plotter.add_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label)

                    color_itr += 1

            else:

                plot_total = True

            if plot_total:

                if _defaults['best_fit'] == 'average':

                    best_fit = bayesian_analyses[key][
                        'fitted point source'].average

                else:

                    best_fit = bayesian_analyses[key][
                        'fitted point source'].median

                positive_error = None
                negative_error = None

                if _defaults['show_contours']:
                    positive_error = bayesian_analyses[key][
                        'fitted point source'].upper_error
                    negative_error = bayesian_analyses[key][
                        'fitted point source'].lower_error

                label = "%s" % key

                if key in duplicate_keys:
                    label = "%s: Bayesian" % label

                plotter.add_model(energy_range=energy_range,
                                  best_fit=best_fit,
                                  color=color_fit[color_itr],
                                  upper_error=positive_error,
                                  lower_error=negative_error,
                                  contour_color=color_contour[color_itr],
                                  label=label)

                color_itr += 1

    else:
        # now we sum sources instead

        # we keep MLE and Bayes apart because it makes no
        # sense to sum them together

        total_analysis_mle, component_sum_dict_mle, num_sources_to_plot = _collect_sums_into_dictionaries(
            mle_analyses, _defaults['use_components'],
            _defaults['components_to_use'])

        total_analysis_bayes, component_sum_dict_bayes, num_sources_to_plot_bayes = _collect_sums_into_dictionaries(
            bayesian_analyses, _defaults['use_components'],
            _defaults['components_to_use'])

        num_sources_to_plot += num_sources_to_plot_bayes

        plotter = SpectralContourPlot(
            num_sources_to_plot,
            xscale=_defaults['xscale'],
            yscale=_defaults['yscale'],
            show_legend=_defaults['show_legend'],
            plot_kwargs=_defaults['plot_style_kwargs'],
            contour_kwargs=_defaults['contour_style_kwargs'],
            legend_kwargs=_defaults['legend_kwargs'],
            emin=_defaults['ene_min'],
            emax=_defaults['ene_max'],
            subplot=_defaults['subplot'])

        color_fit = cmap_intervals(num_sources_to_plot, _defaults['fit_cmap'])
        color_contour = cmap_intervals(num_sources_to_plot,
                                       _defaults['contour_cmap'])
        color_itr = 0

        if _defaults['use_components'] and list(component_sum_dict_mle.keys()):

            # we have components to plot

            for component, values in component_sum_dict_mle.items():

                summed_analysis = sum(values)

                if _defaults['best_fit'] == 'average':

                    best_fit = summed_analysis.average

                else:

                    best_fit = summed_analysis.median

                positive_error = None
                negative_error = None

                if _defaults['show_contours']:
                    positive_error = summed_analysis.upper_error

                    negative_error = summed_analysis.lower_error

                    neg_mask = negative_error <= 0

                    # replace with small number

                    negative_error[neg_mask] = min(best_fit) * 0.9

                if np.any([
                        c.is_dimensionless
                        for c in component_sum_dict_mle[component]
                ]):

                    plotter.add_dimensionless_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: MLE" % component)

                else:

                    plotter.add_model(energy_range=energy_range,
                                      best_fit=best_fit,
                                      color=color_fit[color_itr],
                                      upper_error=positive_error,
                                      lower_error=negative_error,
                                      contour_color=color_contour[color_itr],
                                      label="%s: MLE" % component)

                color_itr += 1

        if total_analysis_mle:

            # we will sum and plot the total
            # analysis

            summed_analysis = sum(total_analysis_mle)

            if _defaults['best_fit'] == 'average':

                best_fit = summed_analysis.average

            else:

                best_fit = summed_analysis.median

            positive_error = None
            negative_error = None

            if _defaults['show_contours']:
                positive_error = best_fit + summed_analysis.upper_error

                negative_error = best_fit - summed_analysis.lower_error

                neg_mask = negative_error <= 0

                # replace with small number

                negative_error[neg_mask] = min(best_fit) * 0.9

            plotter.add_model(energy_range=energy_range,
                              best_fit=best_fit,
                              color=color_fit[color_itr],
                              upper_error=positive_error,
                              lower_error=negative_error,
                              contour_color=color_contour[color_itr],
                              label="total: MLE")

            color_itr += 1

        if _defaults['use_components'] and list(
                component_sum_dict_bayes.keys()):

            # we have components to plot

            for component, values in component_sum_dict_bayes.items():

                summed_analysis = sum(values)

                if _defaults['best_fit'] == 'average':

                    best_fit = summed_analysis.average

                else:

                    best_fit = summed_analysis.median

                positive_error = None
                negative_error = None

                if _defaults['show_contours']:
                    positive_error = summed_analysis.upper_error

                    negative_error = summed_analysis.lower_error

                if np.any([
                        c.is_dimensionless
                        for c in component_sum_dict_bayes[component]
                ]):

                    plotter.add_dimensionless_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: Bayesian" % component)

                else:

                    plotter.add_model(energy_range=energy_range,
                                      best_fit=best_fit,
                                      color=color_fit[color_itr],
                                      upper_error=positive_error,
                                      lower_error=negative_error,
                                      contour_color=color_contour[color_itr],
                                      label="%s: Bayesian" % component)

                color_itr += 1

        if total_analysis_bayes:

            # we will sum and plot the total
            # analysis

            summed_analysis = sum(total_analysis_bayes)

            if _defaults['best_fit'] == 'average':

                best_fit = summed_analysis.average

            else:

                best_fit = summed_analysis.median

            positive_error = None
            negative_error = None

            if _defaults['show_contours']:
                positive_error = summed_analysis.upper_error

                negative_error = summed_analysis.lower_error

            plotter.add_model(energy_range=energy_range,
                              best_fit=best_fit,
                              color=color_fit[color_itr],
                              upper_error=positive_error,
                              lower_error=negative_error,
                              contour_color=color_contour[color_itr],
                              label="total: Bayesian")

        color_itr += 1

    return plotter.finalize(_defaults)
Ejemplo n.º 5
0
def display_spectrum_model_counts(analysis, data=(), **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param data_color: (optional) color for all datasets
    :param model_color: (optional) color for all folded models
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    :param model_subplot: (optional) axe(s) to plot to for overplotting
    with linear interpolation between each bin
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = list(analysis.data_list.keys())

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in list(analysis.data_list.keys()):

            if isinstance(analysis.data_list[key],
                          threeML.plugins.SpectrumLike.SpectrumLike):

                new_data_keys.append(key)

            else:

                custom_warnings.warn(
                    "Dataset %s is not of the SpectrumLike kind. Cannot be plotted by "
                    "display_spectrum_model_counts" % key)

    if not new_data_keys:
        RuntimeError(
            "There were no valid SpectrumLike data requested for plotting. Please use the detector names in the data list"
        )

    data_keys = new_data_keys

    # default settings

    # Default is to show the model with steps
    step = True

    data_cmap = threeML_config["ogip"]["data plot cmap"]  # plt.cm.rainbow
    model_cmap = threeML_config["ogip"][
        "model plot cmap"]  # plt.cm.nipy_spectral_r

    # Legend is on by default
    show_legend = True

    show_residuals = True

    # Default colors

    data_colors = cmap_intervals(len(data_keys), data_cmap)
    model_colors = cmap_intervals(len(data_keys), model_cmap)

    # Now override defaults according to the optional keywords, if present

    if "show_data" in kwargs:

        show_data = bool(kwargs.pop("show_data"))

    else:

        show_data = True

    if "show_legend" in kwargs:
        show_legend = bool(kwargs.pop("show_legend"))

    if "show_residuals" in kwargs:
        show_residuals = bool(kwargs.pop("show_residuals"))

    if "step" in kwargs:
        step = bool(kwargs.pop("step"))

    if "min_rate" in kwargs:

        min_rate = kwargs.pop("min_rate")

        # If min_rate is a floating point, use the same for all datasets, otherwise use the provided ones

        try:

            min_rate = float(min_rate)

            min_rates = [min_rate] * len(data_keys)

        except TypeError:

            min_rates = list(min_rate)

            assert len(min_rates) >= len(data_keys), (
                "If you provide different minimum rates for each data set, you need"
                "to provide an iterable of the same length of the number of datasets"
            )

    else:

        # This is the default (no rebinning)

        min_rates = [NO_REBIN] * len(data_keys)

    if "data_cmap" in kwargs:
        data_cmap = plt.get_cmap(kwargs.pop("data_cmap"))
        data_colors = cmap_intervals(len(data_keys), data_cmap)

    if "model_cmap" in kwargs:
        model_cmap = kwargs.pop("model_cmap")
        model_colors = cmap_intervals(len(data_keys), model_cmap)

    if "data_colors" in kwargs:
        data_colors = kwargs.pop("data_colors")

        assert len(data_colors) >= len(data_keys), (
            "You need to provide at least a number of data colors equal to the "
            "number of datasets")

    elif "data_color" in kwargs:

        data_colors = [kwargs.pop("data_color")] * len(data_keys)

    if "model_colors" in kwargs:
        model_colors = kwargs.pop("model_colors")

        assert len(model_colors) >= len(data_keys), (
            "You need to provide at least a number of model colors equal to the "
            "number of datasets")

    ratio_residuals = False
    if "ratio_residuals" in kwargs:
        ratio_residuals = bool(kwargs["ratio_residuals"])

    elif "model_color" in kwargs:

        model_colors = [kwargs.pop("model_color")] * len(data_keys)

    if "model_labels" in kwargs:

        model_labels = kwargs.pop("model_labels")

        assert len(model_labels) == len(
            data_keys
        ), "you must have the same number of model labels as data sets"

    else:

        model_labels = [
            "%s Model" % analysis.data_list[key]._name for key in data_keys
        ]

    # fig, (ax, ax1) = plt.subplots(2, 1, sharex=True, gridspec_kw={'height_ratios': [2, 1]}, **kwargs)

    residual_plot = ResidualPlot(show_residuals=show_residuals, **kwargs)

    if show_residuals:

        axes = [residual_plot.data_axis, residual_plot.residual_axis]

    else:

        axes = residual_plot.data_axis

    # go thru the detectors
    for key, data_color, model_color, min_rate, model_label in zip(
            data_keys, data_colors, model_colors, min_rates, model_labels):

        # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

        data = analysis.data_list[
            key]  # type: threeML.plugins.SpectrumLike.SpectrumLike

        data.display_model(
            data_color=data_color,
            model_color=model_color,
            min_rate=min_rate,
            step=step,
            show_residuals=show_residuals,
            show_data=show_data,
            show_legend=show_legend,
            ratio_residuals=ratio_residuals,
            model_label=model_label,
            model_subplot=axes,
        )

    return residual_plot.figure
Ejemplo n.º 6
0
def display_photometry_model_magnitudes(analysis, data=(), **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    with linear interpolation between each bin
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = analysis.data_list.keys()

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in analysis.data_list.keys():

            if isinstance(analysis.data_list[key], threeML.plugins.PhotometryLike.PhotometryLike):

                new_data_keys.append(key)

            else:

                custom_warnings.warn("Dataset %s is not of the Photometery kind. Cannot be plotted by "
                                     "display_photometry_model_magnitudes" % key)

    if not new_data_keys:
        RuntimeError(
            'There were no valid Photometry data requested for plotting. Please use the detector names in the data list')

    data_keys = new_data_keys

    # Default is to show the model with steps
    step = True

    data_cmap = threeML_config['photo']['data plot cmap']  # plt.cm.rainbow
    model_cmap = threeML_config['photo']['model plot cmap']  # plt.cm.nipy_spectral_r

    # Legend is on by default
    show_legend = True

    # Default colors

    data_colors = cmap_intervals(len(data_keys), data_cmap)
    model_colors = cmap_intervals(len(data_keys), model_cmap)

    # Now override defaults according to the optional keywords, if present

    if 'show_legend' in kwargs:
        show_legend = bool(kwargs.pop('show_legend'))

    if 'step' in kwargs:
        step = bool(kwargs.pop('step'))



    if 'data_cmap' in kwargs:
        data_cmap = plt.get_cmap(kwargs.pop('data_cmap'))
        data_colors = cmap_intervals(len(data_keys), data_cmap)

    if 'model_cmap' in kwargs:
        model_cmap = kwargs.pop('model_cmap')
        model_colors = cmap_intervals(len(data_keys), model_cmap)

    if 'data_colors' in kwargs:
        data_colors = kwargs.pop('data_colors')

        assert len(data_colors) >= len(data_keys), "You need to provide at least a number of data colors equal to the " \
                                                   "number of datasets"

    if 'model_colors' in kwargs:
        model_colors = kwargs.pop('model_colors')

        assert len(model_colors) >= len(
            data_keys), "You need to provide at least a number of model colors equal to the " \
                        "number of datasets"

    residual_plot = ResidualPlot(**kwargs)


    # go thru the detectors
    for key, data_color, model_color in zip(data_keys, data_colors, model_colors):


        data = analysis.data_list[key]  # type: threeML.plugins.PhotometryLike.PhotometryLike


        # get the expected counts


        avg_wave_length = data._filter_set.effective_wavelength.value #type: np.ndarray

        # need to sort because filters are not always in order

        sort_idx = avg_wave_length.argsort()

        expected_model_magnitudes = data._get_total_expectation()[sort_idx]
        magnitudes = data.magnitudes[sort_idx]
        mag_errors= data.magnitude_errors[sort_idx]
        avg_wave_length = avg_wave_length[sort_idx]

        residuals = (expected_model_magnitudes - magnitudes) / mag_errors

        widths = data._filter_set.wavelength_bounds.widths[sort_idx]


        residual_plot.add_data(x=avg_wave_length,
                               y=magnitudes,
                               xerr=widths,
                               yerr=mag_errors,
                               residuals=residuals,
                               label=data._name,
                               color=data_color)

        residual_plot.add_model(avg_wave_length,
                                expected_model_magnitudes,
                                label='%s Model' % data._name,
                                color=model_color)



        return residual_plot.finalize(xlabel="Wavelength\n(%s)"%data._filter_set.waveunits,
                                      ylabel='Magnitudes',
                                      xscale='linear',
                                      yscale='linear',
                                      invert_y=True)
Ejemplo n.º 7
0
def display_spectrum_model_counts(analysis, data=(), **kwargs):
    """

    Display the fitted model count spectrum of one or more Spectrum plugins

    NOTE: all parameters passed as keyword arguments that are not in the list below, will be passed as keyword arguments
    to the plt.subplots() constructor. So for example, you can specify the size of the figure using figsize = (20,10)

    :param args: one or more instances of Spectrum plugin
    :param min_rate: (optional) rebin to keep this minimum rate in each channel (if possible). If one number is
    provided, the same minimum rate is used for each dataset, otherwise a list can be provided with the minimum rate
    for each dataset
    :param data_cmap: (str) (optional) the color map used to extract automatically the colors for the data
    :param model_cmap: (str) (optional) the color map used to extract automatically the colors for the models
    :param data_colors: (optional) a tuple or list with the color for each dataset
    :param model_colors: (optional) a tuple or list with the color for each folded model
    :param data_color: (optional) color for all datasets
    :param model_color: (optional) color for all folded models
    :param show_legend: (optional) if True (default), shows a legend
    :param step: (optional) if True (default), show the folded model as steps, if False, the folded model is plotted
    :param model_subplot: (optional) axe(s) to plot to for overplotting
    with linear interpolation between each bin
    :return: figure instance


    """

    # If the user supplies a subset of the data, we will use that

    if not data:

        data_keys = analysis.data_list.keys()

    else:

        data_keys = data

    # Now we want to make sure that we only grab OGIP plugins

    new_data_keys = []

    for key in data_keys:

        # Make sure it is a valid key
        if key in analysis.data_list.keys():

            if isinstance(analysis.data_list[key], threeML.plugins.SpectrumLike.SpectrumLike):

                new_data_keys.append(key)

            else:

                custom_warnings.warn("Dataset %s is not of the SpectrumLike kind. Cannot be plotted by "
                                     "display_spectrum_model_counts" % key)

    if not new_data_keys:
        RuntimeError(
            'There were no valid SpectrumLike data requested for plotting. Please use the detector names in the data list')

    data_keys = new_data_keys

    # default settings

    # Default is to show the model with steps
    step = True

    data_cmap = threeML_config['ogip']['data plot cmap']  # plt.cm.rainbow
    model_cmap = threeML_config['ogip']['model plot cmap']  # plt.cm.nipy_spectral_r

    # Legend is on by default
    show_legend = True

    show_residuals = True

    # Default colors

    data_colors = cmap_intervals(len(data_keys), data_cmap)
    model_colors = cmap_intervals(len(data_keys), model_cmap)




    # Now override defaults according to the optional keywords, if present



    if 'show_data' in kwargs:

        show_data = bool(kwargs.pop('show_data'))

    else:

        show_data = True


    if 'show_legend' in kwargs:
        show_legend = bool(kwargs.pop('show_legend'))

    if 'show_residuals' in kwargs:
        show_residuals= bool(kwargs.pop('show_residuals'))

    if 'step' in kwargs:
        step = bool(kwargs.pop('step'))

    if 'min_rate' in kwargs:

        min_rate = kwargs.pop('min_rate')

        # If min_rate is a floating point, use the same for all datasets, otherwise use the provided ones

        try:

            min_rate = float(min_rate)

            min_rates = [min_rate] * len(data_keys)

        except TypeError:

            min_rates = list(min_rate)

            assert len(min_rates) >= len(
                data_keys), "If you provide different minimum rates for each data set, you need" \
                            "to provide an iterable of the same length of the number of datasets"

    else:

        # This is the default (no rebinning)

        min_rates = [NO_REBIN] * len(data_keys)

    if 'data_cmap' in kwargs:
        data_cmap = plt.get_cmap(kwargs.pop('data_cmap'))
        data_colors = cmap_intervals(len(data_keys), data_cmap)

    if 'model_cmap' in kwargs:
        model_cmap = kwargs.pop('model_cmap')
        model_colors = cmap_intervals(len(data_keys), model_cmap)

    if 'data_colors' in kwargs:
        data_colors = kwargs.pop('data_colors')

        assert len(data_colors) >= len(data_keys), "You need to provide at least a number of data colors equal to the " \
                                                   "number of datasets"

    elif 'data_color' in kwargs:

        data_colors = [kwargs.pop('data_color')] * len(data_keys)



    if 'model_colors' in kwargs:
        model_colors = kwargs.pop('model_colors')

        assert len(model_colors) >= len(
            data_keys), "You need to provide at least a number of model colors equal to the " \
                        "number of datasets"
    
    ratio_residuals=False
    if 'ratio_residuals' in kwargs:
        ratio_residuals = bool(kwargs['ratio_residuals'])

    elif 'model_color' in kwargs:

        model_colors = [kwargs.pop('model_color')] * len(data_keys)


    if 'model_labels' in kwargs:


        model_labels = kwargs.pop('model_labels')

        assert len(model_labels) == len(data_keys), 'you must have the same number of model labels as data sets'

    else:

        model_labels = ['%s Model' % analysis.data_list[key]._name for key in data_keys]

    #fig, (ax, ax1) = plt.subplots(2, 1, sharex=True, gridspec_kw={'height_ratios': [2, 1]}, **kwargs)

    residual_plot = ResidualPlot(show_residuals=show_residuals, **kwargs)

    if show_residuals:

        axes = [residual_plot.data_axis,residual_plot.residual_axis]

    else:

        axes = residual_plot.data_axis


    # go thru the detectors
    for key, data_color, model_color, min_rate, model_label in zip(data_keys, data_colors, model_colors, min_rates, model_labels):



        # NOTE: we use the original (unmasked) vectors because we need to rebin ourselves the data later on

        data = analysis.data_list[key] # type: threeML.plugins.SpectrumLike.SpectrumLike

        data.display_model(data_color=data_color,
                           model_color=model_color,
                           min_rate=min_rate,
                           step=step,
                           show_residuals=show_residuals,
                           show_data=show_data,
                           show_legend=show_legend,
                           ratio_residuals=ratio_residuals,
                           model_label=model_label,
                           model_subplot=axes

                           )


    return residual_plot.figure
Ejemplo n.º 8
0
def plot_spectra(*analysis_results, **kwargs) -> plt.Figure:
    """

    plotting routine for fitted point source spectra


    :param analysis_results: fitted JointLikelihood or BayesianAnalysis objects
    :param sources_to_use: (optional) list of PointSource string names to plot from the analysis
    :param energy_unit: (optional) astropy energy unit in string form (can also be frequency)
    :param flux_unit: (optional) astropy flux unit in string form
    :param confidence_level: (optional) confidence level to use (default: 0.68)
    :param ene_min: (optional) minimum energy to plot
    :param ene_max: (optional) maximum energy to plot
    :param num_ene: (optional) number of energies to plot
    :param use_components: (optional) True or False to plot the spectral components
    :param components_to_use: (optional) list of string names of the components to plot: including 'total'
    will also plot the total spectrum
    :param sum_sources: (optional) some all the MLE and Bayesian sources
    :param show_contours: (optional) True or False to plot the contour region
    :param plot_style_kwargs: (optional) dictionary of MPL plot styling for the best fit curve
    :param contour_style_kwargs: (optional) dictionary of MPL plot styling for the contour regions
    :param fit_cmap: MPL color map to iterate over for plotting multiple analyses
    :param contour_cmap: MPL color map to iterate over for plotting contours for  multiple analyses
    :param subplot: subplot to use
    :param xscale: 'log' or 'linear'
    :param yscale: 'log' or 'linear'
    :param include_extended: True or False, also plot extended source spectra.
    :return:
    """

    # allow matplotlib to plot quantities to the access

    quantity_support()

    _sub_menu = threeML_config.model_plot.point_source_plot

    _defaults = {
        "fit_cmap": _sub_menu.fit_cmap.value,
        "contour_cmap": _sub_menu.contour_cmap.value,
        "contour_colors": None,
        "fit_colors": None,
        "confidence_level": 0.68,
        "equal_tailed": True,
        "best_fit": "median",
        "energy_unit": _sub_menu.ene_unit,
        "flux_unit": _sub_menu.flux_unit,
        "ene_min": _sub_menu.emin,
        "ene_max": _sub_menu.emax,
        "num_ene": _sub_menu.num_ene,
        "use_components": False,
        "components_to_use": [],
        "sources_to_use": [],
        "sum_sources": False,
        "show_contours": True,
        "plot_style_kwargs": _sub_menu.plot_style,
        "contour_style_kwargs": _sub_menu.contour_style,
        "show_legend": _sub_menu.show_legend,
        "legend_kwargs": _sub_menu.legend_style,
        "subplot": None,
        "xscale": "log",
        "yscale": "log",
        "include_extended": False,
    }

    for key, value in kwargs.items():

        if key in _defaults:
            _defaults[key] = value

    if isinstance(_defaults["ene_min"], u.Quantity):

        if not isinstance(_defaults["ene_max"], u.Quantity):
            log.error("both energy arguments must be Quantities")
            raise RuntimeError()

    if isinstance(_defaults["ene_max"], u.Quantity):

        if not isinstance(_defaults["ene_min"], u.Quantity):
            log.error("both energy arguments must be Quantities")
            raise RuntimeError()

    if isinstance(_defaults["ene_max"], u.Quantity):

        energy_range = np.linspace(_defaults["ene_min"], _defaults["ene_max"],
                                   _defaults["num_ene"])  # type: u.Quantity

        #_defaults["energy_unit"] = energy_range.unit

        if _defaults["xscale"] == "log":
            energy_range = (np.logspace(
                np.log10(energy_range.min().value),
                np.log10(energy_range.max().value),
                _defaults["num_ene"],
            ) * energy_range.unit)

            energy_range = energy_range.to(_defaults["energy_unit"],
                                           equivalencies=u.spectral())

    else:

        energy_range = np.logspace(
            np.log10(_defaults["ene_min"]),
            np.log10(_defaults["ene_max"]),
            _defaults["num_ene"],
        ) * u.Unit(_defaults["energy_unit"])

        # scale the units to the defaults

        _defaults["ene_min"] = _defaults["ene_min"] * \
            u.Unit(_defaults["energy_unit"])
        _defaults["ene_max"] = _defaults["ene_max"] * \
            u.Unit(_defaults["energy_unit"])

    (
        mle_analyses,
        bayesian_analyses,
        num_sources_to_plot,
        duplicate_keys,
    ) = _setup_analysis_dictionaries(
        analysis_results,
        energy_range,
        _defaults["energy_unit"],
        _defaults["flux_unit"],
        _defaults["use_components"],
        _defaults["components_to_use"],
        _defaults["confidence_level"],
        _defaults["equal_tailed"],
        differential=True,
        sources_to_use=_defaults["sources_to_use"],
        include_extended=_defaults["include_extended"],
    )

    # we are now ready to plot.
    # all calculations have been made.

    # if we are not going to sum sources

    if not _defaults["sum_sources"]:

        if _defaults["fit_colors"] is None:

            color_fit = cmap_intervals(num_sources_to_plot + 1,
                                       _defaults["fit_cmap"])

        else:

            # duck typing
            if isinstance(_defaults["fit_colors"], (str, str)):

                color_fit = [_defaults["fit_colors"]] * num_sources_to_plot

            elif isinstance(_defaults["fit_colors"], list):

                assert len(_defaults["fit_colors"]) == num_sources_to_plot, (
                    "list of colors (%d) must be the same length as sources ot plot (%s)"
                    % (len(_defaults["fit_colors"]), num_sources_to_plot))

                color_fit = _defaults["fit_colors"]

            else:
                raise ValueError("Can not setup color, wrong type:",
                                 type(_defaults["fit_colors"]))

        if _defaults["contour_colors"] is None:

            color_contour = cmap_intervals(num_sources_to_plot + 1,
                                           _defaults["contour_cmap"])

        else:

            # duck typing
            if isinstance(_defaults["contour_colors"], (str, str)):

                color_contour = [_defaults["contour_colors"]
                                 ] * num_sources_to_plot

            elif isinstance(_defaults["contour_colors"], list):

                assert len(
                    _defaults["contour_colors"]
                ) == num_sources_to_plot, (
                    "list of colors (%d) must be the same length as sources ot plot (%s)"
                    % (len(_defaults["contour_colors"]), num_sources_to_plot))

                color_contour = _defaults["fit_colors"]

            else:
                raise ValueError(
                    "Can not setup contour color, wrong type:",
                    type(_defaults["contour_colors"]),
                )

        color_itr = 0

        # go thru the mle analysis and plot spectra

        plotter = SpectralContourPlot(
            num_sources_to_plot,
            xscale=_defaults["xscale"],
            yscale=_defaults["yscale"],
            show_legend=_defaults["show_legend"],
            plot_kwargs=_defaults["plot_style_kwargs"],
            contour_kwargs=_defaults["contour_style_kwargs"],
            legend_kwargs=_defaults["legend_kwargs"],
            emin=_defaults["ene_min"],
            emax=_defaults["ene_max"],
            subplot=_defaults["subplot"],
        )

        for key in list(mle_analyses.keys()):

            # we won't assume to plot the total until the end

            plot_total = False

            if _defaults["use_components"]:

                # if this source has no components or none that we wish to plot
                # then we will plot the total spectrum after this

                if (not list(mle_analyses[key]["components"].keys())) or (
                        "total" in _defaults["components_to_use"]):
                    plot_total = True

                for component in list(mle_analyses[key]["components"].keys()):

                    positive_error = None
                    negative_error = None

                    # extract the information and plot it

                    if _defaults["best_fit"] == "average":

                        best_fit = mle_analyses[key]["components"][
                            component].average

                    else:

                        best_fit = mle_analyses[key]["components"][
                            component].median

                    if _defaults["show_contours"]:
                        positive_error = mle_analyses[key]["components"][
                            component].upper_error
                        negative_error = mle_analyses[key]["components"][
                            component].lower_error

                        neg_mask = negative_error <= 0

                        # replace with small number

                        negative_error[neg_mask] = min(best_fit) * 0.9

                    label = "%s: %s" % (key, component)

                    # this is where we keep track of duplicates

                    if key in duplicate_keys:
                        label = "%s: MLE" % label

                    if mle_analyses[key]["components"][
                            component].is_dimensionless:

                        plotter.add_dimensionless_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label,
                        )

                    else:

                        plotter.add_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label,
                        )

                    color_itr += 1

            else:

                plot_total = True

            if plot_total:

                # it ends up that we need to plot the total spectrum
                # which is just a repeat of the process

                if _defaults["best_fit"] == "average":

                    best_fit = mle_analyses[key]["fitted point source"].average

                else:

                    best_fit = mle_analyses[key]["fitted point source"].median

                if _defaults["show_contours"]:

                    positive_error = mle_analyses[key][
                        "fitted point source"].upper_error
                    negative_error = mle_analyses[key][
                        "fitted point source"].lower_error

                    neg_mask = negative_error <= 0

                    # replace with small number

                    negative_error[neg_mask] = min(best_fit) * 0.9

                else:

                    positive_error = None
                    negative_error = None

                label = "%s" % key

                if key in duplicate_keys:
                    label = "%s: MLE" % label

                plotter.add_model(
                    energy_range=energy_range,
                    best_fit=best_fit,
                    color=color_fit[color_itr],
                    upper_error=positive_error,
                    lower_error=negative_error,
                    contour_color=color_contour[color_itr],
                    label=label,
                )

                color_itr += 1

        # we will do the exact same thing for the bayesian analyses

        for key in list(bayesian_analyses.keys()):

            plot_total = False

            if _defaults["use_components"]:

                if (not list(bayesian_analyses[key]["components"].keys())) or (
                        "total" in _defaults["components_to_use"]):
                    plot_total = True

                for component in list(
                        bayesian_analyses[key]["components"].keys()):

                    positive_error = None
                    negative_error = None

                    if _defaults["best_fit"] == "average":

                        best_fit = bayesian_analyses[key]["components"][
                            component].average

                    else:

                        best_fit = bayesian_analyses[key]["components"][
                            component].median

                    if _defaults["show_contours"]:
                        positive_error = bayesian_analyses[key]["components"][
                            component].upper_error
                        negative_error = bayesian_analyses[key]["components"][
                            component].lower_error

                    label = "%s: %s" % (key, component)

                    if key in duplicate_keys:
                        label = "%s: Bayesian" % label

                    if bayesian_analyses[key]["components"][
                            component].is_dimensionless:

                        plotter.add_dimensionless_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label,
                        )

                    else:

                        plotter.add_model(
                            energy_range=energy_range,
                            best_fit=best_fit,
                            color=color_fit[color_itr],
                            upper_error=positive_error,
                            lower_error=negative_error,
                            contour_color=color_contour[color_itr],
                            label=label,
                        )

                    color_itr += 1

            else:

                plot_total = True

            if plot_total:

                if _defaults["best_fit"] == "average":

                    best_fit = bayesian_analyses[key][
                        "fitted point source"].average

                else:

                    best_fit = bayesian_analyses[key][
                        "fitted point source"].median

                positive_error = None
                negative_error = None

                if _defaults["show_contours"]:
                    positive_error = bayesian_analyses[key][
                        "fitted point source"].upper_error
                    negative_error = bayesian_analyses[key][
                        "fitted point source"].lower_error

                label = "%s" % key

                if key in duplicate_keys:
                    label = "%s: Bayesian" % label

                plotter.add_model(
                    energy_range=energy_range,
                    best_fit=best_fit,
                    color=color_fit[color_itr],
                    upper_error=positive_error,
                    lower_error=negative_error,
                    contour_color=color_contour[color_itr],
                    label=label,
                )

                color_itr += 1

    else:
        # now we sum sources instead

        # we keep MLE and Bayes apart because it makes no
        # sense to sum them together

        (
            total_analysis_mle,
            component_sum_dict_mle,
            num_sources_to_plot,
        ) = _collect_sums_into_dictionaries(mle_analyses,
                                            _defaults["use_components"],
                                            _defaults["components_to_use"])

        (
            total_analysis_bayes,
            component_sum_dict_bayes,
            num_sources_to_plot_bayes,
        ) = _collect_sums_into_dictionaries(
            bayesian_analyses,
            _defaults["use_components"],
            _defaults["components_to_use"],
        )

        num_sources_to_plot += num_sources_to_plot_bayes

        plotter = SpectralContourPlot(
            num_sources_to_plot,
            xscale=_defaults["xscale"],
            yscale=_defaults["yscale"],
            show_legend=_defaults["show_legend"],
            plot_kwargs=_defaults["plot_style_kwargs"],
            contour_kwargs=_defaults["contour_style_kwargs"],
            legend_kwargs=_defaults["legend_kwargs"],
            emin=_defaults["ene_min"],
            emax=_defaults["ene_max"],
            subplot=_defaults["subplot"],
        )

        color_fit = cmap_intervals(num_sources_to_plot, _defaults["fit_cmap"])
        color_contour = cmap_intervals(num_sources_to_plot,
                                       _defaults["contour_cmap"])
        color_itr = 0

        if _defaults["use_components"] and list(component_sum_dict_mle.keys()):

            # we have components to plot

            for component, values in component_sum_dict_mle.items():

                summed_analysis = sum(values)

                if _defaults["best_fit"] == "average":

                    best_fit = summed_analysis.average

                else:

                    best_fit = summed_analysis.median

                positive_error = None
                negative_error = None

                if _defaults["show_contours"]:
                    positive_error = summed_analysis.upper_error

                    negative_error = summed_analysis.lower_error

                    neg_mask = negative_error <= 0

                    # replace with small number

                    negative_error[neg_mask] = min(best_fit) * 0.9

                if np.any([
                        c.is_dimensionless
                        for c in component_sum_dict_mle[component]
                ]):

                    plotter.add_dimensionless_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: MLE" % component,
                    )

                else:

                    plotter.add_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: MLE" % component,
                    )

                color_itr += 1

        if total_analysis_mle:

            # we will sum and plot the total
            # analysis

            summed_analysis = sum(total_analysis_mle)

            if _defaults["best_fit"] == "average":

                best_fit = summed_analysis.average

            else:

                best_fit = summed_analysis.median

            positive_error = None
            negative_error = None

            if _defaults["show_contours"]:
                positive_error = best_fit + summed_analysis.upper_error

                negative_error = best_fit - summed_analysis.lower_error

                neg_mask = negative_error <= 0

                # replace with small number

                negative_error[neg_mask] = min(best_fit) * 0.9

            plotter.add_model(
                energy_range=energy_range,
                best_fit=best_fit,
                color=color_fit[color_itr],
                upper_error=positive_error,
                lower_error=negative_error,
                contour_color=color_contour[color_itr],
                label="total: MLE",
            )

            color_itr += 1

        if _defaults["use_components"] and list(
                component_sum_dict_bayes.keys()):

            # we have components to plot

            for component, values in component_sum_dict_bayes.items():

                summed_analysis = sum(values)

                if _defaults["best_fit"] == "average":

                    best_fit = summed_analysis.average

                else:

                    best_fit = summed_analysis.median

                positive_error = None
                negative_error = None

                if _defaults["show_contours"]:
                    positive_error = summed_analysis.upper_error

                    negative_error = summed_analysis.lower_error

                if np.any([
                        c.is_dimensionless
                        for c in component_sum_dict_bayes[component]
                ]):

                    plotter.add_dimensionless_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: Bayesian" % component,
                    )

                else:

                    plotter.add_model(
                        energy_range=energy_range,
                        best_fit=best_fit,
                        color=color_fit[color_itr],
                        upper_error=positive_error,
                        lower_error=negative_error,
                        contour_color=color_contour[color_itr],
                        label="%s: Bayesian" % component,
                    )

                color_itr += 1

        if total_analysis_bayes:

            # we will sum and plot the total
            # analysis

            summed_analysis = sum(total_analysis_bayes)

            if _defaults["best_fit"] == "average":

                best_fit = summed_analysis.average

            else:

                best_fit = summed_analysis.median

            positive_error = None
            negative_error = None

            if _defaults["show_contours"]:
                positive_error = summed_analysis.upper_error

                negative_error = summed_analysis.lower_error

            plotter.add_model(
                energy_range=energy_range,
                best_fit=best_fit,
                color=color_fit[color_itr],
                upper_error=positive_error,
                lower_error=negative_error,
                contour_color=color_contour[color_itr],
                label="total: Bayesian",
            )

        color_itr += 1

    return plotter.finalize(_defaults)