Add subplots to matplotlib figure after creation - python

I would like to create a figure with 2x2 subplots. Then with some input from the user (on a different thread), the figure changes to MxN group of sub plots without creating another figure box. Is this possible?
x = [1,2,3]
y = [1,2,3]
fig, axs = plt.subplots(222)
threadedPlotShow(ax, x, y) #in a different thread, shows figure with xy on each
#wait for user input
m = raw_input("enter rows")
n = raw_input("enter cols")
update_figure(x,y,M,N,fig)
def update_figure(self, x, y, M, N, fig):
ax=fig.add_subplot(nrows=M,ncols=N, index=M+N+1)
ax.plot(x,y)
plt.draw()
These posts do not help because it creates new figures (at least in my implementation, if they should't let me know and I will keep trying):
Dynamically add/create subplots in matplotlib
matplotlib dynamic number of subplot

user inputs which data to plot, then M,N are auto generated. We clear figure, add gridpsec, add subplot, plot data. Same figure is reused, just new data each time.
clear_figure()
gs = fig.add_gridspec(M, N, wspace=0.1)
ax = fig.add_subplot(gs[M,N])
ax.plot(x,y)

Related

How can i plot multiple graphs as subplot?

I have 5 graphs. My code continue with this way:
plots = zip(x,y)
def loop_plot(plots):
figs = {}
axs = {}
for idx, plot in enumerate(plots):
figs[idx] = plt.figure()
axs[idx] = figs[idx].add_subplot(111)
axs[idx].plot(plot[0],plot[1])
return figs, axs
figs,axs=loop_plot(plots)
This code create 5 different graph. BUt I would like to plot 5 graph in one figure. I mean, I would like to create 5 different figure into one code. How can I manage it? I have 5 different x and y dataset. how can I write subplot code with for loop?
You have to be careful about using the terms figure and axes when talking about matplotlib, as they mean slightly different things to normal English usage. An axes object is a pair of (x,y) axes, and a figure is a container that holds one or more axes. The reason I say that is because the code to solve your problem will be different if you want five different lines on one set of axes, or if you want one figure containing 5 separate axis, each with one line.
5 separate axes
def loop_plot1(plots):
shape = (2, 3) # Fix this to make it more general if you want to handle more than 6 plots!
list_ax = []
fig = plt.figure()
for i, plot in enumerate(plots):
idx = i + 1
list_ax.append(fig.add_subplot(shape[0], shape[1], idx)) # a more general way of writing, eg, add_subplot(231) etc.
list_ax[i].plot(plot[0], plot[1])
loop_plot1(plots)
5 lines on one axes
def loop_plot2(plots):
shape = (2, 3) # Fix this to make it more general if you want to handle more than 6 plots!
fig, ax = plt.subplots() # implicitly does fig = plot.figure() // fig.add_subplot(111)
for i, plot in enumerate(plots):
ax.plot(plot[0], plot[1])
loop_plot2(plots)

Python matplotlib: Save to pdf in multiple pages

I am trying to do the following:
I have created a figure, using matplotlib, with several subplots.
More specifically, 2x4 subplots
The output is great for showing it on the screen, but not for saving it to pdf.
If I just use save_fig, it prints a single page pdf document, with the 2x4 grid.
What I would like to do, is re-arrange my subplots, to let's say a 2x4 grid (choosing which subplot goes where, would be good, but not necessary) and printing it to a 2-page pdf with 4 subplots each. (in order to be able to fit it to A4 page size)
Is this possible?
Thank you in advanced!
As I needed something similar for my work, I put some effort into automating the process of grouping plots into figures depending on the display medium. At first I had the idea to do each plot only once and just add the subplots to the figures to be saved in the pdf, but sadly, according to a comment in this answer, this is not possible, so everything needs to be re-plotted. The code shows the general idea of how this can be automated using PdfPages:
from matplotlib import pyplot as plt
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages
def niter(iterable, n):
"""
Function that returns an n-element iterator, i.e.
sub-lists of a list that are max. n elements long.
"""
pos = 0
while pos < len(iterable):
yield iterable[pos:pos+n]
pos += n
def plot_funcs(x, functions, funcnames, max_col, max_row):
"""
Function that plots all given functions over the given x-range,
max_col*max_row at a time, creating all needed figures while doing
so.
"""
##amount of functions to put in one plot
N = max_col*max_row
##created figures go here
figs = []
##plotted-on axes go here
used_axes = []
##looping through functions N at a time:
for funcs, names in zip(niter(functions, N), niter(funcnames,N)):
##figure and subplots
fig, axes = plt.subplots(max_col, max_row)
##plotting functions
for name,func,ax in zip(names, funcs, axes.reshape(-1)):
ax.plot(x, func(x))
ax.set_title(name)
used_axes.append(ax)
##removing empty axes:
for ax in axes.reshape(-1):
if ax not in used_axes:
ax.remove()
fig.tight_layout()
figs.append(fig)
return figs
##some functions to display
functions = [
lambda x: x, lambda x: 1-x, lambda x: x*x, lambda x: 1/x, #4
np.exp, np.sqrt, np.log, np.sin, np.cos, #5
]
funcnames = ['x','1-x', 'x$^2$', '1/x', 'exp', 'sqrt', 'log', 'sin','cos']
##layout for display on the screen
disp_max_col = 3
disp_max_row = 2
##layout for pdf
pdf_max_col = 2
pdf_max_row = 4
##displaying on the screen:
x = np.linspace(0,1,100)
figs = plot_funcs(x, functions, funcnames, disp_max_row, disp_max_col)
plt.show()
##saving to pdf if user wants to:
answer = input('Do you want to save the figures to pdf?')
if answer in ('y', 'Y', 'yes', ''):
##change number of subplots
N = disp_max_col*disp_max_row
figs = plot_funcs(x, functions, funcnames, pdf_max_row, pdf_max_col)
##from https://matplotlib.org/examples/pylab_examples/multipage_pdf.html
with PdfPages('multipage_pdf.pdf') as pdf:
for fig in figs:
plt.figure(fig.number)
pdf.savefig()
The core function, plot_funcs takes max_col and max_row keywords and then creates figures with the according amount of subplots. It then loops through a given list of functions to be plotted, each on its own subplot. Unused subplots are removed. Finally a list of all figures is returned.
In my example, I have 9 different functions, which I first show on the screen in a 2x3 layout (making a total of two figures, one with 6 subplots and one with 3 subplots). If the user is happy, the plots are redone in a 2x4 layout (again two figures, but this time one with 8 subplots and 1 with 1 subplot) and then saved to a file called multipage_pdf.pdf, following the example in the documentation.
Tested on python 3.5
I would suggest to create 3 figures. One for showing and 2 for saving and plot the same data to them.
import matplotlib.pyplot as plt
import numpy as np
data = np.sort(np.cumsum(np.random.rand(24,16), axis=0), axis=0)
def plot(ax, x, y, **kwargs):
ax.plot(x,y, **kwargs)
colors = ["crimson", "indigo", "limegreen", "gold"]
markers = ["o", "", "s", ""]
lines = ["", "-", "", ":"]
# figure 0 for showing
fig0, axes = plt.subplots(nrows=2,ncols=4)
for i, ax in enumerate(axes.flatten()):
plot(ax, data[:,2*i], data[:,2*i+1], marker=markers[i%4], ls=lines[i%4],color=colors[i%4])
# figure 1 for saving
fig1, axes = plt.subplots(nrows=1,ncols=4)
for i, ax in enumerate(axes.flatten()):
plot(ax, data[:,2*i], data[:,2*i+1], marker=markers[i], ls=lines[i],color=colors[i])
#figure 2 for saving
fig2, axes = plt.subplots(nrows=1,ncols=4)
for i, ax in enumerate(axes.flatten()):
plot(ax, data[:,2*i+4], data[:,2*i+1+4], marker=markers[i], ls=lines[i],color=colors[i])
#save figures 1 and 2
fig1.savefig(__file__+"1.pdf")
fig2.savefig(__file__+"2.pdf")
#close figures 1 and 2
plt.close(fig1)
plt.close(fig2)
#only show figure 0
plt.show()

Plotting a continuous stream of data with MatPlotLib

I want to use MatPlotLib to plot a graph, where the plot changes over time. At every time step, an additional data point will be added to the plot. However, there should only be one graph displayed, whose appearance evolves over time.
In my test example, the plot is a simple linear plot (y = x). Here is what I have tried:
for i in range(100):
x = range(i)
y = range(i)
plt.plot(x, y)
plt.ion()
plt.show()
time.sleep(1)
However, what happens here is that multiple windows are created, so that by the end of the loop I have 100 windows. Also, I have noticed that for the most recent window, it is just a white window, and the plot only appears on the next step.
So, my two questions are:
1) How can I change my code so that only a single window is displayed, whose contents changes over time?
2) How can I change my code so that for the most recent timestep, the plot is actually displayed on the window, rather than it only displaying a white window?
Thanks!
(1)
You can set plt.ion() at the beginning and plot all graphs to the same window. Within the loop use plt.draw() to show the graph and plt.pause(t) to make a pause. Note that t can be very small, but the command needs to be there for the animation to work on most backends.
You might want to clear the axes before plotting new content using plt.gca().cla().
import matplotlib.pyplot as plt
plt.ion()
for i in range(100):
x = range(i)
y = range(i)
# plt.gca().cla() # optionally clear axes
plt.plot(x, y)
plt.title(str(i))
plt.draw()
plt.pause(0.1)
plt.show(block=True) # block=True lets the window stay open at the end of the animation.
Alternatively to this very simple approach, use any of the examples for animations provided in http://matplotlib.org/examples/animation/index.html
(2)
In order to get each plot in a new window, use plt.figure() and remove plt.ion(). Also only show the windows at the end:
import matplotlib.pyplot as plt
for i in range(100):
x = range(i)
y = range(i)
plt.figure()
plt.plot(x, y)
plt.title(str(i))
plt.show()
Note that you might find that in both cases the first plot is empty simply because for i=0, range(i) == [] is an empty list without any points. Even for i=1 there is only one point being plotted, but of course no line can connect a single point with itself.
I think the best way is to create one line plot and then update data in it. Then you will have single window and single graph that will continuously update.
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure(figsize=(16,8))
axes = fig.add_subplot(111)
data_plot=plt.plot(0,0)
line, = axes.plot([],[])
for i in range(100):
x = range(i)
y = range(i)
line.set_ydata(y)
line.set_xdata(x)
if len(y)>0:
axes.set_ylim(min(y),max(y)+1) # +1 to avoid singular transformation warning
axes.set_xlim(min(x),max(x)+1)
plt.title(str(i))
plt.draw()
plt.pause(0.1)
plt.show(block=True)

Python & Matplotlib: creating two subplots with different sizes [duplicate]

This question already has answers here:
Matplotlib different size subplots
(6 answers)
Closed 1 year ago.
I have a script which is creating one or two charts, depending on if one specific condition is met or not. Really basically, what I am doing so far is the following:
import matplotlib.pyplot as plt
list1 = [1,2,3,4]
list2 = [4,3,2,1]
somecondition = True
plt.figure(1) #create one of the figures that must appear with the chart
ax = plt.subplot(211) #create the first subplot that will ALWAYS be there
ax.plot(list1) #populate the "main" subplot
if somecondition == True:
ax = plt.subplot(212) #create the second subplot, that MIGHT be there
ax.plot(list2) #populate the second subplot
plt.show()
This code (with the proper data, but this simple version that I did is executable anyway) generates two subplots of the same size, one above the other. However, what I would like to get is the following:
If somecondition is True, then both subplots should appear in the figure. Hence, I would like the second subplot to be 1/2 smaller than the first one;
If somecondition is False, then just the first subplot should appear and I would like it to be sized as the all figure (without leaving the empty space behind in the case the second subplot will not appear).
I'm pretty sure it's just a matter of sizing the two subplots, probably even by the parameter 211 and 212 (that I don't understand what they stand for, since I'm new to Python and couldn't find a clear explanation on the web yet). Does anyone know how to regulate the size of the subplots in a easy way, proportionally to the number of subplots as well as to the entire size of the figure? To make it easier to understand, could you also please edit my simple code I attached to get the result I'm looking for? Thanks in advance!
does this solution satisfy?
import matplotlib.pyplot as plt
list1 = [1,2,3,4]
list2 = [4,3,2,1]
somecondition = True
plt.figure(1) #create one of the figures that must appear with the chart
if not somecondition:
ax = plt.subplot(111) #create the first subplot that will ALWAYS be there
ax.plot(list1) #populate the "main" subplot
else:
ax = plt.subplot(211)
ax.plot(list1)
ax = plt.subplot(223) #create the second subplot, that MIGHT be there
ax.plot(list2) #populate the second subplot
plt.show()
If you need the same width but with half height, better to use matplotlib.gridspec, reference here
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
list1 = [1,2,3,4]
list2 = [4,3,2,1]
somecondition = True
plt.figure(1) #create one of the figures that must appear with the chart
gs = gridspec.GridSpec(3,1)
if not somecondition:
ax = plt.subplot(gs[:,:]) #create the first subplot that will ALWAYS be there
ax.plot(list1) #populate the "main" subplot
else:
ax = plt.subplot(gs[:2, :])
ax.plot(list1)
ax = plt.subplot(gs[2, :]) #create the second subplot, that MIGHT be there
ax.plot(list2) #populate the second subplot
plt.show()
It seems you are looking for this:
if somecondition:
ax = plt.subplot(3,1,(1,2))
ax.plot(list1)
ax = plt.subplot(3,1,3)
ax.plot(list2)
else:
plt.plot(list1)
The magic numbers are nrows, ncols, plot_number, see the documentation. So 3,1,3 will create 3 rows, 1 column, and will plot into the third cell. An abbreviation for that is 313.
It's possible to use tuple as plot_number, so you can create a plot which lives in the first and second cell: 3,1,(1,2).

Dynamically add/create subplots in matplotlib

I want to create a plot consisting of several subplots with shared x/y axes.
It should look something like this from the documentation (though my subplots will be scatterblots): (code here)
But I want to create the subplots dynamically!
So the number of subplots depends on the output of a previous function. (It will probably be around 3 to 15 subplots per diagram, each from a distinct dataset, depending on the input of my script.)
Can anyone tell me how to accomplish that?
Suppose you know total subplots and total columns you want to use:
import matplotlib.pyplot as plt
# Subplots are organized in a Rows x Cols Grid
# Tot and Cols are known
Tot = number_of_subplots
Cols = number_of_columns
# Compute Rows required
Rows = Tot // Cols
# EDIT for correct number of rows:
# If one additional row is necessary -> add one:
if Tot % Cols != 0:
Rows += 1
# Create a Position index
Position = range(1,Tot + 1)
First instance of Rows accounts only for rows completely filled by subplots, then is added one more Row if 1 or 2 or ... Cols - 1 subplots still need location.
Then create figure and add subplots with a for loop.
# Create main figure
fig = plt.figure(1)
for k in range(Tot):
# add every single subplot to the figure with a for loop
ax = fig.add_subplot(Rows,Cols,Position[k])
ax.plot(x,y) # Or whatever you want in the subplot
plt.show()
Please note that you need the range Position to move the subplots into the right place.
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
x = np.linspace(0, 2*np.pi, 400)
y = np.sin(x**2)
subplots_adjust(hspace=0.000)
number_of_subplots=3
for i,v in enumerate(xrange(number_of_subplots)):
v = v+1
ax1 = subplot(number_of_subplots,1,v)
ax1.plot(x,y)
plt.show()
This code works but you will need to correct the axes. I used to subplot to plot 3 graphs all in the same column. All you need to do is assign an integer to number_of_plots variable. If the X and Y values are different for each plot you will need to assign them for each plot.
subplot works as follows, if for example I had a subplot values of 3,1,1. This creates a 3x1 grid and places the plot in the 1st position. In the next interation if my subplot values were 3,1,2 it again creates a 3x1 grid but places the plot in the 2nd position and so forth.
Based on this post, what you want to do is something like this:
import matplotlib.pyplot as plt
# Start with one
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([1,2,3])
# Now later you get a new subplot; change the geometry of the existing
n = len(fig.axes)
for i in range(n):
fig.axes[i].change_geometry(n+1, 1, i+1)
# Add the new
ax = fig.add_subplot(n+1, 1, n+1)
ax.plot([4,5,6])
plt.show()
However, Paul H's answer points to the submodule called gridspec which might make the above easier. I am leaving that as an exercise for the reader ^_~.
Instead of counting your own number of rows and columns, I found it easier to create the subplots using plt.subplots first, then iterate through the axes object to add plots.
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(12, 8))
x_array = np.random.randn(6, 10)
y_array = np.random.randn(6, 10)
i = 0
for row in axes:
for ax in row:
x = x_array[i]
y = y_array[i]
ax.scatter(x, y)
ax.set_title("Plot " + str(i))
i += 1
plt.tight_layout()
plt.show()
Here I use i to iterate through elements of x_array and y_array, but you can likewise easily iterate through functions, or columns of dataframes to dynamically generate graphs.

Categories

Resources