0

I am trying to make a plot with a double y-axis and a colorbar next to it. However, the label of my second y-axis won't appear and I am having a bit of trouble positioning the colorbar right. My code can be found below:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm #colormap
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.transforms import Transform
from matplotlib.ticker import (
    AutoLocator, AutoMinorLocator)

x = np.linspace(-2.5, 2.5, 100)
y = np.linspace(-2.5, 2.5, 100)
xx, yy = np.meshgrid(x, y)

std_x = 1.
std_y = 1.
A = 10
Gaussian = A * np.exp(-(xx**2/(2*std_x**2) + yy**2/(2*std_y**2)))

fig = plt.figure()
ax = plt.subplot(111)
im = plt.imshow(Gaussian)
ax.set_xlabel("[m]")
ax.set_ylabel("[m]")
ax_new = ax.twinx().twiny()
ticks = np.linspace(0, 1, 5)
ticklabels = np.round(np.arctan(ticks*512/1e3)*3600) #hardcoded
plt.xticks(ticks, ticklabels)
plt.yticks(ticks, ticklabels)
ax_new.set_xlabel("arcsec")
ax_new.set_ylabel("arcsec")
ax_new.set_title("Atmosphere Structure", fontsize=20)
divider = make_axes_locatable(ax_new)
# on the figure total in percent [left, bottom, width, height]
cax = fig.add_axes([0.95, 0.1, 0.02, 0.75])
colorbar_1 = fig.colorbar(im, cax=cax)
colorbar_1.set_label('pwv in mm', labelpad=-10, y=1.05, rotation=0)
plt.show()

With a lot of finetuning, the plot now looks like this (I used a Gaussian for simplicity):

enter image description here

The colorbar is still slightly misaligned and the way I am positioning the colorbar now feels like a quick fix rather than a good solution. Does anyone know how to do this in a 'cleaner'/better way? And does someone know how to get the ylabel on the right to appear (ax_new.set_ylabel("arcsec"))?

I tried out the solution proposed in Plot multiple y-axis AND colorbar in matplotlib, but somehow it does not work for me.

Edit: When I replace cax = fig.add_axes([0.95, 0.1, 0.02, 0.75]) with cax = divider.append_axes("right", size="4%", pad="10%"), I get the following image:

enter image description here

Edit 2:

I have also tried using secondary_xaxis, which can be seen in the code below

def m2deg(x):
    arcsec = np.arctan(x*512/1e3)*180/np.pi
    return arcsec

def deg2m(x):
    m = np.tan(x/(512)*np.pi/180)*1e3
    return m

secax = ax.secondary_xaxis('top', functions=(m2deg, deg2m))
secax2 = ax.secondary_yaxis('right', functions=(m2deg, deg2m))
secax.set_label("degrees")
secax2.set_label("degrees")

It gives me the picture below: enter image description here

again, the axis labels don't show and the tickmarks do not have the right values (tan(0/(512)*pi/180)*1e3 = 0, and arctan(0*512/1e3)*180/pi = 0, so all 0's must be aligned).

Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
Esmee
  • 93
  • 11
  • Check https://matplotlib.org/3.1.1/gallery/axes_grid1/demo_colorbar_with_axes_divider.html – ImportanceOfBeingErnest Dec 06 '19 at 13:31
  • Thanks for your comment. I already tried it that way and edited the question to show what happens if I try that. Do you know why it looks so weird? – Esmee Dec 06 '19 at 14:17
  • Oh yes. Since you create 3 axes (the original, the twinx and twiny) you would need one divider per each of those. Seems quite cumbersome. Did you try using `secondary_xaxis` instead of twinx ? – ImportanceOfBeingErnest Dec 06 '19 at 15:13
  • I also tried that! But somehow, it does not calculate the tickmarks right. You can see it in my new edit. For example, the 0's on both axes are misaligned, which should not be the case when going from m to degrees (see the m2deg function in my code) – Esmee Dec 06 '19 at 15:57
  • Sure, because you faked the labels. Plot the image in the original units which you want to convert using the `extent` keyword. – ImportanceOfBeingErnest Dec 06 '19 at 16:17
  • Hi, I don't really get what you mean. Do you have a link with some explanation/an example of how and where to use the `extent` keyword? – Esmee Dec 06 '19 at 16:47
  • E.g. [here](https://stackoverflow.com/a/44260928/4124317) – ImportanceOfBeingErnest Dec 06 '19 at 16:49

1 Answers1

0

Try constrained_layout?

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm #colormap

x = np.linspace(-2.5, 2.5, 100)
y = np.linspace(-2.5, 2.5, 100)
xx, yy = np.meshgrid(x, y)

std_x = 1.
std_y = 1.
A = 10
Gaussian = A * np.exp(-(xx**2/(2*std_x**2) + yy**2/(2*std_y**2)))

fig, ax = plt.subplots(constrained_layout=True)
im = ax.imshow(Gaussian)
ax_new = ax.twinx().twiny()
ticks = np.linspace(0, 1, 5)
ticklabels = np.round(np.arctan(ticks*512/1e3)*3600) #hardcoded
plt.xticks(ticks, ticklabels)
plt.yticks(ticks, ticklabels)
ax_new.set_xlabel("arcsec")
ax_new.set_ylabel("arcsec")
ax_new.set_title("Atmosphere Structure", fontsize=20)
colorbar_1 = fig.colorbar(im)
colorbar_1.set_label('pwv in mm', labelpad=-10, y=1.05, rotation=0)
plt.show()

enter image description here

Jody Klymak
  • 4,979
  • 2
  • 15
  • 31