Vector graphics + matplotlib pcolorfast - python

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")

Related

Image artefacts when using cyclic colormaps for periodic data

I am currently trying to visualize the phase of an electromagnetic field which is 2pi-periodic. To visualize that e.g. 1.9 pi is almost the same as 0, I am using a cyclic colormap (twilight). However, when I plot my images, there are always lines at the sections where the phase jumps from (almost) 2pi to 0. When you zoom in on these lines, these artefacts vanish.
Here is a simple script and example images that demonstrate this issue.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-3,3,501)
x,y = np.meshgrid(x,x)
data = x**2+y**2
data = np.mod(data, 2)
plt.set_cmap('twilight')
plt.imshow(data)
plt.show()
I tested it with "twilight_shifted" and "hsv" as well and got the same issue. The problem also occurs after saving the image via plt.savefig(). I also tried other image formats like svg but it did not change anything.
As suggested in this answer you can set the image interpolation to "nearest", e.g.,
plt.imshow(data, interpolation="nearest")
See here for a discussion of image antialiasing effects with different interpolation methods.

Matplotlib: uneven grid size when plotting diagonal matrix

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.

Jupyter: Seaborn pairplot difficult to set graph dimensions for?

I was trying to create a bivariate scatterplot of each variable against every other that are in a dataframe, and I found sns.pairplot() was exactly what I needed.
However, no matter what I do (and I have tried all of the advice found in this question), the plots keep coming out too spread out, as well as in general too big. In the picture below, only the first two rows and four columns out of 12 variables display on my entire screen.
I have found out that my use of
%config InlineBackend.figure_format = 'svg'
to create non-blurry graphs on my high-ppi screen is partially at blame, as without it, I instead get this graph, which fits perfectly on my screen, but is now too small and I would prefer to scroll slightly around while having a bigger pic.
(note: the additional options below have no effect)
How can I make the grid of plots customizable in its overall size as well as spacing? As it stands, no options work, and one graphics backend (the default one) produces too small graphs, while the 'svg' backend produces too large ones.
EDIT: Editing sns.set(rc={'figure.figsize':(x,y)}) or the height/ aspect options improve nothing: the former produces no change, while the latter two change how big the individual plots are (with height=1 making them indecipherable), but the overall "grid" is still as bulky and overly large as before.
Essentially you are asking how to display the figure in its original size in a jupyter notebook.
That translates into how to add scrollbars if it exceeds the room it's given by the layout of the output cell.
I think for the horizontal direction this can be done as follows. However for the vertical direction this does not seem to work.
%matplotlib inline
# Cell2
from IPython.display import display, HTML
CSS = """div.output_area img {max-width:None !important;max-height: None !important";}"""
display(HTML('<style>{}</style>'.format(CSS)))
# Cell3
import matplotlib.pyplot as plt
fig, ax = plt.subplots(ncols=8, figsize=(20,10))

Image plotted from a FITS file with matplotlib oriented incorrectly

I'm having a little issue with something regarding plotting a fits image using matplotlib's imshow. It seems that my image is flipped both horizontally and vertically. I'm sure there is something simple I am overlooking, if anyone could point me in the right direction that would be great.
This is what my image should look like:
So, I'm loading my image as:
from astropy.io import fits
import matplotlib
import matplotlib.pyplot as pyplot
#Opening/reading in my fits file
hdulist = fits.open('.../myfits.fits')
#Accessing the image data and specifying the dimensions I wish to use
my_image = hdulist[0].data[0,0:,0:]
#Plotting the image
pyplot.imshow(image_SWIFT_uvm2_plot, cmap='gray', vmin=0, vmax=0.5)
pyplot.show()
This is what my image in the plot looks like (the plot is a little more complex than the code I have included, but I have given the critical lines as, hopefully, a self-sufficient code):
Those of you with keen eyes should see that the image has flipped both horizontally and vertically.
For FITS files the convention is that the origin is at the lower left hand corner of the image, so you need to use origin='lower' (by default Matplotlib uses origin='upper').
I have never used the astropy module, but I know that PyFITS opens the image data as a NumPy array (and from what I'm reading, astropy.io.fits has inherited the functionality of PyFITS anyway, so it should work the same way). If that is the case, then you may use numpy.fliplr and numpy.flipud to flip the array to your desired orientation. Just replace the line
pyplot.imshow(image_SWIFT_uvm2_plot, cmap='gray', vmin=0, vmax=0.5)
with
import numpy as np
pyplot.imshow(np.fliplr(np.flipud(image_SWIFT_uvm2_plot)), cmap='gray',
vmin=0, vmax=0.5)
Alternatively, you could do a little linear algebra to flip it, or just note that performing both of these flips is the same as using np.rot90 twice
pyplot.imshow(np.rot90(image_SWIFT_uvm2_plot, k=2), cmap='gray', vmin=0, vmax=0)

Saving images in Python at a very high quality

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.)

Categories

Resources