The information below may be superfluous if you are trying to understand the error message. Please start off by reading the answer
by #user707650.
Using MatPlotLib, I wanted a generalizable script that creates the following from my data.
A window containing a subplots arranged so that there are b subplots per column. I want to be able to change the values of a and b.
If I have data for 2a subplots, I want 2 windows, each with the previously described "a subplots arranged according to b subplots per column".
The x and y data I am plotting are floats stored in np.arrays and are structured as follows:
The x data is always the same for all plots and is of length 5.
'x_vector': [0.000, 0.005, 0.010, 0.020, 0.030, 0.040]
The y data of all plots are stored in y_vector where the data for the first plot is stored at indexes 0 through 5. The data for the second plot is stored at indexes 6 through 11. The third plot gets 12-18, the fourth 19-24, and so on.
In total, for this dataset, I have 91 plots (i.e. 91*6 = 546 values stored in y_vector).
Attempt:
import matplotlib.pyplot as plt
# Options:
plots_tot = 14 # Total number of plots. In reality there is going to be 7*13 = 91 plots.
location_of_ydata = 6 # The values for the n:th plot can be found in the y_vector at index 'n*6' through 'n*6 + 6'.
plots_window = 7 # Total number of plots per window.
rows = 2 # Number of rows, i.e. number of subplots per column.
# Calculating number of columns:
prim_cols = plots_window / rows
extra_cols = 0
if plots_window % rows > 0:
extra_cols = 1
cols = prim_cols + extra_cols
print 'cols:', cols
print 'rows:', rows
# Plotting:
n=0
x=0
fig, ax = plt.subplots(rows, cols)
while x <= plots_tot:
ax[x].plot(x_vector, y_vector[n:(n+location_of_ydata)], 'ro')
if x % plots_window == plots_window - 1:
plt.show() # New window for every 7 plots.
n = n+location_of_ydata
x = x+1
I get the following error:
cols: 4
rows: 2
Traceback (most recent call last):
File "Script.py", line 222, in <module>
ax[x].plot(x_vector, y_vector[n:(n+location_of_ydata)], 'ro')
AttributeError: 'numpy.ndarray' object has no attribute 'plot'
If you debug your program by simply printing ax, you'll quickly find out that ax is a two-dimensional array: one dimension for the rows, one for the columns.
Thus, you need two indices to index ax to retrieve the actual AxesSubplot instance, like:
ax[1,1].plot(...)
If you want to iterate through the subplots in the way you do it now, by flattening ax first:
ax = ax.flatten()
and now ax is a one dimensional array. I don't know if rows or columns are stepped through first, but if it's the wrong around, use the transpose:
ax = ax.T.flatten()
Of course, by now it makes more sense to simply create each subplot on the fly, because that already has an index, and the other two numbers are fixed:
for x < plots_tot:
ax = plt.subplot(nrows, ncols, x+1)
Note: you have x <= plots_tot, but with x starting at 0, you'll get an IndexError next with your current code (after flattening your array). Matplotlib is (unfortunately) 1-indexed for subplots. I prefer using a 0-indexed variable (Python style), and just add +1 for the subplot index (like above).
The problem here is with how matplotlib handles subplots. Just do the following:
fig, axes = plt.subplots(nrows=1, ncols=2)
for axis in axes:
print(type(axis))
you will get a matplotlib object which is actually a 1D array which can be traversed using single index i.e. axis[0], axis[1]...and so on. But if you do
fig, axes = plt.subplots(nrows=2, ncols=2)
for axis in axes:
print(type(axis))
you will get a numpy ndarray object which is actually a 2D array which can be traversed only using 2 indices i.e. axis[0, 0], axis[1, 0]...and so on. So be mindful how you incorporate your for loop to traverse through axes object.
In case if you use N by 1 graphs, for example if you do like fig, ax = plt.subplots(3, 1) then please do likeax[plot_count].plot(...)
The axes are in 2-d, not 1-d so you can't iterate through using one loop. You need one more loop:
fig,axes=plt.subplots(nrows=2,ncols=2)
plt.tight_layout()
for ho in axes:
for i in ho:
i.plot(a,a**2)
This gives no problem but if I try:
for i in axes:
i.plot(a,a**2)
the error occurs.
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)
Perhaps the title is misleading or someone will come along telling me that is duplicated. However after many (many) hours of browsing I haven't found anything. I want to plot multiple scatter diagrams and merge them up into a subplot for some nrows and ncols?
Assume that we have the following:
new_list=[]
for j in list(set(lala)):
df1 = df[df['Date'] == j]
df1.drop('Date', axis = 1, inplace = True)
df2 = df1.groupby('Z').mean()
df2.reset_index(inplace = True)
new_list.append(df2)
for j in range(0, len(new_list)):
plt.figure(figsize=(6, 6), dpi=80)
plt.scatter(new_list[j]['X'],new_list[j]['Y'])
and let me explain a little bit of what it does; I create a list called new_list, which contains data frames constructed in the for loop (you can ignore the construction since I'm asking for a global approach). Afterwards, I print scatter diagrams (in total as many as the number of elements of new_list) for each data frame in new_list.
Because the number of the printouts is big, I want to create subplots off these printouts to make the final image easier for the eye.
So how can I take all these scatter diagrams and merge them up into a subplot for some nrows and ncols?
Assuming you have 4 rows and 10 columns, you can do something like this (just one way of doing it). Here flatten returns you a list of 40 axis objects (4 x 10) where the order is across the row: first row four columns first, then second row four columns, and so on.
fig, axes = plt.subplots(nrows=4, ncols=10)
for i, ax in enumerate(axes.flatten()):
ax.scatter(new_list[i]['X'],new_list[i]['Y'])
If you don't want to use enumerate, alternatively you can also use the following
fig, axes = plt.subplots(nrows=4, ncols=10)
ax = axes.flatten()
for j in range(0, len(new_list)):
ax[j].scatter(new_list[j]['X'],new_list[j]['Y'])
Sometimes I come across code such as this:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [1, 4, 9, 16, 25]
fig = plt.figure()
fig.add_subplot(111)
plt.scatter(x, y)
plt.show()
Which produces:
I've been reading the documentation like crazy but I can't find an explanation for the 111. sometimes I see a 212.
What does the argument of fig.add_subplot() mean?
I think this would be best explained by the following picture:
To initialize the above, one would type:
import matplotlib.pyplot as plt
fig = plt.figure()
fig.add_subplot(221) #top left
fig.add_subplot(222) #top right
fig.add_subplot(223) #bottom left
fig.add_subplot(224) #bottom right
plt.show()
These are subplot grid parameters encoded as a single integer. For example, "111" means "1x1 grid, first subplot" and "234" means "2x3 grid, 4th subplot".
Alternative form for add_subplot(111) is add_subplot(1, 1, 1).
The answer from Constantin is spot on but for more background this behavior is inherited from Matlab.
The Matlab behavior is explained in the Figure Setup - Displaying Multiple Plots per Figure section of the Matlab documentation.
subplot(m,n,i) breaks the figure window into an m-by-n matrix of small
subplots and selects the ithe subplot for the current plot. The plots
are numbered along the top row of the figure window, then the second
row, and so forth.
My solution is
fig = plt.figure()
fig.add_subplot(1, 2, 1) #top and bottom left
fig.add_subplot(2, 2, 2) #top right
fig.add_subplot(2, 2, 4) #bottom right
plt.show()
import matplotlib.pyplot as plt
plt.figure(figsize=(8,8))
plt.subplot(3,2,1)
plt.subplot(3,2,3)
plt.subplot(3,2,5)
plt.subplot(2,2,2)
plt.subplot(2,2,4)
The first code creates the first subplot in a layout that has 3 rows and 2 columns.
The three graphs in the first column denote the 3 rows. The second plot comes just below the first plot in the same column and so on.
The last two plots have arguments (2, 2) denoting that the second column has only two rows, the position parameters move row wise.
fig.add_subplot(ROW,COLUMN,POSITION)
ROW=number of rows
COLUMN=number of columns
POSITION= position of the graph you are plotting
Examples
`fig.add_subplot(111)` #There is only one subplot or graph
`fig.add_subplot(211)` *and* `fig.add_subplot(212)`
There are total 2 rows,1 column therefore 2 subgraphs can be plotted. Its location is 1st. There are total 2 rows,1 column therefore 2 subgraphs can be plotted.Its location is 2nd
The add_subplot() method has several call signatures:
add_subplot(nrows, ncols, index, **kwargs)
add_subplot(pos, **kwargs)
add_subplot(ax)
add_subplot() <-- since 3.1.0
Calls 1 and 2:
Calls 1 and 2 achieve the same thing as one another (up to a limit, explained below). Think of them as first specifying the grid layout with their first 2 numbers (2x2, 1x8, 3x4, etc), e.g:
f.add_subplot(3,4,1)
# is equivalent to:
f.add_subplot(341)
Both produce a subplot arrangement of (3 x 4 = 12) subplots in 3 rows and 4 columns. The third number in each call indicates which axis object to return, starting from 1 at the top left, increasing to the right.
This code illustrates the limitations of using call 2:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
def plot_and_text(axis, text):
'''Simple function to add a straight line
and text to an axis object'''
axis.plot([0,1],[0,1])
axis.text(0.02, 0.9, text)
f = plt.figure()
f2 = plt.figure()
_max = 12
for i in range(_max):
axis = f.add_subplot(3,4,i+1, fc=(0,0,0,i/(_max*2)), xticks=[], yticks=[])
plot_and_text(axis,chr(i+97) + ') ' + '3,4,' +str(i+1))
# If this check isn't in place, a
# ValueError: num must be 1 <= num <= 15, not 0 is raised
if i < 9:
axis = f2.add_subplot(341+i, fc=(0,0,0,i/(_max*2)), xticks=[], yticks=[])
plot_and_text(axis,chr(i+97) + ') ' + str(341+i))
f.tight_layout()
f2.tight_layout()
plt.show()
You can see with call 1 on the LHS you can return any axis object, however with call 2 on the RHS you can only return up to index = 9 rendering subplots j), k), and l) inaccessible using this call.
I.e it illustrates this point from the documentation:
pos is a three digit integer, where the first digit is the number of rows, the second the number of columns, and the third the index of the subplot. i.e. fig.add_subplot(235) is the same as fig.add_subplot(2, 3, 5). Note that all integers must be less than 10 for this form to work.
Call 3
In rare circumstances, add_subplot may be called with a single argument, a subplot axes instance already created in the present figure but not in the figure's list of axes.
Call 4 (since 3.1.0):
If no positional arguments are passed, defaults to (1, 1, 1).
i.e., reproducing the call fig.add_subplot(111) in the question. This essentially sets up a 1 x 1 grid of subplots and returns the first (and only) axis object in the grid.
fig.add_subplot(111) is just like fig.add_subplot(1, 1, 1), the 111 is just the subplot grid parameters but, encoded as a single integer.
To select the kth subplot in a n*m grid you do so: fig.add_subplot(n, m, k).
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.
I have written code that opens 16 figures at once. Currently, they all open as separate graphs. I'd like them to open all on the same page. Not the same graph. I want 16 separate graphs on a single page/window. Furthermore, for some reason, the format of the numbins and defaultreallimits doesn't hold past figure 1. Do I need to use the subplot command? I don't understand why I would have to but can't figure out what else I would do?
import csv
import scipy.stats
import numpy
import matplotlib.pyplot as plt
for i in range(16):
plt.figure(i)
filename= easygui.fileopenbox(msg='Pdf distance 90m contour', title='select file', filetypes=['*.csv'], default='X:\\herring_schools\\')
alt_file=open(filename)
a=[]
for row in csv.DictReader(alt_file):
a.append(row['Dist_90m(nmi)'])
y= numpy.array(a, float)
relpdf=scipy.stats.relfreq(y, numbins=7, defaultreallimits=(-10,60))
bins = numpy.arange(-10,60,10)
print numpy.sum(relpdf[0])
print bins
patches=plt.bar(bins,relpdf[0], width=10, facecolor='black')
titlename= easygui.enterbox(msg='write graph title', title='', default='', strip=True, image=None, root=None)
plt.title(titlename)
plt.ylabel('Probability Density Function')
plt.xlabel('Distance from 90m Contour Line(nm)')
plt.ylim([0,1])
plt.show()
The answer from las3rjock, which somehow is the answer accepted by the OP, is incorrect--the code doesn't run, nor is it valid matplotlib syntax; that answer provides no runnable code and lacks any information or suggestion that the OP might find useful in writing their own code to solve the problem in the OP.
Given that it's the accepted answer and has already received several up-votes, I suppose a little deconstruction is in order.
First, calling subplot does not give you multiple plots; subplot is called to create a single plot, as well as to create multiple plots. In addition, "changing plt.figure(i)" is not correct.
plt.figure() (in which plt or PLT is usually matplotlib's pyplot library imported and rebound as a global variable, plt or sometimes PLT, like so:
from matplotlib import pyplot as PLT
fig = PLT.figure()
the line just above creates a matplotlib figure instance; this object's add_subplot method is then called for every plotting window (informally think of an x & y axis comprising a single subplot). You create (whether just one or for several on a page), like so
fig.add_subplot(111)
this syntax is equivalent to
fig.add_subplot(1,1,1)
choose the one that makes sense to you.
Below I've listed the code to plot two plots on a page, one above the other. The formatting is done via the argument passed to add_subplot. Notice the argument is (211) for the first plot and (212) for the second.
from matplotlib import pyplot as PLT
fig = PLT.figure()
ax1 = fig.add_subplot(211)
ax1.plot([(1, 2), (3, 4)], [(4, 3), (2, 3)])
ax2 = fig.add_subplot(212)
ax2.plot([(7, 2), (5, 3)], [(1, 6), (9, 5)])
PLT.show()
Each of these two arguments is a complete specification for correctly placing the respective plot windows on the page.
211 (which again, could also be written in 3-tuple form as (2,1,1) means two rows and one column of plot windows; the third digit specifies the ordering of that particular subplot window relative to the other subplot windows--in this case, this is the first plot (which places it on row 1) hence plot number 1, row 1 col 1.
The argument passed to the second call to add_subplot, differs from the first only by the trailing digit (a 2 instead of a 1, because this plot is the second plot (row 2, col 1).
An example with more plots: if instead you wanted four plots on a page, in a 2x2 matrix configuration, you would call the add_subplot method four times, passing in these four arguments (221), (222), (223), and (224), to create four plots on a page at 10, 2, 8, and 4 o'clock, respectively and in this order.
Notice that each of the four arguments contains two leadings 2's--that encodes the 2 x 2 configuration, ie, two rows and two columns.
The third (right-most) digit in each of the four arguments encodes the ordering of that particular plot window in the 2 x 2 matrix--ie, row 1 col 1 (1), row 1 col 2 (2), row 2 col 1 (3), row 2 col 2 (4).
Since this question is from 4 years ago new things have been implemented and among them there is a new function plt.subplots which is very convenient:
fig, axes = plot.subplots(nrows=2, ncols=3, sharex=True, sharey=True)
where axes is a numpy.ndarray of AxesSubplot objects, making it very convenient to go through the different subplots just using array indices [i,j].
To answer your main question, you want to use the subplot command. I think changing plt.figure(i) to plt.subplot(4,4,i+1) should work.
This works also:
for i in range(19):
plt.subplot(5,4,i+1)
It plots 19 total graphs on one page. The format is 5 down and 4 across..
#doug & FS.'s answer are very good solutions. I want to share the solution for iteration on pandas.dataframe.
import pandas as pd
df=pd.DataFrame([[1, 2], [3, 4], [4, 3], [2, 3]])
fig = plt.figure(figsize=(14,8))
for i in df.columns:
ax=plt.subplot(2,1,i+1)
df[[i]].plot(ax=ax)
print(i)
plt.show()
For example, to create 6 subplots with 2 rows and 3 columns you can use:
funcs = [np.cos, np.sin, np.tan, np.arctan]
x = np.linspace(0, 10, 100)
fig = plt.figure(figsize=(10, 5))
for num, func in enumerate(funcs, start=1):
ax = fig.add_subplot(2, 2, num) # plot with 2 rows and 2 columns
ax.plot(x, func(x))
ax.set_title(func.__name__)
# add spacing between subplots
fig.tight_layout()
Result: