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.
I am trying to display data on a graph between 2002-2022 as shown in the attached image. I tried some methods to set it but it's not executing. How do I manually set the limits so the graphs do not show a compressed plot.
Is there any way to plot 2D array as an image using Bokeh with interpolation like in Matplotlib? I am able to plot using an example: https://docs.bokeh.org/en/latest/docs/gallery/image.html
However, the image is to coarse. I like the way interpolation work in Matplotlib: https://matplotlib.org/gallery/images_contours_and_fields/interpolation_methods.html
I tried to perform interpolation beforehand but the matrix size now is to big.
I had the same issue and I've found the answer in pyviz's Gitter.
The solution combines Holoviews and Datashader:
import holoviews as hv
from holoviews import opts
from holoviews.operation.datashader import regrid
img = hv.Image(data)
regrid(img, upsample=True, interpolation='bilinear')
If you are working with a large dataset then you could try Bokeh in combination with Datashader/HoloViews like in this example. When zooming in, Datashader can dynamically create new high quality images from your data that could be displayed in your Bokeh plot.
Not an answer but an observation - I've noticed that plotting an image via an image_url source it appears interpolated when zoomed in whilst if you read in the same image and display it from a columndatasource via 'image' it then appears blocky when zoomed. I'd love to know how to make it appear interpolated too when zoomed, eg like the raw png image appears. Holoview/datashader would be a great solution but in my case I need it to work offline/as a standalone html file.
When I use Matplotlib's plt.show() I get a nice Plot which can can be zoomed to very high precision(practically infinite). But when I save it as a image it loses all this information gives information depending on resolution.
Is there any way I can save the plot with the entire information? i.e Like those interactive plots which can rescaled at any time?
P.S- I know I can set dpi to get high quality images. This is not what I want. I want image similar to Plot which python shows when I run the program. What format is that? Or is it just very high resolution image?
Note- I am plotting .csv files which includes data varying from 10^(-10) to 100's. Thus when I save the plot as .png file I lose all the information/kinks of graph at verŠ½ small scales and only retain features from 1-100.
Maybe the interactive graphic library bokeh is an option for you. See here. It's API is just little different from what you know from matplotlib.
Bokeh creates plots as html files that you can view in your browser. For each graphic you can select wheel zoom to zoom interactively into your graphic. You can change interactively the range that you want to be plotted. Therefore you don't loose information in your graphic.
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.)