Matplotlib: How to copy a contour plot to another figure? - python-3.x

I have a figure with many different plots (contour plots and lots of other stuff). I want to extract the contour plot to another single figure to see more details. But I fail how to do so.
Have a look on this code:
import numpy as np
from matplotlib import gridspec as gs, pyplot as plt
# Figure 1 with many different plots.
fig1 = plt.figure()
gridSpec = gs.GridSpec(2, 3)
for i in range(6):
fig1.add_subplot(gridSpec[i])
# Create contour plot
x = np.arange(-3.0, 3.0, 0.02)
y = np.arange(-2.0, 2.0, 0.01)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) ** 4
# Plot it to a particular axes.
ax1 = fig1.axes[2]
contour = ax1.contour(X, Y, Z)
# Try to copy the contour plot to another figure (with only 1 subplot).
fig2, ax2 = plt.subplots()
# How to copy the content of ax1 to ax2?
plt.show()
This will give me the following:
I want to create a second figure with only 1 subplot and its content should be the same as you can see in top right corner of the first figure with 6 subplots.
First thing I tried was
ax2.add_collection(contour.collections[1])
but I got the error message
RuntimeError: Can not put single artist in more than one figure
This is because the content is already plottet to figure 1, so it is not possible to plot it to figure 2 as well. So I tried to make a copy of the contour plot:
from copy import deepcopy
ax2.add_collection(deepcopy(contour.collections[1]))
But this will get me a new error that copiing is not possible ...
NotImplementedError: TransformNode instances can not be copied. Consider using frozen() instead.
So .. what can I do? Any ideas for that problem? :)
Thanks a lot!
(Python 3.7.4, Matplotlib 3.1.1)

Related

Matplotlib - maintain plot size of uneven subplots

I've been creating uneven subplots in matplotlib based on this question. The gridspec solution (third answer) worked a little better for me as it gives a bit more flexibility for the exact sizes of the subplots.
When I add a plot of a 2D array with imshow() the affected subplot is resized to the shape of the array. Is there any way to avoid that and keep the subplot-sizes (or rather aspect-ratio) fixed?
Here's the example code and the resulting image with the subplot-sizes I'm happy with:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec
# generate data
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# plot
fig = plt.figure(figsize=(12, 9))
gs = gridspec.GridSpec(20, 20)
ax1 = fig.add_subplot(gs[0:5,0:11])
ax1.plot(x, y)
ax2 = fig.add_subplot(gs[6:11,0:11])
ax2.plot(y, x)
ax3 = fig.add_subplot(gs[12:20,0:11])
ax3.plot(y, x)
ax4 = fig.add_subplot(gs[0:9,13:20])
ax4.plot(x, y)
ax5 = fig.add_subplot(gs[11:20,13:20])
ax5.plot(y, x)
plt.show()
This is what happens if I additionally plot data from a 2D array with the following lines (insert before plt.show):
2Ddata = np.arange(0, 10, 0.1).reshape(10, 10)
im = ax3.imshow(2Ddata, cmap='rainbow')
How can I restore the original size of the subplot from ax3 (lower left corner)?
Including the line ax3.set_aspect('auto') seems to have solved the issue.

How to align heights and widths subplot axes with gridspec and matplotlib?

I am trying to use matplotlib with gridspec to create a subplot such that the axes are arranged to look similar to the figure below; the figure was taken from this unrelated question.
My attempt at recreating this axes arrangement is below. Specifically, my problem is that the axes are not properly aligned. For example, the axis object for the blue histogram is taller than the axis object for the image with various shades of green; the orange histogram seems to properly align in terms of width, but I attribute this to luck. How can I properly align these axes? Unlike the original figure, I would like to add/pad extra empty space between axes such that there borders do not intersect; the slice notation in the code below does this by adding a blank row/column. (In the interest of not making this post longer than it has to be, I did not make the figures "pretty" by playing with axis ticks and the like.)
Unlike the original picture, the axes are not perfectly aligned. Is there a way to do this without using constrained layout? By this, I mean some derivative of fig, ax = plt.subplots(constrained_layout=True)?
The MWE code to recreate my figure is below; note that there was no difference between ax.imshow(...) and ax.matshow(...).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
## initialize figure and axes
fig = plt.figure()
gs = fig.add_gridspec(6, 6, hspace=0.2, wspace=0.2)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
## generate data
x = np.random.normal(loc=50, scale=10, size=100)
y = np.random.normal(loc=500, scale=50, size=100)
## get singular histograms
x_counts, x_edges = np.histogram(x, bins=np.arange(0, 101, 5))
y_counts, y_edges = np.histogram(y, bins=np.arange(0, 1001, 25))
x_mids = (x_edges[1:] + x_edges[:-1]) / 2
y_mids = (y_edges[1:] + y_edges[:-1]) / 2
## get meshed histogram
sample = np.array([x, y]).T
xy_counts, xy_edges = np.histogramdd(sample, bins=(x_edges, y_edges))
## subplot histogram of x
ax_bottom.bar(x_mids, x_counts,
width=np.diff(x_edges),
color='darkorange')
ax_bottom.set_xlim([x_edges[0], x_edges[-1]])
ax_bottom.set_ylim([0, np.max(x_counts)])
## subplot histogram of y
ax_left.bar(y_mids, y_counts,
width=np.diff(y_edges),
color='steelblue')
ax_left.set_xlim([y_edges[0], y_edges[-1]])
ax_left.set_ylim([0, np.max(y_counts)])
## subplot histogram of xy-mesh
ax_big.imshow(xy_counts,
cmap='Greens',
norm=Normalize(vmin=np.min(xy_counts), vmax=np.max(xy_counts)),
interpolation='nearest',
origin='upper')
plt.show()
plt.close(fig)
EDIT:
One can initialize the axes by explicitly setting width_ratios and height_ratios per row/column; this is shown below. This doesn't affect the output, but maybe I'm using it incorrectly?
## initialize figure and axes
fig = plt.figure()
gs = gridspec.GridSpec(ncols=6, nrows=6, figure=fig, width_ratios=[1]*6, height_ratios=[1]*6)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
The problem is with imshow, which resizes the axes automatically to maintain a square pixel aspect.
You can prevent this by calling:
ax_big.imshow(..., aspect='auto')

making multiple plot at the same time in python3

I have a list and a python array like these 2 examples:
example:
Neg = [37.972200755611425, 32.14963079785344]
Pos = array([[15.24373185, 13.66099865, 11.86959384, 9.72792045, 7.12928302, 6.04439412],[14.5235007 , 13. , 11.1792871 , 9.14974712, 6.4429435 , 5.04439412]
both Neg and Pos have 2 elements (in this example) therefore I would like to make 2 separate plots (pdf file) for every element.
in every plot there would be 2 lines:
1- comes from Pos and is a line plot basically which is made of all the elements in the sub-list.
2- comes from Neg and is a horizontal line on the y-axis.
I am trying to do that in a for loop for all elements at the same time. to do so, I made the following code in python but it does not return what I would like to get. do you know how to fix it ?
for i in range(len(Neg)):
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(concentration, Pos[i], label='gg')
plt.axhline(y=Neg[i], color='b', linestyle='-')
ax.legend()
ax.set_xlabel("log2 concentration")
ax.set_ylabel("log2 raw counts")
ax.set_ylim(0, 40)
plt.savefig(f'{i}.pdf')
Not quite sure exactly what you want but this code creates two subplots of the data in the way I think you're describing it:
import numpy as np
from matplotlib import pyplot as plt
Neg = [37.972200755611425, 32.14963079785344]
Pos = np.array([[15.24373185, 13.66099865, 11.86959384, 9.72792045, 7.12928302, 6.04439412],[14.5235007 , 13. , 11.1792871 , 9.14974712, 6.4429435 , 5.04439412]])
fig = plt.figure()
for i in range(len(Neg)):
ax = fig.add_subplot(2,1,i+1)
ax.plot(Pos[i], label='gg')
plt.axhline(y=Neg[i], color='b', linestyle='-')
ax.legend()
ax.set_xlabel("log2 concentration")
ax.set_ylabel("log2 raw counts")
ax.set_ylim(0, 40)
plt.subplots_adjust(hspace=1.0)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
fig.savefig(f'{i}.pdf', bbox_inches=extent.expanded(1.2, 1.9))
Edited the code to save each subplot individually to file by grabbing a specific part of the plot for saving, as used in this question: Save a subplot in matplotlib.
Also included some additional spacing between each subplot by calling subplots_adjust(), so that each subplot can be saved to individual files without any detail from the other subplots being included. This might not be the best way of doing what you want, but I think it will do what you want now.
Alternatively, if you're not set on using subplots, you could always just use a plot per element:
fig = plt.figure()
for i in range(len(Neg)):
plt.plot(Pos[i], label='gg')
plt.axhline(y=Neg[i], color='b', linestyle='-')
plt.legend()
plt.xlabel("log2 concentration")
plt.ylabel("log2 raw counts")
plt.ylim(0, 40)
fig = plt.gcf()
fig.savefig(f'{i}.pdf')
plt.show()

matplotlib.pyplot: create a subplot of stored plots

python 3.6 om mac
matplotlib 2.1.0
using matplotlib.pyplot (as plt)
Let's say i have a few plt.figures() that i appended into a list called figures as objects. When in command line i do: figures[0]it produces the plot for the index 0 of the list figures.
However, how can i arrange to have all the plots in figures to be in a subplot.
# Pseudo code:
plt.figure()
for i, fig in enumerate(figures): # figures contains the plots
plt.subplot(2, 2, i+1)
fig # location i+1 of the subplot is filled with the fig plot element
So as a result, i would a 2 by 2 grid that contains each plot found in figures.
hoping this makes sense.
A figure is a figure. You cannot have a figure inside a figure. The usual approach is to create a figure, create one or several subplots, plot something in the subplots.
In case it may happen that you want to plot something in different axes or figures, it might make sense to wrap the plotting in a function which takes the axes as argument.
You could then use this function to plot to an axes of a new figure or to plot to an axes of a figure with many subplots.
import numpy as np
import matplotlib.pyplot as plt
def myplot(ax, data_x, data_y, color="C0"):
ax.plot(data_x, data_y, color=color)
ax.legend()
x = np.linspace(0,10)
y = np.cumsum(np.random.randn(len(x),4), axis=0)
#create 4 figures
for i in range(4):
fig, ax = plt.subplots()
myplot(ax, x, y[:,i], color="C{}".format(i))
# create another figure with each plot as subplot
fig, ax = plt.subplots(2,2)
for i in range(4):
myplot(ax.flatten()[i], x, y[:,i], color="C{}".format(i))
plt.show()

Plotting with specific window in matplotlib

I'm trying to print the error ratio of the Hilbert matrix.
I'm trying to make the x axis 3 → 9 and the y axis rather for a large range:
8.71244799e+01 6.44297999e+02 4.50711567e+03 3.04673758e+04
2.01144882e+05 1.30525476e+06 8.35964228e+06
I'm struggling adjust the window, or plotting correctly for that.
Any help would be great!
Here is my attempt at plotting (something)
import matplotlib.pyplot as plt
plot = 6
plt.xlabel ('n')
plt.ylabel ('Error Ration')
fig = plt.figure()
ax = fig.add_subplot(111)
for a in range (0, plot, 1):
y = xratio[a]
x = plot + 3
ax.plot(x,y, mfc='orange', mec='orange', marker='.')
plt.show()
I think you just need to use:
ax.set_xlim([x_min, x_max])
ax.set_ylim([y_min, y_max])

Resources