How to define zorder when using 2 y-axis? - python

I plot using two y-axis, on the left and the right of a matplotlib figure and use zorder to control the position of the plots. I need to define the zorder across axes in the same figure.
Problem
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10,0.01)
fig, ax1 = plt.subplots( 1, 1, figsize=(9,3) )
ax1.plot( x, np.sin(x), color='red', linewidth=10, zorder=1 )
ax2 = ax1.twinx()
ax2.plot( x, x, color='blue', linewidth=10, zorder=-1)
In the previous diagram, I would expect the blue line to appear behind the red plot.
How do I control the zorder when using twin axes?
I am using:
python: 3.4.3 + numpy: 1.11.0 + matplotlib: 1.5.1

This should work
ax1.set_zorder(ax2.get_zorder()+1)
ax1.patch.set_visible(False)

the following codes works
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker as tick
x = np.arange(-10,10,0.01)
plt.figure(figsize=(10, 5))
fig = plt.subplot(111)
"""be attention to here. it's fig.plot, not ax1.plot
if you write ax1.plot, then it does not work.
"""
fig.plot(x, x, color ='blue', linewidth =10)
ax2 = fig.twinx()
ax2.plot(x, np.sin(x), color='red', linewidth =10)
"""
It looks like the two axes have separate z-stacks.
The axes are z-ordered with the most recent axis on top
"""
fig.set_zorder(ax2.get_zorder()+1)
fig.patch.set_visible(False)
plt.show()

It looks like the two axes have separate z-stacks. The axes are z-ordered with the most recent axis on top, so you need to move the curve you want on top to the last axis you create:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10,0.01)
fig, ax1 = plt.subplots( 1, 1, figsize=(9,3) )
ax1.plot( x, x, color='blue', linewidth=10 )
ax2 = ax1.twinx()
ax2.plot( x, np.sin(x), color='red', linewidth=10 )

Related

Relative size of subplots in matplotlib

I am trying to achieve generate plot made of subplots: plt.plot() and plt.matshow(), in which two plots had exactly the same size. What I mean is that lower border of one plot and lower borders of second plots were located on same "height". Similarly with the top border line. Current effect is presented on the plot below.
I haven't found any way in the available resources which would help me to achieve this effect. I would be grateful if you could help me.
shape=(2500, 2500)
matrix=np.zeros(shape)
print "Start of computing"
for x in range(shape[0]) :
for y in range(shape[1]) :
matrix[x, y]=shapeFuction((x-shape[0]/2)/13.0, (y-shape[1]/2)/13.0, 2.0e-4, 9e-5, 1.0)
print "Start of plotting"
fig=plt.figure()
ax = fig.add_subplot(1,2,2, aspect=1)
ax.matshow(matrix, cmap="autumn") #data[250:501,150:351])
ax.set(adjustable='datalim', aspect=1)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.xaxis.set_ticks_position('bottom')
ax.set(adjustable='box-forced') #adjustable='datalim'
ax.grid(b=False)
print "Start of plotting part 2"
ax = fig.add_subplot(1,2,1)
phase=(9.0e-5*np.power(np.arange(0, shape[1])-shape[1]/2,3 ))/7
g=ax.get_ylim()
asp=shape[1]/float(abs(g[0]-g[1]))
ax.plot(phase) #data[250:501,150:351])
ax.set(adjustable='box-forced')#, aspect=1.06/6.0) #adjustable='datalim''box-forced'
ax.set_xlabel("x")
ax.set_ylabel("Phase")
plt.savefig('testData-x3.png')
# plt.show()
One option you have is to set the aspect of the imshow plot (which is usually 1, such that pixels are squared), to "auto", ax2.imshow(z, cmap="autumn", aspect="auto").
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3,3)
y = np.tan(x)
z = np.random.rand(30,30)
fig, (ax, ax2) = plt.subplots(ncols=2)
ax.plot(x,y)
ax2.imshow(z, cmap="autumn", aspect="auto")
plt.show()
If instead you want to keep the aspect ratio of the image plot, you can change the aspect of the line plot by comparing the different axis limits,
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3,3)
y = np.tan(x)
z = np.random.rand(30,30)
fig, (ax, ax2) = plt.subplots(ncols=2)
ax.plot(x,y)
ax2.imshow(z, cmap="autumn")
ratio = np.diff(ax.get_ylim())[0]/np.diff(ax.get_xlim())[0]
ratio2 = np.diff(ax2.get_ylim())[0]/np.diff(ax2.get_xlim())[0]
aspect = ratio2/ratio
ax.set_aspect(float(np.abs(aspect)))
plt.show()

Merge matplotlib subplots with shared x-axis

I have two graphs to where both have the same x-axis, but with different y-axis scalings.
The plot with regular axes is the data with a trend line depicting a decay while the y semi-log scaling depicts the accuracy of the fit.
fig1 = plt.figure(figsize=(15,6))
ax1 = fig1.add_subplot(111)
# Plot of the decay model
ax1.plot(FreqTime1,DecayCount1, '.', color='mediumaquamarine')
# Plot of the optimized fit
ax1.plot(x1, y1M, '-k', label='Fitting Function: $f(t) = %.3f e^{%.3f\t} \
%+.3f$' % (aR1,kR1,bR1))
ax1.set_xlabel('Time (sec)')
ax1.set_ylabel('Count')
ax1.set_title('Run 1 of Cesium-137 Decay')
# Allows me to change scales
# ax1.set_yscale('log')
ax1.legend(bbox_to_anchor=(1.0, 1.0), prop={'size':15}, fancybox=True, shadow=True)
Now, i'm trying to figure out to implement both close together like the examples supplied by this link
http://matplotlib.org/examples/pylab_examples/subplots_demo.html
In particular, this one
When looking at the code for the example, i'm a bit confused on how to implant 3 things:
1) Scaling the axes differently
2) Keeping the figure size the same for the exponential decay graph but having a the line graph have a smaller y size and same x size.
For example:
3) Keeping the label of the function to appear in just only the decay graph.
Any help would be most appreciated.
Look at the code and comments in it:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec
# Simple data to display in various forms
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig = plt.figure()
# set height ratios for subplots
gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
# the first subplot
ax0 = plt.subplot(gs[0])
# log scale for axis Y of the first subplot
ax0.set_yscale("log")
line0, = ax0.plot(x, y, color='r')
# the second subplot
# shared axis X
ax1 = plt.subplot(gs[1], sharex = ax0)
line1, = ax1.plot(x, y, color='b', linestyle='--')
plt.setp(ax0.get_xticklabels(), visible=False)
# remove last tick label for the second subplot
yticks = ax1.yaxis.get_major_ticks()
yticks[-1].label1.set_visible(False)
# put legend on first subplot
ax0.legend((line0, line1), ('red line', 'blue line'), loc='lower left')
# remove vertical gap between subplots
plt.subplots_adjust(hspace=.0)
plt.show()
Here is my solution:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, subplot_kw=dict(frameon=False)) # frameon=False removes frames
plt.subplots_adjust(hspace=.0)
ax1.grid()
ax2.grid()
ax1.plot(x, y, color='r')
ax2.plot(x, y, color='b', linestyle='--')
One more option is seaborn.FacetGrid but this requires Seaborn and Pandas libraries.
Here are some adaptions to show how the code could work to add a combined legend when plotting a pandas dataframe. ax=ax0 can be used to plot on a given ax and ax0.get_legend_handles_labels() gets the information for the legend.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dates = pd.date_range('20210101', periods=100, freq='D')
df0 = pd.DataFrame({'x': np.random.normal(0.1, 1, 100).cumsum(),
'y': np.random.normal(0.3, 1, 100).cumsum()}, index=dates)
df1 = pd.DataFrame({'z': np.random.normal(0.2, 1, 100).cumsum()}, index=dates)
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True, gridspec_kw={'height_ratios': [2, 1], 'hspace': 0})
df0.plot(ax=ax0, color=['dodgerblue', 'crimson'], legend=False)
df1.plot(ax=ax1, color='limegreen', legend=False)
# put legend on first subplot
handles0, labels0 = ax0.get_legend_handles_labels()
handles1, labels1 = ax1.get_legend_handles_labels()
ax0.legend(handles=handles0 + handles1, labels=labels0 + labels1)
# remove last tick label for the second subplot
yticks = ax1.get_yticklabels()
yticks[-1].set_visible(False)
plt.tight_layout()
plt.show()

Python's matplotlib legend in separate axis with gridspec

Let suppose I have a matplotlib's gridspec instance in a python script. What I want to do is to create two axis and have the plot in one axis and the legend in the other one. Something like
import numpy as np
from matplotlib import gridspec, pyplot as plt
x = np.linspace(0,100)
y = np.sin(x)
gs = gridspec.GridSpec( 100, 100 )
ax1 = fig.add_subplot(gs[ :50, : ])
ax2 = fig.add_subplot(gs[ 55:, : ])
ax1.plot( s, y, label=r'sine' )
ax2.legend() # ?? Here I want legend of ax1
plt.show()
Is there any way of doing that?
You can grab the legend handles and labels from the first subplot using ax1.get_legend_handles_labels(), and then use them when you create the legend on the second subplot.
From the docs:
get_legend_handles_labels(legend_handler_map=None)
Return handles and labels for legend
ax.legend() is equivalent to:
h, l = ax.get_legend_handles_labels()
ax.legend(h, l)
import numpy as np
from matplotlib import gridspec, pyplot as plt
x = np.linspace(0, 100)
y = np.sin(x)
fig = plt.figure()
gs = gridspec.GridSpec(100, 100 )
ax1 = fig.add_subplot(gs[:50, :])
ax2 = fig.add_subplot(gs[55:, :])
ax1.plot(x, y, label=r'sine')
h, l = ax1.get_legend_handles_labels() # get labels and handles from ax1
ax2.legend(h, l) # use them to make legend on ax2
plt.show()

Position colorbar inside figure

I have a simple scatter plot where each point has a color given by a value between 0 and 1 set to a chosen colormap. Here's a MWE of my code:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
x = np.random.randn(60)
y = np.random.randn(60)
z = [np.random.random() for _ in range(60)]
fig = plt.figure()
gs = gridspec.GridSpec(1, 2)
ax0 = plt.subplot(gs[0, 0])
plt.scatter(x, y, s=20)
ax1 = plt.subplot(gs[0, 1])
cm = plt.cm.get_cmap('RdYlBu_r')
plt.scatter(x, y, s=20 ,c=z, cmap=cm)
cbaxes = fig.add_axes([0.6, 0.12, 0.1, 0.02])
plt.colorbar(cax=cbaxes, ticks=[0.,1], orientation='horizontal')
fig.tight_layout()
plt.show()
which looks like this:
The problem here is that I want the small horizontal colorbar position to the lower left of the plot but using the cax argument not only feels a bit hacky, it apparently conflicts with tight_layout which results in the warning:
/usr/local/lib/python2.7/dist-packages/matplotlib/figure.py:1533: UserWarning: This figure includes Axes that are not compatible with tight_layout, so its results might be incorrect.
warnings.warn("This figure includes Axes that are not "
Isn't there a better way to position the colorbar, ie without getting a nasty warning thrown at you whenever you run the code?
Edit
I wanted the colorbar to show only the max and min values, ie: 0 and 1 and Joe helped me do that by adding vmin=0, vmax=1 to scatter like so:
plt.scatter(x, y, s=20, vmin=0, vmax=1)
so I'm removing this part of the question.
One may use a mpl_toolkits.axes_grid1.inset_locator.inset_axes to place an axes inside another axes. This axes can be used to host the colorbar. Its position is relative the the parent axes, similar to how legends are placed, using a loc argument (e.g. loc=3 means lower left). Its width and height can be specified in absolute numbers (inches) or relative to the parent axes (percentage).
cbaxes = inset_axes(ax1, width="30%", height="3%", loc=3)
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
x = np.random.randn(60)
y = np.random.randn(60)
z = [np.random.random() for _ in range(60)]
fig = plt.figure()
gs = gridspec.GridSpec(1, 2)
ax0 = plt.subplot(gs[0, 0])
plt.scatter(x, y, s=20)
ax1 = plt.subplot(gs[0, 1])
cm = plt.cm.get_cmap('RdYlBu_r')
plt.scatter(x, y, s=20 ,c=z, cmap=cm)
fig.tight_layout()
cbaxes = inset_axes(ax1, width="30%", height="3%", loc=3)
plt.colorbar(cax=cbaxes, ticks=[0.,1], orientation='horizontal')
plt.show()
Note that in order to suppress the warning, one might simply call tight_layout prior to adding the inset axes.

How to plot with x-axis at the top of the figure?

I would like to ask how to produce a plot similar to that in the figure below? Basically, how to have x-axis at the top of the figure. Thanks
Image from: http://oceanographyclay1987.blogspot.com/2010/10/light-attenuation-in-ocean.html
Use
ax.xaxis.set_ticks_position("top")
For example,
import numpy as np
import matplotlib.pyplot as plt
numdata = 100
t = np.linspace(0, 100, numdata)
y = 1/t**(1/2.0)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.xaxis.set_ticks_position('top')
ax.yaxis.grid(linestyle = '-', color = 'gray')
ax.invert_yaxis()
ax.plot(t, y, 'g-', linewidth = 1.5)
plt.show()

Categories