We're in the process of migrating from Basemap to Cartoy and finding the savefig functionality to be a far too slow for our app, specifically when using the LambertConformal CRS. I ran a quick check using the the code snippet below and found that it takes about 10x as long to save using the LambertConformal projection vs PlateCarree. The plots within our app include numerous data layers including colormaps and contours but I found that savefig is consistently much slower when using the LCC CRS. I saw that others suggested users set PYPROJ_GLOBAL_CONTEXT=ON but since our app is multithreaded that isn't an option for us. We can adopt the PlateCarree CRS as our default if needed but I thought I would check to see if other users in the community had similar issues.
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import time
extent = [-10, 10, 50, 60]
lcc_projection = ccrs.LambertConformal(
central_latitude=55,
central_longitude=0,
)
pc_projection = ccrs.PlateCarree()
pc_plot = plt.axes(projection=pc_projection, facecolor="dimgrey")
pc_plot.set_extent(extent, crs=pc_projection)
pc_plot.add_feature(cfeature.LAND)
t = time.time()
plt.savefig('map_pc.png')
print(f"pc savefig time : {time.time() - t}") # Takes ~3s
plt.close()
lcc_plot = plt.axes(projection=lcc_projection, facecolor="dimgrey")
lcc_plot.set_extent(extent, crs=pc_projection)
lcc_plot.add_feature(cfeature.LAND)
t = time.time()
plt.savefig('map_lcc.png')
print(f"lcc savefig time : {time.time() - t}") # Takes ~30s
plt.close()
Resulting images:
PC plot
LCC Plot
Related
https://www.youtube.com/watch?v=pl3D4SosO_4&list=PL9mhv0CavXYjiIniCLj_5KKN58PaxJBVj&index=2
Right at time stamp 2:13.
I am trying to follow a youtube tutorial and I have hit a road block. There is no documentation online that tells me how to implement matplotlib.collections.PathCollection
The youtuber in this video that I am following runs the first bit of his code (at about 2:13) and a plot appears with some color data points. Above this plot it says <matplotlib.collections.PathCollection at 0x208cd62cef0>
If anyone could tell me how this youtuber got this plot to appear I would be forever grateful.
I have found documentation for matplotlib.collections, but zero information on how it is used, I asked the youtuber how he got to this point in the comments and am waiting on an answer.
Thank you Craig, I am adding the code that doesn't work for me here
EDIT:
(I have been attempting this in a pycharm IDE and the video is using Jupyter, idk if that makes a difference)
import numpy as np
from matplotlib import plyplot as plt
from sklearn.datasets import make_blobs
X,y = make_blobs(n_samples = 500, centers = 5, random_state = 3)
plt.figure(0)
plt.grid(True)
plt.scatter(X[:,0], X[:,1],c=y)
when this is run in the video a plot with color clusters appears.
Maybe I should be trying this in jupyter, maybe some image libraries are pre-loaded there?
Jupyter is a special interactive environment and it automatically renders matplotlib plots when it runs a cell that creates a plot. If you are doing the same thing in an IDE, then you will need to explicitly render the plot by calling plt.show() when you want the plot to appear. You can do this for your code by adding it to the end:
import numpy as np
from matplotlib import plyplot as plt
from sklearn.datasets import make_blobs
X,y = make_blobs(n_samples = 500, centers = 5, random_state = 3)
plt.figure(0)
plt.grid(True)
plt.scatter(X[:,0], X[:,1],c=y)
plt.show() # <-- show the plot
I generate a lots of figures with a script which I do not display but store to harddrive. After a while I get the message
/usr/lib/pymodules/python2.7/matplotlib/pyplot.py:412: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam figure.max_num_figures).
max_open_warning, RuntimeWarning)
Thus, I tried to close or clear the figures after storing. So far, I tried all of the followings but no one works. I still get the message from above.
plt.figure().clf()
plt.figure().clear()
plt.clf()
plt.close()
plt.close('all')
plt.close(plt.figure())
And furthermore I tried to restrict the number of open figures by
plt.rcParams.update({'figure.max_num_figures':1})
Here follows a piece of sample code that behaves like described above. I added the different options I tried as comments at the places I tried them.
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
#plt.rcParams.update({'figure.max_num_figures':1})
for i in range(0,30):
fig, ax = plt.subplots()
ax.hist([df])
plt.savefig("/home/userXYZ/Development/pic_test.png")
#plt.figure().clf()
#plt.figure().clear()
#plt.clf()
#plt.close() # results in an error
#plt.close('all') # also error
#plt.close(plt.figure()) # also error
To be complete, that is the error I get when using plt.close:
can't invoke "event" command: application has been destroyed
while executing "event generate $w <>"
(procedure "ttk::ThemeChanged" line 6)
invoked from within "ttk::ThemeChanged"
The correct way to close your figures would be to use plt.close(fig), as can be seen in the below edit of the code you originally posted.
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
fig, ax = plt.subplots()
ax.hist(df)
name = 'fig'+str(i)+'.png' # Note that the name should change dynamically
plt.savefig(name)
plt.close(fig) # <-- use this line
The error that you describe at the end of your question suggests to me that your problem is not with matplotlib, but rather with another part of your code (such as ttk).
plt.show() is a blocking function, so in the above code, plt.close() will not execute until the fig windows are closed.
You can use plt.ion() at the beginning of your code to make it non-blocking. Even though this has some other implications the fig will be closed.
I was still having the same issue on Python 3.9.7, matplotlib 3.5.1, and VS Code (the issue that no combination of plt.close() closes the figure). I have three loops which the most inner loop plots more than 20 figures. The solution that is working for me is using agg as backend and del someFig after plt.close(someFig). Subsequently, the order of code would be something like:
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plt
someFig = plt.figure()
.
.
.
someFig.savefig('OUTPUT_PATH')
plt.close(someFig) # --> (Note 1)
del someFig
.
.
.
NOTE 1: If this line is removed, the output figures may not be plotted correctly! Especially when the number of elements to be rendered in the figure is high.
NOTE 2: I don't know whether this solution could backfire or not, but at least it is working and not hugging RAM or preventing plotting figures!
import tensorflow as tf
from matplotlib import pyplot as plt
sample_image = tf.io.read_file(str(PATH / 'Path to your file'))
sample_image = tf.io.decode_jpeg(sample_image)
print(sample_image.shape)
plt.figure("1 - Sample Image ")
plt.title(label="Sample Image", fontsize=12, color="red")
plt.imshow(sample_image)
plt.show(block=False)
plt.pause(3)
plt.close()
plt.show(block=False)
plt.pause(interval) do the trick
This does not really solve my problem, but it is a work-around to handle the high memory consumption I faced and I do not get any of the error messages as before:
from pandas import DataFrame
from numpy import random
df = DataFrame(random.randint(0,10,40))
import matplotlib.pyplot as plt
plt.ioff()
for i in range(0,30):
plt.close('all')
fig, ax = plt.subplots()
ax.hist([df])
plt.savefig("/home/userXYZ/Development/pic_test.png")
I have written a relatively simple function in python that can be used to plot the time domain history of a data set as well as the frequency domain response of a data set after a fast fourier transform. In this function I use the command from pylab import * to bring in all the necessary functionality. However, despite successfully creating the plot, I get a warning stating
import * only allowed at the module level.
So if using the command from pylab import * is not the preferred methodology, how do I properly load all the necessary functionality from pylab. The code is attached below. Also, is there a way to close the figure after the function is exited, I have tried plt.close() which is not recognized for subplots?
def Time_Domain_Plot(Directory,Title,X_Label,Y_Label,X_Data,Y_Data):
# Directory: The path length to the directory where the output file is
# to be stored
# Title: The name of the output plot, which should end with .eps or .png
# X_Label: The X axis label
# Y_Label: The Y axis label
# X_Data: X axis data points (usually time at which Yaxis data was acquired
# Y_Data: Y axis data points, usually amplitude
from pylab import *
from matplotlib import rcParams
rcParams.update({'figure.autolayout': True})
Output_Location = Directory.rstrip() + Title.rstrip()
fig,plt = plt.subplots()
matplotlib.rc('xtick',labelsize=18)
matplotlib.rc('ytick',labelsize=18)
plt.set_xlabel(X_Label,fontsize=18)
plt.set_ylabel(Y_Label,fontsize=18)
plt.plot(X_Data,Y_Data,color='red')
fig.savefig(Output_Location)
plt.clear()
From the matplotlib documentation:
pylab is a convenience module that bulk imports matplotlib.pyplot (for plotting) and numpy (for mathematics and working with arrays) in a single name space. Although many examples use pylab, it is no longer recommended.
I would recommend not importing pylab at all, and instead try using
import matplotlib
import matplotlib.pyplot as plt
And then prefixing all of your pyplot functions with plt.
I also noticed that you assign the second return value from plt.subplots() to plt. You should rename that variable to something like fft_plot (for fast fourier transform) to avoid naming conflicts with pyplot.
With regards to your other question (about fig, save fig()) you're going to need to drop that first fig because it's not necessary, and you'll call savefig() with plt.savefig() because it is a function in the pyplot module. So that line will look like
plt.savefig(Output_Location)
Try something like this:
def Time_Domain_Plot(Directory,Title,X_Label,Y_Label,X_Data,Y_Data):
# Directory: The path length to the directory where the output file is
# to be stored
# Title: The name of the output plot, which should end with .eps or .png
# X_Label: The X axis label
# Y_Label: The Y axis label
# X_Data: X axis data points (usually time at which Yaxis data was acquired
# Y_Data: Y axis data points, usually amplitude
import matplotlib
from matplotlib import rcParams, pyplot as plt
rcParams.update({'figure.autolayout': True})
Output_Location = Directory.rstrip() + Title.rstrip()
fig,fft_plot = plt.subplots()
matplotlib.rc('xtick',labelsize=18)
matplotlib.rc('ytick',labelsize=18)
fft_plot.set_xlabel(X_Label,fontsize=18)
fft_plot.set_ylabel(Y_Label,fontsize=18)
plt.plot(X_Data,Y_Data,color='red')
plt.savefig(Output_Location)
plt.close()
I'm trying to draw some charts generated by Matplotlib but I am finding that whilst it works correctly the first time, if I refresh my page, Matplotlib will just hang on _tkinter.create deep within its inner workings (Tkinter.py) when creating the figure (plt.figure). I've managed to narrow down the issue with the following small example..
Template (Only needs this one line)
<img src="data:image/png;base64,{{ graph }}"/>
Chart creation
class PolarChart(object):
#staticmethod
def example_chart():
from math import pi, radians
import cStringIO
import base64
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
axis = fig.gca(polar=True)
n = 20
theta = np.linspace(0.0, 2 * pi, n, endpoint=False)
radii = 10 * np.random.rand(n)
axis.plot(theta, radii, marker='.', alpha=0.5, linewidth=1)
jpg_image_buffer = cStringIO.StringIO()
fig.savefig(jpg_image_buffer)
plt.close(fig)
base_array = base64.b64encode(jpg_image_buffer.getvalue())
jpg_image_buffer.close()
return base_array
View
graph = PolarChart.example_chart()
return render(request, "test.html", {'graph': graph})
Other stackoverflow questions have suggested using fig.clear() but this also results in the page not rendering (the same hanging effect)
The issue turned out to be an issue with using the wrong backend... The issue was resolved with jenshnielsen's suggestion to change the backend that is being used before importing pyplot
import matplotlib
matplotlib.use('agg')
If your reading this, Jens, please post your own answer and I'll gladly delete this one!
I will be making animations. In each frame I want to contain both a mayavi plot obtained with
mlab.pipeline.iso_surface(source, some other superfluous args)
and a matplotlib plot obtained using simply
pylab.plot(args)
I have scripts to do both separately, but have no idea how to go about combining them into one figure. I want the end product to be one script which contains the code from both the scripts that I currently have.
AFAIK, there is no direct way because the backends used are so different. It does not seem possible to add matplotlib axes to mayavi.figure or vice versa.
However, there is a "kind of a way" by using the the mlab.screenshot.
import mayavi.mlab as mlab
import matplotlib.pyplot as plt
# create and capture a mlab object
mlab.test_plot3d()
img = mlab.screenshot()
mlab.close()
# create a pyplot
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax1.plot([0,1], [1,0], 'r')
# add the screen capture
ax2 = fig.add_subplot(122)
ax2.imshow(img)
ax2.set_axis_off()
This is not necessarily the nicest possible way of doing things, and you may bump into resolution problems, as well (check the size of the mayavi window). However, it gets the job done in most cases.
Adding to the answer by DrV which helped me a great deal, you can work with the mlab figure to set resolution before screenshot such as with batch plotting:
mfig = mlab.figure(size=(1024, 1024))
src = mlab.pipeline.scalar_field(field_3d_numpy_array)
mlab.pipeline.iso_surface(src)
iso_surface_plot = mlab.screenshot(figure=mfig, mode='rgba', antialiased=True)
mlab.clf(mfig)
mlab.close()
# Then later in a matplotlib fig:
plt.imshow(iso_surface_plot)