I am trying to add multiple lines on a single figure without knowing the number of lines in advance. I currently have a class that has x and y values that are used for the line within a single session.
I am unsure how to add a new line for each new session within the same figure. Creating that association to be specific.
Within my main function I have the following code.
fig = plt.figure()
ax = fig.add_subplot(111)
line,= plt.figure().plot(0,0)
In my session class. I have the following code.
class Session:
x = []
y = []
# I think I should add a line here... but I am not sure
# how to make this association to the main.
For each session, it stores the x and y values and I can retrieve those values through methods. That part is easy but associating each line to the same graph is what I am having trouble at. How should I approach this problem?
You can call .plot() multiple times. I added an example of how to change the line color. I will leave the styling to you.
import matplotlib.pyplot as plt
fig = plt.Figure()
ax = fig.add_subplot(111)
colors = ('b','g','r','c','m','y','k','w',)
sessions = (sess1, sess2, sess3)
for sess, color in zip(sessions, colors):
ax.plot(sess.x, sess.y, color=color)
If you want to use, and/or re-use, a specific set of colors for all of your lines itertools.cycle makes short work of it:
import itertools as it
colors = ('b','g','r',)
sessions = (sess1, sess2, sess3, sess4, sess5, sess6)
for sess, color in zip(sessions, it.cycle(colors)):
ax.plot(sess.x, sess.y, color=color)
Related
I am trying to merge an arbitrary number of line charts into a single image, and while there are many, many questions about this sort of thing, none of them seem applicable to the code I'm working with.
Unlike a large number of answers, I don't want to have the separate graphs displayed side by side, or above one another, in a single output, but rather, combined together.
For all of these graphs the value of the "y_x" column would be the same, but the "yhat_y" produced during each loop would be different.
Adding subplots = True to the plot method of a dataframe seems to change the return type to something that is no longer compatible with the code numpy.ndarray' object has no attribute 'get_figure'
#ax = plt.subplot(111) doesnt seem to do anything
for variable in range(max_num):
forecast = get_forecast(variable)
cmp1 = forecast.set_index("ds")[["yhat", "yhat_lower", "yhat_upper"]].join(
both.set_index("ds")
)
e.augmented_error[variable]= sklearn.metrics.mean_absolute_error(
cmp["y"].values, cmp1["yhat"].values
)
cmp2=cmp.merge(cmp1,on='ds')
plot = cmp2[['y_x', 'yhat_y']].plot(title =e)
fig1 = plot.get_figure()
plot.set_title("prediction")
plt.show()
fig1.savefig('output.pdf', format="pdf")
plt.close()
The most straightforward way would be to create a reusable ax handle outside the loop, then call ax.plot inside the loop:
fig, ax = plt.subplots() # create reusable `fig` and `ax` handles
for variable in range(max_num):
...
ax.plot(cmp2['y_x'], cmp2['yhat_y']) # use `ax.plot(cmp2...)` instead of `cmp2.plot()`
ax.set_title('predictions')
fig.savefig('output.pdf', format='pdf')
I have a piece of code that I have acquired from a collaborator in work. This piece of code produces a plot like the one seen below.
example image of a plot
It does this by referencing another function in another piece of code; which I do not want to alter in any way.
What I would like to do is to write a piece of code that saves this plot as a png file i.e. I am looking for a function that i can put the other function as a variable that would save it is as a png/ jpeg file.
Code:
Here is the code:
for file in files:
import matplotlib.pyplot as plt
connection = sqlite3.connect( file )
animalPool = AnimalPool( )
animalPool.loadAnimals( connection )
# show the mask of animals at frame 300
animalPool.showMask( 701 )
It is calling the following function:
def showMask(self, t ):
'''
show the mask of all animals in a figure
'''
fig, ax = plt.subplots()
ax.set_xlim(90, 420)
ax.set_ylim(-370, -40)
for animal in self.getAnimalList():
mask = animal.getBinaryDetectionMask( t )
mask.showMask( ax=ax )
plt.show()
I have already tried the matplotlib "savefig" function, but this just saves a blank image.
I am very new to coding, and am trying to learn on the fly, so if this question is not well worded or explained please let me know what is confusing, because I'm also learning how to ask questions about this kind of thing.
Functions that produce matplotlib plots should take a figure or axes as input and only optionally create those if needed. They should return the created objects for further use. Finally, they should not call plt.show(), or if they must, provide an opt-out option.
For example, for a single axes plotting function, it could look like
def plottingfunction(*arguments, ax=None, show=True):
if ax is None:
fig, ax = plt.subplots()
else:
fig = ax.figure
# do something with fig and ax here, e.g.
line, = ax.plot(*arguments)
if show:
plt.show()
return fig, ax, line
If you adhere to such structure, it's easy to do whatever you need to after calling the function
fig, _, _ = plottingfunction([1,2,3], [3,2,4], show=False)
fig.savefig("myplot.png")
plt.show()
Based on the John Hunter's answer to a question regarding using patches in animations here, I am under the impression that:
I can create a patch object, with its animated stated being True.
Add it to an existing axis (let's say the axis object is called ax) using the add_patch method.
Then when I want to draw the patch, I do: ax.draw_artist(patch).
Doing this, I am faced with the following error:
File "environment.py", line 254, in animation_function
ax.draw_artist(patches[index])
File "A:\Anaconda\lib\site-packages\matplotlib\axes\_base.py", line 2121, in draw_artist
assert self._cachedRenderer is not None
AssertionError
The top level code is organized as follows:
a function creates patches from data, and then adds them to an axis object -- basically, I get a list of patch objects back, patches, where each patch has been added to ax; I think the issue might be here, since the patch objects in patches are not really connected to ax..., they were added to it, but passed by copy, not reference?
the animation function uses the number (let's say n) received from FuncAnimation to reference relevant patch objects, and then calls ax.draw_artist(patches[n])
At first I was doing the following:
patches = []
...
patch = mpatches.PathPatch(...)
patch.set_animated(True)
ax.add_patch(patch)
patches.append(patch)
...
ax.draw_artist(patches[n])
Then, after reading the documentation, which suggests that a patch object (possibly now connected to an axes object?) is returned, I tried the following:
patches = []
...
patch = mpatches.PathPatch(...)
patch.set_animated(True)
ax_patch = ax.add_patch(patch)
patches.append(ax_patch)
...
ax.draw_artist(patches[n])
However, the issue is still the same.
Can you comment on what you think the issue might be, or where I might need to provide additional information in order to figure out the issue?
EDIT: the top-level function where the error arises from.
def create_animation_from_data(self, vertex_marker_radius=0.25, labels = ['a', 'a', 'b', 'b'], unit_line=0.5, colours=['red', 'blue', 'green', 'orange']):
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(-2, 100), ylim=(-2, 100))
ax.grid()
print "Initializing patches..."
patches, patch_indices_per_timestep, num_polys, num_timesteps = self.make_patches_from_environment(ax, vertex_marker_radius, labels, unit_line, colours)
def animation_function(n):
relevant_patch_indices = patch_indices_per_timestep[n]
for polygon_based_index_group in relevant_patch_indices:
for index in polygon_based_index_group:
patches[index].draw(fig.canvas.get_renderer())
return patches,
print "Beginning animation..."
ani = animation.FuncAnimation(fig, animation_function, blit=True)
plt.show()
You're understanding everything correctly, but missing a couple of lower-level steps. The renderer hasn't been initialized yet, and you're getting an error that reflects that.
In a nutshell, you can't use draw_artist before the plot has been drawn the first time. You'll need to call fig.canvas.draw() (in some cases you can get away with just fig.canvas.get_renderer()) at least once before you can use draw_artist.
If you're running into this problem, it's often because you're trying to go "against the grain" and do things that are better not to be handled directly.
What exactly are you trying to do? There's likely an easier way to handle this (e.g. if you're trying to grab a background, put this part of your code in a callback to the draw event).
Let me back up and explain what's happening. Matplotlib Artists draw on a FigureCanvas (e.g. fig.canvas) using an instance of Renderer (e.g. fig.canvas.renderer). The renderer is backend-specific and low-level. You normally won't touch it directly.
ax.draw_artist is a lower-level function than fig.canvas.draw. More specifically, it's shorthand for artist.draw(renderer).
Initializing the renderer is relatively slow, so it's cached and reused unless the figure's size or dpi changes. This is what the error you're getting is saying: The canvas doesn't have a renderer yet.
You have a few different options. You could manually initialize the renderer (the easy way is to call fig.canvas.get_renderer()). However, sometimes you're want to get something (such as the size of a text object) that isn't defined until after it's been drawn. In those cases, you'll need a "full" fig.canvas.draw().
Usually, though, running into things like this is a sign that there's an easier way to do it. Often it's best to put code that needs a draw to have happened into a callback to the draw event. (Especially if it's something that depends on the exact size of the figure -- e.g. a background for blitting).
Update based on code sample
If you're using the matplotlib.animation framework, then you don't need to draw the artists inside of the update function. The animation framework will take care of that step for you.
It sounds like you're trying to display only a subset of the artists you've plotted at each timestep?
If so, you might consider toggling their visibility instead. As a quick example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation
def main():
fig, ax = plt.subplots()
p = Plotter(ax)
# Note: We need to save a referce to the animation object, otherwise it
# will be garbage collected and never properly animate.
anim = matplotlib.animation.FuncAnimation(fig, p.update,
init_func=p.anim_init)
plt.show()
class Plotter(object):
def __init__(self, ax):
self.ax = ax
def anim_init(self):
self.triangles = [self.random_triangle() for _ in range(10)]
# Initially, show a blank plot...
for tri in self.triangles:
tri.set_visible(False)
return self.triangles
def update(self, i):
"""Animation step."""
# Hide all triangles
for tri in self.triangles:
tri.set(visible=False)
# pick 2 at random to show (e.g. your patch_indices_per_timestep)
for tri in np.random.choice(self.triangles, size=2):
tri.set(visible=True)
return self.triangles
def random_triangle(self):
x, y = np.random.random((2,3))
x += 10 * np.random.random(1)
y += 10 * np.random.random(1)
return self.ax.fill(x, y)[0]
main()
I have multiple lines to be drawn on the same axes, and each of them are dynamically updated (I use set_data), The issue being that i am not aware of the x and y limits of each of the lines. And axes.autoscale_view(True,True,True) / axes.set_autoscale_on(True) are not doing what they are supposed to. How do i auto scale my axes?
import matplotlib.pyplot as plt
fig = plt.figure()
axes = fig.add_subplot(111)
axes.set_autoscale_on(True)
axes.autoscale_view(True,True,True)
l1, = axes.plot([0,0.1,0.2],[1,1.1,1.2])
l2, = axes.plot([0,0.1,0.2],[-0.1,0,0.1])
#plt.show() #shows the auto scaled.
l2.set_data([0,0.1,0.2],[-1,-0.9,-0.8])
#axes.set_ylim([-2,2]) #this works, but i cannot afford to do this.
plt.draw()
plt.show() #does not show auto scaled
I have referred to these already, this , this.
In all cases I have come across, the x,y limits are known. I have multiple lines on the axes and their ranges change, keeping track of the ymax for the entire data is not practical
A little bit of exploring got me to this,
xmin,xmax,ymin,ymax = matplotlib.figure.FigureImage.get_extent(FigureImage)
But here again, i do not know how to access FigureImage from the Figure instance.
Using matplotlib 0.99.3
From the matplotlib docs for autoscale_view:
The data limits are not updated automatically when artist data are changed after the artist has been added to an Axes instance. In that case, use matplotlib.axes.Axes.relim() prior to calling autoscale_view.
So, you'll need to add two lines before your plt.draw() call after the set_data call:
axes.relim()
axes.autoscale_view(True,True,True)
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.