I have a retina MacBook and I would like to display plots in Matplotlib that are high-dpi so that the plots don't look blurry on my screen.
When using set_matplotlib_formats and setting it to svg, everything looks perfect: the image is crisp and scalable, and the text size looks fine. However, scalable graphics are often not convenient when I'm making a plot that has hundreds of thousands of elements.
Setting it to png with a dpi of 300 doesn't work well since it makes the image bigger. The image is also not crisp—although the plot is rendered at high dpi, is it displayed quite large and thus the displayed dpi is low.
Trying retina seems to fix this issue (the plot size is correct and the image is crisp). However, it makes the text size really small (compare it to the svg option):
Is there an option that sizes the plot and the text correctly like svg but which doesn't use scalable graphics? Here is the code for reference:
import matplotlib
import matplotlib.pyplot as plt
from IPython.display import set_matplotlib_formats
%matplotlib inline
set_matplotlib_formats('retina')
plt.plot(range(5))
plt.show()
Related
I have noticed a frustrating problem with Pythons Matplotlib where matrix plotting produces
an uneven grid. This issue is persistent with and without high DPI, as well as in EPS files.
The following code is used for the image generation:
import matplotlib.pyplot as plt
import numpy as np
arr = np.zeros((200,200))
# Set the diagonal to 1
arr[np.arange(200), np.arange(200)] = 1
plt.matshow(arr)
plt.savefig('matshow_test.png', dpi=1000)
DPI=1000:
Which has the sizes 65x65, 90x90, 95x95, 90x90, 95x95 and so on.
DPI=default
Which varies between 1x1 and 2x2 for each cell.
EPS rendered in latex:
Which is clearly distorted.
My questions are:
Why is this the default behaviour of Matplotlib?
How can I fix this?
Using Python 3.9.10 with Matplotlib 3.5.1
The matplotlib function matshow uses an antialiasing filter on the images. Unfortunately it is enabled even for vector graphic backends such as (e)ps, pdf or svg. That means, the image is rasterized, antialiased to a specific size and than inlined in the vector graphic.
Antialiasing takes into account a specific display resolution (dpi) and image size. If you change those parameters when viewing an image (for example when zooming in) the image can get heavily distorted, as you have experienced.
There is a discussion about the default antialiasing for matplotlib imshow (and also matshow which uses the same mechanism) here.
You should be able to fix your issue (and get true vector graphics) by disabling the antialiasing with the
matshow(..., interpolation='none')
option.
To keep it short: Is there a way to export plots created with methods like
pcolorfast which basically draw pixels as "real" vector graphics?
I tried to do just that using savefig and saving to a PDF but what would happen is that the plot was actually a vector graphic but the parts drawn by pcolorfast(so basically, what is inside the axes) something like a bitmap. - I checked this using Inkscape.
This resulted in really low resolution plots even though the arrays drawn with pcolorfast where about 3000x4000. I achieved higher resolution by increasing the dpi when exporting, but I'd really appreciate a conversion to a real vector graphic.
Edit: I updated my original code by the piece of code below that should serve to illustrate what exactly I am doing. I tried to involucrate the rasterized tip, but it has had no effect. I still end up with a supersmall PDF-file where the plots are acually raster images (png). I am going to provide you with the data I used and the resulting PDF.
http://www.megafileupload.com/k5ku/test_array1.txt
http://www.megafileupload.com/k5kv/test_array2.txt
http://www.megafileupload.com/k5kw/test.pdf
import numpy as np
import matplotlib.pyplot as plt
arr1= np.loadtxt("test_array1.txt")
arr2= np.loadtxt("test_array2.txt")
fig, (ax1, ax2)=plt.subplots(1, 2)
ax1.set_rasterized(False)
ax1.pcolorfast(arr1)
ax2.pcolorfast(arr2, rasterized=False)
plt.show()
fig.set_rasterized(False)
fig.savefig("test.pdf")
It seems that the figsize option only changes the ratio of the height to width. Atleast this is the case when using jupyter notebooks. Here is an example:
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
plt.figure(figsize=(16,8))
plt.plot(np.arange(1,10),np.arange(1,10))
plt.show()
plt.figure(figsize=(24,6))
plt.plot(np.arange(1,10),np.arange(1,10))
plt.show()
I was hoping that figsize intended inches, not a relative ratio. How would you go about enforcing that in python/ jupyter notebooks.
If you use a large figsize, say figsize=(50, 5) you will notice that the lines, the labels, everything is incredibly thin and small with respect to a plot with normal size.
This happens because you are using widths that are not compatible with the width of the output cell
and the notebook just scales down the figure to make it fit in the output cell.
To have the behavior you asked for, you need a horizontal scrolling capability in the output cell. I don't know of a `nbextension` that can enable horizontal scrolling in output cells.
After a bit of experimenting, it looks like using the nbagg backend
%matplotlib nbagg
gives you a scrollable output cell, and an interactive one as well, inside the notebook and possibly it is what you want.
Addendum
I've found this issue on IPython's github, with a request for horizontal scrolling in output cell — as you can see it's dated 2012 and there is no followup of sort.
plt.gcf().set_size_inches(16, 8)
After change figsize the figure size do changed when the parameter in a certain range.In my condition,size not growing after size above (24,8).When it's still below the range the size do increase.It's base on your displayer dpi, you can set the dpi in figure but eventually it's rely on your hardware.
The figaspect is set by matplotlib.figure.figaspect
If you save figures to files use savefig,you will see the image size increase also.
In my current iPython, matplotlib plots are being displayed inline. I wanted a way to display images with specific pixel sizes, as I usually only work with pixels and I don't print anything out.
My screen PPI is 208, so I ran these 2 pieces of code:
plt.figure(figsize=(2000/float(208), 1000/float(208)), dpi=208)
# other code here...
plt.savefig('my_fig.png', dpi=208)
What I'm confused about is this: When I examine my_fig.png, it is in fact 2000 pixels by 1000 pixels, this is what I want. Also according to https://stackoverflow.com/a/7912007/582917 the DPI settings for rendering to the display device and rendering a file have different defaults. That's why I have to also add in dpi=208 to the plt.savefig function.
However the image rendered on the iPython notebook, which is in the browser, is much smaller. Using my browser ruler, it's roughly 600 by 300 pixels.
Using the same DPI for both functions, why is that the inline rendered image in iPython notebook is so small, while my saved image is at the correct resolution that I want?
Note that I'm running iPython in a Virtualbox (that is also headless), I'm not sure if this can cause any differences.
After a few trial and errors, in order to get close the correct size for inline rendering, the figure dpi needed to be 58. But even then the images generated inline do not match exactly with my specified pixel count. It's always +- 10 to 20 pixels.
This might be due to the fact that %matplotlib inline by default will pass bbox_inches='tight' to plt.figure(...).
Try the following,
After calling%matplotlib inline, do
%config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
then create your plot. Here's what it did for me:
Before
After
How can I save Python plots at very high quality?
That is, when I keep zooming in on the object saved in a PDF file, why isn't there any blurring?
Also, what would be the best mode to save it in?
png, eps? Or some other? I can't do pdf, because there is a hidden number that happens that mess with Latexmk compilation.
If you are using Matplotlib and are trying to get good figures in a LaTeX document, save as an EPS. Specifically, try something like this after running the commands to plot the image:
plt.savefig('destination_path.eps', format='eps')
I have found that EPS files work best and the dpi parameter is what really makes them look good in a document.
To specify the orientation of the figure before saving, simply call the following before the plt.savefig call, but after creating the plot (assuming you have plotted using an axes with the name ax):
ax.view_init(elev=elevation_angle, azim=azimuthal_angle)
Where elevation_angle is a number (in degrees) specifying the polar angle (down from vertical z axis) and the azimuthal_angle specifies the azimuthal angle (around the z axis).
I find that it is easiest to determine these values by first plotting the image and then rotating it and watching the current values of the angles appear towards the bottom of the window just below the actual plot. Keep in mind that the x, y, z, positions appear by default, but they are replaced with the two angles when you start to click+drag+rotate the image.
Just to add my results, also using Matplotlib.
.eps made all my text bold and removed transparency. .svg gave me high-resolution pictures that actually looked like my graph.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Do the plot code
fig.savefig('myimage.svg', format='svg', dpi=1200)
I used 1200 dpi because a lot of scientific journals require images in 1200 / 600 / 300 dpi, depending on what the image is of. Convert to desired dpi and format in GIMP or Inkscape.
Obviously the dpi doesn't matter since .svg are vector graphics and have "infinite resolution".
You can save to a figure that is 1920x1080 (or 1080p) using:
fig = plt.figure(figsize=(19.20,10.80))
You can also go much higher or lower. The above solutions work well for printing, but these days you want the created image to go into a PNG/JPG or appear in a wide screen format.
Okay, I found spencerlyon2's answer working. However, in case anybody would find himself/herself not knowing what to do with that one line, I had to do it this way:
beingsaved = plt.figure()
# Some scatter plots
plt.scatter(X_1_x, X_1_y)
plt.scatter(X_2_x, X_2_y)
beingsaved.savefig('destination_path.eps', format='eps', dpi=1000)
In case you are working with seaborn plots, instead of Matplotlib, you can save a .png image like this:
Let's suppose you have a matrix object (either Pandas or NumPy), and you want to take a heatmap:
import seaborn as sb
image = sb.heatmap(matrix) # This gets you the heatmap
image.figure.savefig("C:/Your/Path/ ... /your_image.png") # This saves it
This code is compatible with the latest version of Seaborn. Other code around Stack Overflow worked only for previous versions.
Another way I like is this. I set the size of the next image as follows:
plt.subplots(figsize=(15,15))
And then later I plot the output in the console, from which I can copy-paste it where I want. (Since Seaborn is built on top of Matplotlib, there will not be any problem.)