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()
Related
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)
Currently my chart is showing only the main big chart on the left.
However, I now want to add the two smaller plots to the right-hand side of my main plot; with each individual set of data.
I am struggling with subplots to figure out how to do this. My photo below shows my desired output.
filenamesK = glob("C:/Users/Ke*.csv")
filenamesZ = glob("C:/Users/Ze*.csv")
K_Z_Averages = {'K':[], 'Z':[]}
# We will create a function for plotting, instead of nesting lots of if statements within a long for-loop.
def plot_data(filename, fig_ax, color):
df = pd.read_csv(f, sep=',',skiprows=24) # Read in the csv.
df.columns=['sample','Time','ms','Temp1'] # Set the column names
df=df.astype(str) # Set the data type as a string.
df["Temp1"] = df["Temp1"].str.replace('\+ ', '').str.replace(' ', '').astype(float) # Convert to float
# Take the average of the data from the Temp1 column, starting from sample 60 until sample 150.
avg_Temp1 = df.iloc[60-1:150+1]["Temp1"].mean()
# Append this average to a K_Z_Averages, containing a column for average from each K file and the average from each Z file.
# Glob returns the whole path, so you need to replace 0 for 10.
K_Z_Averages[os.path.basename(filename)[0]].append(avg_Temp1)
fig_ax.plot(df[["Temp1"]], color=color)
fig, ax = plt.subplots(figsize=(20, 15))
for f in filenamesK:
plot_data(f, ax, 'blue')
for f in filenamesZ:
plot_data(f, ax, 'red')
plt.show()
#max 's answer is fine, but something you can also do matplotlib>=3.3 is
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=True)
axs = fig.subplot_mosaic([['Left', 'TopRight'],['Left', 'BottomRight']],
gridspec_kw={'width_ratios':[2, 1]})
axs['Left'].set_title('Plot on Left')
axs['TopRight'].set_title('Plot Top Right')
axs['BottomRight'].set_title('Plot Bottom Right')
Note hw the repeated name 'Left' is used twice to indicate that this subplot takes up two slots in the layout. Also note the use of width_ratios.
This is a tricky question. Essentially, you can place a grid on a figure (add_gridspec()) and than open subplots (add_subplot()) in and over different grid elements.
import matplotlib.pyplot as plt
# open figure
fig = plt.figure()
# add grid specifications
gs = fig.add_gridspec(2, 3)
# open axes/subplots
axs = []
axs.append( fig.add_subplot(gs[:,0:2]) ) # large subplot (2 rows, 2 columns)
axs.append( fig.add_subplot(gs[0,2]) ) # small subplot (1st row, 3rd column)
axs.append( fig.add_subplot(gs[1,2]) ) # small subplot (2nd row, 3rd column)
I've coded in python to drawing plots from excel file. In my case, the first column (= X) is fix and other columns (= Y) are changing to drawing. Code is here (and make pdf file):
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import xlrd
def rasm(x, y, ylable): #plot
plt.clf()
plt.bar(x,y) #some extra codes for briefing are deleted.
return (plt.draw())
#save file as pdf
pdf = PdfPages(output_path)
wb = xlrd.open_workbook(input_address)
sh1 = wb.sheet_by_name(u'Sheet1')
#axes of chart
x = sh1.col_values(0) # column 0 from sheet 1
ylable = x[0]
ab=sh1.row_values(0)
for i in range(1, len(ab)): #draw any fig in one page, x axis is constant, y axis are change
y = sh1.col_values(i) # column i
fig = rasm(x, y, ylable)
pdf.savefig(fig)
pdf.close()
Now I want to use an algorithm that draw 6 figures per page by using subplot (in python, i.e matplotlib). And continue drawing next 6 subplots and do it until end of records (in excel). in the other words, plot 6 figures in one page and then go to next page. Because figures are a lot, I had to do it. In this time, I don't have an idea ;). But algorithm can follow:
until reords not end:
for i in range(1,6):
subplot my figs from excel
go next page and do it for next 6 columns (7:12, 13:18, ...).
Thanks.
a=pd.DataFrame({'length':[20,10,30,40,50],
'width':[5,10,15,20,25],
'height':[7,14,21,28,35]})
for i,feature in enumerate(a,1):
sns.regplot(x = feature,y= 'height',data = a)
print("{} plotting {} ".format(i,feature))
I want to plot 3 different plots with three different columns i.e 'length','width' and 'height' on x-axis and 'height' on y-axis in each one of them .
This is the code i wrote but it overlays 3 different plots over one another.I intend to plot 3 different plots.
It depends on what you want to do. It you want several individual plots, you can create a new figure for each dataset:
import matplotlib.pyplot as plt
for i, feature in enumerate(a, 1):
plt.figure() # forces a new figure
sns.regplot(data=a, x=feature, y='height')
print("{} plotting {} ".format(i,feature))
Alternatively, you can draw them all on the same figure, but in different subplots. I.E next to each other:
import matplotlib.pyplot as plt
# create a figure with 3 subplots
fig, axes = plt.subplots(1, a.shape[1])
for i, (feature, ax) in enumerate(zip(a, axes), 1):
sns.regplot(data=a, x=feature, y='height', ax=ax)
print("{} plotting {} ".format(i,feature))
plt.subplots has several options that allow you to align the plots the way you like. check the docs for more on that!
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.