Fill facecolor in convex hulls for custom seaborn mapping function

Question:

I’m trying to overlay shaded convex hulls to the different groups in a scatter seaborn.relplot using Matplotlib. Based on this question and the Seaborn example, I’ve been able to successfully overlay the convex hulls for each sex in the penguins dataset.

# Import libraries
import pandas as pd
import numpy as np

from scipy.spatial import ConvexHull
import matplotlib.pyplot as plt
import seaborn as sns

sns.set_style('whitegrid')

data = sns.load_dataset("penguins")
xcol = 'bill_length_mm'
ycol = 'bill_depth_mm'

g = sns.relplot(data = data, x=xcol, y = ycol,
                hue = "sex", style = "sex",
                col = 'species', palette="Paired",
                kind = 'scatter')

def overlay_cv_hull_dataframe(x, y, color, **kwargs):

    data = kwargs.pop('data')
    # Get the Convex Hull for each group based on hue
    for _, group in data.groupby('hue'):
        points = group[['x', 'y']].values
        hull = ConvexHull(points)
        for simplex in hull.simplices:
            plt.fill(points[simplex, 0], points[simplex, 1], 
                    facecolor = color, alpha=0.5, 
                    edgecolor = color)

# Overlay convex hulls
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol,
                hue='sex')
g.set_axis_labels(xcol, ycol)

enter image description here

However, the convex hulls are not filled in with the same color as the edge, even though I specified that

plt.fill(points[simplex, 0], points[simplex, 1], 
                    facecolor = color, alpha=0.5,
                    edgecolor = color, # color is an RGB tuple like (0.12, 0.46, 0.71)
                    )

I’ve also tried setting facecolor='lightsalmon' like this example and removing the alpha parameter, but get the same plot. I think I’m really close but I’m not sure what else to try.

How can I get the convex hulls to be filled with the same color as edgecolor and the points?

Asked By: m13op22

||

Answers:

(Your code seems to have some typos, writing 'x', 'y' and 'hue' instead of x, y and hue).

simplex contains the indices of the coordinates of one edge of the convex hull. To fill a polygon, you need all edges, or hull.vertices.

g.map_dataframe only calls the function once per subplot. As such, color is only usable if you wouldn’t be using hue. Instead, you’ll need to store the individual colors in a palette dictionary. In plt.fill, alpha applies both to the face and the edge color. You can use to_rgba to give a transparrency to the face color while using the edge color without alpha.

import pandas as pd
import numpy as np
from scipy.spatial import ConvexHull
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgba
import seaborn as sns

sns.set_style('whitegrid')

data = sns.load_dataset("penguins")
xcol = 'bill_length_mm'
ycol = 'bill_depth_mm'

hues = data["sex"].unique()
colors = sns.color_palette("Paired", len(hues))
palette = {hue_val: color for hue_val, color in zip(hues, colors)}
g = sns.relplot(data=data, x=xcol, y=ycol, hue="sex", style="sex", col='species', palette=palette, kind='scatter')

def overlay_cv_hull_dataframe(x, y, color, data, hue):
    # Get the Convex Hull for each group based on hue
    for hue_val, group in data.groupby(hue):
        hue_color = palette[hue_val]
        points = group[[x, y]].values
        hull = ConvexHull(points)
        plt.fill(points[hull.vertices, 0], points[hull.vertices, 1],
                 facecolor=to_rgba(hue_color, 0.2),
                 edgecolor=hue_color)

# Overlay convex hulls
g.map_dataframe(overlay_cv_hull_dataframe, x=xcol, y=ycol, hue='sex')
g.set_axis_labels(xcol, ycol)

plt.show()

sns.relplot with convex hulls per hue

Answered By: JohanC
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.