matplotlib: colour bar resizing image? - python

I'm assuming I have a really simple question, which has been driving me insane for the past hour. So, I am trying to produce a contour plot with the following axis lengths x=37,y=614. I can produce a contour plot no problem, but when I add a colour bar the image becomes resized to what i'm assuming is the size of the colour bar.
Image without colour bar:
Image with colour bar:
The figure becomes resized and I do not know why.
How can I plot a figure like my first figure but with the colour scheme of the second figure and with a colour bar?
code:
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
from matplotlib import pylab
y = np.arange(1, 615)
x = np.arange(1, 37)
z = np.loadtxt('145_contact_matrix_605.txt')
fig = plt.figure()
ax = plt.subplot(111)
CS = ax.contour(x, y, z)
plt.clabel(CS, inline=1, fontsize=10)
# COLOUR BAR CODE
im_out = ax.imshow(z, cmap=cm.jet)
ax.matshow(z,cmap=plt.cm.jet)
axcolor = fig.add_axes([0.9,0.1,0.02,0.8]) # adjust these vaules to position colour bar
pylab.colorbar(im_out, cax=axcolor)
plt.show()

It's the imshow command that's changing the aspect ratio of the axes, not the colorbar.
imshow assumes you want an aspect ratio of 1.0 so that a square in data coordinates will appear square (i.e. square pixels).
If you want it to behave like contour, the just specify aspect='auto'.
ax.imshow(z, cmap=cm.jet)
You should also remove the ax.matshow line (or use it instead of imshow). As it is, you'll have two images that partially overlap and hide each other.
If you do decide to use matshow instead of imshow, you'll need to specify aspect='auto' for it, as well.

Related

transform colors in colorbar, not the ticks

When using a custom normalization, for instance PowerNorm, we can adjust the mapping between values and the colors. If we then show a corresponding colorbar, we can see the change when observing the ticks (compare left and right plot in the following picture).
Is there a way to use the normalization like on the left, but then have a colorbar where the colours are "squished" to one end, but the ticks remain equidistant (like on the right side)?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import PowerNorm
x, _ = np.meshgrid(*2*(np.linspace(-1, 1, 100),))
# with normalization: transformation is applied to image as desired, but the ticks at the colorbar are not equidistant
plt.subplot(121)
plt.imshow(x, norm=PowerNorm(gamma=4, vmin=-1, vmax=1))
plt.colorbar()
# without normalization
plt.subplot(122)
plt.imshow(x)
plt.colorbar()
plt.show()
It looks like you need to set the y axis of the color bar to be linear:
cb = plt.colorbar()
cb.ax.set_yscale('linear')

Mirror the color scale of a Seaborn heatmap, both colors and labels

I have a heatmap produced with the Seaborn module as shown here.
As the values in the table generally increase with the distance from MSL (the values increase going down in the table), I want to mirror the color scale such that the dark blue color is at the bottom with the corresponding label (12), and the light yellow color is at the top, with label (3).
I only found a way to invert the colors, but then the labels remain in place. How can I mirror the entire scale (both colors and labels)?
Thanks!
invert_yaxis() on the ax of the colorbar reverses both the ticklabels as well as the colorbar (invert_xaxis() does the same for a horizontal colorbar).
Seaborn's heatmap doesn't directly return a handle to the generated colorbar. It can be obtained via the plot's ax: cbar = ax.collections[0].colorbar.
Here is an example:
import seaborn as sns
import numpy as np
import matplotlib.pylab as plt
x, y = np.meshgrid(np.arange(20), np.arange(20))
arr = (x * y) % 10
ax = sns.heatmap(arr, annot=True, cbar=True)
cbar = ax.collections[0].colorbar
cbar.ax.invert_yaxis()
plt.show()

Python matplotlib - add borders to grid plot based on value

I wonder if you can help me with this. I have a grid of 0's and 1's that I want to add a border colour to the plot cell area if it's a 1. I've used imshow to produce a grid coloured according to value, e.g.:
a = np.random.randint(2, size=(10,10))
im = plt.imshow(a, cmap='Blues', interpolation='none', vmin=0, vmax=1, aspect='equal')
However, I can't find any border properties to change for each grid cell in imshow. I've read add_patch could be used to place a rectangle at certain points to mimic a border using on the axes values, but is there a better way than looping and applying cell-wise?
Thanks for any help you can give.
There is no build-in function to partially colorize imshow edges. The easiest option is probably indeed to draw a rectangle for each cell.
import matplotlib.pyplot as plt
import numpy as np
a = np.random.randint(2, size=(10,10))
im = plt.imshow(a, cmap='Blues', interpolation='none', vmin=0, vmax=1, aspect='equal')
def rect(pos):
r = plt.Rectangle(pos-0.5, 1,1, facecolor="none", edgecolor="k", linewidth=2)
plt.gca().add_patch(r)
x,y = np.meshgrid(np.arange(a.shape[1]),np.arange(a.shape[0]))
m = np.c_[x[a.astype(bool)],y[a.astype(bool)]]
for pos in m:
rect(pos)
plt.show()

Add image behind scatter subplot independent of scatter points axes

I am trying to add an image behind each subplot of my scatter plot figure. I want my image to take up all the subplot space. But I do not want to map my scatter points onto the image: That is, I want the axes of my scatter points to be independent to that of the image.
When I simply use imread() and implot() while making a subplot to insert the image, like so:
im = plt.imread("/Users/mac/Desktop/image.jpeg")
two = plt.subplot(222)
implot = plt.imshow(im)
plt.title('4-8 Hz')
plt.scatter(X,Y, s=100, marker ='o', c=AveragedHursts4to8, cmap = cm.plasma)
plt.colorbar()
two.axis('off')
I get the right-most image down below, where, clearly, the image axes and scatter points axes are shared.
I tried to use the twiny() function to make a new set of axes for the image, with the image set as the first axes and the second axes set to the scatter points, like so:
onetwin = plt.subplot(221)
plt.title('1-4 Hz')
implot = plt.imshow(im, zorder=1)
onetwin.axis('off')
one = onetwin.twiny()
plt.scatter(X,Y, s=100, marker ='o', c=AveragedHursts1to4, cmap = cm.plasma, zorder = 2)
plt.colorbar()
one.axis('off')
There I get the leftmost image, where the scatter points are squished on the y axis and the image, for some reason, has been shrunk.
And when I switch the ordering of the creation of the axes for twiny, the image takes up the whole subplot and the scatter points do not show at all.
Suggestions?
My suggestion would be to leave the points' positions untouched and scale the background image accordingly. One can use the extent keyword to imshow for that purpose.
In the example below I plot some random points on four different scales. Each time the image is scaled to the scatterplot's dimensions using the extent keyword.
import matplotlib.pyplot as plt
import numpy as np
x = np.random.rand(8*8).reshape((8,8))
image = plt.imread("https://upload.wikimedia.org/wikipedia/en/2/27/EU_flag_square.PNG")
fig, ax = plt.subplots(ncols=4, figsize=(11,3.8))
for i in range(len(ax)):
ax[i].scatter(x[2*i,:]*10**(i-1), x[2*i+1,:]*10**(i-1), c="#ffcc00", marker="*", s=280, edgecolors='none')
xlim = ax[i].get_xlim()
ylim = ax[i].get_ylim()
mini = min(xlim[0],ylim[0])
maxi = max(xlim[1],ylim[1])
ax[i].imshow(image, extent=[mini, maxi, mini, maxi])
plt.tight_layout()
plt.show()
The simplest, fastest solution I came up with is to solve for x and y in:
largest_x_coodinate_value(x) = x_dimension of image_in_pixels
largest_y_coordinate_value(y) = y_dimension_of_image_in_pixels
And then do vectorized multiplication over the numpy arrays containing the X and Y coordinates with those calculated x,y values, effectively scaling the coordinates to the size of the image.

How to completely remove the white space around a scatter plot

I am trying to plot a scatterplot over an image without having any white space around it.
If I plot just the image as follows, then there is no white space:
fig = plt.imshow(im,alpha=alpha,extent=(0,1,1,0))
plt.axis('off')
fig.axes.axis('tight')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
but as I add a scatter plot over the image as follows:
fig = plt.scatter(sx, sy,c="gray",s=4,linewidths=.2,alpha=.5)
fig.axes.axis('tight')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
At this point, by using the following savefig command, the white space is added around the image:
plt.savefig(im_filename,format="png",bbox_inches='tight',pad_inches=0)
Any idea on how to remove the white space definitely?
By switching to the mpl object-oriented style, you can plot both the image and the scatter plot on the same axes, and hence only have to set the whitespace once, by using ax.imshow and ax.scatter.
In the example below, I've used subplots_adjust to remove the whitespace around the axes, and ax.axis('tight') to set the axis limits to the data range.
import matplotlib.pyplot as plt
import numpy as np
# Load an image
im = plt.imread('stinkbug.png')
# Set the alpha
alpha = 0.5
# Some random scatterpoint data
sx = np.random.rand(100)
sy = np.random.rand(100)
# Creare your figure and axes
fig,ax = plt.subplots(1)
# Set whitespace to 0
fig.subplots_adjust(left=0,right=1,bottom=0,top=1)
# Display the image
ax.imshow(im,alpha=alpha,extent=(0,1,1,0))
# Turn off axes and set axes limits
ax.axis('tight')
ax.axis('off')
# Plot the scatter points
ax.scatter(sx, sy,c="gray",s=4,linewidths=.2,alpha=.5)
plt.show()
This worked for expanding images to full screen in both show and savefig with no frames, spines or ticks, Note everything is done in the plt instance with no need to create the subplot, axis instance or bbox:
from matplotlib import pyplot as plt
# create the full plot image with no axes
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)
plt.imshow(im, alpha=.8)
plt.axis('off')
# add scatter points
plt.scatter(sx, sy, c="red", s=10, linewidths=.2, alpha=.8)
# display the plot full screen (backend dependent)
mng = plt.get_current_fig_manager()
mng.window.state('zoomed')
# save and show the plot
plt.savefig('im_filename_300.png', format="png", dpi=300)
plt.show()
plt.close() # if you are going on to do other things
This worked for at least 600 dpi which was well beyond the original image resolution at normal display widths.
This is very convenient for displaying OpenCV images without distortion using
import numpy as np
im = img[:, :, ::-1]
to convert the colour formats before the plt.imshow.

Categories

Resources