Ticks plotted with an offset in colorbar - python

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()

Related

Change colorbar ticks from powers of 10 to plain numbers

I am trying to read an .nc file and display the data on a map. I want the colorbar ticks to be not in powers of 10 scale, but rather in plain numbers, so from 0.1 to 10. Moreover, it will be welcome if I can format it so it goes from 0.1 to 10 in like 7 ticks, so the result is the same as in the attached picture.
Please note that I have not added the code snippet related to the data downloading, so the script is not runnable. If you cannot spot the mistake without running the code, please let me know, I will attach it so you can download the .nc file.
Here is the code I am using. Sorry for the redundant imports.
import xarray as xr
import cartopy
import matplotlib
from matplotlib.colors import LogNorm
from matplotlib.offsetbox import AnchoredText
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter
import sys
import os
#First we open the dataset and read the varaible of interest
ds = xr.open_dataset(OUTPUT_FILENAME)
chl = ds.CHL.sel(time=min_date)
#Setting the figure size and projection
fig = plt.figure(figsize=(15,15))
ax = plt.axes(projection=ccrs.PlateCarree())
#Adding coastlines and land
ax.coastlines(resolution="10m") #Coastline resolution
ax.set_extent([-9,2,35,37.6]) #Map extent
ax.add_feature(cartopy.feature.LAND) #Adding land
#Formatting colorbar <---------------------------------------------------DOES NOT WORK
ax.ticklabel_format(style='plain',useMathText=None)
#Adding gridlines
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
linewidth=0.5, color='black', alpha=0.7, linestyle='--')
gl.top_labels = False
gl.right_labels = False
#Adding lat/lon labels in the axes
ax.text(-0.07, 0.55, 'Latitude [deg]', va='bottom', ha='center',
rotation='vertical', rotation_mode='anchor',
transform=ax.transAxes)
ax.text(0.5, -0.2, 'Longitude [deg]', va='bottom', ha='center',
rotation='horizontal', rotation_mode='anchor',
transform=ax.transAxes)
#Adding (C) info to the figure
SOURCE = 'ICMAN CSIC'
text = AnchoredText('$\copyright$ {}'.format(SOURCE),
loc=1, prop={'size': 9}, frameon=True)
ax.add_artist(text)
#Drawing the plot
chl.plot(ax=ax, transform=ccrs.PlateCarree(),
vmin=0.1, vmax=10, extend='both', cbar_kwargs={'shrink': 0.2, 'pad':0.01},
cmap="jet", norm=LogNorm(vmax=10))
#Figure title
ax.set_title("Chlorophyll NN (mg/m$^{3}$) "+ min_date_h + " - " + max_date_h)
The colorbar should be the last axes in the figure (fig.axes[-1]).
You can manually set the colorbar's ticks and tick labels:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
X, Y = np.mgrid[-3:3:100j, -2:2:100j]
Z = 10*np.exp(-X**2 - Y**2)
fig, ax = plt.subplots()
pcm = ax.pcolor(X, Y, Z, norm=colors.LogNorm(vmin=.1, vmax=10), cmap='jet')
fig.colorbar(pcm, ax=ax)
cb = fig.axes[-1]
ticks = [.1,.2,.5,1,2,5,10]
cb.yaxis.set_ticks(ticks, labels=[f"{t:g}" for t in ticks])
cb.minorticks_off()
(prior to matplotlib 3.5.0 you must set ticks and labels separately).

How to center "hue" coloring using seaborn stripplot

This is my plot:
I would like the coloring to be centered at 0 within the plot. While I managed to have the legend centered at 0, this does not apply to the dots in the plot (i.e. I would expect them to be gray at the zero value).
This is my code which generates the plots:
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import seaborn as sns
def plot_jitter(df):
plot = sns.stripplot(x='category', y='overall_margin', hue='overall_margin', data=df,
palette='coolwarm_r',
jitter=True, edgecolor='none', alpha=.60)
plot.get_legend().set_visible(False)
sns.despine()
plt.axhline(0, 0,1,color='grey').set_linestyle("--")
#Drawing the side color bar
normalize = mcolors.TwoSlopeNorm(vcenter=0, vmin=df['overall_margin'].min(), vmax=df['overall_margin'].max())
colormap = cm.coolwarm_r
[plt.plot(color=colormap(normalize(x))) for x in df['overall_margin']]
scalarmappaple = cm.ScalarMappable(norm=normalize, cmap=colormap)
scalarmappaple.set_array(df['overall_margin'])
plt.colorbar(scalarmappaple)
By using sns.scatterplot instead of sns.stripplot you can use the c, norm and cmap parameters like so.
# Load demo data, scale `total_bill` to be in the range [0, 1]
tips = sns.load_dataset("tips")
tips["total_bill"] = tips["total_bill"].div(100)
Building the plot:
fig, ax = plt.subplots()
# Get/set params for the colour mapping
vcenter = 0.15
vmin, vmax = tips["total_bill"].min(), tips["total_bill"].max()
normalize = mcolors.TwoSlopeNorm(vcenter=vcenter, vmin=vmin, vmax=vmax)
colormap = cm.coolwarm_r
# plot with:
# - `c`: array of floats for colour mapping
# - `cmap`: the colourmap you want
#  - `norm`: to scale the data from `c`
sns.scatterplot(
x="day",
y="total_bill",
data=tips,
c=tips["total_bill"],
norm=normalize,
cmap=colormap,
ax=ax,
)
ax.axhline(vcenter, color="grey", ls="--")
# Tweak the points to mimic `sns.stripplot`
pts = ax.collections[0]
pts.set_offsets(pts.get_offsets() + np.c_[np.random.uniform(-.1, .1, len(tips)), np.zeros(len(tips))])
ax.margins(x=0.15)
scalarmappaple = cm.ScalarMappable(norm=normalize, cmap=colormap)
scalarmappaple.set_array(tips["total_bill"])
fig.colorbar(scalarmappaple)
Which produces:
The code to mimic stripplot is from seaborn's github issues

How to change color bar to align with main plot in Matplotlib?

When plotting matrix with imshow in Matplotlib, how to change colorbar legend bar size, location, font and other parameters?
Here I created an example code
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, title='example', cmap=plt.cm.Blues):
plt.imshow(mat, interpolation='nearest', cmap=cmap)
plt.grid(False)
plt.title(title)
plt.colorbar()
data = np.random.random((20, 20))
plt.figure(figsize=(8,8))
plt.tick_params(axis='both', which='major', labelsize=12)
plot_matrix(data)
In a real use case, I got complex labels and the legend bar becomes much higher then the matrix itself. I want to change the legend bar to make the plot more efficiently use the space.
I found a documentation for the matplotlib.pyplot.colorbar, however have not figure out a good way to set the size, location and font size for the color legend bar.
imshow enforces a 1:1 aspect (by default, but you can change it with aspect parameter), which makes things a little trickier. To always get consistent result, I might suggest manually specify the size of axes:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
def plot_matrix(mat, figsize, title='example', cmap=plt.cm.Blues):
f = plt.figure(figsize=figsize)
ax = plt.axes([0, 0.05, 0.9, 0.9 ]) #left, bottom, width, height
#note that we are forcing width:height=1:1 here,
#as 0.9*8 : 0.9*8 = 1:1, the figure size is (8,8)
#if the figure size changes, the width:height ratio here also need to be changed
im = ax.imshow(mat, interpolation='nearest', cmap=cmap)
ax.grid(False)
ax.set_title(title)
cax = plt.axes([0.95, 0.05, 0.05,0.9 ])
plt.colorbar(mappable=im, cax=cax)
return ax, cax
data = np.random.random((20, 20))
ax, cax = plot_matrix(data, (8,8))
Now you have the axis where the colorbar is plotted in, cax. You can do a lot of thing with that, say, rotate the labels, using plt.setp(cax.get_yticklabels(), rotation=45)

How to add colorbar to a histogram?

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,

getting a matplotlib colorbar tick outside data limits for use with boundaries keyword

I am trying to use a colorbar to label discrete, coded values plotted using imshow. I can achieve the colorbar that I want using the boundaries and values keywords, which makes the maximum value of the colorbar effectively 1 greater than the maximum value of the data being plotted.
Now I want ticks to be in the middle of each color range in the colorbar, but cannot specify a tick position for the largest color block in the colorbar, seemingly because it is outside of the data value limits.
Here's a quick block of code to demonstrate the problem:
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
ax.imshow(data[None], aspect='auto')
cax = fig.add_subplot(122)
cbar = fig.colorbar(ax.images[0], cax=cax, boundaries=[0,1,2,3,4], values=[0,1,2,3])
cbar.set_ticks([.5, 1.5, 2.5, 3.5])
cbar.set_ticklabels(['one', 'two', 'three', 'four'])
Note the missing tick where 'four' should be. What's the right way to do this?
To summarize, this works for me:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import cm
from matplotlib import colors
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cmap = cm.get_cmap('jet', 4)
bounds = np.arange(5)
vals = bounds[:-1]
norm = colors.BoundaryNorm(bounds, cmap.N)
ax.imshow(data[None], aspect='auto', interpolation='nearest', cmap=cmap, norm=norm)
cax = fig.add_subplot(122)
cbar = fig.colorbar(ax.images[0], cax=cax, boundaries=bounds, values=vals)
cbar.set_ticks(vals + .5)
cbar.set_ticklabels(['one', 'two', 'three', 'four'])
The solution was to specify the colormap explicitly for the image using get_cmap and bounded by BoundaryNorm. Then specifying the tick positions just works. The resulting plot is:
You are not using the same colormap in imshow and cbar. As your data and cbar is defined in the same way (same limits etc.) so you do not realize the inconsistency in the above example. You should define the colormap first.
Let's say you want to divide your data into 4-discrete colors, then you can use
import numpy as np
import pylab as plt
from matplotlib import colors, cm
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cax = fig.add_subplot(122)
cmap = cm.get_cmap('jet', 4) # 4 discrete color
im=ax.imshow(data[None], aspect='auto',cmap=cmap)
cbar = fig.colorbar(ax.images[0], cax=cax, cmap=cmap)
plt.show()
You can now put the ticks according to your need.
In case you want to define the bounds as well as the colors in these bounds then you can use ListedColormap as follows:
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cax = fig.add_subplot(122)
cmap = colors.ListedColormap(['b','g','y','r'])
bounds=[0,1,2,3,4]
norm = colors.BoundaryNorm(bounds, cmap.N)
im=ax.imshow(data[None], aspect='auto',cmap=cmap, norm=norm)
cbar = fig.colorbar(im, cax=cax, cmap=cmap, norm=norm, boundaries=bounds, ticks=[0.5,1.5,2.5,3.5],)
plt.show()

Categories