I need to save graphics in very high quality, like eps. Basically I need to save 4 images of a hyperspectral data. Showing the graphics is not a problem, so I know my figures are ok, but I can't save them.
I have already tried other formats, like jpg,png or pdf, and none of them worked. I also already tried to save 4 figures instead of one figure with 4 subplots, but the problem persisted. I changed also matplotlib's backend a lot of times, and none of them worked.
Here is my code:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(0)
RGB = np.random.randint(255, size=(3518,117,3))
var = RGB[:,:,0]
cmap = plt.cm.get_cmap('cividis')
col = 3
forma = "eps"
fig, ax = plt.subplots(1,col,figsize = (1.5,45))
plt.subplots_adjust(left = 2, right = 4)
im = ax[0].imshow(RGB.astype(np.uint8), cmap = cmap)
ax[1].pcolormesh(var, cmap = cmap)
ax[2].plot(np.mean(var,axis = 1),np.arange(var.shape[0]))
plt.colorbar(im)
fig.savefig("runnable" + "." + forma, format = forma,dpi=1200 )
plt.show()
I get a warning that I don't understand:
RunTimeWarning:"Mean of empty slice"
I've done some research and it seems like this is common when there is NaN in the data. However, I looked for it and didn't find any.
edit: I changed the code so it can be runnable.
Related
My current goal is to animate two sets of data simultaneous on a single plot with 2 subplots. I previously asked a question here about clearing the axes data with animated seaborn graphs, but rather than clearing the data on the axes, I need to append/update them (i.e.: something like the animated plots here). To make matters more complicated, one of my sets of data are stored in numpy arrays whereas the other set is stored in a pandas dataframe.
I tried using some test data before using my actual data, but that doesn't work, especially when I try to animate the data stored in the dataframe:
import math, os
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import animation
def update(i,pass_data,fig,axes,sort_yrs,ani,x_val,y_val):
fil_pass_data = pass_data[pass_data['year'] == sort_yrs[i]]
fig.suptitle(r"Year = {}".format(sorted_yrs[i]),fontsize = 35,y = 1.000)
axes[1] = sns.lineplot(x = 'year',y = 'passengers',data = fil_pass_data,hue = 'month',
hue_order = sort_yrs,palette = 'Set1',legend = False)
ani1.set_data(x_val[:i],y_val[:i])
#Read/Generate Data
passenger_df = sns.load_dataset('flights')
yrs_of_int = ['Jan','Mar','Jun','Jul','Dec']
fil_pass_df = passenger_df[passenger_df['month'].isin(yrs_of_int)]
sorted_yrs = sorted(fil_pass_df['year'].unique())
x = np.linspace(0,2*np.pi,fil_pass_df['year'].unique().shape[0])
y = np.sin(x)
#Initialize figure and plot initial points
fig,axes = plt.subplots(ncols = 2,figsize = (15,7))
axes[0].plot(x[0],y[0],color = 'tab:red',label = 'Initial Point',marker = 'o',markersize = 5)
sns.lineplot(x = 'year',y = 'passengers',
data = fil_pass_df[fil_pass_df['year'] == sorted_yrs[0]],
hue = 'month',hue_order = yrs_of_int,palette = 'Set1')
ani1, = axes[0].plot(x[0],y[0],color = 'tab:blue',label = 'Dynamic Response')
#Formatting
axes[0].legend(loc='lower right')
axes[0].set_ylim(-1.1*np.pi,1.1*np.pi)
axes[0].set_xlim(-0.1,1.1*2*np.pi)
axes[1].set_xlim(fil_pass_df['year'].min()-1,fil_pass_df['year'].min()+1)
axes[1].set_ylim(100,700)
axes[0].set_title('Test Plot',fontsize = 15,pad = 5)
axes[1].set_title('Passengers by Month',fontsize = 15,pad = 5)
axes[0].set_ylabel(r"$\sin(x)$",fontsize = 20,labelpad = 10)
axes[0].set_xlabel(r"$x$",fontsize = 20,labelpad = 10)
axes[1].set_ylabel("Passenger Count",fontsize = 20,labelpad = 10)
axes[1].set_xlabel("Year",fontsize = 20,labelpad = 10)
#Create animation and save it
animation = animation.FuncAnimation(fig, update, fargs =
(fil_pass_df,fig,axes,sorted_yrs,ani1,x,y),
frames=range(0,len(sorted_yrs)),interval=0.5,
blit=False,repeat=True)
animation.save('test.mp4',
fps = 1, extra_args=['-vcodec', 'libx264'],dpi = 200)
The test mp4 file that is generated from here animates the left most plot (numpy array data), but fails to plot the right most plot (seaborn data). I have four theories as to why it doesn't work:
I'm not supposed to initialize the right most figure parameters before the update call. Instead, the figure parameters need to be set in update.
The fact that I'm specifying hue in the update function is somehow messing up with matplotlib's animation.
The fact that my data is either in numpy arrays or a pandas dataframe.
I'm overthinking this and forgot to one command in my code that makes this work.
I tried moving the right figure formatting into update but that didn't seem to work, so it might be bullet points #2-4.
Does anyone know why this is happening/how to solve it? For subplot animations, is there a rule as to whether everything should be stored in the same data type?
I have some simple code in a notebook to visualize an image with matplotlib
f = plt.figure()
plt.imshow(rgb_img)
# f.tight_layout(pad=0) doesn't fix the issue
f.canvas.draw()
# save figure as a np array for easy visualization w/ imshow later
fig_as_np_array = np.array(f.canvas.renderer.buffer_rgba())
At this point everything looks fine:
I then try to view the saved np array (plt.imshow(fig_as_np_array)) which I expect to display the same thing but instead I get odd whitespace plus a new sets of axis:
I can't for the life of me figure out what is adding the extra whitespace/axis, the shapes are slightly different as well:
print(f'rgb shape: {rgb_img.shape}') # prints: rgb shape: (480, 640, 3)
print(f'saved fig shape: {fig_as_np_array.shape}') # prints: saved fig shape: (288, 432, 4)
Any idea what is going on (fwiw I am visualizing this in a notebook). Thanks for your time
If I understood your question correctly, you'll have to ensure to create the figure with the correct dimensions and then remove the axes (via ax.set_axis_off()) and the frame of the figure around the image (via frameon=False) before writing to buffer, see the comments below:
import matplotlib as mpl
mpl.use("tkagg") # <— you may not need this,
# but I had to specify an agg backend manually
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
## image taken from
# "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Empty_road_at_night.jpg/1024px-Empty_road_at_night.jpg"
filename = "1024px-Empty_road_at_night.jpg"
im = mpimg.imread(filename)
## create the figure with the correct dpi & resolution
# and make sure that you specify to show "no frame" around the image
figure_dpi = 72
fig = plt.figure(figsize=(1024/figure_dpi,768/figure_dpi),dpi=figure_dpi,frameon=False,facecolor="w")
ax = fig.add_subplot()
## turn of axes, make imshow use the whole frame
ax.set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
plt.margins(0,0)
## show image
ax.imshow(im,zorder=0,alpha=1.0,origin="upper")
## add some text label
ax.text(300,600,"this is the middle lane",fontsize=30,color="w")
def fig2rgb_array(fig):
"""adapted from: https://stackoverflow.com/questions/21939658/"""
fig.canvas.draw()
buf = fig.canvas.tostring_rgb()
ncols, nrows = fig.canvas.get_width_height()
print("to verify, our resolution is: ",ncols,nrows)
return np.frombuffer(buf, dtype=np.uint8).reshape(nrows, ncols, 3)
## make a new figure and read from buffer
fig2,ax2 = plt.subplots()
ax2.imshow(fig2rgb_array(fig))
plt.show()
yields (note there is now only one set of axes around the image, not two):
i am trying to plot 2 rows of 4 pictures in the jupyter output, here is my code
for name in names_pred:
onlyfiles2 = [ f for f in listdir(os.path.join(TOP_DATA,names_supcetcs)) ]
posibles = plt.figure(figsize = (20,20))
for i in range(1,9):
plt.subplot(2,4,i)
plt.subplots_adjust(wspace=None)
img = mpimg.imread(TOP_DATA+names_supcetcs+ '/'+ onlyfiles2[i-1])
plt.imshow(img)
plt.show()
and the output is an iteration of the next pic but when the i=2,3,4... starts, there is no skipped space
how can i delete this space? i already tried
Improve subplot size/spacing with many subplots in matplotlib
but it make it worst some pictures are not shown
Did you try incorporating plt.tight_layout() in your code?
Wrote a trivial example because I don't have access to the data you're using, but it should be enough for you to get rolling.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = {i: fig.add_subplot(2,4,i) for i in range(1,9)}
x = np.linspace(1,10,100)
plots = [ax[i].plot(x,i*x) for i in range(1,9)]
plt.tight_layout()
plt.show()
thanks for the help, i actually manage it with an if in the nested for loop, it is not the most fancy way but it works
for i in range(1,9):
if i == 1:
fig.add_subplot(2,4,i)
plt.imshow(img0)
else:
fig.add_subplot(2,4,i)
img = mpimg.imread(TOP_DATA+names_supcetcs+ '/'+ onlyfiles2[i-1])
plt.imshow(img)
I'm struggling with the pdf backend of matplotlib and the contourf function. I try to plot forbidden areas on a 2D colored map. The forbidden areas are represented by hatched contourf with transparent (alpha=0.4) black color. the used code is given below, with two classes written to generate a user defined legend:
import matplotlib
print matplotlib.__version__
import matplotlib.patches as mpatches
class ConstrainedArea(object):
def __init__(self,axe,_xdata,_ydata,_zdata,boundaries,fc='none',ec='none',lw=None,alpha=None,hatch='//',ls='-',fill=False,label=None):
self.bnd = boundaries
self.fc = fc
self.ec = ec
self.lw = lw
self.ls = ls
self.al = alpha
self.hh = hatch
self.fl = fill
self.lb = label
self.ctr = axe.contour(_xdata,_ydata,_zdata,boundaries,linewidths=lw,colors=ec,linestyles=ls)
#self.ctf = axe.contourf(_xdata,_ydata,_zdata,boundaries,hatches=hatch,colors=fc,facecolors=fc,alpha=alpha)
self.ctf = axe.contourf(_xdata,_ydata,_zdata,boundaries,hatches=hatch,colors=fc,alpha=alpha,antialiased=False)
pass
class ConstrainedAreaHandler(object):
def legend_artist(self,legend,orig_handle,fontsize,handlebox):
x0,y0 = handlebox.xdescent,handlebox.ydescent
wi,he = handlebox.width,handlebox.height
patch = mpatches.Rectangle([x0,y0],wi,he,facecolor=orig_handle.fc,edgecolor=orig_handle.ec,hatch=orig_handle.hh,lw=orig_handle.lw,ls=orig_handle.ls,fill=orig_handle.fl,transform=handlebox.get_transform(),label=orig_handle.lb)
handlebox.add_artist(patch)
if __name__ == "__main__":
matplotlib.rcParams['backend'] = 'PDF'
import numpy,matplotlib.pyplot as plt
xs, ys = numpy.mgrid[0:30, 0:40]
zs = (xs - 15) ** 2 + (ys - 20) ** 2 + (numpy.sin(ys) + 10) ** 2
fig = plt.figure('test',figsize=(16.0,11.8875))
axe = fig.add_subplot(111)
pcm = axe.pcolormesh(xs,ys,zs,shading='gouraud')
cas = []
for bnd,hch,ls,lb in zip([[zs.min(),200],[400,zs.max()]],['/','\\'],['-','--'],[r'$f<200$',r'$f>400$']):
cas.append(ConstrainedArea(axe,xs,ys,zs,bnd,hatch=hch,fc='k',ec='k',lw=3,ls=ls,alpha=0.2,fill=False,label=lb))
cbr = fig.colorbar(pcm)
legframe = axe.legend(cas,[c.lb for c in cas],loc=3,handler_map={ConstrainedArea:ConstrainedAreaHandler()},ncol=3,fontsize=matplotlib.rcParams['font.size']*1.2**4,numpoints=1,framealpha=0.8)
#fig.savefig('test.pdf',bbox_inches='tight',facecolor='none',edgecolor='none',transparent=True)
fig.savefig('test.pdf',bbox_inches='tight',transparent=True)
After reading the tracks on matplotlib issues
GitHub matplotlib issue 3023, and
GitHub matplotlib issue 7421, I installed matplotlib 2.0.0 thinking it would solve my problem, but it didn't.
PROBLEM DEFINITION
Using the pdf backend I save the result as pdf, but reading the same file with evince, okular, or Acrobat Reader gives different screenshots, as illustrated on the figures below:
INFORMATION
The expected output is the one given by evince (visible hatches). As already mentioned in other tracks, the rasterization of the contourf object does give the expected result but I need vectorial images. Furthermore, if rasterized hatches are used with high dpi (>300), the hatch width tends to 0 yielding wrong output. Finally I found this track matplotlib generated PDF cannot be viewed with acrobat reader issue which yielded this workaround solution :
open the matplotlib output pdf file with evince
print it to pdf
vizualise the evince-printed output with okular
which gives the screenshot below:
Thanks a lot in advance for any explanation or solution for this problem. Don't hesitate if orther details/information are needed,
Tariq
I have a matplotlib figure with 3 sub-plots. The consensus from stackexchange seems to be to use the plt.tight_layout in order to get rid of the whitespace around the figure. This does not solve the problem in my case.
My code is as follows:
import numpy as np
import os
import pandas as pd
import matplotlib as mpl
from matplotlib import pyplot as plt
my_dpi = 1500
plt.ion()
index = np.linspace(0,5,10)
combined = np.linspace(0,1,10)
case1 = np.linspace(0,1,10)
case2 = np.linspace(0,1,10)
case3 = np.linspace(0,1,10)
tsfont = {'fontname':'Times-Roman'}
mpl.rcParams.update({'font.size': 18})
mpl.rc('font',family='Arial')
ms = 0
f = plt.figure()
f.set_size_inches(7.5,10)
f.axison=False
lw = 2
asp = 5
axarr1 = plt.subplot(3,1,1, adjustable='box',aspect = asp)
axarr1.set_xlim(0,5,10)
axarr1.set_ylim(0,1)
axarr1.set_ylabel('y')
axarr1.set_xlabel('$\\tau$', fontsize =25)
p = axarr1.plot(index,combined,color='navy', linewidth=lw, label = "Healthy")
axarr1.xaxis.set_label_coords(0.5, -0.05)
'''
Duct 2
'''
axarr2 = plt.subplot(3,1,2, adjustable='box',aspect = asp)
#axarr2.set_aspect('auto')
axarr2.set_xlim(0,5,10)
axarr2.set_ylim(0,1)
axarr2.set_ylabel('y')
axarr2.set_xlabel('$\\tau$', fontsize = 25)
g = axarr2.plot(index,combined,color='navy', linewidth=lw)
axarr2.xaxis.set_label_coords(0.5, -0.05)
'''
Duct 3
'''
axarr3 = plt.subplot(3,1,3, adjustable='box',aspect = asp)
axarr3.set_xlim(0,5,10)
axarr3.set_ylim(0,1)
axarr3.set_ylabel('y')
axarr3.set_xlabel('$\\tau$', fontsize = 25)
w = axarr3.plot(index,combined,color='navy', linewidth=lw)
axarr3.xaxis.set_label_coords(0.5, -0.05)
#plt.tight_layout()
plt.show()
Without using plt.tight_layout() I get the following result
Uncommenting the relevant line gives me
As is obvious, while the vertical spacing changes, the horizontal spacing does not
I'd like to know how to get rid of the horizontal whitespacing to the left and right.
The tight_layout option places the images closer to the borders. However, in your case, there is nothing to fill this empty space with, so it doesn't work.
If you want a narrower figure, you should change the horizontal dimension, e.g.:
plt.figure(..., tight_layout=True, figsize=(12, 5))
This is in inches, experiment with the numbers until it looks good.
You could also use 2 by 2 subplots to keep a square figure and use the space better:
plt.subplot(2, 2, ...)
although this may look suboptimal with a prime number of figures.