How can i plot multiple graphs as subplot? - python

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)

Related

Setting the same x-scale but different x-limits for adjacent subplots matplotlib

I am trying to create a figure with three bar plots side by side. These bar plots have different yscales, but the data is fundamentally similar so I'd like all the bars to have the same width.
The only way I was able to get the bars to have the exact same width was by using sharex when creating the subplots, in order to keep the same x scale.
import matplotlib.pyplot as plt
BigData = [[100,300],[400,200]]
MediumData = [[40, 30],[50,20],[60,50],[30,30]]
SmallData = [[3,2],[11,3],[7,5]]
data = [BigData, MediumData, SmallData]
colors = ['#FC766A','#5B84B1']
fig, axs = plt.subplots(1, 3, figsize=(30,5), sharex=True)
subplot = 0
for scale in data:
for type in range(2):
bar_x = [x + type*0.2 for x in range(len(scale))]
bar_y = [d[type] for d in scale]
axs[subplot].bar(bar_x,bar_y, width = 0.2, color = colors[type])
subplot += 1
plt.show()
This creates this figure:
The problem with this is that the x-limits of the plot are also shared, leading to unwanted whitespace. I've tried setting the x-bounds after the fact, but it doesn't seem to override sharex. Is there a way to make the bars have the same width, without each subplot also being the same width?
Additionally, is there a way to create such a plot (one with different y scales to depending on the size of the data) without having to sort the data manually beforehand, like shown in my code?
Thanks!
Thanks to Jody Klymak for help finding this solution! I thought I should document it for future users.
We can make use of the 'width_ratios' GridSpec parameter. Unfortunately there's no way to specify these ratios after we've already drawn a graph, so the best way I found to implement this is to write a function that creates a dummy graph, and measures the x-limits from that graph:
def getXRatios(data, size):
phig, aks = plt.subplots(1, 3, figsize=size)
subplot = 0
for scale in data:
for type in range(2):
bar_x = [x + type*0.2 for x in range(len(scale))]
bar_y = [d[type] for d in scale]
aks[subplot].bar(bar_x,bar_y, width = 0.2)
subplot += 1
ratios = [aks[i].get_xlim()[1] for i in range(3)]
plt.close(phig)
return ratios
This is essentially identical to the code that creates the actual figure, with the cosmetic aspects removed, as all we want from this dummy figure is the x-limits of the graph (something we can't get from our actual figure as we need to define those limits before we start in order to solve the problem).
Now all you need to do is call this function when you're creating your subplots:
fig, axs = plt.subplots(1, 3, figsize=(40,5), gridspec_kw = {'width_ratios':getXRatios(data,(40,5))})
As long as your XRatio function creates your graph in the same way your actual graph does, everything should work! Here's my output using this solution.
To save space you could re-purpose the getXRatios function to also construct your final graph, by calling itself in the arguments and giving an option to return either the ratios or the final figure. I couldn't be bothered.

Add subplots to matplotlib figure after creation

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)

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

Creating a 3D plot of several lines in one figure

I'm currently having a small problem with plotting several different lines in a 3d plot. I have a list of lists containing three numpy arrays corresponding to the xyz coordinates for the three points on each line, i.e.
lines=[[array([10,0,0]),array([10,0,101.5]),array([-5,0,250])],[array([9,0,0]), array([9,0,101.5]),array([-4,0,250])]]
would represent 2 lines with 3 sets of xyz coordinates in each (the first one here would be (10,0,0),(10,0,101.5) and (-5,0,250)).
In general I would have n lines in this list each with 3 sets of xyz coordinates each. I would like to plot these lines on a single 3d plot with matplotlib. All I've managed to do so far is to create n plots each containing a single line.
Thanks for the help!
EDIT:
I have a list 'lines' containing 'line' objects which are just lists themselves containing 3 numpy arrays for the 3 points on each line. I tried to use the following method:
for line in lines:
fig = plt.figure()
ax = fig.gca(projection='3d')
z = []
for i in [0,1,2]:
z.append(line[i][2])
x = []
for i in [0,1,2]:
x.append(line[i][0])
y = []
for i in [0,1,2]:
y.append(line[i][1])
ax.plot(x, y, z, label='path')
plt.show()
I think I understand why this gives me 2 plots of lines 1 and 2 but I can't figure out a way to put both lines on the same plot.
You almost got it. The solution to your problem is simple, just move required statments out of for loop:
import matplotlib.pyplot as plt
lines=[[array([10,0,0]),array([10,0,101.5]),array([-5,0,250])],[array([9,0,0]), array([9,0,101.5]),array([-4,0,250])]]
fig = plt.figure()
ax = fig.gca(projection='3d')
for line in lines:
z = []
for i in [0,1,2]:
z.append(line[i][2])
x = []
for i in [0,1,2]:
x.append(line[i][0])
y = []
for i in [0,1,2]:
y.append(line[i][1])
ax.plot(x, y, z, label='path')
plt.show()
I had a similar problem trying to plot a 3D path between locations, and this was about the closest / most helpful solution I found. So just if anybody else is trying to do this and might find this similar solution sheds a bit of light :
for location in list_of_locations:
x_list.append(locdata[location].x) # locdata is a dictionary with the co-ordinates of each named location
y_list.append(locdata[location].y)
z_list.append(locdata[location].z)
fig = plt.figure()
ax = fig.gca(projection='3d')
for i in range(len(x_list)-1):
xs = [x_list[i], x_list[i+1]]
ys = [y_list[i], y_list[i+1]]
zs = [z_list[i], z_list[i+1]]
ax.plot(xs,ys,zs)
plt.show()
I'm sure it doesn't need to be two separate for loops but for my little data set this was totally fine, and easy to read.

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