Create a scaled secondary y-axis in Matplotlib - python

The objective is to plot a scatter plot and create secondary y-axis. Here, the secondary y-axis is just scaled copy of the original scatter plot.
Assume the scaling can be calculated
y2=y1/2.5
where, y1 and y2 is the y axis from the scatter plot,and scaled copy of the original scatter plot, respectively.
This can be visualized as below.
However, using the code below,
import numpy as np
import matplotlib.pyplot as plt
x, y = np.random.random((2,50))
fig, ax1 = plt.subplots()
ax1.scatter(x, y*10, c='b')
ax2 = ax1.twinx()
y2=y/2.5
ax2.plot(1, 1, 'w-')
ax1.set_xlabel('X1_z')
ax1.set_ylabel('x1_y', color='g')
ax2.set_ylabel('x2_y', color='r')
which produced
There are three issues
The secondary y-axis is not scaled properly
As expected but not intended the existence multiple horizontal line root from the secondary y-axis
Is there a possible way to create the scaled y-axis without the need of the line ax2.plot(1, 1, 'w-')
May I know how to handle this?

As suggested in the comment, using secondary_yaxis
x, y = np.random.random((2,50))
fig, ax = plt.subplots()
ax.scatter(x, y*10, c='b')
ax.set_xlabel('X1_z')
ax.set_ylabel('x1_y')
ax.set_title('Adding secondary y-axis')
def a2b(y):
return y/2.5
def b2a(y):
return 2.5*y
secax = ax.secondary_yaxis('right', functions=(a2b,b2a))
secax.set_ylabel('x2_y')
plt.show()
Produced

Related

Prevent grid lines from twin axis to be drawn on top of artists from original axis

I have an axis on which I plot some data and I have another twin axis which I use to draw grid lines at specific tick positions (other than the ticks of the original axis):
import matplotlib.pyplot as plt
import numpy as np
f, ax = plt.subplots()
ax.set_xlim([0, 1])
ax2 = ax.twiny()
ax2.set_xlim([0, 1])
ax2.set_xticks(np.linspace(0, 1, 11))
ax2.xaxis.grid()
x = np.linspace(0, 1, 100)
ax.plot(x, np.sin(x), label='sin(x)')
ax.legend()
plt.show()
Now this has the undesirable effect that the grid lines of the twin axes are drawn on top of the legend and line plot of the original axis. As far as I understand this is because matplotlib draws the axes in the order they were created and for that reason zorder won't help (because zorder only specifies the order among the artists of a single axis).
I know I could plot the data on the twin axis ax2 instead (followed by ax2.legend()) but I'd prefer to have the setup as is. Instead changing the order in which the two axes are drawn should solve the problem, but I couldn't figure out how to do that. There is f.get_axes() which seems to return the axes in the order they were created but no option to revert it.
Or maybe there exists even another solution?
You can change the zorder of the axes themselves.
ax.set_zorder(2)
ax2.set_zorder(1)
ax.patch.set_visible(False)

Z-order across axes when using matplotlib's twinx [duplicate]

In pyplot, you can change the order of different graphs using the zorder option or by changing the order of the plot() commands. However, when you add an alternative axis via ax2 = twinx(), the new axis will always overlay the old axis (as described in the documentation).
Is it possible to change the order of the axis to move the alternative (twinned) y-axis to background?
In the example below, I would like to display the blue line on top of the histogram:
import numpy as np
import matplotlib.pyplot as plt
import random
# Data
x = np.arange(-3.0, 3.01, 0.1)
y = np.power(x,2)
y2 = 1/np.sqrt(2*np.pi) * np.exp(-y/2)
data = [random.gauss(0.0, 1.0) for i in range(1000)]
# Plot figure
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax2.hist(data, bins=40, normed=True, color='g',zorder=0)
ax2.plot(x, y2, color='r', linewidth=2, zorder=2)
ax1.plot(x, y, color='b', linewidth=2, zorder=5)
ax1.set_ylabel("Parabola")
ax2.set_ylabel("Normal distribution")
ax1.yaxis.label.set_color('b')
ax2.yaxis.label.set_color('r')
plt.show()
Edit: For some reason, I am unable to upload the image generated by this code. I will try again later.
You can set the zorder of an axes, ax.set_zorder(). One would then need to remove the background of that axes, such that the axes below is still visible.
ax2 = ax1.twinx()
ax1.set_zorder(10)
ax1.patch.set_visible(False)

Change the color of the plot depending on the density (stored in an array) in line plot in matplotlib

I have a file with three columns, lets say, x y z. I need to plot x Vs y but I need to change the color of that (x,y) value depending on its density (stored in z column). I understand that I need to use color map and have to map the values of the color with the z array. I can do that via scatter plot as also shown in this post: How can I make a scatter plot colored by density in matplotlib?
But I do not need the scatter plot, I need the points to be connected, ie I need a line plot. Can it be done in line plot?
It's not possible to connect points from a scatter plot directly. But the same effect can be achieved by plotting a line behind the scatter points.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,6)
y = np.sin(x)
z = 0.5+np.random.rand(len(x))
fig, ax = plt.subplots()
ax.plot(x, y, color="k", marker=None, zorder=0)
sc = ax.scatter(x, y, c=z, s=100, edgecolor='',zorder=3)
plt.colorbar(sc, label="Density")
plt.show()

Two y axes for a single plot

I'm trying to create a plot with two Y axes (left and right) for the same data, that is, one is a scaled version of the other. I would like also to preserve the tick positions and grid positions, so the grid will match the ticks at both sides.
I'm trying to do this by plotting twice the same data, one as-is and the other scaled, but they are not coincident.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(17, 27, 0.1)
y1 = 0.05 * x + 100
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y1, 'g-')
ax2.plot(x, y1/max(y1), 'g-')
ax1.set_xlabel('X data')
ax1.set_ylabel('Y data', color='g')
ax2.set_ylabel('Y data normalized', color='b')
plt.grid()
plt.show()
Any help will be appreciated.
Not sure if you can achieve this without getting ugly-looking numbers on your normalized axis. But if that doesn't bother you, try adding this to your code:
ax2.set_ylim([ax1.get_ylim()[0]/max(y1),ax1.get_ylim()[1]/max(y1)])
ax2.set_yticks(ax1.get_yticks()/max(y1))
Probably not the most elegant solution, but it scales your axis limits and tick positions similarly to what you do with the data itself so the grid matches both axes.

Use matplotlib: plot error bars on two y axes

I'd like to plot a series with x and y error bars, then plot a second series with x and y error bars on a second y axis all on the same subplot. Can this be done with matplotlib?
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.errorbar(voltage, dP, xerr=voltageU, yerr=dPU)
ax2 = plt.errorbar(voltage, current, xerr=voltageU, yerr=currentU)
plt.show()
Basically, I'd like to put ax2 on a second axis and have the scale on the right side.
Thanks!
twinx() is your friend for adding a secondary y-axis, e.g.:
import matplotlib.pyplot as pl
import numpy as np
pl.figure()
ax1 = pl.gca()
ax1.errorbar(np.arange(10), np.arange(10), xerr=np.random.random(10), yerr=np.random.random(10), color='g')
ax2 = ax1.twinx()
ax2.errorbar(np.arange(10), np.arange(10)+5, xerr=np.random.random(10), yerr=np.random.random(10), color='r')
There is not a lot of documentation except for:
matplotlib.pyplot.twinx(ax=None)
Make a second axes that shares the x-axis. The new axes will overlay ax (or the current axes if ax is None). The ticks for ax2 will be placed on the right, and the ax2 instance is returned.
I was struggling to share the x-axis, but thank you #Bart you saved me!
The simple solution is use twiny instead of twinx
ax1.errorbar(layers, scores_means[str(epoch)][h,:],np.array(scores_stds[str(epoch)][h,:]))
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_xlabel('depth', color='b')
ax1.tick_params('x', colors='b')
ax2 = ax1.twiny()
ax2.errorbar(hidden_dim, scores_means[str(epoch)][:,l], np.array(scores_stds[str(epoch)][:,l]))
ax2.set_xlabel('width', color='r')
ax2.tick_params('x', colors='r')
fig.tight_layout()
plt.show()

Categories