Matplotlib doesn't render data exactly square - python

I'm using matplotlib.imshow to render a 2D numpy-array of integer-values as a heatmap. The problem is that the pixels in the final image are not entirely square. Sometimes they're a little bit rectangular. This is a big problem for me as I'm using this "heatmap" as an overlay in a map and this behaviour creates a weird visual glitch.
I'm rendering it like so:
fig = plt.imshow(data2d, cmap=cmap, norm=norm, aspect='equal', interpolation='none')
plt.axis('off')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
fig.axes.set_adjustable('box-forced')
plt.savefig("output.png", bbox_inches='tight', pad_inches=0, dpi=72)
I thought setting the "aspect"-attribute to "equal" would take care of making the pixels exactly square. I've noticed that if I increase the DPI the effect is less noticeable as there are more pixels to work with but the rendering-time then becomes an issue.
I'd be glad if someone could point me in the right direction.

What I ended up doing is replacing matplotlib with rasterio and handling the colormap myself. It's definitely not an easy solution...

Related

Making the marker size as small as possible with matplotlib.pyplot

My program can generate up to 2000000 points within 5 seconds, so I don't have a problem with speed. Right now I am using matplotlib.pyplot.scatter to scatter all my points on my graph. For s=1, it gives me small circles, but not small enough, because it does not show the intricate patterns. When I use s=0.1, it gives me this weird marker shape:
Which makes the marker larger despite me making the size smaller. I have searched all over the internet including stack overflow, but they do not tell how to minimize the size further. Unfortunately, I have to show all the points, and cannot just show a random sample of them.
I have come to the conclusion that matplotlib is made for a small sample of points and not meant for plotting millions of points. However, if it is possible to make the size smaller please let me know.
Anyway, for my points, I have all the x values in order in one array, and all the y values in order in another array. Could someone suggest a graphing package in python I could use to graph all the points in a way that the size would be very small since when I plot the points now it just becomes one big block of color instead of intricate designs forming in the shape as they should be.
Thanks for any help in advance!
EDIT: My code that I am using to scatter the points is:
plt.savefig(rootDir+"b"+str(Nvertices)+"_"+str(xscale)+"_"+str(yscale)+"_"+str(phi)+"_"+str(psi)+"_"+CurrentRun+"_color.png", dpi =600)
EDIT: I got my answer, I added linewidths = 0 and that significantly reduced the size of the points, giving me what I needed.
Perhaps you can try making the linewidths as 0 i.e., the line width of the marker edges. Notice the difference in the two plots below
fig, ax = plt.subplots(figsize=(6, 4))
plt.scatter(np.random.rand(100000), np.random.rand(100000), s=0.1)
fig, ax = plt.subplots(figsize=(6, 4))
plt.scatter(np.random.rand(100000), np.random.rand(100000), s=0.1, linewidths=0)
you can set the marker to a single pixel using marker=',' in your call to scatter.
See the markers documentation here

Uneven squares in grid image upon saving

So, I am trying to save a matplotlib figure as a TIFF. The image has all of the correct information and content but for some reason, after saving, the squares on the grid that I plot appear uneven. In the matplotlib image window that comes up after running the code though, the image has perfect squares. I have attached a code snippet and samples of the produced images below. They are screenshots of a much larger, 332x335 grid. The image generally looks okay but if it is to be used in scientific papers, as I intend, it should be as close to perfect as possible. If someone could help here, I would greatly appreciate it.
fname = tif_file_name+'.tif'
aspect = grid_x/grid_y
plt.figure()
plt.imshow(circ_avg, cmap='gray', aspect=aspect, interpolation='none',)
plt.gca().invert_yaxis()
plt.savefig(fname, dpi = 1000, format='tif', bbox_inches='tight', pad_inches = 0)
plt.show()
Perfect squares from screenshot in plt.show() window:
Uneven squares when viewed after saving:
I was actually able to resolve this. It turns out the more effective way of doing this is by using the PIL library. This also greatly reduced the overall file size.
from PIL import Image
#scale to pixel vals (only multiplied by 255 here since my data already had 1 as the maximum)
vals= orig_vals*255
final_image = Image.fromarray(np.uint8(vals), mode='L')
final_image.save('blah.tif')

project numpy array to aitoff projection

I have an (as yet incomplete) image of the whole sky. In order to display it properly it needs to be projected onto an ellipse (specifically an Aitoff projection).
I have tried various versions of this:
plt.subplot(111, projection="aitoff")
plt.imshow(image, vmin=0.004, vmax=0.01, extent=[0,360,-90,90])
plt.show()
...and have tried changing the values in the extent kwarg to radians, as well as using pcolor or pcolormesh instead of imshow.
These have given me: an empty Aitoff plot, various Aitoff plots with all or part of my image sitting inside it, but not filling it, or an Aitoff plot with a small part of my image (one or two pixels by the looks of things) completely filling it.
My whole image sitting within a plot
The unprojected image
I also do not have access to things like Basemap or astroproj as I'm using a machine owned by my university.
Edit: As was pointed out by another user the above example is not Minimal, Complete, and Verifiable. Below is a version which should be:
A=np.random.rand(180,360)
plt.imshow(A)
plt.show()
plt.subplot(111, projection="aitoff")
plt.pcolormesh(A)
plt.show()
I want the entire image generated in the plt.imshow() command to be projected in the Aitoff figure. Instead only a few pixels are. Any ideas?
Thanks!
Using imshow in non-rectilinear projections will mostly fail. But instead pcolormesh may be used.
The aitoff projection ranges from -π to π in horizontal and from -π/2 to π/2 in vertical direction. This is the range of values to use when plotting the pcolormesh plot.
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread("house.jpg")
x = np.linspace(-np.pi,np.pi,im.shape[1])
y = np.linspace(-np.pi/2,np.pi/2,im.shape[0])
X,Y = np.meshgrid(x,y)
plt.subplot(111, projection="aitoff")
plt.pcolormesh(X,Y[::-1],im[:,:,2])
plt.show()

Vector graphics + matplotlib pcolorfast

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

Matplotlib offset errorbar bug workaround?

The bug is documented here:
Matplotlib errorbar not centered on marker
and here:
https://github.com/matplotlib/matplotlib/issues/3400
Basically, the markers are plotted off by 1 pixel all the time.. You can even see this on Matplotlib's own tutorial page if you look closely at the second plot: http://matplotlib.org/1.2.1/examples/pylab_examples/errorbar_demo.html
This is very frustrating as I cannot produce publication-quality plots from matplotlib, and I'm very surprised this has not been fixed.
In any case, I have too much time and code invested into matplotlib to switch to a different package. So my question is how would you go about making a workaround? I suppose one solution is to plot the markers 1 pixel to the left/right from the errorbars. I don't know how to do this. I figured out how to get the display coordinates of my plot points, but how can I make an interactive plot that preserves the 1-pixel offset? I can plot them with 1-pixel offsets, but then you can't zoom or manipulate the plot.
It seems like the Matplotlib team have fixed the issue when calling savefig() using .svg or .pdf, but for .png I've found that you can circumvent this issue by using an odd number for the error line thickness. Using the first example on the Matplotlib tutorial, if we use
plt.errorbar(x, y, yerr=0.4, marker='X', markersize=15)
then the bars are offset like this:
However if we use a line width of 3
plt.errorbar(x, y, yerr=0.4, marker='X', markersize=15, elinewidth=3)
then the bars are centred like this:
This isn't a perfect solution, but it does the job if you don't mind having slightly thicker lines.

Categories

Resources