Python matplotlib - add borders to grid plot based on value - python

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

Related

How to color individual elements in matplotlib plot_trisurf

I have a triangular surface mesh, generated via marching cubes. I want to color the surface elements based on an arbitrary variable in my plot. I am currently using plot_trisurf, but as I read the documentation, it looks like the color is always based on the Z component, or else a constant? Can I not specify a color per element? Or is there a better plot routine to use? For example, something along the lines of the code below is what I am after, where the color is based on the 'value' output from the marching cubes. Thanks!
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure
from skimage.draw import ellipsoid
from matplotlib import cm
# Generate a level set about zero of two identical ellipsoids in 3D
ellip_base = ellipsoid(6, 10, 16, levelset=True)
# Use marching cubes to obtain the surface mesh of these ellipsoids
verts, faces, normals, values = measure.marching_cubes_lewiner(ellip_double, 0)
fig = plt.figure(figsize=(20, 10))
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 0], verts[:,1], verts[:, 2], triangles=faces, cmap=cm.rainbow, color=values)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_xlim(0, 24)
ax.set_ylim(0, 20)
ax.set_zlim(0, 32)
plt.tight_layout()
plt.show()
As noted by JohanC, in this question they reset the face colors after the fact. This works for me, as I already have an array of colors defined.
# set the face colors of the Poly3DCollection
p3dc.set_fc(colors)

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 plot a 2D ellipse (with its interior color filled) in 3D?

I've seen how to plot a color filled ellipse in 2D. I would like to plot ellipses like that, but in a 3D plot.
To do that you need to use PyPlot. Now, PyPlot makes MatPlotLib work like MATLAB. Now, in order to make an ellipse you can try the below example code.
from pylab import figure, show, rand
from matplotlib.patches import Ellipse
NUM = 100
ells = [Ellipse(xy=rand(2)*10, width=rand(), height=rand(), angle=rand()*360)
for i in range(NUM)]
fig = figure()
ax = fig.add_subplot(111, aspect='equal')
for e in ells:
ax.add_artist(e)
e.set_clip_box(ax.bbox)
e.set_alpha(rand())
e.set_facecolor(rand(3))
ax.set_xlim(0, 5)
ax.set_ylim(0, 5)
show()

matplotlib: colour bar resizing image?

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.

Scale image in matplotlib without changing the axis

I have a GUI that displays a plot. I want to fit that plot to an existing image. I displayed the image under the plot using:
myaxe.plot(...)
myaxeimage = myaxe.imshow(myimage, axpect='auto', extent=myaxe.axis(), zorder=-1)
I'm already able to play with the opacity of the image, using
myaxeimage.set_alpha()
Now I'd like to be able to zoom in and out and to move around the image, using the GUI, without touching to the existing plot and axes, in order to align it with my plot. In other words, I want to scale to given sx and sy factors, and to put origin of the image at a given (x,y) point, clipping parts of the image going outside the axes. How can I do that?
There is a watermark example distributed with matplotlib that is sort of similar. Starting from that code, we can modify as follows:
Use ax.imshow to plot the image first. I do this because the extent parameter affects the final extent of ax. Since we want the final extent to be governed by the plt.plot(...), let's put it last.
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, origin='upper', zorder=-1)
Instead of extent=myaxe.axis(), use extent to control the position and size of the image. extent=(1,15,0.3,0.7) places the image in the rectangle with (1, 0.3) as the bottom left corner and (15, 0.7) as the top right corner.
With origin='upper', the [0,0] index of the array im is placed at the upper left corner of the extent. With origin='lower' it would have been placed at the lower left corner.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(1,15,0.3,0.7), alpha=0.5, zorder=-1)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.grid()
plt.show()
If you want to expand the image and clip it to the extent of the plot, you might need to use ax.set_xlim and ax.set_ylim as well:
myaximage = ax.imshow(im, aspect='auto', extent=(-1,25,0.3,0.7), alpha=0.5, zorder=-1,
origin='upper')
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange')
ax.set_xlim(0,20)
ax.set_ylim(0,1)
Or, for more control, you can clip the image to an arbitrary path by using myaximage.set_clip_path:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.image as image
import matplotlib.patches as patches
np.random.seed(1)
datafile = cbook.get_sample_data('logo2.png', asfileobj=False)
im = image.imread(datafile)
fig, ax= plt.subplots()
myaximage = ax.imshow(im, aspect='auto', extent=(-5,25,0.3,0.7),
alpha=0.5, origin='upper',
zorder=-2)
# patch = patches.Circle((300,300), radius=100)
patch = patches.Polygon([[5, 0.4], [15, 0.4], [15, 0.6], [5, 0.6]], closed=True,
transform=ax.transData)
myaximage.set_clip_path(patch)
ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=1.0, mfc='orange',
zorder=-1)
ax.set_xlim(0, 20)
ax.set_ylim(0, 1)
plt.show()
Finally, I followed tcaswell suggestion and used 2 different axes. This way, I simply have to play with set_xlim() and set_ylim() of my image axes to change the origin and/or the zooming factor of my image. I order to get the image below my plot, without hiding it with the frame of the plot, I removed the frame of the plot and used the frame of the image axes instead. I also hidden the ticks from the image axes.
from matplotlib import pyplot
f = pyplot.figure()
a = f.add_subplot(111, frameon=False) # Remove frame
a.plot(...)
myimg = pyplot.imread(...)
imgaxes = f.add_axes(a.get_position(), # new axes with same position
label='image', # label to ensure imgaxes is different from a
zorder=-1, # put image below the plot
xticks=[], yticks=[]) # remove the ticks
img = imgaxes.imshow(myimg, aspect='auto') # ensure image takes all the place
# now, to modify things
img.set_alpha(...)
imgaxes.set_xlim((x1, x2)) # x1 and x2 must be calculated from
# image size, origin, and zoom factor

Categories

Resources