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

1d side by side update #171

Merged
merged 9 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions docs/advanced/other_advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,15 @@ Compare data and stacked histogram for a flatten 2D variable:
:start-after: ###


Multiple histograms, side by side, with numbers on top
======================================================

Side-by-side categorical histograms
===================================
This example shows how to plot multiple 1D histograms side by side, with numbers on top of each bars. The code is similar to the one used in the :ref:`basics-1d_hist_side_by_side-label` section.

Here is an example to put three histograms side by side with a categorical axis and boost-histogram:

.. image:: ../img/1d_side_by_side.svg
:alt: Side by side histograms
:width: 500
.. image:: ../img/1d_side_by_side_with_numbers.svg
:alt: Multiple histograms side by side
:width: 500

.. literalinclude:: ../examples/advanced/1d_side_by_side.py
:language: python
:start-after: ###
.. literalinclude:: ../examples/advanced/1d_side_by_side_with_numbers.py
:language: python
:start-after: ###
17 changes: 16 additions & 1 deletion docs/basics/1d_hist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ To easily get the values and the uncertainties of the comparison, the :func:`get
h1, h2, comparison="ratio"
)

.. _1d-profile-plot-label:
.. _basics-1d_hist_profile_plot-label:
Mean histogram (profile plot)
=============================

Expand Down Expand Up @@ -265,3 +265,18 @@ String category
.. image:: ../img/1d_str_category.svg
:alt: String category plot
:width: 500


.. _basics-1d_hist_side_by_side-label:
Using multiple histograms
-------------------------

With multiple histograms, the :func:`plot_hist() <plothist.plotters.plot_hist>` function will correctly put them side by side, because it is a wrapper around the ``hist()`` function from ``matplotlib`` that provides this functionality.

.. image:: ../img/1d_side_by_side.svg
:alt: Side by side histograms
:width: 500

.. literalinclude:: ../examples/1d_hist/1d_side_by_side.py
:language: python
:start-after: ###
57 changes: 57 additions & 0 deletions docs/examples/1d_hist/1d_side_by_side.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Side-by-side categories
=======================

Plot multiple 1D histograms with categories side by side.
"""

###
import boost_histogram as bh
import numpy as np
import matplotlib.pyplot as plt
from plothist import plot_hist

rng = np.random.default_rng(8311311)

# String categories
categories = ["A", "B", "C"]

# Axis with the 3 bins
axis = bh.axis.StrCategory(categories=categories)

## Works also with integers
# categories = [-5, 10, 137]
# axis = bh.axis.IntCategory(categories=categories)

# Generate data for 3 histograms
data = [
rng.choice(categories, 20),
rng.choice(categories, 30),
rng.choice(categories, 40),
]

# Create and fill the histograms
histos = [bh.Histogram(axis, storage=bh.storage.Weight()) for _ in range(len(data))]
histos = [histo.fill(data[i]) for i, histo in enumerate(histos)]

labels = [f"$h_{{{i}}}$" for i in range(len(histos))]

# Plot the histogram
fig, ax = plt.subplots()

# Use a specificity of matplotlib: when a list of histograms is given, it will plot them side by side unless stacked=True or histtype is a "step" type.
plot_hist(histos, ax=ax, label=labels)

# Set the x-ticks to the middle of the bins and label them
ax.set_xlim(0, len(categories))
ax.set_xticks([i + 0.5 for i in range(len(categories))])
ax.set_xticklabels(categories)
ax.minorticks_off()
# Get nice looking y-axis ticks
ax.set_ylim(top=int(np.max([np.max(histo.values()) for histo in histos]) * 1.5))

ax.set_xlabel("Category")
ax.set_ylabel("Entries")
ax.legend()

fig.savefig("1d_side_by_side.svg", bbox_inches="tight")
84 changes: 0 additions & 84 deletions docs/examples/advanced/1d_side_by_side.py

This file was deleted.

81 changes: 81 additions & 0 deletions docs/examples/advanced/1d_side_by_side_with_numbers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Side-by-side categories, with numbers on top
============================================

Plot multiple 1D histograms with categories side by side, and add the number of entries on top of each bar.
"""

###
import boost_histogram as bh
import numpy as np
import matplotlib.pyplot as plt
from plothist import plot_hist, get_color_palette

rng = np.random.default_rng(83113111)

# Integer categories
categories = [-137, 12, 1234]
axis = bh.axis.IntCategory(categories=categories)

# Generate data for 3 histograms
data = [
rng.choice(categories, 50),
rng.choice(categories, 30),
rng.choice(categories, 35),
rng.choice(categories, 30),
]

# Create and fill the histograms
histos = [bh.Histogram(axis, storage=bh.storage.Weight()) for _ in range(len(data))]
histos = [histo.fill(data[i]) for i, histo in enumerate(histos)]

labels = [f"$h_{{{i}}}$" for i in range(len(histos))]
colors = get_color_palette("ggplot", 5)
colors = colors[:3] + [colors[4]]

# Plot the histogram
fig, ax = plt.subplots()

# Use a specificity of matplotlib: when a list of histograms is given, it will plot them side by side unless stacked=True or histtype is a "step" type.
plot_hist(histos, ax=ax, label=labels, color=colors)


# Add the number of entries on top of each bar
# Get the correct shift in x-axis for each bar
def calculate_shifts(width, n_bars):
half_width = width / 2
shift = np.linspace(-half_width, half_width, n_bars, endpoint=False)
shift += width / (2 * n_bars)
return shift


bin_width = 0.8
shift = calculate_shifts(bin_width, len(histos))

# Loop over the histograms, add on top of each bar the number of entries
for i, histo in enumerate(histos):
for j, value in enumerate(histo.values()):
ax.text(
j + 0.5 + shift[i],
value,
int(
value
), # If weighted, f"{height:.1f}" can be used as a better representation of the bin content
color="black",
ha="center",
va="bottom",
)

# Set the x-ticks to the middle of the bins and label them
ax.set_xlim(0, len(categories))
ax.set_xticks([i + 0.5 for i in range(len(categories))])
ax.set_xticklabels(categories)
ax.minorticks_off()
# Get nice looking y-axis ticks
ax.set_ylim(top=int(np.max([np.max(histo.values()) for histo in histos]) * 1.5))

ax.set_xlabel("Category")
ax.set_ylabel("Entries")
ax.legend()

fig.savefig("1d_side_by_side_with_numbers.svg", bbox_inches="tight")
Loading
Loading