How to add colorbar to a histogram? - python

I have a histogram like this (just like a normal histogram):
In my situation, there are 20 bars always (spanning x axis from 0 to 1) and the color of the bar is defined based on the value on the x axis.
What I want is to add a color spectrum like one of those in http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps at the bottom of the histogram but I don't know how to add it.
Any help would be appreciated!

You need to specify the color of the faces from some form of colormap, for example if you want 20 bins and a spectral colormap,
nbins = 20
colors = plt.cm.spectral(np.linspace(nbins))
You can then use this to specify the color of the bars, which is probably easiest to do by getting histogram data first (using numpy) and plotting a bar chart. You can then add the colorbar to a seperate axis at the bottom.
As a minimal example,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
nbins = 20
minbin = 0.
maxbin = 1.
data = np.random.normal(size=10000)
bins = np.linspace(minbin,maxbin,20)
cmap = plt.cm.spectral
norm = mpl.colors.Normalize(vmin=data.min(), vmax=data.max())
colors = cmap(bins)
hist, bin_edges = np.histogram(data, bins)
fig = plt.figure()
ax = fig.add_axes([0.05, 0.2, 0.9, 0.7])
ax1 = fig.add_axes([0.05, 0.05, 0.9, 0.1])
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap,
norm=norm,
orientation='horizontal')
ax.bar(bin_edges[:-1], hist, width=0.051, color=colors, alpha=0.8)
ax.set_xlim((0., 1.))
plt.show()
Which yields,

Related

Subplot several scatter histograms

Can someone share an example to create 4 scatter hist plots as a subplot?
To clarify. I am planning to create a pdf of plots. Each page will have 4 subplots. Each subplot being the scatter histogram.
The example of creating scatter histogram seems to be this
Would there be any alternate functions to do this in fewer lines than using this scatter plot example and sub-plotting each of them ?
Using the linked example, all you need to do is increase the number of subplots.
Then for each subplot, you go through the example code to make each one a scatter histogram.
I've pasted a toy example below:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig, axes = plt.subplots(figsize=(10,10),nrows=2, ncols=2)
print(axes)
colors = ['r','b','g','m']
for row in axes:
for axScatter in row:
print()
x = np.random.randn(1000)
y = np.random.randn(1000)
# the scatter plot:
# gets color from the end ('m' will be first)
color = colors.pop()
axScatter.scatter(x, y,color = color)
axScatter.set_aspect(1.)
# create new axes on the right and on the top of the current axes
# The first argument of the new_vertical(new_horizontal) method is
# the height (width) of the axes to be created in inches.
divider = make_axes_locatable(axScatter)
axHistx = divider.append_axes("top", 1.2, pad=0.1, sharex=axScatter)
axHisty = divider.append_axes("right", 1.2, pad=0.1, sharey=axScatter)
# make some labels invisible
axHistx.xaxis.set_tick_params(labelbottom=False)
axHisty.yaxis.set_tick_params(labelleft=False)
# now determine nice limits by hand:
binwidth = 0.25
xymax = max(np.max(np.abs(x)), np.max(np.abs(y)))
lim = (int(xymax/binwidth) + 1)*binwidth
bins = np.arange(-lim, lim + binwidth, binwidth)
axHistx.hist(x, bins=bins,color=color)
axHisty.hist(y, bins=bins, orientation='horizontal',color=color)
# the xaxis of axHistx and yaxis of axHisty are shared with axScatter,
# thus there is no need to manually adjust the xlim and ylim of these
# axis.
axHistx.set_yticks([0, 50, 100])
axHisty.set_xticks([0, 50, 100])
plt.show()

matplotlib polar plot axis label position

I have been playing with polar plots for some time now, but can't figure out how I get my axis labels automatically placed in the correct spot.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[5, 5])
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection="polar")
r = np.random.normal(loc=50, scale=5, size=50)
theta = np.deg2rad(np.random.normal(loc=190, scale=2, size=50))
# Plot
ax.scatter(theta, r)
# Adjust limits
ax.set_rorigin(0)
ax.set_thetamin(180)
ax.set_thetamax(200)
ax.set_rmin(40)
ax.set_rmax(60)
# Labels
ax.set_xlabel("r")
ax.set_ylabel(r"$\theta$")
plt.show()
This produces such a plot:
https://ibb.co/geo4WK
As you can see, the "r" label does not appear on the top axis where the tick labels are and I have similar problem for other ranges of theta. Is there a way to always have the axis label appear with the axis that has tick labels? Or can I have the tick labels for the radii always at the bottom axis?
thanks!
You can use ax.xaxis.set_label_coords() to move "r" to the top center location. As the figure is set to be (5,5) and you have r limits adjusted, the "r" label should stay the same as you change theta.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(figsize=[5, 5])
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], projection="polar")
r = np.random.normal(loc=50, scale=5, size=50)
theta = np.deg2rad(np.random.normal(loc=190, scale=2, size=50))
# Plot
ax.scatter(theta, r)
# Adjust limits
ax.set_rorigin(0)
ax.set_thetamin(180)
ax.set_thetamax(200)
ax.set_rmin(40)
ax.set_rmax(60)
# Labels
ax.set_xlabel("r")
ax.set_ylabel(r"$\theta$")
ax.xaxis.set_label_coords(0.5, 1.05)
plt.show()

Matplotlib colorbar with consistent size for multiple subplots

I am trying to create a figure with several subplots that have a common colorbar. The subplots have to have an equal aspect ratio and the colorbar has to have the same height as the subplots. However, I don't manage to get a narrow colorbar with the same height as the other subplots.
I am using this recipe to generate a colorbar with a range suitable for all subplots; hence this issue is not addressed in the MWE.
When using the axes divider recipe to attach the colorbar, the height of the subplot changes due to the aspect ratio.
Here's the MWE
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import itertools as it
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
axes = dict(enumerate(fig.get_axes(),1))
for i,ax in axes.items():
if i==6:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
divider = make_axes_locatable(axes[6])
cax = divider.append_axes("left", size="15%", pad=0.0)
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
I can plot the colorbar over the complete subplot, in which case the height is correct, but it's much to wide to be appealing.
Does anybody have a "robust" solution, i.e. without manually fiddling around with the dimension of the subplots holding the colorbar?
Thanks in advance!
EDIT: Increased width of colorbar to emphasize that it becomes smaller in height.
If the only aim is to get the height of the colorbar correctly aligned with its horizontal neighbor, the last solution from this answer would help.
If however you also want the colorbar to be left-aligned with the plot on top of it, the solution is probably more complicated.
You may use a callback to set the position of the colorbar explicitely as follows:
from matplotlib import pyplot as plt
from matplotlib.transforms import Bbox
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
for i,ax in enumerate(axes.flat):
if i==5:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
cax = fig.add_axes([0.6,0.01,0.1,0.4])
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
def align_cbar(cax, hax, vax):
hpos = hax.get_position()
vpos = vax.get_position()
bb = Bbox.from_extents(vpos.x0, hpos.y0, vpos.x0+vpos.width*.05,hpos.y1)
if cax.get_position() != bb:
cax.set_position(bb)
fig.canvas.draw_idle()
align_cbar(cax, axes[1,1], axes[0,2])
fig.canvas.mpl_connect("draw_event", lambda x: align_cbar(cax, axes[1,1], axes[0,2]))
plt.show()

Ticks plotted with an offset in colorbar

Problem: I am plotting the colorbar using Matplotlib, but these ticks are set at 0.0, 0.1.. to 0.5.
I wanted to get more intervals in between, but that leads to me having this following problem : Irregularly spaced tick labels.
In this picture, I have marked in red the offset in the ticks.
Code:
plt.pcolor(data_mod, vmin = 0.01, vmax = 0.5, cmap=cmap)
cb = plt.colorbar(extend='both')
cb.set_label('CPRESS', fontsize=7, labelpad=-10, y=1.05, rotation=0)
tick_locator = ticker.MaxNLocator(nbins = 10)
cb.locator = tick_locator
cb.update_ticks()
plt.imshow(data_mod)
What could I be doing wrong? Would it be possible to make the ticks just on top (starting) of the colors?
I could imagine you want to fix the boundaries of colors shown on the plot and in the colorbar to the values of numpy.linspace(0,.5,11).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
cmap = plt.get_cmap("jet",11)
data = np.random.rand(10,10)/2.
norm=matplotlib.colors.BoundaryNorm(np.linspace(0,0.5,11),11)
plt.pcolor(data, norm=norm, cmap=cmap)
cb = plt.colorbar(extend='both', ticks=np.linspace(0,0.5,11))
cb.set_label('CPRESS', fontsize=7, labelpad=-10, y=1.05, rotation=0)
plt.show()

Imshow subplots with the same colorbar

I want to make 4 imshow subplots but all of them share the same colormap. Matplotlib automatically adjusts the scale on the colormap depending on the entries of the matrices. For example, if one of my matrices has all entires as 10 and the other one has all entries equal to 5 and I use the Greys colormap then one of my subplots should be completely black and the other one should be completely grey. But both of them end up becoming completely black. How to make all the subplots share the same scale on the colormap?
To get this right you need to have all the images with the same intensity scale, otherwise the colorbar() colours are meaningless. To do that, use the vmin and vmax arguments of imshow(), and make sure they are the same for all your images.
E.g., if the range of values you want to show goes from 0 to 10, you can use the following:
import pylab as plt
import numpy as np
my_image1 = np.linspace(0, 10, 10000).reshape(100,100)
my_image2 = np.sqrt(my_image1.T) + 3
plt.subplot(1, 2, 1)
plt.imshow(my_image1, vmin=0, vmax=10, cmap='jet', aspect='auto')
plt.subplot(1, 2, 2)
plt.imshow(my_image2, vmin=0, vmax=10, cmap='jet', aspect='auto')
plt.colorbar()
When the ranges of data (data1 and data2) sets are unknown and you want to use the same colour bar for both/all plots, find the overall minimum and maximum to use as vmin and vmax in the call to imshow:
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=1, ncols=2)
# generate randomly populated arrays
data1 = np.random.rand(10,10)*10
data2 = np.random.rand(10,10)*10 -7.5
# find minimum of minima & maximum of maxima
minmin = np.min([np.min(data1), np.min(data2)])
maxmax = np.max([np.max(data1), np.max(data2)])
im1 = axes[0].imshow(data1, vmin=minmin, vmax=maxmax,
extent=(-5,5,-5,5), aspect='auto', cmap='viridis')
im2 = axes[1].imshow(data2, vmin=minmin, vmax=maxmax,
extent=(-5,5,-5,5), aspect='auto', cmap='viridis')
# add space for colour bar
fig.subplots_adjust(right=0.85)
cbar_ax = fig.add_axes([0.88, 0.15, 0.04, 0.7])
fig.colorbar(im2, cax=cbar_ax)
It may be that you don't know beforehand the ranges of your data, but you may know that somehow they are compatible. In that case, you may prefer to let matplotlib choose those ranges for the first plot and use the same range for the remaining plots. Here is how you can do it. The key is to get the limits with properties()['clim']
import numpy as np
import matplotlib.pyplot as plt
my_image1 = np.linspace(0, 10, 10000).reshape(100,100)
my_image2 = np.sqrt(my_image1.T) + 3
fig, axes = plt.subplots(nrows=1, ncols=2)
im = axes[0].imshow(my_image1)
clim=im.properties()['clim']
axes[1].imshow(my_image2, clim=clim)
fig.colorbar(im, ax=axes.ravel().tolist(), shrink=0.5)
plt.show()

Categories