Matplotlib path contains_point - python

I've just discovered the matplotlib path functionality and I'm using it with path.contains_point to check whether points are found within a region defined by 2 bezier curves.
I'm getting some unexpected behaviour where contains_point is returning True when I would have expected it to return False. Specifically, if the point to be tested is to the left of the region then it seems to be incorrect. On the right is ok.
Defining my paths as a number of straight lines rather than curves seems to work as expected.
A failing test case is as follows:
import matplotlib
import matplotlib.path as mplPath
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import pylab
import pandas as pd
print "MPL Version {}".format(matplotlib.__version__) #1.5.0
print "MPL NP Version {}".format(matplotlib.__version__numpy__) #1.6
path_data = [
(mplPath.Path.MOVETO, (2, 10)),
(mplPath.Path.CURVE4, (0, 100)),
(mplPath.Path.CURVE4, (20, 100)),
(mplPath.Path.CURVE4, (40, 150)),
(mplPath.Path.MOVETO, (40, 150)),
(mplPath.Path.CURVE4, (42, 45)),
(mplPath.Path.CURVE4, (20, 30)),
(mplPath.Path.CURVE4, (2, 10))
]
codes, verts = zip(*path_data)
path = mplPath.Path(verts, codes)
patch = patches.PathPatch(path, facecolor='r', alpha=0.5)
#Plot the patch and a some of the test points to visualise
fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_patch(patch)
ax.set_xlim(0, 50)
ax.set_ylim(0, 200)
ax.scatter(1, 50)
ax.scatter(20, 120)
ax.scatter(20, 25)
print path.contains_point((1,50)) #This should be false but is actually true
print path.contains_point((20,120)) #This should be false but is actually true
print path.contains_point((20, 25)) #This should be false and it is
plt.show()
Thanks in advance for any help you can provide. Python version is 2.7, Anaconda Distro on Linux Mint 17.3
Jim

You have an open path (extra moveto command). Once you comment it out, it works fine.
path_data = [
(mplPath.Path.MOVETO, (2, 10)),
(mplPath.Path.CURVE4, (0, 100)),
(mplPath.Path.CURVE4, (20, 100)),
(mplPath.Path.CURVE4, (40, 150)),
# (mplPath.Path.MOVETO, (40, 150)),
(mplPath.Path.CURVE4, (42, 45)),
(mplPath.Path.CURVE4, (20, 30)),
(mplPath.Path.CURVE4, (2, 10))
]

Related

How to use set_clip_path() with multiple polygons?

I'm trying to clip a cloud of points by several polygons, but I don't know if this is possible with plt.axis.set_clip_path().
Since set_clip_path() requires a Path or a Patch as arguments, how could you create a geometry formed by several Polygons? It would be something like a plt.MultiPolygon(), but that doesn't exist. I've tried to create a matplotlib.PatchCollection with all the Polygons, but that does not work.
Here is the desired goal (from upper to lower figure):
Here is how I'd like the code to look like:
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
import numpy as np
fig, ax = plt.subplots()
points = np.array([np.random.random(100)*400,
np.random.random(100)*100]).T
A = plt.Polygon( np.array([( 0, 0),(50,100),(100, 0)]), color='w', ec='k' )
B = plt.Polygon( np.array([(120 , 0),(170 , 100), (220, 0)]), color='w', ec='k' )
C = plt.Polygon( np.array([(240 , 0),(290 , 100), (340, 0)]), color='w', ec='k' )
[ax.add_patch(i) for i in (A,B,C)]
ax.scatter(points[:,0], points[:,1], zorder=3).set_clip_path([A,B,C])
You can concatenate the vertices and the codes of all polygons, and use them to create a "compound path". Matplotlib's path tutorial contains an example creating a histogram from just one compound path.
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import numpy as np
points = np.array([np.random.random(100) * 400,
np.random.random(100) * 100]).T
A = plt.Polygon(np.array([(0, 0), (50, 100), (100, 0)]), color='w', ec='k')
B = plt.Polygon(np.array([(120, 0), (170, 100), (220, 0)]), color='w', ec='k')
C = plt.Polygon(np.array([(240, 0), (290, 100), (340, 0)]), color='w', ec='k')
fig, ax = plt.subplots()
all_polys = [A, B, C]
[ax.add_patch(i) for i in all_polys]
vertices = np.concatenate([i.get_path().vertices for i in all_polys])
codes = np.concatenate([i.get_path().codes for i in all_polys])
dots = ax.scatter(points[:, 0], points[:, 1], zorder=3)
dots.set_clip_path(PathPatch(Path(vertices, codes), transform=ax.transData))
plt.show()

Custom pcolor in matplotlib

I am plotting several heatmaps in matplotlib as shown below.
Here is my loop:
with open(gene_peak) as f:
count = 1
for line in f:
np_array=[]
gene_peak = line.strip().split("\t")
gene_id = gene_peak[0]
peaks = gene_peak[1].split(",")
for peak in peaks:
np_array.append(enhancer_fc[peak])
data, pval = stats.spearmanr(np.transpose(np.array(np_array)))
plt.subplot(4,3,count+1)
# plt.title(gene_id)
plt.pcolor(data, cmap=plt.cm.OrRd, vmin=-1, vmax=1)
plt.gca().invert_yaxis()
plt.gca().set_aspect(aspect='equal', adjustable='box-forced')
plt.xticks([])
plt.yticks([])
print count
count += 1
plt.show()
I am plotting the spearman correlations of different 2D arrays of different dimensions.
Question:
There are correlation values, so they range from -1 to 1. I want to add custom colorbar() such that values above 0.4 starts showing a gradient of red and below -0.4 shows a gradient of blue, such that I show only the points that are more than 0.4 and less than -0.4.
Also I would like to plot only one colorbar() such that the image looks cleaner. Any help would be appreciated, Thanks.
You can define your own discrete colormap using the ListedColorMap from Matplotlib. You can use the colorbar from one of the plots, and place it in position so it represent all of the plots visually. Here is an example with the colours you have given:
from matplotlib import colors
discrete_colors = [(255, 0, 20), (255, 70, 65), (255, 128, 110), (255, 181, 165), (64, 64, 64),
(0, 0, 0), (64, 64, 64), (124, 128, 217), (102, 107, 216), (69, 76, 215), (33, 33, 245)]
discrete_colors = [(r/255., g/255., b/255.) for r, g, b in discrete_colors]
my_colormap = colors.ListedColormap(discrete_colors)
subplot(211)
data = 2 * np.random.rand(10, 10) - 1.0
pcolor(data, cmap=my_colormap, vmin=-1, vmax=1)
subplot(212) # Some other plot
data = 2 * np.random.rand(10, 10) - 1.0
pc = pcolor(data, cmap=my_colormap, vmin=-1, vmax=1)
fig = gcf()
fig.subplots_adjust(right=0.70)
cax = fig.add_axes([0.80, 0.15, 0.05, 0.7])
fig.colorbar(pc, cax=cax)
You might have to adjust the code a bit. I'm using IPython 2.7.

why does my pyplot show no lines?

I am new to pyplot and wondering what I'm doing wrong here.
I would like to plot a series of random line segments:
Here's some example code:
import matplotlib.pyplot as plt
def testPlot():
minx = miny = -1
maxx = maxy = 30
# some points randomly generated
points = [((10, 21), (19, 22)), ((11, 9), (22, 27)), ((9, 13), (5, 9)), ((18, 4), (2, 21)), ((25, 27
for pair in points:
print pair
for point in pair: #plot each point with a small dot
x = point[0]
y = point[1]
plt.plot(x,y,'bo')
# draw a line between the pairs of points
plt.plot(pair[0][0],pair[0][1],pair[1][0],pair[1][1],color='r',linewidth=2)
plt.axis([minx,maxx,miny,maxy])
plt.show()
Here is what I get after running that, there should be lines between the points but where are the lines?
((10, 21), (19, 22))
((11, 9), (22, 27))
((9, 13), (5, 9))
((18, 4), (2, 21))
((25, 27), (11, 13))
Thank you for any clues
This is the line of problem:
...
plt.plot(pair[0][0],pair[0][1],pair[1][0],pair[1][1],color='r',linewidth=2)
...
You're trying to draw the referring to x,y,x1,y1, which in fact should be ((x, x1), (y, y1)). Correcting this seems working fine:
def testPlot():
minx = miny = -1
maxx = maxy = 30
# some points randomly generated
points = [((10, 21), (19, 22)), ((11, 9), (22, 27)), ((9, 13), (5, 9)), ((18, 4), (2, 21)), ((25, 27), (11, 13))]
for pair in points:
print pair
for point in pair: #plot each point with a small dot
x = point[0]
y = point[1]
plt.plot(x,y,'bo')
# change this line to ((x, x1), (y, y1))
plt.plot((pair[0][0],pair[1][0]),(pair[0][1],pair[1][1]), color='r',linewidth=2)
plt.axis([minx,maxx,miny,maxy])
plt.show()
Results:

3D surface not transparent inspite of setting alpha

I am trying to create a 3D surface with transparency. When I try the following code below, I expect to get two semi-transparent faces of a cube. However, both the faces are opaque inspite of supplying the alpha=0.5 argument. Any pointer on why this is happening and how to fix it ? I am using Python 3.3 (IPython notebook with the QT backend)and Matplotlib 1.3.1.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d as mp3d
bot = [(0, 0, 0),
(1, 0, 0),
(1, 1, 0),
(0, 1, 0),
]
top = [(0, 0, 1),
(1, 0, 1),
(1, 1, 1),
(0, 1, 1),
]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
face1 = mp3d.art3d.Poly3DCollection([bot], alpha=0.5, linewidth=1)
face2 = mp3d.art3d.Poly3DCollection([top], alpha=0.5, linewidth=1)
ax.add_collection3d(face1)
ax.add_collection3d(face2)
Based on David Zwicker's input, I was able to get transparency working by setting the facecolor directly as a 4-tuple with alpha.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d as mp3d
bot = [(0, 0, 0),
(1, 0, 0),
(1, 1, 0),
(0, 1, 0),
]
top = [(0, 0, 1),
(1, 0, 1),
(1, 1, 1),
(0, 1, 1),
]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
face1 = mp3d.art3d.Poly3DCollection([bot], alpha=0.5, linewidth=1)
face2 = mp3d.art3d.Poly3DCollection([top], alpha=0.5, linewidth=1)
# This is the key step to get transparency working
alpha = 0.5
face1.set_facecolor((0, 0, 1, alpha))
face2.set_facecolor((0, 0, 1, alpha))
ax.add_collection3d(face1)
ax.add_collection3d(face2)

Matplotlib: Broken_barh plots with same height

I'm working with broken_barh plots. Is there any way to get a fixed height of a single broken_barh? The image should get bigger vertically, but proportions should stay the same.
Here is a simple example.
import matplotlib.pyplot as plt
import matplotlib as mlp
fig = plt.figure()
ax = fig.add_subplot(111)
broken_barh(self, xranges, yrange, **kwargs)
ax.broken_barh([(110, 30), (150, 10)], (0, 10), facecolors='blue')
ax.broken_barh([(10, 50), (100, 20), (130, 10)] , (10, 10),
facecolors=('red', 'yellow', 'green'))
ax.broken_barh([(50, 30), (85, 10)], (20, 10), facecolors='black')
ax.set_xlim(0,200)
ax.set_xlabel('seconds since start')
ax.set_yticks([0,10,20])
ax.set_yticklabels(['Bill', 'Jim', 'Jeff'])
ax.grid(True)
plt.savefig('broken_barh_example.png', bbox_inches='tight')
plt.show()
If I generate two plots, one with two broken_barh and the other with three, it looks like this:
with 2 broken_barh
http://imageshack.us/a/img195/747/brokenbarhexample2.png
with 3 broken_barh
http://img341.imageshack.us/img341/5650/brokenbarhexamplenoyran.png
The render fits everything into the available space. If you want the size of the figure to grow as you add more rows, you can do it by hand via
fig.set_size_inches(w, h * num_rows, forward=True)
to force a fixed bar height.
(doc)

Categories