Draw Circles on Top Level of Figure - python

I'm working on a figure where I'm trying to draw a circle on top of a combination colormap and contour plot. The circle keeps getting drawn under the contours instead of on top of them (see the figure below). I've tried reordering how I call imshow, contour, and Circle to see if I can get the circle to display on top, but I haven't had any luck. Is there a way to force Circle to be on the top most level of the figure? Thanks for your help!

Use the zorder kwarg. That controls which elements go on top of each other. So, in this case, you want to increase the zorder of the circle. You may need to experiment to find a zorder that gives you the result you require, but the rule is that higher zorder objects appear on top of lower zorder objects.
Its hard to know exactly without any of your code, but assuming you've used pcolormesh, contour and a Circle patch, this example shows the effect of not setting a zorder (white circle), and setting zorder=10 (red circle).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
# Fake data
x = np.arange(100)
y = np.arange(100)
X, Y = np.meshgrid(x, y)
z = X**0.5 * Y**0.5
fig, ax = plt.subplots(1)
ax.set_aspect('equal')
ax.pcolormesh(X, Y, z, cmap='viridis')
ax.contour(X, Y, z, colors='k', linewidths=3)
circ1 = Circle((65, 65), 30, facecolor='None', edgecolor='w', lw=5)
circ2 = Circle((35, 35), 30, facecolor='None', edgecolor='r', lw=5, zorder=10)
ax.add_patch(circ1)
ax.add_patch(circ2)
plt.show()
Note that the white circle lies beneath the black contour lines, but by increasing the zorder to 10, the red circle lies on top of the contour lines.

You can set the zorder property of the plot object to force it to be on top of other plots within the same axes. A higher zorder value will appear on top of a lower zorder value.
plt.plot([1, 2], [1, 2], zorder=100)
By default, patches have a zorder of 1, 2D line objects have a zorder of 2 and text has a zorder of 3.

Related

How to create a custom blended_transform in matplotlib that acts on rotated directions?

I am developing a python GUI that plots many lines, arrows and rectangles on a matplotlib canvas.
The rectangles go aligned with the lines: Rotated rectangle above line
Here is the picture.
I want to set a transform on the Rectangle, so that the side's length perpendicular to the line are in axes coordinates units (transAxes), and the sides parallel to the line are in data coordinates units (transData).
I know that blended_transform is can be used to define to different transforms for x-axis and y-axis. This is similar, but the directions in which the transforms are applied are not neccessary the horizontal and vertical direction. Is there a way of defining a custom blended transform that works on rotated directions instead of x-y directions? The documentation on transforms is not very helpful when trying to create a custom one.
Thanks!
The questions in the comments weren't answered, so one needs to make some assumptions. Let's say the rotation is supposed to happen in display space and the axes coordinates are those in y-axis direction. Then a possible transform could look like
trans = ax.get_xaxis_transform() + mtrans.Affine2D().rotate_deg(angle)
In this case the first dimension are data coordinates, the second are axes coordinates.
Some example:
import matplotlib.pyplot as plt
import matplotlib.transforms as mtrans
fig, ax = plt.subplots()
angle = 38 # degrees
trans = ax.get_xaxis_transform() + mtrans.Affine2D().rotate_deg(angle)
ax.plot([5,9],[0,0], marker="o", transform=trans)
rect = plt.Rectangle((5,0), width=4, height=0.2, alpha=0.3,
transform=trans)
ax.add_patch(rect)
ax.set(xlim=(3,10))
plt.show()
If instead you want rotation about a point in data coordinates, a single transform is not doing the job. For example for a rotation about (5,5) in data space,
import matplotlib.pyplot as plt
import matplotlib.transforms as mtrans
fig, ax = plt.subplots()
ax.set(xlim=(3,10),ylim=(4,10))
fig.canvas.draw()
angle = 38 # degrees
x, y = ax.transData.transform((5,5))
_, yax = ax.transAxes.inverted().transform((0,y))
transblend = ax.get_xaxis_transform()
x, y = transblend.transform((5,yax))
trans = transblend + mtrans.Affine2D().rotate_deg_around(x,y, angle)
ax.plot([5,9],[yax,yax], marker="o", transform=trans)
rect = plt.Rectangle((5,yax), width=4, height=0.2, alpha=0.3,
transform=trans)
ax.add_patch(rect)
plt.show()
Note that this invalidates as soon as you change the limits or figure size.

Is it possible to draw xticklabels on top of the xaxis?

I want to mark a specific x-axis position with a colored asterisk drawn on top of the x-axis.
I use an x-tick label as the marker (because I couldn't figure out if it is possible to place markers anywhere in the fig coords) at it's aligned properly but is drawn below the x-axis so it's partially covered.
MWE:
import numpy as np
import matplotlib.pyplot as plt
fig,ax=plt.subplots(1,1)
ax.scatter([-1,1],[1,1])
ax.set_xticks([0],minor=True)
ax.set_xticklabels(['*'],minor=True,color='r',fontsize=20,verticalalignment='center')
plt.setp(ax.spines.values(), linewidth=3)
plt.show()
That's what it looks like right now:
You can specify the coordinates of a scatter in a blended system (data coordinates for the y axis and axis coordinates for the x axis).
To then have the scatter marker above the spines set the zorder property of the scatter to something above 2.5.
import matplotlib.pyplot as plt
fig,ax=plt.subplots(1,1)
ax.scatter([-1,1],[1,1])
ax.scatter(0,0, s=100, marker="*", color="red",
transform=ax.get_xaxis_transform(), clip_on=False, zorder=3)
plt.show()
What you are looking for is zorder parameter. By using zorder = 0, you basically define the order of stacking of the plot sequence. 0 would send the axis/frame in the background putting the asterisk over the axis line as desired. I increased the size of the asterisk to highlight it.
ax.scatter([-1,1],[1,1])
ax.set_xticks([0],minor=True)
ax.set_xticklabels(['*'],minor=True,color='r',fontsize=30,verticalalignment='center')
plt.setp(ax.spines.values(), linewidth=3, zorder=0)
Alternatively, you can also specify the zorder for both plotting commands but use a higher zorder for the asterisk
ax.set_xticklabels(['*'],minor=True,color='r',fontsize=30,verticalalignment='center', zorder=2)
plt.setp(ax.spines.values(), linewidth=3, zorder=1)

matplotlib vertical space in the background

I've plated a bar graph where I set a default horizontal span:
plt.axhspan(0,2, color='yellow', alpha=0.25)
However it looks like that yellow area covers my original bars. How can I make a horizontal span to be in a background?
Use zorder and set zorder of the histogram higher than zorder of the hspan:
plt.hist(x, 50, normed=1, zorder=1)
plt.axhspan(0, 0.01, facecolor='yellow', zorder=0)
You can use zorder when plotting your graph.
For example with a scatter plot;
plt.axhspan(0, 2, color='yellow', alpha=0.25)
plt.scatter(X, Y, zorder=10)
This ensures your graph is on top of the axhspan.
zorder can be defined for many plt functions, just check the api, this is useful if you have many overlaying subplots for example.

How to plot heat map with matplotlib?

How to use python and matplotlib to plot a picture like following?
I know how to plot the 2D heat map, but it frustrated me a lot with plotting the bar on top of the heat map, and the bar between the color bar and heat map.
How to add those two bars on the picture, and show the number in x axis or y axis belongs to which group?
Thanks very much for all the responses.
A systematic and straightforward approach, although a bit more cumbersome at the start, is to use matplotlib.gridspec.GridSpec.
First set up the grid:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure()
gs = GridSpec(2, 3, width_ratios=[10, 1, 1], height_ratios=[1, 10])
This gives us a grid of 2 rows and 3 columns, where the lower left axis will be 10x10 and the other axes will be either 10x1 or 1x10 in relative sizes. These ratios can be tweaked to your liking. Note that the top center/right axes will be empty.
big_ax = fig.add_subplot(gs[1,0]) # bottom left
top_ax = fig.add_subplot(gs[0,0]) # top left
right_ax = fig.add_subplot(gs[1,1]) # bottom center
cbar_ax = fig.add_subplot(gs[1,2]) # bottom right
I will use a generic genome picture I found via google for the top and right image:
and will generate a random heatmap. I use imshow(aspect='auto') so that the image objects and heatmap take up the full space of their respective axes (otherwise they will override the height/width ratios set by gridspec).
im = plt.imread('/path/to/image.png')
# Plot your heatmap on big_ax and colorbar on cbar_ax
heatmap = big_ax.imshow(np.random.rand(10, 10), aspect='auto', origin='lower')
cbar = fig.colorbar(heatmap, cax=cbar_ax)
# Show your images on top_ax and right_ax
top_ax.imshow(im, aspect='auto')
# need to rotate my image.
# you may not have to if you have two different images
from scipy import ndimage
right_ax.imshow(ndimage.rotate(im, 90), aspect='auto')
# Clean up the image axes (remove ticks, etc.)
right_ax.set_axis_off()
top_ax.set_axis_off()
# remove spacing between axes
fig.subplots_adjust(wspace=0.05, hspace=0.05)
It's not super glamorous (especially with the default jet colormap), but you could easily use this to reproduce the figure your OP.
Edit: So if you want to generate that genome-like plot on the top and right, you could try something like this for the top bar:
from matplotlib.patches import Rectangle
from matplotlib.collections import PatchCollection
# draw the black line
top_ax.axhline(0, color='k', zorder=-1)
# box x-coords and text labels
boxes = zip(np.arange(0.1, 1, 0.2), np.arange(0.2, 1, 0.2))
box_text = ('A1', 'B1', 'B2', 'A2')
# color indicators for boxes
colors = (0, 1, 1, 0)
# construct Rects
patches = [Rectangle(xy=(x0, -1), width=(x1-x0), height=2) for x0,x1 in boxes]
p = PatchCollection(patches, cmap='jet')
# this maps the colors in [0,1] to the cmap above
p.set_array(np.array(colors))
top_ax.add_collection(p)
# add text
[top_ax.text((x0+x1)/2., 1.2, text, ha='center')
for (x0,x1), text in zip(boxes, box_text)]
# adjust ylims
top_ax.set_ylim(-2, 2)
For something the right axis, you can do the same thing but use axvline and swap the x-coords for y-coords.
right_ax.axvline(0, color='k', zorder=-1)
patches = [Rectangle(xy=(-1, y0), width=2, height=(y1-y0)) for y0, y1 in boxes]
p = PatchCollection(patches, cmap='jet')
p.set_array(np.array(colors))
right_ax.add_collection(p)
[right_ax.text(1.2, (y0+y1)/2., text, va='center')
for (y0, y1), text in zip(boxes, box_text)]
right_ax.set_xlim(-2,2)
These modifications lead to something like:

matplotlib 3D scatter plot colors of dots very light

I used the command below to create a 3D scatter plot:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
sizeseq = 2
colorseq = "k"
fig = plt.figure(1, (5,5), dpi=300)
ax = Axes3D(fig)
ax.view_init(20, -45)
x, y, z = [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10]
ax.scatter(x, y, z, c=colorseq, s=sizeseq, lw=0, alpha=.8)
plt.show()
But the color of the dots look so light even when I set alpha to 1. They almost look like being behind a mask. It also seems dependent on the 3D position of the individual sots. Is there a way to make all of the dots look really dark and opaque?
This appears to be scatter using a fancy 3d effect so you can distinguish between dots in the foreground and dots in the background. You might have to hack mplot3d to get it to stop doing that.
Alternatively, you may be able to use plot3D which doesn't show this behaviour.
ax.plot3D(x, y, z, 'k.', alpha=.8)
With alpha=0.8 dots already look very transparent. Don't use alpha.
In addition you can give a darker look to your dots by drawing their edgelines in a darker color than their respective facecolor.
Use scatter keyword parameter edgecolor/edgecolors or set after scatter creation(for example to bold all the points with a black border) with myscatterplot.set_edgecolors(color)

Categories