Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting a clip on a seaborn plot

I am having trouble clipping a seaborn plot (a kdeplot, specifically) as I thought would be fairly simple per this example in the matplotlib docs.

For example, the following code:

import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

fig = plt.figure()
ax = fig.add_subplot(111, frameon=False, xticks=[], yticks=[])

random_points = np.array([p for p in np.random.random(size=(100, 2)) if 0 < p[0] < 1 and 0 < p[1] < 1])

kde = sns.kdeplot(random_points[:,0], random_points[:,1], ax=ax)

xmin, xmax = kde.get_xlim()
ymin, ymax = kde.get_ylim()

patch = mpl.patches.Circle(((xmin + xmax)/2, (ymin + ymax) / 2), radius=0.4)
ax.add_patch(patch)
kde.set_clip_path(patch)

Results in the following output:

enter image description here

I would like to clip this result so that the KDE contour lines do not appear outside of the circle. I haven't found a way to do it thus far...is this possible?

like image 263
Aleksey Bilogur Avatar asked Oct 17 '22 20:10

Aleksey Bilogur


1 Answers

Serenity's answer works for simple shapes, but breaks down for reasons unknown when the shape contains more than three or so vertices (I had difficulty establishing the exact parameters, even). For sufficiently large shapes the fill flows into where the edge should be, as for example here.

It did get me thinking along the right path, however. While it doesn't seem to be possible to do so simply using matplotlib natives (perhaps there's an error in the code he provided anyway?), it's easy as pie when using the shapely library, which is meant for tasks like this one.

Generating the Shape

In this case you will need shapely's symmetric_difference method. A symmetric difference is the set theoretic name for this cut-out operation.

For this example I've loaded a Manhattan-shaped polygon as a shapely.geometry.Polygon object. I won't covert the initialization process here, it's easy to do, and everything you expect it to be.

We can draw a box around our manhattan using manhattan.envelope, and then apply the difference. This is the following:

unmanhattan = manhattan.envelope.symmetric_difference(manhattan)

Doing which gets us to:

enter image description here

Adding it to the Plot

Ok, but this is a shapely object not a matplotlib Patch, how do we add it to the plot? The descartes library handles this conversion.

unmanhattan_patch = descartes.PolygonPatch(unmanhattan)

This is all we need! Now we do:

unmanhattan_patch = descartes.PolygonPatch(unmanhattan)
ax.add_patch(unmanhattan_patch)
sns.kdeplot(x=points['x_coord'], y=points['y_coord'], ax=ax)

And get:

enter image description here

And with a little bit more work extending this to the rest of the polygons in the view (New York City), we can get the following final result:

enter image description here

like image 165
Aleksey Bilogur Avatar answered Oct 21 '22 09:10

Aleksey Bilogur