Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier to plot ratio of multiple histograms [FEATURE] #466

Open
angelcampoverde opened this issue Nov 21, 2022 · 1 comment
Open
Labels
enhancement New feature or request

Comments

@angelcampoverde
Copy link

In:

import matplotlib.pyplot as plt
import numpy
from   hist import Hist

#------------------------------------------------------
def get_hist():
    h = (
      Hist.new
      .Reg(10, -2 ,2, name='x', label='x-axis')
      .Int64()
    )
    data=numpy.random.normal(size=10000)
    h.fill(data)

    return h
#------------------------------------------------------
def main():
    h_1 = get_hist()
    h_2 = get_hist()
    h_3 = get_hist()

    fig, (ax, rax) = plt.subplots(nrows=2, gridspec_kw={"height_ratios": (3, 1)})
    axs = {"main_ax": ax, "ratio_ax": rax}

    h_2.plot_ratio(h_1,
        rp_ylabel          ='Ratio',
        rp_num_label       ='data',
        rp_denom_label     ='sim 1',
        rp_uncert_draw_type='bar',
        ax_dict = axs,
    )

    h_3.plot_ratio(h_1,
        rp_ylabel          ='Ratio',
        rp_num_label       ='data',
        rp_denom_label     ='sim 2',
        rp_uncert_draw_type='bar',
        ax_dict = axs,
    )

    plt.show()
#------------------------------------------------------
if __name__ == '__main__':
    main()

The code is unable to:

  1. Plot the ratio of data to both simulations and there is no easy way to make this work. I see repeated histograms.
  2. The axes are not aligned by default
  3. I do not see documentation (or at least it is not easy to find) fr the rp_* arguments.

Describe the feature you'd like

The user should be able to do:

    h_1 = get_hist()
    h_2 = get_hist()
    h_3 = get_hist()

   h_1.plot_ratio([h_2, h_3])

   plt.show()

and we should get two figures, upper one with h_* overlaid. Lower with two ratios, to h_2 and h_3. The axes should be aligned, the labels and legends should be taken from the histograms themselves and we should not have to do any manipulation of artists.

Describe alternatives, if any, you've considered

The way the code is implemented is bad, it's too complicated, and I do not have time to make it work the way I need to, so I am moving back to pure matplotlib. The plots I need do not need to be perfect and matplotlib is good enough for me. It would be nice if hist can do quickly what we need though.

Cheers.

@angelcampoverde angelcampoverde added the enhancement New feature or request label Nov 21, 2022
@JaLuka98
Copy link

JaLuka98 commented Mar 5, 2023

Hi! Has there been any progress on this issue? I would also really appreciate a function that allows for ratio plots with more than two input histograms. I think the two problems labelled above as 1. and 2. are the main ones.

  1. In the above example, h1 is plotted twice on the upper panel.
  2. The axes are not "fused", meaning that there is empty space between the panel. The x-axis of the upper panel should ideally vanish and the lower end of the upper panel should be glued on top of the lower panel just as in https://hist.readthedocs.io/en/latest/user-guide/notebooks/Plots.html#Via-Plot-Ratio.

The user can actually manually solve 2. by specifying sharex=True and plt.subplots_adjust(hspace=.0) in their script. In my opinio, this should not be added to _plot_ratiolike in src/hist/plot.py so that users have more control over the spacing (if they desire to have non-standard spacing).
I think if one does not give axes to the first plot_ratio, they are created first and fused correctly and this unwanted behaviour occurs when one always gives axes to plot_ratio since they are just extracted from the dict in

if ax_dict:
without further modifications.
There is one complication when using this solution to 2.: The ticks of the upper and the lower limit can overlap, which does not look too nice. The user can prune the ticks using something like ax2.yaxis.set_major_locator(MaxNLocator(nbins=nbins, prune='upper')) with matplotlib.ticker.MaxNLocator where nbins = len(ax1.get_xticklabels()) but of course this is some manual hassle again, sadly.

I see two options to solve problem 1, which would enter at

other_artists = histplot(other, ax=main_ax, label=rp_kwargs["denom_label"])
(because I assume everybody wants to plot ratios with a common denominator...)

  • Check if more than one "lines" object is on the upper panel already and if yes, do not histplot the denominator.
  • Add an additional argument (boolean flag) and give control to the user if the denominator should be histplotted, defaulting to True.
    I am reluctant to directly propose any particular implementation to solve 1. but if you have a preference I could try myself.

Moving plot_ratio to mplhep might also be an option but I do not know if this is preferred long-term...?

Overall, development in this area would probably be really appreciated by the community because it would be so great to have an easy-to-use interface for flexible ratio plots!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants