Matplotlib Colorbar Display Digtis - python

How do I exactly specify the colorbar labels in matplotlib? Frequently, I need to create very specific color scales, but the colorbar labels display so poorly you can't tell what the scale is. I would like to manually define the text next to the colorbar tick marks, or at least have them display in scientific notation.
Here is an example plot where you can't tell what the bottom four color bins represent:
And here is a working example of how that plot was created:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
# mock up some data
x = np.random.random(50)
y = np.random.random(50)
c = np.arange(0, 1, 1.0/50.0) # color of points
c[0] = 0.00001
c[1] = 0.0001
c[2] = 0.001
c[3] = 0.01
s = 500 * np.random.random(50) + 25 # size of points
# set up some custom color scaling
lcmap = colors.ListedColormap(['#FFFFFF', '#FF99FF', '#8000FF',
'#0000FF', '#0080FF', '#58FAF4',
'#00FF00', '#FFFF00', '#FF8000',
'#FF0000'])
bounds = [0.0, 0.000001, 0.00001, 0.0001,
0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 1.0]
norm = colors.BoundaryNorm(bounds, lcmap.N)
# create some plot
fig, ax = plt.subplots()
im = ax.scatter(x, y, c=c, s=s, cmap=lcmap, norm=norm)
# add the colorbar
fig.colorbar(im, ax=ax)
fig.savefig('temp.jpg')

cbar = fig.colorbar(cax, ticks=[-1, 0, 1])
cbar.ax.set_xticklabels(['Low', 'Medium', 'High'])
and use whatever iterable you want instead of ['Low', 'Medium', 'High']
see: http://matplotlib.org/examples/pylab_examples/colorbar_tick_labelling_demo.html

Related

Matplotlib stacked histogram label

Here is my picture. I need to make label for those bars however every upper layer contains lower layer - so the label should containt grouped colors, i.e. blue - dataset 1, blue/orange - dataset 2, blue/orange/green - dataset 3 and finally blue/orange/green/purple - dataset 4. Is it plausible to make it? Thank you.
enter image description here
binwidth = 1
n, bins, patches = ax1.hist(C, bins=range(81, 105, binwidth),
density=False, histtype='barstacked' ,
edgecolor='gray',
color=barvy_histogram,linewidth=0.3)
hatches = ['//','x','..','oo']
for patch_set, hatch in zip(patches, hatches):
for patch in patch_set.patches:
patch.set_hatch(hatch)
patch.set_linewidth=0.1
patch.set_color='gray'
mpl.rcParams['hatch.linewidth'] = 0.5
The following approach uses the tuple legend handler (HandlerTuple) to combine the legend handles. It produces a horizontal layout, while maybe a vertical stacking would be more interesting.
The code starts with creating some test data, supposing C is an Nx4 array of integers. The bin edges are set at halves to make sure that floating point accuracy wouldn't place values in the wrong bin.
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
import numpy as np
# first, create some test data
C = (np.random.normal(0.001, 1, (100, 20)).cumsum(axis=0) * 1.2 + 90).astype(int).reshape(-1, 4)
c_min = C.min()
c_max = C.max()
mpl.rcParams['hatch.linewidth'] = 0.5
fig, ax1 = plt.subplots(figsize=(12, 5))
binwidth = 1
colors = plt.cm.Set2.colors[:C.shape[1]]
_, _, patches = ax1.hist(C, bins=np.arange(c_min - 0.5, c_max + binwidth, binwidth),
density=False, histtype='barstacked',
edgecolor='gray', color=colors, linewidth=0.3,
label=[f'N={p}' for p in range(25, 101, 25)])
hatches = ['//', 'x', '..', 'oo']
for patch_set, hatch in zip(patches, hatches):
for patch in patch_set.patches:
patch.set_hatch(hatch)
patch.set_linewidth = 0.1
handles, labels = ax1.get_legend_handles_labels()
ax1.legend(handles=[tuple(handles[:i + 1]) for i in range(C.shape[1])], labels=labels,
handlelength=6, handler_map={tuple: HandlerTuple(ndivide=None, pad=0)})
plt.show()

Use of "extend" in a contourf plot with a discrete colorbar not working

I would like to create a contourf plot with an imposed maximum value and with everything above that value shaded with the last color of the colorbar. In the example code below, which reproduces my problem in my setup, I would like the colorbar to range between -1 and 1, with an extend arrow indicating that values above 1.0 will be shaded with the last color of the colorbar. However, although I have tried several solutions from various stackexchange discussions, the colorbar ranges between -4 and 4, and there is no extend arrow. Please see the minimum reproducible example below.
# import matplotlib (v 3.1.1)
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import matplotlib.path as mpath
import matplotlib as mpl
# import numpy (v 1.17.2)
import numpy as np
# define grid
lon = np.linspace(start = 0, stop = 359, num = 360)
lat = np.linspace(start = -78, stop = -25, num = 52)
[X,Y] = np.meshgrid(lon, lat)
# generate random gaussian data for example purposes
mean = [0, 0]
cov = [[1, 0], [0, 100]]
zz = np.random.multivariate_normal(mean, cov, (np.size(lon),np.size(lat))).T
Z = zz[0,:,:]
# illutrate the maximum value of Z
np.max(Z)
# create plot
plt.figure(figsize=(10, 12))
# select plotting levels (missing min/max on purpose)
mylevs = [-1.0, -0.5, 0, 0.5, 1.0]
# colormap
cmap_cividis = plt.cm.get_cmap('cividis',len(mylevs))
mycolors = list(cmap_cividis(np.arange(len(mylevs))))
cmap = colors.ListedColormap(mycolors[:-1], "")
# set over-color to last color of list
cmap.set_over(mycolors[-1])
# contour plot: random pattern
C1 = plt.contourf(X, Y, Z, cmap = cmap, vmin=-1.0, vmax=1.0,
norm = colors.BoundaryNorm(mylevs, ncolors=len(mylevs)-1, clip=False))
# create colorbar
cbar = plt.colorbar(C1, orientation="horizontal", extend='max')
cbar.ax.tick_params(labelsize=20)
cbar.set_label('Random field', size='xx-large')
I would like the colorbar to stop at 1.0, with an extend arrow pointing to the right, shaded by the last color of the colorbar. Thanks in advance for any help you can provide.
Link to example image produced by the above code
Does this solve it?
fig,ax = plt.subplots()
mylevs = [-1.0, -0.5, 0, 0.5, 1.0]
C1 = ax.contourf(X, Y, Z, cmap = cmap, vmin=-1.0, vmax=1.0,levels=mylevs,extend='both')
fig.colorbar(C1)

Modifying saved plot with matplotlib

I am having a problem right now. I have run an extremely heavy simulation and, thus, generated a plot with matplotlib containing the results and saved it (as .jpg). However, there are some elemnts of the plot I would like to change, such as labels size and one vertical line. Is there a straighforward way to do this using matplotlib? I know I could have stored the data and now just replot changing the parameters (and, actually, I have done this), but I was wondering whether there is an easier way. Maybe something like:
fig, ax = plt.figure(path_to_figure)
ax.set_ylabel("Y_label")
...
You can refer to below example, which gives you more idea on how you can do this while plotting everything.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline
plt.rc('text', usetex=True)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=0)
If you want to make all the fonts bold, you can also use below code to make everything bold:
font = {'weight' : 'bold',
'size' : 14 }
plt.rc('font', **font)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
plt.xlabel("This is X-label.", fontsize=12)
plt.ylabel("This is Y-label.", fontsize=16)
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=(1.15,0.2))

Matplotlib Colorbar - Non-Linear

I have created a diverging colorbar with it's midpoint normalised at the median value of the data.
I would like to extend the midpoint color ('white') and apply it to the range (+- 15%) from the midpoint, and then have the diverging colorbar continue normally from that point.
My current colorbar is created using the following code:
#Initial ZValues contour plot
Colorbar_min = np.around(ZValues.min()*0.9,0)
Colorbar_max = np.around(ZValues.max()*1.1,0)
Colorbar_mid = np.median(ZValues)
#Colormap
cmap = plt.cm.seismic # define the colormap
cmaplist = [cmap(i) for i in range(cmap.N)] # extract all colors from the .seismic map
# create the new colourmap
cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N)
# define the bins and normalize
bounds = np.linspace(Colorbar_min, Colorbar_max, 30)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
Chosen_CS = ax.tricontourf(Chosen_tri_refi, Chosen_Z_refi, cmap=cmap, levels=bounds,
norm=MidpointNormalize(midpoint=Colorbar_mid, vmin=Colorbar_min, vmax=Colorbar_max))
#Create a second axis for the colorbar
ax2 = fig.add_axes([0.87, 0.12, 0.04, 0.75]) #The numbers in the square brackets of add_axes refer to [left, bottom, width, height], where the coordinates are just fractions that go from 0 to 1 of the plotting area.
cb = mpl.colorbar.ColorbarBase(ax2, cmap=cmap, norm=MidpointNormalize(midpoint=Colorbar_mid, vmin=Colorbar_min, vmax=Colorbar_max),spacing='uniform', ticks=bounds, boundaries=bounds, format='%1i')
cb.set_label('ZValues', fontsize=7, weight="bold", rotation=270, labelpad=14)
You can create your custom colormap with a white midrange color at 50 ± 15 % by inserting a white section (i.e. 1. for all three colors) from .5 - .15 to .5 + .15 like so:
import matplotlib.pyplot as plt
import matplotlib as mpl
seismic_cdict = plt.cm.seismic._segmentdata
cdict = dict()
for c in seismic_cdict:
cdict[c] = [t for t in seismic_cdict[c] if t[0] < .35] + \
[(.35,1.,1.), (.65,1.,1.)] + \
[t for t in seismic_cdict[c] if t[0] > .65]
custom_cmap = mpl.colors.LinearSegmentedColormap('Custom cmap', cdict)
fig, ax = plt.subplots(figsize=(8, 1))
mpl.colorbar.ColorbarBase(ax, cmap=custom_cmap, orientation='horizontal')

Change colour scale increments in Python

I have created a frequency time spectrogram plot seen below.
I want to edit the colour scale so that the higher frequencies shown from 20 seconds are more prominent. I think having smaller increments at the lower end of the colour scale (blues) would achieve this but am not sure how to do it. Any help would be great!
Here is what I have so far:
import numpy as np
import matplotlib.pyplot as plt
from obspy.core import read
from obspy.signal.tf_misfit import cwt
import pylab
tr = read("whole.sac")[0]
npts = tr.stats.npts
dt = tr.stats.delta
t = np.linspace(0, dt * npts, npts)
f_min = 1
f_max = 10
scalogram = cwt(tr.data, dt, 8, f_min, f_max)
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.7, 0.60])
ax2 = fig.add_axes([0.1, 0.75, 0.75, 0.2])
ax3 = fig.add_axes([0.83, 0.1, 0.03, 0.6])
img = ax1.imshow(np.abs(scalogram)[-1::-1], extent=[t[0], t[-1], f_min, f_max],
aspect='auto', interpolation="nearest")
ax1.set_xlabel("Time after %s [s]" % tr.stats.starttime)
ax1.set_ylabel("Frequency [Hz]")
ax1.set_yscale('linear')
ax2.plot(t, tr.data, 'k')
pylab.xlim([30,72])
fig.colorbar(img, cax=ax3)
plt.show()
You could try other colormaps or make you own according to this recipe.
Or you may want to filter the data to set all values above a given threshold (e.g. 60) to the threshold value. This would use the entire range of the colormap on the range of interest. You can easily use np.clip() to do this.
So...
np.abs(scalogram)[-1::-1]
becomes
np.clip(np.abs(scalogram)[-1::-1], 0, 100)
to clip between 0 and 100.

Categories