I want to plot two separate quantities while running through a loop. I want to create a separate figure window for each quantity, such that each iteration of the loop creates one subplot for each quantity.
Basically, I want my code to do something like this:
import numpy as np
import matplotlib.pyplot as plt
nr = [10, 15, 20, 25, 35, 50]
fig1 = plt.figure(1)
fig2 = plt.figure(2)
for y in range(len(nr)):
m = np.arange(y+1)
n = (y+1)*np.arange(y+1)
fig1.subplot(3,2,y+1)
fig1.plot(m,n, 'b')
fig1.title('y=%s'%y)
m1 = np.square(np.arange(y+1))
n1 = (y+1)*np.arange(y+1)
fig2.subplot(3,2,y+1)
fig2.plot(m1,n1, 'r')
fig2.title('y=%s'%y)
fig1.show()
fig2.show()
This code doesn't work; gives me the error message that 'Figure' object has no attribute 'subplot'. I've tried many variations on this link - http://matplotlib.org/api/pyplot_api.html, but I am unable to understand how to do it the right way.
In the output, I want two figure windows, each with 6 subplots, such that the first one contains plots of m vs n, and the second one contains plots of m1 vs n1.
Okay, long explanation because there are multiple issues here.
The biggest problem you are running into is that there are multiple ways to handle things in matplotlib. In fact, there are effectively multiple interfaces. The easiest and most commonly used method is to just create your plot using pyplot and its methods like pyplot.subplot and pyplot.plot. This can work well for quick plots, but will not work well for your situation.
Since you want to create two figures and alternate plotting to those figures, you are going to want to use the more powerful objects in pyplot. You have gotten part way there yourself, but I'll try to help you with the last part.
You are good up until here:
import numpy as np
import matplotlib.pyplot as plt
nr = [10, 15, 20, 25, 35, 50]
fig1 = plt.figure(1)
fig2 = plt.figure(2)
for y in range(len(nr)):
m = np.arange(y+1)
n = (y+1)*np.arange(y+1)
but when you try to use the methods of Figure, you are getting confused and trying to use similar methods that belong to pyplot. The next portion should be rewritten as:
ax1 = fig1.add_subplot(3,2,y)
ax1.plot(m,n, 'b')
ax1.set_title('y=%s'%y)
m1 = np.square(np.arange(y+1))
n1 = (y+1)*np.arange(y+1)
ax2 = fig2.add_subplot(3,2,y)
ax2.plot(m1,n1, 'r')
ax2.set_title('y=%s'%y)
Here, what you have done is capture the Axes instance that is returned from add_subplot(). Then you plot onto the Axes instance. Also, when specifying where you want the subplot to be placed (the third input to Figure.add_subplot()), you do not want to use y+1 because that would start at 1 and end at 6 which would go out of the available range of 0-5. Remember that Python indices start with zero.
Finally, to show the figures you just created, you can either call pyplot.show() like this:
plt.show()
or you can save the figures to files like this:
fig1.savefig('fig1.png')
fig2.savefig('fig2.png')
The resulting figures look like this:
Related
N.B.: I have edited the question as it was probably unclear: I am looking for the best method to understand the type of plot in a given axis.
QUESTION:
I am trying to make a generic function which can arrange multiple figures as subplots.
As I loop over the subplots to set some properties (e.g. axis range) iterating over fig.axes, I need to understand which type every plot is in order to determine which properties I want to set for each of them (e.g. I want to set x range on images and line plots, but not on colorbar, otherwise my plot will explode).
My question is then how I can distinguish between different types.
I tried to play with try and except and select on the basis of different properties for different plot types, but they seem to be the same for all of them, so, at the moment, the best way I found is to check the content of each axis: in particular ax.images is a non empty list if a plot is an image, and ax.lines is not empty if it is a line plot, (and a colorbar has both empty).
This works for simple plots, but I wonder if this is still the best way and still working for more complex cases (e.g. insets, overlapped lines and images, subclasses)?
This is just an example to illustrate how the different type of plots can be accessed, with the following code creating three axes l, i and cb (respectively line, image, colorbar):
# create test figure
plt.figure()
b = np.arange(12).reshape([4,3])
plt.subplot(121)
plt.plot([1,2,3],[4,5,6])
plt.subplot(122)
plt.imshow(b)
plt.colorbar()
# create test objects
ax=plt.gca()
fig=plt.gcf()
l,i,cb = fig.axes
# do a simple test, images are different:
for o in l,i,cb: print(len(o.images))
# this also doesn't work in finding properties not in common between lines and colobars, gives empty list.
[a for a in dir(l) if a not in dir(cb)]
After creating the image above in IPython
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots()
ax.imshow(((0,1),(2,3)))
ax.scatter((0,1),(0,1), fc='w', ec='k')
ax.plot((0,1),(0,1))
fig.colorbar(ScalarMappable(), ax=ax)
plt.show()
I tried to investigate
In [48]: fig.axes
Out[48]: [<AxesSubplot:>, <AxesSubplot:label='<colorbar>'>]
I can recognize that one of the two axes is a colorbar — but it's easy to inspect the content of the individual axes
In [49]: fig.axes[0]._children
Out[49]:
[<matplotlib.image.AxesImage at 0x7fad9dda2b30>,
<matplotlib.collections.PathCollection at 0x7fad9dad04f0>,
<matplotlib.lines.Line2D at 0x7fad9dad09d0>]
In [50]: fig.axes[1]._children
Out[50]:
[<matplotlib.patches.Polygon at 0x7fad9db525f0>,
<matplotlib.collections.LineCollection at 0x7fad9db52830>,
<matplotlib.collections.QuadMesh at 0x7fad9dad2320>]
I have to remind you that
Matplotib provides you with many different container objects,
You can store the Axes destination in a list, or a dictionary, when you use it — you can even say ax.ax_type = 'lineplot'.
That said, e.g.,
from matplotlib.pyplot import subplots, plot
fig, ax = subplots()
plot((1, 2), (2, 1))
...
axes_types = []
for ax_i in fig.axes:
try:
ax_i.__getattr__('get_clabel')
axes_types.append('colorbar')
except AttributeError:
axes_types.append('lineplot')
...
In other word, chose a method that is unique to each one of the differnt types you're testing and check if it's available.
I'm trying to plot a number of segments of a timeseries, let's say 5 segments.
I want each segment to be plotted individually and one after another after a given input (key press)
For example, 1) plot first segment, 2) wait for input and only after my input 3) plot next segment. I need python to wait for an input (key press) before plotting the next segment.
I've manged to almost make it work, but on jupyter notebook all figures are displayed at once only after I input something for all the plots (i.e. 5 inputs)
segments = segments.iloc[0:5] # reduced number for testing
list = []
for i in segments.itertuples(): # loop over df
f, ax = plt.subplots()
ax.plot(time, yy) # plot timeseries
plt.xlim([segments.start_time, segments.end_time]) # only show between limits
plt.show()
# get user input
a = input()
list.append(a) # add input to the list
I've been banging my head but haven't managed to solve this. Any suggestion on how to solve this issue?
I have one that works from adapting an example I had used before, but note that I don't use subplot here!:
import matplotlib.pyplot as plt
inp_ = []
for i in range(3):
labels = ['part_1','part_2','part_3']
pie_portions = [5,6,7]
plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
plt.title(f'figure_no : {i+1}')
plt.show()
# get user input
a = input()
inp_.append(a) # add input to the list
If you use subplot, then you get what you are seeing where it waits to show them all at the end because the figure is only complete and available to display after the last subplot is specified. Otherwise it is blocked. The easiest solution is to switch away from using subplots, like in my block of code posted above.
If you needed it to absolutely work with subplot, you can in fact update the figure after, like so;
#Using subplots based on https://matplotlib.org/stable/gallery/pie_and_polar_charts/pie_demo2.html
import matplotlib.pyplot as plt
import numpy as np
def update_subplot():
'''
based on https://stackoverflow.com/a/36279629/8508004
'''
global fig, axs
ax_list = axs.ravel()
# ax_list[0] refers to the first subplot
ax_list[1].imshow(np.random.randn(100, 100))
#plt.draw()
# Some data
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15, 30, 45, 10]
# Make figure and axes
fig, axs = plt.subplots(1, 3)
# A standard pie plot
axs[0].pie(fracs, labels=labels, autopct='%1.1f%%', shadow=True)
axs[1].axis('off') # based on https://stackoverflow.com/a/10035974/8508004
axs[2].axis('off')
plt.show()
import time
time.sleep(2)
update_subplot()
fig
However, if you run that, you'll see you get successive views with one plot and then two and the first (with just one of two subplots) stays around in the notebook output and so it is less than desirable.
Always best to provide a minimal reproducible example when posting your question. That way you get something close to what works for your case.
Also, it is a bad idea to use a built-in type as a name of variable. (list = []) It can lead to errors you aren't expecting later. Imagine you wanted to typecast a set back to a list later in your code example.
Compare:
list = []
my_set= {1,2,3}
a = list(my_set)
to
my_list = []
my_set= {1,2,3}
a = list(my_set)
The first will give TypeError: 'list' object is not callable.
I have been trying to merge these two plots together but have not found a built-in in the documentation for MatPlotLib on how to do so. I want to show the two bar values next to each and for every new entry, add the new entry to the graph while shifting the other entries over to make space. The plots are below.
As stated prior, when I say merge, I do not simply mean just plop Plot A onto Plot B, but rather join the plots together so both bar values are shown in the same graph, like this:
The reasoning for this is that I will be able to log all the entries in a single plot without having to manually do so. By implementing something like this in my code, it would make entries go a lot quicker.
EDIT: I understand that I can graph these two together, but that is not what I am looking for. Once I get the necessary input, my program creates a graph of that data and saves it as a file. I am looking to append any new data to that original file by just shifting the original value over to the left in order to make space.
EDIT 2: How could I extract the data from each plot and after doing so, create a new graph? This would seem to be another acceptable workaround.
Is there anything preventing you from plotting each of them side by side but changing the index?
a, b, c = 2, 5, 3
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
count = 0
ax.bar(count, a)
# if prgoram produces a new output then...
count += 1
ax.bar(count, b) # index means new bar plot has shifted
# again
count += 1
ax.bar(count, c) # shifted again
This should automatically expand the x-axis anyway. You may have to alter this slightly if you've particularly concenred about the width of these bars.
If this isn't what you wanted you could consider replotting with the bar container or even just stripping the height to reuse.
fig, ax = plt.subplots(1, 1)
count = 0
bar_cont = ax.bar(count, a) # reference to the bar container of interest
print(bar_cont.get_height())
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
people = ['JOHN DOE', 'BOB SMITH']
values = [14,14]
ax.bar(people,values)
plt.show()
Should be the solution. You just have to pass a list instead of a single value to the plt.bar() function. More detailed explaination here.
I am looping through a bunch of CSV files containing various measurements.
Each file might be from one of 4 different data sources.
In each file, I merge the data into monthly datasets, that I then plot in a 3x4 grid. After this plot has been saved, the loop moves on and does the same to the next file.
This part I got figured out, however I would like to add a visual clue to the plots, as to what data it is. As far as I understand it (and tried it)
plt.subplot(4,3,1)
plt.hist(Jan_Data,facecolor='Red')
plt.ylabel('value count')
plt.title('January')
does work, however this way, I would have to add the facecolor='Red' by hand to every 12 subplots. Looping through the plots wont work for this situation, since I want the ylabel only for the leftmost plots, and xlabels for the bottom row.
Setting facecolor at the beginning in
fig = plt.figure(figsize=(20,15),facecolor='Red')
does not work, since it only changes the background color of the 20 by 15 figure now, which subsequently gets ignored when I save it to a PNG, since it only gets set for screen output.
So is there just a simple setthecolorofallbars='Red' command for plt.hist(… or plt.savefig(… I am missing, or should I just copy n' paste it to all twelve months?
You can use mpl.rc("axes", color_cycle="red") to set the default color cycle for all your axes.
In this little toy example, I use the with mpl.rc_context block to limit the effects of mpl.rc to just the block. This way you don't spoil the default parameters for your whole session.
import matplotlib as mpl
import matplotlib.pylab as plt
import numpy as np
np.random.seed(42)
# create some toy data
n, m = 2, 2
data = []
for i in range(n*m):
data.append(np.random.rand(30))
# and do the plotting
with mpl.rc_context():
mpl.rc("axes", color_cycle="red")
fig, axes = plt.subplots(n, m, figsize=(8,8))
for ax, d in zip(axes.flat, data):
ax.hist(d)
The problem with the x- and y-labels (when you use loops) can be solved by using plt.subplots as you can access every axis seperately.
import matplotlib.pyplot as plt
import numpy.random
# creating figure with 4 plots
fig,ax = plt.subplots(2,2)
# some data
data = numpy.random.randn(4,1000)
# some titles
title = ['Jan','Feb','Mar','April']
xlabel = ['xlabel1','xlabel2']
ylabel = ['ylabel1','ylabel2']
for i in range(ax.size):
a = ax[i/2,i%2]
a.hist(data[i],facecolor='r',bins=50)
a.set_title(title[i])
# write the ylabels on all axis on the left hand side
for j in range(ax.shape[0]):
ax[j,0].set_ylabel(ylabel[j])
# write the xlabels an all axis on the bottom
for j in range(ax.shape[1]):
ax[-1,j].set_xlabel(xlabels[j])
fig.tight_layout()
All features (like titles) which are not constant can be put into arrays and placed at the appropriate axis.
How can I use matplotlib to create many different chart objects and then have the ability to control each chart object separately (without affecting the other chart objects)?
Ideally, I'd like to have something of the following:
# creating the chart handler object
chartHandler = ChartHandler()
# plotting some values for chart #0
chartHandler[0].plot( range(0,100) )
# plotting some values for chart #5
chartHandler[5].plot( range(500,700) )
Unless you are talking about something that I haven't dealt with in matplotlib yet, I think that what you are looking for is figure.add_subplot(). You should be able to capture the return from each figure.add_subplot() and operate on each individually from then on, kind of like this:
import matplotlib.pyplot as plt
#Create an 11x5 figure
fig = plt.figure(figsize=(11,5))
#Create subplots[0]
subplts = []
subplt = fig.add_subplot(121)
subplts.append(subplt)
#Create subplots[1:20]
for xind in range(4,8):
for yind in range(0,5):
subplt = fig.add_subplot(5,8,(yind*8+xind))
subplts.append(subplt)
plt.show()
It should be noted that there are a few problems with the above script. Mainly, the subplots overlap slightly. This can be solved using the position keyword to add_subplot and some simple math.
In any case, you can now modify each subplot by referencing its index in subplots. It should be pretty simple to add plots, modify ranges, etc.