Plotting of 2D histogram with rootpy and matplotlib - python

I am trying to plot a ROOT 2D histogram with rootpy and matplotlib.
The code I use for this is:
from rootpy.io import File
from rootpy.plotting import Hist
import rootpy.plotting.root2matplotlib as rplt
import matplotlib.pyplot as plt
inputFile = File('mydata.root', 'read')
h_response = inputFile.myfolder.response
plt.figure(figsize=(16, 10), dpi=100)
rplt.hist(h_response, label='response matrix')
h_response.Draw()
plt.xlabel('reconstructed $E_{\mathrm{T}}^{miss}$')
plt.ylabel('Generated $E_{\mathrm{T}}^{miss}$')
plt.title('Response Matrix')
plt.savefig('ResponseMatrix.png')
However, this leaves me with the error msg:
Traceback (most recent call last):
File "/storage/Dropbox/Workspace/Analysis/DailyPythonScripts/src/unfolding.py", line 66, in <module>
rplt.hist(h_response, label='response matrix')
File "/usr/local/lib/python2.7/dist-packages/rootpy-0.7.0_a0-py2.7-linux-x86_64.egg/rootpy/plotting/root2matplotlib.py", line 140, in hist
snap_zero=snap_zero)
File "/usr/local/lib/python2.7/dist-packages/rootpy-0.7.0_a0-py2.7-linux-x86_64.egg/rootpy/plotting/root2matplotlib.py", line 82, in _set_bounds
ywidth = ymax - ymin
TypeError: unsupported operand type(s) for -: 'list' and 'list'
Obviously I am using the wrong rootpy2matplotlib module, so I had a look:
The module provides: hist, bar and errorbar functions - no specific for >= 2D.
Am I missing something? Is there an easy workaround?
PS: I would like to tag this question with a 'rootpy' tag, but it is not possible. So I apologise, since this question is quite specific.

rootpy's root2matplotlib interface now provides the hist2d, imshow and contour functions for plotting 2D ROOT histograms. See the example here:
https://github.com/rootpy/rootpy/blob/master/examples/plotting/plot_matplotlib_hist2d.py
from matplotlib import pyplot as plt
from rootpy.plotting import root2matplotlib as rplt
from rootpy.plotting import Hist2D
import numpy as np
a = Hist2D(100, -3, 3, 100, 0, 6)
a.fill_array(np.random.multivariate_normal(
mean=(0, 3),
cov=np.arange(4).reshape(2, 2),
size=(1E6,)))
fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, figsize=(15, 5))
ax1.set_title('hist2d')
rplt.hist2d(a, axes=ax1)
ax2.set_title('imshow')
im = rplt.imshow(a, axes=ax2)
ax3.set_title('contour')
rplt.contour(a, axes=ax3)
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(im, cax=cbar_ax)
plt.show()

I just tried your script on a ROOT file containing a TH2D histogram. Everything worked.
/opt/rootpy # cat version.txt
gives me: 0.7.0
if I check my
/usr/local/lib/python2.7/dist-packages/rootpy-dev-py2.7.egg/rootpy/plotting/root2matplotlib.py
and compare it to the error messages you get, then it looks like we are using different versions of rootpy.
Try the newest version of rootpy.

Related

How to adapt this python script to apt installed matplotlib vs pip3 installed

I have a script (MWE supplied)
import matplotlib.pyplot as plt
import matplotlib
s_xLocs = [864]
s_yLocs = [357]
s_score = [0.33915146615180547]
sMax = 0.34704810474264386
for i in range(len(s_xLocs)):
plt.scatter(s_xLocs[i], s_yLocs[i], c=s_score[i], s=(20*(s_score[i]+1.5)**4), cmap="plasma", marker='.', vmin=0, vmax=sMax)
matplotlib.pyplot.close()
which was being used to generate some plots using matplotlib. On my dev machine, I used matplotlib installed via pip3. The script is now being used on some other machines managed by IT and limited to using the version of matplotlib installed via apt install python3-matplotlib. This has caused my script to fail, throwing the error
Traceback (most recent call last):
File "./heatmaps.py", line 9, in <module>
plt.scatter(s_xLocs[i], s_yLocs[i], c=s_score[i], s=(20*(s_score[i]+1.5)**4), cmap="plasma", marker='.', vmin=0, vmax=sMax)
File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 2836, in scatter
__ret = gca().scatter(
File "/usr/lib/python3/dist-packages/matplotlib/__init__.py", line 1601, in inner
return func(ax, *map(sanitize_sequence, args), **kwargs)
File "/usr/lib/python3/dist-packages/matplotlib/axes/_axes.py", line 4451, in scatter
self._parse_scatter_color_args(
File "/usr/lib/python3/dist-packages/matplotlib/axes/_axes.py", line 4264, in _parse_scatter_color_args
n_elem = c_array.shape[0]
IndexError: tuple index out of range
After reading this Q/A I was able to seemingly narrow down the issue to the colormap c argument. After also reading the documentation I also tried passing in the entire list of s_score with no indexing ala
plt.scatter(s_xLocs[i], s_yLocs[i], c=s_score, s=(20*(s_score[i]+1.5)**4), cmap="plasma", marker='.', vmin=0, vmax=sMax)
but that gave a different and more confusing (IMO) error:
...
ValueError: Invalid RGBA argument: 0.33915146615180547
During handling of the above exception, another exception occurred:
...
ValueError: 'c' argument has 1 elements, which is not acceptable for use with 'x' with size 1, 'y' with size 1.
I am hoping someone can provide a solution to this issue which will work with python3-matplotlib and perhaps also clarify the errors/what is different between the version installed with pip3 vs apt.
This could be occasioned because of different versions of matplotlib installed.
As the problem is with the c parameter, I suggest creating a pallet and then getting the color based on the float value:
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib as mpl
s_xLocs = [864]
s_yLocs = [357]
s_score = [0.33915146615180547]
sMax = 0.34704810474264386
palette = cm.get_cmap('plasma')
norm = mpl.colors.Normalize(vmin=0, vmax=sMax)
for i in range(len(s_xLocs)):
color = palette(norm(s_score[i]))
plt.scatter(s_xLocs[i], s_yLocs[i], color=color, s=(20*(s_score[i]+1.5)**4), marker='.')
Another solution that did not break other functionality was to change plotting to this method:
import matplotlib.pyplot as plt
import matplotlib
s_xLocs = [864]
s_yLocs = [357]
s_score = [0.33915146615180547]
sMax = 0.34704810474264386
sSizes = [(20*(size+1.5)**4) for size in s_score]
plt.scatter(s_xLocs, s_yLocs, c=s_score, s=sSizes, cmap="plasma", marker='.', vmin=0, vmax=sMax)
plt.show()

Update Line2D properties from line on different axes in matplotlib

In matplotlib, the update_from method of a Line2D object can be used to copy properties from another line (see e.g. this answer). This is not working if the two lines live on different axes. The following code:
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
line2.update_from(line1)
raises
AttributeError: 'NoneType' object has no attribute 'extents'
while the traceback leaves me puzzled.
My questions are:
Why is this error raised?
How can I copy (all) Line2D properties of line1 to line2 instead?
EDIT
After a bit more testing I can say that the AttributeError above is for example raised in a Jupyter notebook session with the %matplotlib inline backend. With the %matplotlib notebook backend or in a regular Python script (e.g. with the "qt5agg" backend), the code passes without an error but line2 is "invisible" afterwards.
For completeness, the above image was created using (Anaconda) Python 3.7.9 and matplotlib 3.3.1 with:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use("qt5agg")
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
line2.update_from(line1)
plt.savefig("test.png")
The problem remains that I cannot copy the Line2D properties from line1 to line2.
EDIT 2
Throwing a plt.tight_layout() into the mix brings back the AttributeError.
EDIT 3
As requested in the comments, here is the traceback for the error I get with plt.tight_layout() (EDIT 2):
Traceback (most recent call last):
File "test.py", line 11, in <module>
plt.tight_layout()
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/cbook/deprecation.py", line 451, in wrapper
return func(*args, **kwargs)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/pyplot.py", line 1490, in tight_layout
gcf().tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
return func(*inner_args, **inner_kwargs)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/figure.py", line 2615, in tight_layout
pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/tight_layout.py", line 308, in get_tight_layout_figure
pad=pad, h_pad=h_pad, w_pad=w_pad)
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/tight_layout.py", line 84, in auto_adjust_subplotpars
bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
File "/home/janjoswig/.pyenv/versions/miniconda3-4.7.12/envs/md379/lib/python3.7/site-packages/matplotlib/axes/_base.py", line 4199, in get_tightbbox
if np.all(clip_extent.extents == axbbox.extents):
AttributeError: 'NoneType' object has no attribute 'extents'
It seems update_from updates too much, including the transformation and the clipbox. Maybe the error comes from the object being totally invisible after clipping to the wrong clipbox?
A workaround can be to save both before updating and setting them back:
from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1)
line1, = ax1.plot(range(10), "r.")
line2, = ax2.plot(*line1.get_xydata().T)
old_transform = line2.get_transform()
old_clipbox = line2.clipbox
line2.update_from(line1)
line2.set_transform(old_transform)
line2.clipbox = old_clipbox
plt.tight_layout()
plt.draw()

How to solve " 'PathCollection' object has no attribute 'yaxis' " error?

I'm a MSc Student and I used to make graphs and plots with commercial packages like OriginPro, Excel and Matlab. Although these softwares provide a great user experience, there are some major disadvantages as they are specific OS dependent and, in general, very expensive.
Hence, I started to learn Python using matplotlib library with VS Code, however I'm having some problems with some library functions and statements that seems to be standard from matplotlib and numPy, but it doesnt work.
For example, I'm making some templates for scatter plots and I can't control minor ticks because it doesn't recognize the statements xaxix and yaxix:
Sample of the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, AutoMinorLocator
.
.
.
fig = plt.figure(figsize=(x_pixels/my_dpi, y_pixels/my_dpi), dpi=my_dpi)
ax = plt.scatter(x*format_x, y*format_y, s = size, alpha = transparency, color = color, label = legend_text)
.
.
.
# Major Ticks
plt.tick_params(axis = 'both', which = 'major', length = majorT_length, direction = majorT_direction, color = majorT_color, labelsize = label_size, top = 'on', right = 'on')
# Minor Ticks
plt.minorticks_on()
plt.tick_params(axis='both', which='minor', length = minorT_length, direction = minorT_direction, color = minorT_color, top = 'on', right = 'on')
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.xaxis.set_minor_locator(AutoMinorLocator(2))
# Figure Layout
plt.tight_layout()
plt.savefig(output_file, dpi=my_dpi, bbox_inches=borders)
plt.show()
and the Terminal show this error:
File "c:/Users/luagu/Desktop/Python Matplotlib Training/Scatter_Template.py", line 128, in <module>
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
AttributeError: 'PathCollection' object has no attribute 'yaxis'
What I'm doing wrong?
Thanks in advance!
You wrote ax = plt.scatter but your ax here is an artist returned by the scatter method, not an Axes object. What you want to do is:
plt.scatter(...)
...
ax = plt.gca()
ax.yaxis.set_minor_locator(AutoMinorLocator(2))
ax.xaxis.set_minor_locator(AutoMinorLocator(2))

RuntimeError: Can not put single artist in more than one figure when using matplotlib 1.5

Here is my code:
import matplotlib.pyplot as plt
plt.figure(1) # the first figure
plt.subplot(211) # the first subplot in the first figure
plt.plot([1, 2, 3])
plt.subplot(212) # the second subplot in the first figure
plt.plot([4, 5, 6])
plt.figure(2) # a second figure
plt.plot([4, 5, 6]) # creates a subplot(111) by default
plt.text(.5,1.5,'211',figure = 211) #tring to add text in previous subplot
plt.figure(1) # figure 1 current; subplot(212) still current
plt.subplot(211) # make subplot(211) in figure1 current
plt.title('Easy as 1, 2, 3') # subplot 211 title
The error:
Traceback (most recent call last):
File "C:/Users/ezhou/Desktop/python/test3.py", line 11, in <module>
plt.text(.5,1.5,'211',figure = 211)
File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 3567, in text
ret = gca().text(x, y, s, fontdict=fontdict, withdash=withdash, **kwargs)
File "C:\Python27\lib\site-packages\matplotlib\axes\_axes.py", line 619, in text
self._add_text(t)
File "C:\Python27\lib\site-packages\matplotlib\axes\_base.py", line 1720, in _add_text
self._set_artist_props(txt)
File "C:\Python27\lib\site-packages\matplotlib\axes\_base.py", line 861, in _set_artist_props
a.set_figure(self.figure)
File "C:\Python27\lib\site-packages\matplotlib\artist.py", line 640, in set_figure
raise RuntimeError("Can not put single artist in "
RuntimeError: Can not put single artist in more than one figure
I was trying to understand the kwargs 'figure' in class matplotlib.text.Text(), but it will always reply 'Can not put single artist in more than one figure'. So I was confused about how to use this 'figure' kwarg. Can anyone give me some advise? Thanks!
You shouldn't pass figure as a kwarg, instead use text method of a Figure (or Axes) instance. Example:
import matplotlib.pyplot as plt
fig1, fig2 = plt.figure(1), plt.figure(2)
sp1, sp2 = fig1.add_subplot(211), fig2.add_subplot(211)
sp1.plot([1, 2, 3])
sp2.plot([0, 1, 3])
fig1.text(.5, .3, 'whole figure')
sp2.text(.5, .5, 'subplot')
Please note that coordinates are relative (0, 1).
P.S if you find matplotlib needlessly complicated (as I do), you may wish to have a look at Plotly

How can I add a label to colorbar using ImageGrid?

In a previous question,
colobar label matplotlib in ImageGrid,
had a solution for adding a label to the colorbar, but this seems to be broken with the current version.
Platforms I have tried:
Mac w/ Canopy:
python: 2.7
matplotlib: 1.4.3-6
Linux:
python: 2.7
matplotlib: 1.3.1
Below is the code from the previous question, with some extra code for running in an iPython notebook:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid
def get_demo_image():
import numpy as np
from matplotlib.cbook import get_sample_data
f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
z = np.load(f)
# z is a numpy array of 15x15
return z, (-3,4,-4,3)
def demo_grid_with_single_cbar(fig):
"""
A grid of 2x2 images with a single colorbar
"""
grid = AxesGrid(fig, 132, # similar to subplot(132)
nrows_ncols = (2, 2),
axes_pad = 0.0,
share_all=True,
label_mode = "L",
cbar_location = "top",
cbar_mode="single",
)
Z, extent = get_demo_image()
for i in range(4):
im = grid[i].imshow(Z, extent=extent, interpolation="nearest")
#plt.colorbar(im, cax = grid.cbar_axes[0])
#grid.cbar_axes[0].colorbar(im)
cbar = grid.cbar_axes[0].colorbar(im)
cbar.ax.set_label_text("$[a.u.]$")
for cax in grid.cbar_axes:
cax.toggle_label(False)
# This affects all axes as share_all = True.
grid.axes_llc.set_xticks([-2, 0, 2])
grid.axes_llc.set_yticks([-2, 0, 2])
#
F = plt.figure(1, (10.5, 2.5))
F.subplots_adjust(left=0.05, right=0.95)
demo_grid_with_single_cbar(F)
plt.draw()
plt.show()
The error message from the code is of the form:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-1-60ebdb832699> in <module>()
40 F = plt.figure(1, (10.5, 2.5))
41 F.subplots_adjust(left=0.05, right=0.95)
---> 42 demo_grid_with_single_cbar(F)
43
44 plt.draw()
<ipython-input-1-60ebdb832699> in demo_grid_with_single_cbar(fig)
29 #grid.cbar_axes[0].colorbar(im)
30 cbar = grid.cbar_axes[0].colorbar(im)
---> 31 cbar.ax.set_label_text("$[a.u.]$")
32
33 for cax in grid.cbar_axes:
AttributeError: 'CbarAxes' object has no attribute 'set_label_text'
Has the matplotlib interface changed since the original question was asked? If so, how do I add the colorbar label?
Personally, I've always perceived matplotlib as black magic, similar to TeX, so I cannot guarantee that my answer is the "official" way of doing what you want, or that it will continue to work in later versions. But thanks to this gallery example, I could devise the following incantation:
grid[0].cax.colorbar(im)
cax = grid.cbar_axes[0]
axis = cax.axis[cax.orientation]
axis.label.set_text("$[a.u.]$")
(don't forget to remove all your colorbar-related code). This works in the current matplotlib version (1.4.3). The result:

Categories