Redrawing legend when the figure is closed - python

Using matplotlib, I'm trying to execute a callback function when a figure is closed, which redraws the figure legend. However, when I call ax.legend(), it seems to block any further code being executed. So in the code below, 'after' is never printed.
Could someone explain why this is? And is it possible for me to run code after the legend() call, but before the figure closes? The ultimate goal is to save two different versions of a figure when it is closed, redrawing the legend in between saves. Thanks.
from __future__ import print_function
import matplotlib.pyplot as plt
def handle_close(evt):
f = evt.canvas.figure
print('Figure {0} closing'.format(f.get_label()))
ax = f.get_axes()
print('before')
leg = ax.legend() # This line causes a problem
print('after') # This line (and later) is not executed
xs = range(0, 10, 1)
ys = [x*x for x in xs]
zs = [3*x for x in xs]
fig = plt.figure('red and blue')
ax = fig.add_subplot(111)
ax.plot(xs, ys, 'b-', label='blue plot')
ax.plot(xs, zs, 'r-', label='red plot')
fig.canvas.mpl_connect('close_event', handle_close)
ax.legend()
plt.show()

Ok, sorry, I have figured it out. f.get_axes() returns a list of axes objects. So the later call to ax.legend() doesn't work correctly.
Changing to the lines below fixes the problem:
axs = f.get_axes()
for ax in axs:
leg = ax.legend()
I'm still not sure why this didn't produce some kind of interpreter error though.

Related

How to plot using a prewritten library function into a subplot?

I've been stumbling around this issue for a while, but today I really want to figure out if it can be done.
Say you have a function from a library that does some plotting, such as:
def plotting_function():
fig, ax = plt.subplots()
ax.plot([1,2,3], [2,4,10])
return fig
If I want to add this single plot multiple times to my own subplots, how could I do this?
I'm not able to change the plotting_function, as it's from a library, so what I've tried is:
fig, axs = plt.subplots(1,3)
for i in range(3):
plt.sca(axs[i])
plotting_function()
plt.show()
This results in an empty subplot with the line graphs plotting separate.
Is there any simple answer to this problem? Thanks in advance.
I think you might be better off to monkey patch plt.subplots(), but it is possible to move a subplot from one figure to another. Here's an example, based on this post:
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox
def plotting_function1():
fig, ax = plt.subplots()
ax.plot([1,2,3], [2,4,10])
return fig
def plotting_function2():
fig, ax = plt.subplots()
ax.plot([10,20,30], [20,40,100])
return fig
def main():
f1 = plotting_function1()
ax1 = plt.gca()
ax1.remove()
f2 = plotting_function2()
ax2 = plt.gca()
ax1.figure = f2
f2.axes.append(ax1)
f2.add_axes(ax1)
# These positions are copied from a call to subplots().
ax1.set_position(Bbox([[0.125, 0.11], [0.477, 0.88]]))
ax2.set_position(Bbox([[0.55, 0.11], [0.9, 0.88]]))
plt.show()
main()

How to can display two distplots next to each other?

I am unable to display two distplots next to each other, when plotted alone both work fine.
f, (ax1, ax2) = plt.subplots(1,2)
sns.distplot(df_reqd_data_0['Total_Hood_Group_Earnings'], ax=ax1)
plt.show()
sns.distplot(df_reqd_data_0['Total_Partner_Earnings'], ax=ax2 )
plt.show()
You need to call the plot.show() command once after both the distplot commands.
Remove the extra plot.show(), so that the code looks like this.
f, (ax1, ax2) = plt.subplots(1,2)
sns.distplot(df_reqd_data_0['Total_Hood_Group_Earnings'], ax=ax1)
sns.distplot(df_reqd_data_0['Total_Partner_Earnings'], ax=ax2 )
plt.show()
EDIT:
Apart from the extra plt.show(), I am not sure what is sns here. But just to illustrate my point and answer the question posted by the OP:
"How to can display two distplots next to each other?"
try this code,
import matplotlib.pyplot as plt
x = range(10)
y = range(10)
plt.subplot(2,1,1)
plt.plot(y)
plt.subplot(2,1,2)
plt.plot(x)
plt.show()
and you can see why it works.

Python: matplotlib - loop, clear and show different plots over the same figure

I want to see how a plot varies with different values using a loop. I want to see it on the same plot. But i do not want to remains of the previous plot in the figure. In MATLAB this is possible by creating a figure and just plotting over the same figure. Closing it when the loop ends.
Like,
fh = figure();
%for loop here
%do something with x and y
subplot(211), plot(x);
subplot(212), plot(y);
pause(1)
%loop done
close(fh);
I am not able to find the equivalent of this in matplotlib. Usually all the questions are related to plotting different series on the same plot, which seems to come naturally on matplotlib, by plotting several series using plt.plot() and then showing them all finally using plt.show(). But I want to refresh the plot.
There are essentially two different ways to create animations in matplotlib
interactive mode
Turning on interactive more is done using plt.ion(). This will create a plot even though show has not yet been called. The plot can be updated by calling plt.draw() or for an animation, plt.pause().
import matplotlib.pyplot as plt
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,17)
ax1.set_ylim(-400,3000)
plt.ion()
for i in range(15):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
plt.pause(0.1)
plt.ioff()
plt.show()
FuncAnimation
Matplotlib provides an animation submodule, which simplifies creating animations and also allows to easily save them. The same as above, using FuncAnimation would look like:
import matplotlib.pyplot as plt
import matplotlib.animation
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,18)
ax1.set_ylim(-400,3000)
def update(i):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=14, repeat=False)
plt.show()
An example to animate a sine wave with changing frequency and its power spectrum would be the following:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
x = np.linspace(0,24*np.pi,512)
y = np.sin(x)
def fft(x):
fft = np.abs(np.fft.rfft(x))
return fft**2/(fft**2).max()
fig, (ax1,ax2) = plt.subplots(nrows=2)
line1, = ax1.plot(x,y)
line2, = ax2.plot(fft(y))
ax2.set_xlim(0,50)
ax2.set_ylim(0,1)
def update(i):
y = np.sin((i+1)/30.*x)
line1.set_data(x,y)
y2 = fft(y)
line2.set_data(range(len(y2)), y2)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=60, repeat=True)
plt.show()
If you call plt.show() inside the loop you will see the plot for each element on the loop as long as you close the window containing the figure. The process, will be plot for the first element, then if you close the window you will see the plot for the second element in the loop, etc

cannot update pyplot dynamically in python 3.5

Want to use the following piece of code to dynamically demonstrate how the prediction (<<2) is approaching the real values (<<1). However, only <<1 shows up and totally cannot see <<2 showing up. Any idea to fix it ?
.....
# plot the real data
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x_data, y_data) <<<< 1
plt.ion()
plt.show()
for i in range(1001):
sess.run(train_step, feed_dict={xs:x_data, ys:y_data})
if i % 50 == 0:
print(sess.run(loss,feed_dict={xs:x_data, ys:y_data}))
try:
ax.lines.remove(lines[0])
except Exception: # if plotting the first time
pass
prediction_value = sess.run(prediction, feed_dict={xs:x_data})
lines = ax.plot(x_data,prediction_value,'r-', lw = 5) <<< 2
plt.pause(0.1)
Thanks in advance !
plt.show() waits for you to close the plot windows before proceeding. If you remove it, but still keep the plt.ion() and your platform supports it, it should run fine:
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(0, 0, 'o')
for i in range(1,10):
ax.plot(i, i, 'o')
plt.pause(1)
works for me with matplotlib 2.0.0, Python 3.5 on Mac (draws a point every second)
You need to call plt.show() at the end of your script. Otherwise it blocks everything else, as the plotting window takes over the event loop.
In order to show the figure, use plt.draw() in interactive mode.
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x_data, y_data)
plt.ion()
plt.draw()
for i in range(1001):
...
lines = ax.plot(x_data,prediction_value,'r-', lw = 5)
plt.draw()
plt.pause(0.1)
plt.ioff()
plt.show()
Adding magic command of %matplotlib serves the intention, with a popped-up window drawn on.
But just wondering whether this is the only way ... any other way without any additional window popped up ?

matplotlib: multiple plots on one figure

I have some code:
import matplotlib.pyplot as plt
def print_fractures(fractures):
xpairs = []
ypairs = []
plt.figure(2)
plt.subplot(212)
for i in range(len(fractures)):
xends = [fractures[i][1][0], fractures[i][2][0]]
yends = [fractures[i][1][1], fractures[i][2][1]]
xpairs.append(xends)
ypairs.append(yends)
for xends,yends in zip(xpairs,ypairs):
plt.plot(xends, yends, 'b-', alpha=0.4)
plt.show()
def histogram(spacings):
plt.figure(1)
plt.subplot(211)
plt.hist(spacings, 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
plt.show()
histogram(spacings)
print_fractures(fractures)
This code will produce the following output:
My questions are:
1) Why are two separate figures being created? I thought the subplot command would combine them into one figure. I thought it might be the multiple plt.show() commands, but I tried commenting those out and only calling it once from outside my functions and I still got 2 windows.
2) How can I combine them into 1 figure properly? Also, I would want figure 2 axes to have the same scale (i.e. so 400 m on the x axis is the same length as 400 m on the y-axis). Similarly, I'd like to stretch the histogram vertically as well - how is this accomplished?
As you observed already, you cannot call figure() inside each function if you intend to use only one figure (one Window). Instead, just call subplot() without calling show() inside the function. The show() will force pyplot to create a second figure IF you are in plt.ioff() mode. In plt.ion() mode you can keep the plt.show() calls inside the local context (inside the function).
To achieve the same scale for the x and y axes, use plt.axis('equal'). Below you can see an illustration of this prototype:
from numpy.random import random
import matplotlib.pyplot as plt
def print_fractures():
plt.subplot(212)
plt.plot([1,2,3,4])
def histogram():
plt.subplot(211)
plt.hist(random(1000), 100)
plt.xlabel('Spacing (m)', fontsize=15)
plt.ylabel('Frequency (count)', fontsize=15)
histogram()
print_fractures()
plt.axis('equal')
plt.show()

Categories