why plt.show() shows one extra blank figure - python

I use python 2.7 and trying to plot a simple percentile bat chart.
I get the figure that i want, the problem is that, with it, when using plt.show() i get an extra blank image,
I tried plt.close(), plt.clf() and plt.figure() to create a clean plt object, this is my function:
import matplotlib.pyplot as plt
plt.grid(True)
data = zip(*percentiles)
data = [list(i) for i in data]
tick_range = data[0]
ticks = [str(i) + "%" for i in tick_range]
tick_range = [x+2.5 for x in tick_range]
fig, ax = plt.subplots()
plt.bar(data[0], data[1], width=5)
plt.show()
the data (percentiles) variable is of the following structure [(i,v),(i,v)....] when i is a index, and v is a floating point value.
Thanks!

The issue is that plt.grid(True) operates on the current figure and since no figure currently exists when you get to that line, one is created automatically. Then you create another figure when you call plt.subplots()
You should add the gridlines after you create your plots
plt.bar(data[0], data[1], width=5)
plt.grid(True)
plt.show()
Alternately, just call bar without calling subplots since bar will automatically create a figure and axes as needed.
plt.grid(True)
plt.bar(data[0], data[1], width=5)
plt.show()

Related

How to show multiple already plotted matplotlib figures side-by-side or on-top in Python without re-plotting them?

I have already plotted two figures separately in a single jupyter notebook file, and exported them.
What I want is to show them side by side, but not plot them again by using matplotlib.pyplot.subplots.
For example, in Mathematica, it's easier to do this by just saving the figures into a Variable, and displaying them afterwards.
What I tried was saving the figures, using
fig1, ax1 = plt.subplots(1,1)
... #plotting using ax1.plot()
fig2, ax2 = plt.subplots(1,1)
... #plotting using ax2.plot()
Now, those fig1 or fig2 are of type Matplotlib.figure.figure which stores the figure as an 'image-type' instance. I can even see them separately by calling just fig1 or fig2 in my notebook.
But, I can not show them together as by doing something like
plt.show(fig1, fig2)
It returns nothing since, there wasn't any figures currently being plotted.
You may look at this link or this, which is a Mathematica version of what I was talking about.
assuming u want to merge those subplots in the end.
Here is the code
import numpy as np
import matplotlib.pyplot as plt
#e.x function to plot
x = np.linspace(0, 10)
y = np.exp(x)
#almost your code
figure, axes = plt.subplots(1,1)
res_1, = axes.plot(x,y) #saving the results in a tuple
plt.show()
plt.close(figure)
figure, axes = plt.subplots(1,1)
res_2, = axes.plot(x,-y) #same before
plt.show()
#restructure to merge
figure_2, (axe_1,axe_2) = plt.subplots(1,2) #defining rows and columns
axe_1.plot(res_1.get_data()[0], res_1.get_data()[1]) #using the already generated data
axe_2.plot(res_2.get_data()[0], res_2.get_data()[1])
#if you want show them in one
plt.show()
Not quite sure what you mean with:
but not plot them again by using matplotlib.pyplot.subplots.
But you can display two figures next to each other in a jupyter notebook by using:
fig, ax = plt.subplots(nrows=1, ncols=2)
ax[0] = ... # Code for first figure
ax[1] = ... # Code for second figure
plt.show()
Or above each other:
fig, ax = plt.subplots(nrows=2, ncols=1)
ax[0] = ... # Top figure
ax[1] = ... # Bottom figure
plt.show()

dividing long xticks in 2 lines matplotlib

I have the following matplotlib
I would like to divide x-ticks into 2 lines instead of 1 because sometimes they are so long that is why they come over another and then it is impossible to read x-ticks.
KEEP IN MIND X-ticks are not hard coded and they are changing. So not always same x-ticks.
So for following example it would be good if I have instead of to Schleswig-Holstein I could have:
to Schleswig-
Holstein
How would I put the string after - in newline for the x ticks? or simply after lets say 10 letters I wanna go to a new line
Btw it would be also good if I could center all the text like the example above
So following is also okay but not the best.
to Schleswig-
Holstein
PS: Here is the code I use:
# create figure
fig = plt.figure()
# x-Axis (sites)
i = np.array(i)
i_pos = np.arange(len(i))
# y-Axis (values)
u = urbs_values
o = oemof_values
plt.bar(i_pos-0.15, list(u.values()), label='urbs', align='center', alpha=0.75, width=0.2)
plt.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
plt.bar(i_pos+0.15, list(o.values()), label='oemof', align='center', alpha=0.75, width=0.2)
plt.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
# tick names
plt.xticks(i_pos, list(map((' to ').__add__, list(u.keys()))))
# plot specs
plt.xlabel('Lines')
plt.ylabel('Capacity [MW]')
plt.title(site+' '+name)
plt.grid(True)
plt.legend()
plt.ticklabel_format(style='sci', axis='y')
# plt.show()
# save plot
fig.savefig(os.path.join(result_dir, 'comp_'+name+'_'+site+'.png'), dpi=300)
plt.close(fig)
You can use re as suggested on this answer and create a list of new labels with a new line character after every 10th character.
import re
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
xlabels = ["to Schleswig-Holstein", "to Mecklenburg-Vorpommern", r"to Lower Saxony"]
xlabels_new = [re.sub("(.{10})", "\\1\n", label, 0, re.DOTALL) for label in xlabels]
plt.plot(range(3))
plt.xticks(range(3), xlabels_new)
plt.show()
Alternative
xlabels_new = [label.replace('-', '-\n') for label in xlabels]

Empty figures in python and cannot save the figure

Here is the code of plotting the figures. But why are there always two empty figures before the third expected figure, it seems I created two blank fig.
And I cannot save the figure in my local computer fig.savefig('Sens.png'). There is an error The C++ part of the object has been deleted, attribute access no longer allowed(actually successfully saved only for one time).
fig = plt.figure(figsize=(10,10))
m = 1
for s in dataList:
plt.subplot(2,2,m)
f = interp1d(FXSpotList, s, 'cubic')
xnew = np.linspace(FXSpotList[0], FXSpotList[-1], 40, True)
plt.plot(xnew, f(xnew), '-')
plt.xlabel('Spot')
plt.ylabel(titleList[m-1])
plt.axvline(x=tradeTest.Pair().Spot(), linestyle='--')
plt.axhline(y=0, linestyle='--')
m = m + 1
plt.figtext(0.5, 0.01, 'Type='+str(tradeTest.Types()[0]), ha='center')
plt.tight_layout()
plt.show()
plt.close()
fig.savefig('Sens.png')
Although you did not provide a Minimal, Complete, and Verifiable example, it is obvious that there are things wrong with your loop construction. You show, close, then save the plot in every loop, which is probably not, what you are intending to do. A minimal example of your loop would be
import numpy as np
from matplotlib import pyplot as plt
#sample list to iterate over
dataList = ["fig1", "fig2", "fig3"]
plt.figure(figsize=(10,10))
#loop over the list, retrieve data entries and index
for i, s in enumerate(dataList):
#define position of the plot in a 2 x 2 grid
plt.subplot(2, 2, i + 1)
#random plot, insert your calculations here
plt.plot(range(3), np.random.randint(0, 10, 3))
#utilize list data
plt.title(s)
#save figure
plt.savefig('test.png')
#show figure
plt.show()

Matplotlib: Saved files in a loop aren't the same as in show()

My program shows the correct graph in the plt.show() pop up but not in the fig.savefig one. I'm quite new to python so apologies if it is something simple.
I'm using python 2.7.10, windows (10).
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt('strike_details.txt') #, skip_header= 0
header= 3
information=10000
width = 5
files = 16
types = 4
length = information + header
frames = data[header:length,0]
fig= plt.figure()
plt.grid(True)
for i in range(0,int(files)):
density=data[(header+i*length):(length+i*length),4]
plt.plot(frames,density, label=data[i*length+1][2])
for j in range (0,files/types):
if i==(types*(j+1)-1):
plt.legend(loc='best')
plt.xlabel('$Frames$', fontsize=22)
plt.ylabel('$Density$', fontsize=22)
fig.savefig(str(data[j*length+1][0])+'_'+str(data[j*length+1][1])+'_'+str(data[j*length+1][2])+'.png',format='png', dpi=fig.dpi)
plt.show()
plt.clf()
The program produces four files with different file names but they're all of the first group you see in the plt.show pop up.
If I missed out anything important let me know.
Thanks,
Lio
I think this is due to mixing the API-style and interactive-styles of matplotlib. When you call plt.show() the link between the active figure and fig is broken, and so you continue to output the first figure you created. I can reproduce this problem with this minimal example:
import matplotlib.pyplot as plt
fig = plt.figure()
for n in range(0,10):
plt.plot(list(range(0,n)))
fig.savefig('test%d.png' % n)
plt.show()
plt.clf()
If you remove the show() the issue goes away.
The correct way to do this is to access the current interactive figure via plt.gcf():
plt.gcf().savefig(...)
Alternatively, you can workaround it by recreating the figure object on each loop:
for i in range(0,int(files)):
fig= plt.figure()
plt.grid(True)
...

Matplotlib Animation for custom artist classes

Goal
Hi,
I am trying to animate a complex figure with several subplots and have started testing with the artist animation and the function animation methods.
For now, my goal is to have the subplot on the left show a moving colored line (not the problem) and the subplot on the right show an updated representation of a brain scan (the problem). Static, this looks something like this.
# Imports
import nilearn as nil
from nilearn import plotting as nlp
from matplotlib import pyplot as plt
window = np.arange(0,200-50)
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
a = ax.axvspan(window[0], window[0]+50, color='blue', alpha=0.5)
ay = fig.add_subplot(122)
b = nlp.plot_stat_map(nil.image.index_img(s_img, 0), axes=ay, colorbar=False, display_mode='x', cut_coords=(0,))
Problem
As you can see, I am using nilearn for plotting the brain image. For some reason, the nilearn object from plot_stat_map does not have an attribute set_visible unlike the matplotlib object from axvspan.
So when I attempt a simple animation like so:
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
ay = fig.add_subplot(122)
iml = list()
for i in np.arange(50):
a = ax.axvspan(window[i], window[i]+50, color='blue', alpha=0.5)
b = nlp.plot_stat_map(nil.image.index_img(s_img, i), axes=ay)
iml.append((a,b))
ani = animation.ArtistAniTruemation(fig, iml, interval=50, blit=False,
repeat_delay=1000)
it crashes with the following error:
/home/surchs/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/matplotlib/animation.pyc in _init_draw(self)
974 for f in self.new_frame_seq():
975 for artist in f:
--> 976 artist.set_visible(False)
977 # Assemble a list of unique axes that need flushing
978 if artist.axes not in axes:
AttributeError: 'OrthoSlicer' object has no attribute 'set_visible'
Makes sense, nilearn does maybe not conform to matplotlibs expectations. So I try the function animation method like so:
def show_things(i, window, ax, ay):
ax.axvspan(window[i], window[i]+50, color='blue', alpha=0.5)
nlp.plot_stat_map(nil.image.index_img(s_img, i), axes=ay, colorbar=False, display_mode='x', cut_coords=(0,))
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ax.set_xlim([0, 200])
ay = fig.add_subplot(122)
ani = animation.FuncAnimation(fig, show_things, interval=10, blit=False, fargs=(window, ax, ay))
Although I am not sure if I am using things correctly, this gives me an animated brain plot on the right. However, the plot on the left is now not updated but just drawn over. So instead of a sliding bar, I get an expanding color surface. Something like this:
Question
How do I
get the plot on the left to update (as opposed to overwrite) on each iteration when using the function animation method? I already tried the ax.cla() function in matplotlib but since this also clears all axis attributes (like xlim) this is not a solution for me. Are there altneratives?
get the plot on the right to work with the artist animation method even though the custom plotting class is obviously missing a crucial attribute.
Also, I am not sure if I am doing the whole implementation part right, so any advice on that front is also very appreciated.
I suspect you may need to clear the axvspan axis between plots with ax.cla() to get the correct left plot (N.B. probably should clear the right plot too). To get round the problem of missing attributes, I'd suggest extracting the data from the returned handle from nlp.plot_stat_map and plotting with matplotlib pcolormesh (or imshow). Another possibility is creating a child class and adding this method yourself. It may also be worth submitting a bug/feature request to nilearn if this should be present.
By the way, if you're only after a quick and easy plot, you can do a poor man's version of animation using interactive plots, as a minimal example,
import matplotlib.pyplot as plt
import numpy as np
import time
#Interactive plot
plt.ion()
#Setup figures
fig = plt.figure(figsize=(7,4))
ax = fig.add_subplot(121)
ay = fig.add_subplot(122)
plt.show()
x = np.linspace(0,2*np.pi)
for i in range(10000):
print(i)
#Clear axes
ax.cla(); ay.cla()
#Update data
yx = np.sin(x+i*0.1)
yy = np.sin(2.*(x+i*0.1))
#Replot
ax.plot(x,yx)
ay.plot(x,yy)
#Pause to allow redraw
plt.draw()
plt.pause(0.01)

Categories

Resources