Python's matplotlib legend in separate axis with gridspec - python

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

Related

Hiding axes values in Matplotlib

I want to hide the x,y axes values as highlighted in the figure. Is it possible to do it? I also attach the expected representation.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
n=3
X = np.arange(n)
Y = -X
x_sorted = np.sort(X)
y_sorted = np.sort(Y)
ax.set_xticks(x_sorted)
ax.set_yticks(y_sorted)
ax.set_xlim(x_sorted[0], x_sorted[-1])
ax.set_ylim(y_sorted[0], y_sorted[-1])
ax.grid()
ax.set_aspect('equal', 'box')
plt.show()
The expected representation is
You need to empty x and y tick labels from ax variable:
ax.set_yticklabels([])
ax.set_xticklabels([])

matplotlib: colorbar make subplots unequal size

I make two subplots with a common shared colorbar. So naturally I want to plot the colorbar only once.
However, when I do so, then my subplots become unequal in size.
How to place the colorbar outside the subplots on the right?
Minimal working example below
import numpy as np
from matplotlib import colors
import matplotlib.pyplot as plt
res = 100
x = np.linspace(0, 2*np.pi, res)
y = np.sin(x)
z = np.cos(x)
y2 = -np.sin(x)+0.4
z2 = 0.5*np.cos(2*x)
fig_width = 200/25.4
fig_height = 100/25.4
fig = plt.figure(figsize=(fig_width, fig_height))
gs = fig.add_gridspec(1, 2, wspace=0)
(ax, ax2) = gs.subplots(sharey='row')
images = []
images.append(ax.scatter(x, y, c=z))
images.append(ax2.scatter(x, y2, c=z2))
vmin = min(image.get_array().min() for image in images)
vmax = max(image.get_array().max() for image in images)
norm = colors.Normalize(vmin=vmin, vmax=vmax)
for im in images:
im.set_norm(norm)
cbar = fig.colorbar(images[0], ax=ax2)
cbar.set_label("mylabel", loc='top')
fig.tight_layout()
plt.show()
Try 1) pass the two axes as ax, and 2) move tight_layout before colorbar:
# other stuff
fig.tight_layout()
cbar = plt.colorbar(images[0], ax=(ax,ax2))
# other - other stuff
Output:

How to define zorder when using 2 y-axis?

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 )

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

Matplotlib: Plotting the same graph in two different figures without writting the "plot(x,y)" line twice

I have this simple code that plots exactly the same thing in two different figures (fig1 and fig2). However, I have to write the line ax?.plot(x, y) twice, once for ax1 and once for ax2. How can I have only one plot expression (having multiple redondant ones could be a source of troubles for my more complex code). Something like ax1,ax2.plot(x, y) ... ?
import numpy as np
import matplotlib.pyplot as plt
#Prepares the data
x = np.arange(5)
y = np.exp(x)
#plot fig1
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
#plot fig2
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
#adds the same fig2 plot on fig1
ax1.plot(x, y)
ax2.plot(x, y)
plt.show()
You can either add each axes to a list, like this:
import numpy as np
import matplotlib.pyplot as plt
axes_lst = []
#Prepares the data
x = np.arange(5)
y = np.exp(x)
#plot fig1
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
axes_lst.append(ax1)
#plot fig2
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
axes_lst.append(ax2)
for ax in axes_lst:
ax.plot(x, y)
plt.show()
or you can use this unsupported feature to pull all of the figures in pyplot. Taken from https://stackoverflow.com/a/3783303/1269969
figures=[manager.canvas.figure
for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
for figure in figures:
figure.gca().plot(x,y)
Without knowing about matplotlib, you could add all your axes (?) to a list:
to_plot = []
to_plot.append(ax1)
...
to_plot.append(ax2)
...
# apply the same action to each ax
for ax in to_plot:
ax.plot(x, y)
You could then add as many as you like, and the same thing will happen to each.

Categories