├── .gitignore
├── LICENSE
├── README.md
├── histpoints.png
├── histpoints_binwidth.png
├── matplotlib_hep
└── __init__.py
├── pull.png
├── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Sphinx documentation
57 | docs/_build/
58 |
59 | # PyBuilder
60 | target/
61 |
62 | #Ipython Notebook
63 | .ipynb_checkpoints
64 |
65 | # pyenv
66 | .python-version
67 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Igor Babuschkin
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # matplotlib-hep
2 |
3 | An add-on for matplotlib that simplifies the creation of plots for high energy physics.
4 |
5 | ## Getting started
6 |
7 | This package is not yet on PyPI.
8 | You can install it from this repository by running
9 | ```bash
10 | pip install --user git+https://github.com/ibab/matplotlib-hep
11 | ```
12 |
13 | ## The histpoints plotting function
14 |
15 | In high energy physics (HEP), histograms are often displayed as a collection of
16 | data points, one for each bin. This allows one to easily compare the data to
17 | an underlying probability model.
18 |
19 | It can be debated if it is correct to attach error bars to the individual bin
20 | contents as opposed to the underlying model, as we want to know if our
21 | expectation for any given bin could fluctuate to match the data and not vice
22 | versa. But this is a convention widely used by the HEP community, and thus a
23 | way to use this kind of plot in Python is often necessary.
24 |
25 | A simple way to create these kinds of plots is missing from other Python
26 | packages like matplotlib. The `histpoints` function is designed to produce
27 | these plots conventiently, like in the following example:
28 |
29 | ```python
30 | from matplotlib_hep import histpoints
31 | import matplotlib.pyplot as plt
32 | import numpy as np
33 | import scipy.stats as stats
34 |
35 | data = np.random.normal(0, 1, 200)
36 | x, y, norm = histpoints(data)
37 |
38 | xs = np.linspace(-4, 4, 200)
39 | plt.plot(xs, norm * stats.norm.pdf(xs, 0, 1), 'b-', lw=2)
40 |
41 | plt.savefig('histpoints.png')
42 | ```
43 |
44 |

45 |
46 | Or, displaying horizontal error bars to mark the bin width:
47 | ```python
48 | histpoints(data, xerr='binwidth')
49 | ```
50 |
51 | 
52 |
53 | Note that the `histpoints` function returns the `x` and `y` coordinates of the
54 | points, as well as `norm`, the integral of the histogram. This can be used to
55 | scale a probability distribution to the histogram, as opposed to the other way
56 | around, like in the example above.
57 | This is often preferred in HEP, as it allows you to gauge the amount of entries
58 | in your data sample more easily.
59 |
60 | By default, `histpoints` chooses the number of bins automatically via the
61 | [Freedman-Diaconis](https://en.wikipedia.org/wiki/Freedman%E2%80%93Diaconis_rule)
62 | rule.
63 |
64 | ## The plot\_pull function
65 |
66 | ```python
67 | from matplotlib_hep import plot_pull
68 | import matplotlib.pyplot as plt
69 | import numpy as np
70 | import scipy as sp
71 |
72 | data = np.random.normal(0, 1, 1000)
73 |
74 | func = lambda x: sp.stats.norm.pdf(x, 0, 1)
75 |
76 | plot_pull(data, func)
77 | plt.savefig('pull.png')
78 | ```
79 |
80 | 
81 |
82 |
--------------------------------------------------------------------------------
/histpoints.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibab/matplotlib-hep/7ff83ffbc059a0ca9326f1ecb39979b13e33b22d/histpoints.png
--------------------------------------------------------------------------------
/histpoints_binwidth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibab/matplotlib-hep/7ff83ffbc059a0ca9326f1ecb39979b13e33b22d/histpoints_binwidth.png
--------------------------------------------------------------------------------
/matplotlib_hep/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 |
3 | import numpy as np
4 | import scipy.stats as stats
5 | import scipy as sp
6 | import logging
7 |
8 |
9 | __all__ = ['histpoints', 'make_split', 'calc_nbins', 'plot_pull']
10 |
11 | def calc_nbins(x, maximum=150):
12 | n = (max(x) - min(x)) / (2 * len(x)**(-1/3) * (np.percentile(x, 75) - np.percentile(x, 25)))
13 | return np.floor(min(n, maximum))
14 |
15 | def poisson_limits(N, kind, confidence=0.6827):
16 | alpha = 1 - confidence
17 | upper = np.zeros(len(N))
18 | lower = np.zeros(len(N))
19 | if kind == 'gamma':
20 | lower = stats.gamma.ppf(alpha / 2, N)
21 | upper = stats.gamma.ppf(1 - alpha / 2, N + 1)
22 | elif kind == 'sqrt':
23 | err = np.sqrt(N)
24 | lower = N - err
25 | upper = N + err
26 | else:
27 | raise ValueError('Unknown errorbar kind: {}'.format(kind))
28 | # clip lower bars
29 | lower[N==0] = 0
30 | return N - lower, upper - N
31 |
32 | def histpoints(x, bins=None, xerr=None, yerr='gamma', normed=False, **kwargs):
33 | """
34 | Plot a histogram as a series of data points.
35 |
36 | Compute and draw the histogram of *x* using individual (x,y) points
37 | for the bin contents.
38 |
39 | By default, vertical poisson error bars are calculated using the
40 | gamma distribution.
41 |
42 | Horizontal error bars are omitted by default.
43 | These can be enabled using the *xerr* argument.
44 | Use ``xerr='binwidth'`` to draw horizontal error bars that indicate
45 | the width of each histogram bin.
46 |
47 | Parameters
48 | ---------
49 |
50 | x : (n,) array or sequence of (n,) arrays
51 | Input values. This takes either a single array or a sequence of
52 | arrays, which are not required to be of the same length.
53 |
54 | """
55 | import matplotlib.pyplot as plt
56 |
57 | if bins is None:
58 | bins = calc_nbins(x)
59 |
60 | h, bins = np.histogram(x, bins=bins)
61 | width = bins[1] - bins[0]
62 | center = (bins[:-1] + bins[1:]) / 2
63 | area = sum(h * width)
64 |
65 | if isinstance(yerr, str):
66 | yerr = poisson_limits(h, yerr)
67 |
68 | if xerr == 'binwidth':
69 | xerr = width / 2
70 |
71 | if normed:
72 | h = h / area
73 | yerr = yerr / area
74 | area = 1.
75 |
76 | if not 'color' in kwargs:
77 | kwargs['color'] = 'black'
78 |
79 | if not 'fmt' in kwargs:
80 | kwargs['fmt'] = 'o'
81 |
82 | plt.errorbar(center, h, xerr=xerr, yerr=yerr, **kwargs)
83 |
84 | return center, (yerr[0], h, yerr[1]), area
85 |
86 | def make_split(ratio, gap=0.12):
87 | import matplotlib.pyplot as plt
88 | from matplotlib.gridspec import GridSpec
89 | from matplotlib.ticker import MaxNLocator
90 | cax = plt.gca()
91 | box = cax.get_position()
92 | xmin, ymin = box.xmin, box.ymin
93 | xmax, ymax = box.xmax, box.ymax
94 | gs = GridSpec(2, 1, height_ratios=[ratio, 1 - ratio], left=xmin, right=xmax, bottom=ymin, top=ymax)
95 | gs.update(hspace=gap)
96 |
97 | ax = plt.subplot(gs[0])
98 | plt.setp(ax.get_xticklabels(), visible=False)
99 | bx = plt.subplot(gs[1], sharex=ax)
100 |
101 | return ax, bx
102 |
103 | def plot_pull(data, func):
104 |
105 | import numpy as np
106 | import matplotlib.pyplot as plt
107 | from matplotlib.ticker import MaxNLocator
108 |
109 | ax, bx = make_split(0.8)
110 |
111 | plt.sca(ax)
112 |
113 | x, y, norm = histpoints(data)
114 |
115 | lower, upper = ax.get_xlim()
116 |
117 | xs = np.linspace(lower, upper, 200)
118 | plt.plot(xs, norm * func(xs), 'b-')
119 |
120 | #plt.gca().yaxis.set_major_locator(MaxNLocator(prune='lower'))
121 |
122 | plt.sca(bx)
123 |
124 | resid = y[1] - norm * func(x)
125 | err = np.zeros_like(resid)
126 | err[resid >= 0] = y[0][resid >= 0]
127 | err[resid < 0] = y[2][resid < 0]
128 |
129 | pull = resid / err
130 |
131 | plt.errorbar(x, pull, yerr=1, color='k', fmt='o')
132 | plt.ylim(-5, 5)
133 | plt.axhline(0, color='b')
134 |
135 | plt.sca(ax)
136 |
137 | return ax, bx
138 |
139 |
--------------------------------------------------------------------------------
/pull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ibab/matplotlib-hep/7ff83ffbc059a0ca9326f1ecb39979b13e33b22d/pull.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(name='matplotlib-hep',
4 | version='0.1.0',
5 | description='A package for creating High Energy Physics plots more easily',
6 | url='http://github.com/ibab/matplotlib-hep',
7 | author='Igor Babuschkin',
8 | author_email='igor@babuschk.in',
9 | license='MIT',
10 | packages=['matplotlib_hep'],
11 | zip_safe=False)
12 |
--------------------------------------------------------------------------------