Exemple #1
0
def plot_surface(spectra, dimension1, dimension2,
                 fig_num=1, show_plot=True, **kwargs):
    """ Plot the two dimensions from spectra as a 2D histogram

    Args:
      spectra (:class:`echidna.core.spectra`): The spectra to plot.
      dimension1 (string): The name of the dimension you want to plot.
      dimension2 (string): The name of the dimension you want to plot.

    Returns:
      matplotlib.pyplot.figure: Plot of the surface of the two dimensions.
    """
    fig = plt.figure(num=fig_num)
    axis = fig.add_subplot(111)
    index1 = spectra.get_config().get_index(dimension1)
    index2 = spectra.get_config().get_index(dimension2)
    if index1 < index2:
        x = _produce_axis(spectra, dimension2)
        y = _produce_axis(spectra, dimension1)
    else:
        x = _produce_axis(spectra, dimension1)
        y = _produce_axis(spectra, dimension2)
    data = spectra.surface(dimension1, dimension2)
    par1 = spectra.get_config().get_par(dimension1)
    par2 = spectra.get_config().get_par(dimension2)
    if index1 < index2:
        axis.set_xlabel("%s (%s)" % (dimension2, par2.get_unit()))
        axis.set_ylabel("%s (%s)" % (dimension1, par1.get_unit()))
    else:
        axis.set_xlabel("%s (%s)" % (dimension1, par1.get_unit()))
        axis.set_ylabel("%s (%s)" % (dimension2, par2.get_unit()))

    # `plot_surface` expects `x` and `y` data to be 2D
    X, Y = numpy.meshgrid(x, y)

    # Set sensible levels, pick the desired colormap and define normalization
    if kwargs.get("color_scheme") is None:
        color_scheme = "hot_r"  # default
    else:
        color_scheme = kwargs.get("color_scheme")
    color_map = plt.get_cmap(color_scheme)
    linear = numpy.linspace(numpy.sqrt(data.min()),
                            numpy.sqrt(data.max()), num=100)
    locator = FixedLocator(linear**2)
    levels = locator.tick_values(data.min(), data.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)

    # Plot color map
    color_map = axis.pcolormesh(X, Y, data, cmap=color_map, norm=norm)
    color_bar = fig.colorbar(color_map)
    color_bar.set_label("Counts per bin")

    if show_plot:
        plt.show()
    return fig
Exemple #2
0
def chi_squared_map(syst_analyser, fig_num=1, **kwargs):
    """ Plot chi squared surface for systematic vs. signal counts

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * contours (*bool*): if True produces a contour plot of chi
          squared surface. Default (*False*).
        * preferred_values (*bool*): if False "preferred values" curve
          is not overlayed on colour map. Default (*True*)
        * minima (*bool*): if False "minima" are not overlayed on
          colour map. Default (*True*)
        * save_as (*string*): supply file name to save image

      Default is to produce a colour map, with "preferred values" curve
      and "minima" overlayed.

    """
    # Set kwargs defaults
    if kwargs.get("preferred_values") is None:
        kwargs["preferred_values"] = True
    if kwargs.get("minima") is None:
        kwargs["minima"] = True

    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Set preferred value values
    y_2 = numpy.average(syst_analyser.get_preferred_values(), axis=1)

    # Set minima values
    x_3 = syst_analyser.get_minima()[0]
    y_3 = syst_analyser.get_minima()[1]

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('hot_r')

    linear = numpy.linspace(numpy.sqrt(data.min()), numpy.sqrt(data.max()),
                            num=100)
    locator = FixedLocator(linear**2)
    levels = locator.tick_values(data.min(), data.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)

    if kwargs.get("contours"):
        fig = plt.figure(fig_num, figsize=(16, 10))  # Fig. 2
        fig.text(0.1, 0.9, syst_analyser._name, **BOLD_FONT)
        ax = Axes3D(fig)
        ax.view_init(elev=17.0, azim=-136.0)  # set intial viewing position

        # Plot surface
        surf = ax.plot_surface(X, Y, data, rstride=1, cstride=1,
                               cmap=color_map, norm=norm, linewidth=0,
                               antialiased=False)
        ax.zaxis.set_minor_locator(locator)
        ax.ticklabel_format(style="scientific", scilimits=(3, 4))

        # Set axis labels
        ax.set_xlabel("\nSignal counts", **BOLD_FONT)
        ax.set_ylabel("\nValue of systematic", **BOLD_FONT)
        for label in (ax.get_xticklabels() +
                      ax.get_yticklabels() +
                      ax.get_zticklabels()):
            label.set_fontsize(MAIN_FONT.get("size"))  # tick label size

        ax.dist = 11  # Ensures tick labels are not cut off
        ax.margins(0.05, 0.05, 0.05)  # Adjusts tick margins

        # Draw colorbar
        color_bar = fig.colorbar(surf, ax=ax, orientation="vertical",
                                 fraction=0.2, shrink=0.5, aspect=10)
        # kwargs here control axes that the colorbar is drawn in
        color_bar.set_label(r"$\chi^2$", size=MAIN_FONT.get("size"))
        color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size"))

        plt.show()
        if kwargs.get("save_as") is not None:
            fig.savefig(kwargs.get("save_as") + "_contour.png", dpi=300)
    else:
        fig = plt.figure(fig_num, figsize=(12, 10))  # Fig. 2
        fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
        ax = fig.add_subplot(1, 1, 1)

        # Set labels
        ax.set_xlabel("Signal counts", **BOLD_FONT)
        ax.set_ylabel("Value of systematic", **BOLD_FONT)

        # Plot color map
        color_map = ax.pcolormesh(X, Y, data, cmap=color_map, norm=norm)
        color_bar = fig.colorbar(color_map)
        color_bar.set_label("$\chi^2$", size=MAIN_FONT.get("size"))
        color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size"))  # tick label size

        # Set axes limits
        ax.set_xlim([X.min(), X.max()])
        ax.set_ylim([Y.min(), Y.max()])

        if kwargs.get("preferred_values"):
            ax.plot(x, y_2, "bo-", label="Preferred values")
        if kwargs.get("minima"):
            ax.plot(x_3, y_3, "ko", label="Minima")

        # Set axes tick label size
        for label in (ax.get_xticklabels() + ax.get_yticklabels()):
            label.set_fontsize(MAIN_FONT.get("size"))

        ax.legend(loc="upper left")
        if kwargs.get("save_as") is not None:
            fig.savefig(kwargs.get("save_as") + "_color_map.png", dpi=300)
    return fig
Exemple #3
0
def push_pull(syst_analyser, fig=1, **kwargs):
    """ Plot penalty value - poisson likelihood chi squared.

    When does minimising chi squared, which wants to "pull" the away
    from the data/prior value dominate and when does the penalty term,
    which wants to "pull" towards the data/prior, constraining the fit
    dominate?

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * save_as (*string*): supply file name to save image
    """
    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Define an array penalty values
    penalty_values = syst_analyser._penalty_values[1, 0:len(y)]
    penalty_array = numpy.zeros(data.shape)  # zeroed array the same size as data
    for y, penalty_value in enumerate(penalty_values):
        for x in range(len(penalty_array[y])):
            penalty_array[y][x] = penalty_value

    # Define the push pull array penalty term - chi_squared
    # --> push_pull > 0 when penalty_value > chi_squared
    # --> push_pull < 1 when penalty_value < chi_squared
    push_pull = (2.*penalty_array) - data

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('coolwarm')

    if push_pull.min() < 0.:
        negatives = numpy.linspace(push_pull.min(), 0.,
                                   num=50, endpoint=False)
    else:
        negatives = numpy.zeros((50))
    if push_pull.max() > 0.:
        positives = numpy.linspace(0., push_pull.max(), num=51)
    else:
        positives = numpy.zeros((51))

    # Add the pull part to the push part
    full_scale = numpy.append(negatives, positives)
    locator = FixedLocator(full_scale)
    levels = locator.tick_values(push_pull.min(), push_pull.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)
    fig = plt.figure(fig, figsize=(12, 10))  # Fig. 4
    fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
    ax = fig.add_subplot(1, 1, 1)

    # Set labels
    ax.set_xlabel("Signal counts", **BOLD_FONT)
    ax.set_ylabel("Value of systematic", **BOLD_FONT)

    # Plot color map
    color_map = ax.pcolormesh(X, Y, push_pull, cmap=color_map, norm=norm)
    color_bar = fig.colorbar(color_map)
    color_bar.set_label("$s-\chi^{2}_{\lambda,p}$", size=MAIN_FONT.get("size"))
    color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size"))  # tick label size

    # Set axes limits
    ax.set_xlim([X.min(), X.max()])
    ax.set_ylim([Y.min(), Y.max()])

    # Set axes tick label size
    for label in (ax.get_xticklabels() + ax.get_yticklabels()):
        label.set_fontsize(MAIN_FONT.get("size"))

    ax.legend(loc="upper left")
    if kwargs.get("save_as") is not None:
        fig.savefig(kwargs.get("save_as") + "_push_pull.png", dpi=300)
    return fig
Exemple #4
0
def turn_on(syst_analyser, signal_config, fig=1, **kwargs):
    """ Plot deviation from chi-squared with no floated systematics.

    When does the effect of floating the systematic "turn on"?

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      signal_config (:class:`echidna.limit.limit_config.LimitConfig`): Signal
        config class, where chi squareds have been stored.
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * save_as (*string*): supply file name to save image
    """
    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Define an array of \chi_0 values - chi squared without floating systematics
    chi_squareds = signal_config.get_chi_squareds()[0]
    data_np = numpy.zeros(data.shape)  # zeroed array the same size as data
    for y in range(len(data_np)):
        for x, chi_squared in enumerate(chi_squareds):
            data_np[y][x] = chi_squared
    #if numpy.any((numpy.average(data_np, axis=0) != chi_squareds)):
    #    raise AssertionError("Incorrect chi squareds (no floating) array.")

    # Make an array of the offsets
    offsets = data - data_np

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('coolwarm')

    positives = numpy.linspace(numpy.log10(offsets.max())*-1.,
                               numpy.log10(offsets.max()), num=50)
    # linear array in log space
    if offsets.min() < 0.:
        negatives = numpy.linspace(offsets.min(), 0.0, num=51)
    else:
        negatives = numpy.zeros((51))

    # Add the positive part to the negative part
    full_scale = numpy.append(negatives, numpy.power(10, positives))
    locator = FixedLocator(full_scale)
    levels = locator.tick_values(offsets.min(), offsets.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)
    fig = plt.figure(fig, figsize=(12, 10))  # Fig. 4
    fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
    ax = fig.add_subplot(1, 1, 1)

    # Set labels
    ax.set_xlabel("Signal counts", **BOLD_FONT)
    ax.set_ylabel("Value of systematic", **BOLD_FONT)

    # Plot color map
    color_map = ax.pcolormesh(X, Y, offsets, cmap=color_map, norm=norm)
    color_bar = fig.colorbar(color_map)
    color_bar.set_label("$\chi^2 - \chi_0^2$", size=MAIN_FONT.get("size"))
    color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size"))  # tick label size

    # Set axes limits
    ax.set_xlim([X.min(), X.max()])
    ax.set_ylim([Y.min(), Y.max()])

    # Set axes tick label size
    for label in (ax.get_xticklabels() + ax.get_yticklabels()):
        label.set_fontsize(MAIN_FONT.get("size"))

    ax.legend(loc="upper left")
    if kwargs.get("save_as") is not None:
        fig.savefig(kwargs.get("save_as") + "_turn_on.png", dpi=300)
    return fig
def push_pull(syst_analyser, fig=1, **kwargs):
    """ Plot penalty value - poisson likelihood chi squared.

    When does minimising chi squared, which wants to "pull" the away
    from the data/prior value dominate and when does the penalty term,
    which wants to "pull" towards the data/prior, constraining the fit
    dominate?

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * save_as (*string*): supply file name to save image
    """
    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Define an array penalty values
    penalty_values = syst_analyser._penalty_values[1, 0:len(y)]
    penalty_array = numpy.zeros(
        data.shape)  # zeroed array the same size as data
    for y, penalty_value in enumerate(penalty_values):
        for x in range(len(penalty_array[y])):
            penalty_array[y][x] = penalty_value

    # Define the push pull array penalty term - chi_squared
    # --> push_pull > 0 when penalty_value > chi_squared
    # --> push_pull < 1 when penalty_value < chi_squared
    push_pull = (2. * penalty_array) - data

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('coolwarm')

    if push_pull.min() < 0.:
        negatives = numpy.linspace(push_pull.min(), 0., num=50, endpoint=False)
    else:
        negatives = numpy.zeros((50))
    if push_pull.max() > 0.:
        positives = numpy.linspace(0., push_pull.max(), num=51)
    else:
        positives = numpy.zeros((51))

    # Add the pull part to the push part
    full_scale = numpy.append(negatives, positives)
    locator = FixedLocator(full_scale)
    levels = locator.tick_values(push_pull.min(), push_pull.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)
    fig = plt.figure(fig, figsize=(12, 10))  # Fig. 4
    fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
    ax = fig.add_subplot(1, 1, 1)

    # Set labels
    ax.set_xlabel("Signal counts", **BOLD_FONT)
    ax.set_ylabel("Value of systematic", **BOLD_FONT)

    # Plot color map
    color_map = ax.pcolormesh(X, Y, push_pull, cmap=color_map, norm=norm)
    color_bar = fig.colorbar(color_map)
    color_bar.set_label("$s-\chi^{2}_{\lambda,p}$", size=MAIN_FONT.get("size"))
    color_bar.ax.tick_params(
        labelsize=MAIN_FONT.get("size"))  # tick label size

    # Set axes limits
    ax.set_xlim([X.min(), X.max()])
    ax.set_ylim([Y.min(), Y.max()])

    # Set axes tick label size
    for label in (ax.get_xticklabels() + ax.get_yticklabels()):
        label.set_fontsize(MAIN_FONT.get("size"))

    ax.legend(loc="upper left")
    if kwargs.get("save_as") is not None:
        fig.savefig(kwargs.get("save_as") + "_push_pull.png", dpi=300)
    return fig
def turn_on(syst_analyser, signal_config, fig=1, **kwargs):
    """ Plot deviation from chi-squared with no floated systematics.

    When does the effect of floating the systematic "turn on"?

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      signal_config (:class:`echidna.limit.limit_config.LimitConfig`): Signal
        config class, where chi squareds have been stored.
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * save_as (*string*): supply file name to save image
    """
    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Define an array of \chi_0 values - chi squared without floating systematics
    chi_squareds = signal_config.get_chi_squareds()[0]
    data_np = numpy.zeros(data.shape)  # zeroed array the same size as data
    for y in range(len(data_np)):
        for x, chi_squared in enumerate(chi_squareds):
            data_np[y][x] = chi_squared
    #if numpy.any((numpy.average(data_np, axis=0) != chi_squareds)):
    #    raise AssertionError("Incorrect chi squareds (no floating) array.")

    # Make an array of the offsets
    offsets = data - data_np

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('coolwarm')

    positives = numpy.linspace(numpy.log10(offsets.max()) * -1.,
                               numpy.log10(offsets.max()),
                               num=50)
    # linear array in log space
    if offsets.min() < 0.:
        negatives = numpy.linspace(offsets.min(), 0.0, num=51)
    else:
        negatives = numpy.zeros((51))

    # Add the positive part to the negative part
    full_scale = numpy.append(negatives, numpy.power(10, positives))
    locator = FixedLocator(full_scale)
    levels = locator.tick_values(offsets.min(), offsets.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)
    fig = plt.figure(fig, figsize=(12, 10))  # Fig. 4
    fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
    ax = fig.add_subplot(1, 1, 1)

    # Set labels
    ax.set_xlabel("Signal counts", **BOLD_FONT)
    ax.set_ylabel("Value of systematic", **BOLD_FONT)

    # Plot color map
    color_map = ax.pcolormesh(X, Y, offsets, cmap=color_map, norm=norm)
    color_bar = fig.colorbar(color_map)
    color_bar.set_label("$\chi^2 - \chi_0^2$", size=MAIN_FONT.get("size"))
    color_bar.ax.tick_params(
        labelsize=MAIN_FONT.get("size"))  # tick label size

    # Set axes limits
    ax.set_xlim([X.min(), X.max()])
    ax.set_ylim([Y.min(), Y.max()])

    # Set axes tick label size
    for label in (ax.get_xticklabels() + ax.get_yticklabels()):
        label.set_fontsize(MAIN_FONT.get("size"))

    ax.legend(loc="upper left")
    if kwargs.get("save_as") is not None:
        fig.savefig(kwargs.get("save_as") + "_turn_on.png", dpi=300)
    return fig
def chi_squared_map(syst_analyser, fig_num=1, **kwargs):
    """ Plot chi squared surface for systematic vs. signal counts

    Args:
      syst_analyser (:class:`echidna.limit.limit_setting.SystAnalyser`): Systematic
        analyser object, created during limit setting. Can be used
        during limit setting setting or can load an instance from
        hdf5
      fig_num (int): Fig number. When creating multiple plots in the
        same script, ensures matplotlib doesn't overwrite them.

    .. note::

      Keyword arguments include:

        * contours (*bool*): if True produces a contour plot of chi
          squared surface. Default (*False*).
        * preferred_values (*bool*): if False "preferred values" curve
          is not overlayed on colour map. Default (*True*)
        * minima (*bool*): if False "minima" are not overlayed on
          colour map. Default (*True*)
        * save_as (*string*): supply file name to save image

      Default is to produce a colour map, with "preferred values" curve
      and "minima" overlayed.

    """
    # Set kwargs defaults
    if kwargs.get("preferred_values") is None:
        kwargs["preferred_values"] = True
    if kwargs.get("minima") is None:
        kwargs["minima"] = True

    # Set x and y axes
    x = syst_analyser.get_actual_counts()
    y = syst_analyser.get_syst_values()

    # Set chi squared map values
    data = numpy.average(syst_analyser.get_chi_squareds(), axis=1)
    data = numpy.transpose(data)  # transpose it so that axes are correct

    # Set preferred value values
    y_2 = numpy.average(syst_analyser.get_preferred_values(), axis=1)

    # Set minima values
    x_3 = syst_analyser.get_minima()[0]
    y_3 = syst_analyser.get_minima()[1]

    # Create meshgrid
    X, Y = numpy.meshgrid(x, y)

    # Set sensible levels, pick the desired colormap and define normalization
    color_map = plt.get_cmap('hot_r')

    linear = numpy.linspace(numpy.sqrt(data.min()),
                            numpy.sqrt(data.max()),
                            num=100)
    locator = FixedLocator(linear**2)
    levels = locator.tick_values(data.min(), data.max())
    norm = BoundaryNorm(levels, ncolors=color_map.N)

    if kwargs.get("contours"):
        fig = plt.figure(fig_num, figsize=(16, 10))  # Fig. 2
        fig.text(0.1, 0.9, syst_analyser._name, **BOLD_FONT)
        ax = Axes3D(fig)
        ax.view_init(elev=17.0, azim=-136.0)  # set intial viewing position

        # Plot surface
        surf = ax.plot_surface(X,
                               Y,
                               data,
                               rstride=1,
                               cstride=1,
                               cmap=color_map,
                               norm=norm,
                               linewidth=0,
                               antialiased=False)
        ax.zaxis.set_minor_locator(locator)
        ax.ticklabel_format(style="scientific", scilimits=(3, 4))

        # Set axis labels
        ax.set_xlabel("\nSignal counts", **BOLD_FONT)
        ax.set_ylabel("\nValue of systematic", **BOLD_FONT)
        for label in (ax.get_xticklabels() + ax.get_yticklabels() +
                      ax.get_zticklabels()):
            label.set_fontsize(MAIN_FONT.get("size"))  # tick label size

        ax.dist = 11  # Ensures tick labels are not cut off
        ax.margins(0.05, 0.05, 0.05)  # Adjusts tick margins

        # Draw colorbar
        color_bar = fig.colorbar(surf,
                                 ax=ax,
                                 orientation="vertical",
                                 fraction=0.2,
                                 shrink=0.5,
                                 aspect=10)
        # kwargs here control axes that the colorbar is drawn in
        color_bar.set_label(r"$\chi^2$", size=MAIN_FONT.get("size"))
        color_bar.ax.tick_params(labelsize=MAIN_FONT.get("size"))

        plt.show()
        if kwargs.get("save_as") is not None:
            fig.savefig(kwargs.get("save_as") + "_contour.png", dpi=300)
    else:
        fig = plt.figure(fig_num, figsize=(12, 10))  # Fig. 2
        fig.text(0.1, 0.95, syst_analyser._name, **BOLD_FONT)
        ax = fig.add_subplot(1, 1, 1)

        # Set labels
        ax.set_xlabel("Signal counts", **BOLD_FONT)
        ax.set_ylabel("Value of systematic", **BOLD_FONT)

        # Plot color map
        color_map = ax.pcolormesh(X, Y, data, cmap=color_map, norm=norm)
        color_bar = fig.colorbar(color_map)
        color_bar.set_label("$\chi^2$", size=MAIN_FONT.get("size"))
        color_bar.ax.tick_params(
            labelsize=MAIN_FONT.get("size"))  # tick label size

        # Set axes limits
        ax.set_xlim([X.min(), X.max()])
        ax.set_ylim([Y.min(), Y.max()])

        if kwargs.get("preferred_values"):
            ax.plot(x, y_2, "bo-", label="Preferred values")
        if kwargs.get("minima"):
            ax.plot(x_3, y_3, "ko", label="Minima")

        # Set axes tick label size
        for label in (ax.get_xticklabels() + ax.get_yticklabels()):
            label.set_fontsize(MAIN_FONT.get("size"))

        ax.legend(loc="upper left")
        if kwargs.get("save_as") is not None:
            fig.savefig(kwargs.get("save_as") + "_color_map.png", dpi=300)
    return fig