简体   繁体   中英

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:

在此处输入图片说明

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?

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:

在此处输入图片说明

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:

在此处输入图片说明

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:

在此处输入图片说明

I guess your example work only for 'imshow'.

To hide contours lines over the circle you have to plot 'inverse' polygon of desired color.

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

# Color plot except polygon
def mask_outside(poly_verts, facecolor = None, ax = None):
    from matplotlib.patches import PathPatch
    from matplotlib.path import Path

    if ax is None: ax = plt.gca()
    if facecolor is None: facecolor = plt.gcf().get_facecolor()

    # Construct inverse polygon
    xlim, ylim = ax.get_xlim(), ax.get_ylim()
    bound_verts = [(xlim[0], ylim[0]), (xlim[0], ylim[1]), 
                   (xlim[1], ylim[1]), (xlim[1], ylim[0]), (xlim[0], ylim[0])]
    bound_codes = [Path.MOVETO] + (len(bound_verts) - 1) * [Path.LINETO]
    poly_codes = [Path.MOVETO] + (len(poly_verts) - 1) * [Path.LINETO]

    # Plot it
    path = Path(bound_verts + poly_verts, bound_codes + poly_codes)
    ax.add_patch(PathPatch(path, facecolor = facecolor, edgecolor = 'None', zorder = 1e+3))

# Your example
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)
mask_outside([tuple(x) for x in patch.get_verts()]) # call before add_patch!
ax.add_patch(patch)

plt.show()

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM