1

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?

m13op22
  • 2,168
  • 2
  • 16
  • 35
  • Upon closer inspection, it looks like the edgecolors for both convex hulls are the same, so maybe it's related to the way I'm accessing (or not accessing) the right colors according to hue... – m13op22 Feb 03 '23 at 17:23

1 Answers1

1

(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

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Ah, those aren't typos. Inside Seaborn, the `data` columns are replaced with `'x'`, `'y'`, and `'hue'` (literally). I tested that by printing the first 5 lines of `data` inside my overlay function. If I fix those, your function works perfectly. Thanks for the color dictionary and the fill correction, it's brilliant! – m13op22 Feb 03 '23 at 20:23
  • ... and for including `alpha` for shading! – m13op22 Feb 03 '23 at 20:55
  • 1
    When I run this with seaborn 0.12.2, I need `x` and `y` without the quotes. Maybe you have seaborn 0.11 installed? – JohanC Feb 03 '23 at 21:40