I am generating plots in pandas/matplotlib and wish to write them to an XLSX file. I am not looking to create native Excel charts; I am merely writing the plots as non-interactive images. I am using the XlsxWriter library/engine.
The closest solution I have found is the answer to this SO question, which suggests using the XlsxWriter.write_image() method. However, this method appears to take a filename as its input. I am trying to programmatically pass the direct output from a pandas/matplotlib plot() call, e.g. something like this:
h = results.resid.hist()
worksheet.insert_image(row, 0, h) # doesn't work
or this:
s = df.plot(kind="scatter", x="some_x_variable", y="resid")
worksheet.insert_image(row, 0, s) # doesn't work
Is there any way to accomplish this, short of the workaround of writing the image to a disk file first?
Update
Answer below got me on the right track and am accepting. I needed to make a few changes, mainly (I think) because I am using Python 3 and perhaps some API changes. Here is the solution:
from io import BytesIO
import matplotlib.pyplot as plt
imgdata = BytesIO()
fig, ax = plt.subplots()
results.resid.hist(ax=ax)
fig.savefig(imgdata, format="png")
imgdata.seek(0)
worksheet.insert_image(
row, 0, "",
{'image_data': imgdata}
)
The "" in the insert_image() code is to trick Excel, which is still expecting a filename/URL/etc.
You can save the image to memory as a file object (not to disk) and then use that when inserting to Excel file:
import matplotlib.pyplot as plt
from cStringIO import StringIO
imgdata = StringIO()
fig, ax = plt.subplots()
# Make your plot here referencing ax created before
results.resid.hist(ax=ax)
fig.savefig(imgdata)
worksheet.insert_image(row, 0, imgdata)
Related
Here's a dummy script that makes three plots and saves them to PDF.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({"A":np.random.normal(100),
"B":np.random.chisquare(5, size = 100),
"C":np.random.gamma(5,size = 100)})
for i in df.columns:
plt.hist(df[i])
plt.savefig(i+".pdf", format = "pdf")
plt.close()
I'm using spyder, which uses IPython. When I run this script, three windows pop at me and then go away. It works, but it's a little annoying.
How can I make the figures get saved to pdf without ever being rendered on my screen?
I'm looking for something like R's
pdf("path/to/plot/name.pdf")
commands
dev.off()
inasmuch as nothing gets rendered on the screen, but the pdf gets saved.
Aha. Partially based on the duplicate suggestion (which wasn't exactly a duplicate), this works:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
df = pd.DataFrame({"A":np.random.normal(100),
"B":np.random.chisquare(5, size = 100),
"C":np.random.gamma(5,size = 100)})
import matplotlib
old_backend = matplotlib.get_backend()
matplotlib.use("pdf")
for i in df.columns:
plt.hist(df[i])
plt.savefig(i+".pdf", format = "pdf")
plt.close()
matplotlib.use(old_backend)
Basically, set the backend to something like a pdf device, and then set it back to whatever you're accustomed to.
I am referring you to this StackOverflow answer which cites this article as an answer. In the SO answer they also suggest plt.ioff() but are concerned that it could disable other functionality should you want it.
I have created multiple figures using python with matplotlib. PyQt5 is used as a backend to display the figures. I am saving the figures in svg format using savefig.
My problem is that I would like to create a single zip file containing all of the svg files. A simplified version of what I have is:
import matplotlib
figure = matplotlib.figure.Figure(dpi=72)
canvas = matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg(figure)
axes = figure.add_axes((0.1, 0.15, 0.85, 0.8))
axes.plot(data)
# Do all of that three times to give me three separate figures
# (let's call them figure, figure1 and figure2)
Now I can save each figure using
figure.savefig(filename, format='svg')
figure1.savefig .....
But I would actually like to have something like
fileobject = figure.savefig(format='svg')
where I could use tarfile module to compress and store all fileobjects in a single archive. My current workaround is to save the files, read them and use those objects to create the tar file and deleting the originals afterwards. Having a more direct way to do this would be nice...
you can use a buffered stream to save your file to. Something like:
import io
fileobject1 = io.BytesIO()
figure1.savefig(fileobject1, format='svg')
fileobject2 = io.BytesIO()
figure2.savefig(fileobject2, format='svg')
etc.
In python (for one figure created in a GUI) I was able to save the figure under .jpg and also .pdf by either using:
plt.savefig(filename1 + '.pdf')
or
plt.savefig(filename1 + '.jpg')
Using one file I would like to save multiple figures in either .pdf or .jpg (just like its done in math lab). Can anybody please help with this?
Use PdfPages to solve your problem. Pass your figure object to the savefig method.
For example, if you have a whole pile of figure objects open and you want to save them into a multi-page PDF, you might do:
import matplotlib.backends.backend_pdf
pdf = matplotlib.backends.backend_pdf.PdfPages("output.pdf")
for fig in xrange(1, figure().number): ## will open an empty extra figure :(
pdf.savefig( fig )
pdf.close()
Do you mean save multiple figures into one file, or save multiple figures using one script?
Here's how you can save two different figures using one script.
>>> from matplotlib import pyplot as plt
>>> fig1 = plt.figure()
>>> plt.plot(range(10))
[<matplotlib.lines.Line2D object at 0x10261bd90>]
>>> fig2 = plt.figure()
>>> plt.plot(range(10,20))
[<matplotlib.lines.Line2D object at 0x10263b890>]
>>> fig1.savefig('fig1.png')
>>> fig2.savefig('fig2.png')
...which produces these two plots into their own ".png" files:
To save them to the same file, use subplots:
>>> from matplotlib import pyplot as plt
>>> fig = plt.figure()
>>> axis1 = fig.add_subplot(211)
>>> axis1.plot(range(10))
>>> axis2 = fig.add_subplot(212)
>>> axis2.plot(range(10,20))
>>> fig.savefig('multipleplots.png')
The above script produces this single ".png" file:
I struggled with the same issue. I was trying to put 2,000 scatter plots into a single .pdf. I was able to start the procedure, but it aborted after a few hundred.
Even when I created six scatter charts into one .pdf, the .pdf file was enormous (like 7mb) for just six .jpg's that were 30kb each. When I opened the .pdf, it appeared that the .pdf was painting every point on the chart (each chart had thousands of points) instead of displaying an image. Some day, I will figure out the correct options, but here is a quick and dirty work-around. I printed the scatter plots to individual .jpg files in a local directory.
Is there a way to convert a pyplot figure created with pyplot.Figure into a wand image? I have tried using the following to no avail:
image_data = BytesIO()
figure.savefig(image_data, format='png')
image_data.seek(0)
image = Image(file=image_data, resolution=250)
The end goal of this is to convert a list of figures into a long png. The only other method (which is ugly) is to convert to pdf and then concatenate the pages.
I was trying to figure out how to do this same thing. I went down a rabbit hole for a bit thinking I needed to also use PIL (Pillow) to accomplish this task. With the help of the previous answer I was able to come up with a complete example:
import matplotlib
from io import BytesIO
import numpy
import matplotlib.pyplot as plt
from wand.display import display
from wand.image import Image
plt.plot([1,5,3,2])
plt.ylabel('y axis numbers')
plt.xlabel('x axis numbers')
image_data = BytesIO() #Create empty in-memory file
plt.savefig(image_data, format='png') #Save pyplot figure to in-memory file
image_data.seek(0) #Move stream position back to beginning of file
img = Image(file=image_data) #Create wand.image
display(img) #Use wand to display the img
I believe you are on the right track. Without seeing the figure, I would assume the issue would be related to wand holding the C structure pointer using the with keyword.
image_data = BytesIO()
figure.savefig(image_data, dpi=250, format='png')
image_data.seek(0)
with Image(file=image_data) as img:
# ... do work
img.save(filename='/tmp/out.png')
I tried the recommended code above and had no luck. I posted the question to the WandB forum (here) and the following was recommended:
fig, ax1 = plt.subplots(...)
...
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
wandb.log(({"chart": wandb.Image(Image.open(buf)) }))
fig.show()
It seems that using the file parameter is no longer allowed.
In python (for one figure created in a GUI) I was able to save the figure under .jpg and also .pdf by either using:
plt.savefig(filename1 + '.pdf')
or
plt.savefig(filename1 + '.jpg')
Using one file I would like to save multiple figures in either .pdf or .jpg (just like its done in math lab). Can anybody please help with this?
Use PdfPages to solve your problem. Pass your figure object to the savefig method.
For example, if you have a whole pile of figure objects open and you want to save them into a multi-page PDF, you might do:
import matplotlib.backends.backend_pdf
pdf = matplotlib.backends.backend_pdf.PdfPages("output.pdf")
for fig in xrange(1, figure().number): ## will open an empty extra figure :(
pdf.savefig( fig )
pdf.close()
Do you mean save multiple figures into one file, or save multiple figures using one script?
Here's how you can save two different figures using one script.
>>> from matplotlib import pyplot as plt
>>> fig1 = plt.figure()
>>> plt.plot(range(10))
[<matplotlib.lines.Line2D object at 0x10261bd90>]
>>> fig2 = plt.figure()
>>> plt.plot(range(10,20))
[<matplotlib.lines.Line2D object at 0x10263b890>]
>>> fig1.savefig('fig1.png')
>>> fig2.savefig('fig2.png')
...which produces these two plots into their own ".png" files:
To save them to the same file, use subplots:
>>> from matplotlib import pyplot as plt
>>> fig = plt.figure()
>>> axis1 = fig.add_subplot(211)
>>> axis1.plot(range(10))
>>> axis2 = fig.add_subplot(212)
>>> axis2.plot(range(10,20))
>>> fig.savefig('multipleplots.png')
The above script produces this single ".png" file:
I struggled with the same issue. I was trying to put 2,000 scatter plots into a single .pdf. I was able to start the procedure, but it aborted after a few hundred.
Even when I created six scatter charts into one .pdf, the .pdf file was enormous (like 7mb) for just six .jpg's that were 30kb each. When I opened the .pdf, it appeared that the .pdf was painting every point on the chart (each chart had thousands of points) instead of displaying an image. Some day, I will figure out the correct options, but here is a quick and dirty work-around. I printed the scatter plots to individual .jpg files in a local directory.