Rounding the marker sizes to a given list of ranges

Question:

I have marker sizes varied based on a column in my geodataframe but I want the sizes in 5 groups. I don’t want every value to have its own size, instead I’d like a range of values to have one marker size.

Here is the code:

fig, ax = mpl.pyplot.subplots(1, figsize = (10,10))

sns.scatterplot(
    data=fishpts_clip, x="Lon", y="Lat", color='Green', size='SpeciesCatch',
    sizes=(100, 300), legend="full"
)

plt.legend(loc='center left', bbox_to_anchor=(1.05, 0.5), ncol=1, title='Sizes')

This is what I got:

enter image description here

Instead, I’d like something like this:

enter image description here

Asked By: Cindy

||

Answers:

You could create an extra column with each of the values rounded up to one of the desired bounds. That new column can be used for the sizes and the hue. To update the legend, the values are located in the list of bounds; the value itself and the previous one form the new legend label.

The following code illustrates the concept from simplified test data.

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from scipy import interpolate

df = pd.DataFrame({'val': np.arange(1, 61),
                   'x': np.arange(60) % 10,
                   'y': np.arange(60) // 10 * 10})

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(16, 5))

sns.scatterplot(data=df, x="x", y="y", hue='val', palette='flare',
                size='val', sizes=(100, 300), legend='full', ax=ax1)
sns.move_legend(ax1, loc='center left', bbox_to_anchor=(1.01, 0.5), ncol=6, title='Sizes')
ax1.set_title('using the given values')

# create an extra column with the values rounded up towards one of the bounds
bounds = [0, 5, 10, 20, 40, 60]
round_to_bound = interpolate.interp1d(bounds, bounds, kind='next', fill_value='extrapolate', bounds_error=False)
df['rounded'] = round_to_bound(df['val']).astype(int)

sns.scatterplot(data=df, x="x", y="y", hue='rounded', palette='flare',
                size='rounded', sizes=(100, 300), ax=ax2)
sns.move_legend(ax2, loc='center left', bbox_to_anchor=(1.01, 0.5), ncol=1, title='Sizes')
for t in ax2.legend_.texts:
     v = int(t.get_text())
     t.set_text(f"{bounds[bounds.index(v) - 1]} - {v}")
ax2.set_title('rounding up the values towards given bounds')
sns.despine()
plt.tight_layout()
plt.show()

sns.scatterplot with list of sizes

Combining a seaborn legend with other elements can be complicated, depending on the situation. If you just add a pandas plot on top of the seaborn scatter plot, it seems to work out well. In this case, pandas adds a new element to the existing legend, which can be moved via sns.move_legend() at the end.

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
from scipy import interpolate

df = pd.DataFrame({'val': np.arange(1, 61),
                   'x': np.arange(60) % 10,
                   'y': np.arange(60) // 10 * 10})

fig, ax = plt.subplots(figsize=(16, 5))

# create an extra column with the values rounded up towards one of the bounds
bounds = [0, 5, 10, 20, 40, 60]
round_to_bound = interpolate.interp1d(bounds, bounds, kind='next', fill_value='extrapolate', bounds_error=False)
df['rounded'] = round_to_bound(df['val']).astype(int)

sns.scatterplot(data=df, x="x", y="y", hue='rounded', palette='flare',
                size='rounded', sizes=(100, 300), ax=ax)
for t in ax.legend_.texts:
    v = int(t.get_text())
    t.set_text(f"{bounds[bounds.index(v) - 1]} - {v}")

# add a pandas plot on top, which extends the legend
xs = np.linspace(0, 9, 200)
ys = np.random.randn(len(xs)).cumsum() * 2 + 25
dams_clip = pd.DataFrame({'dams_ys': ys}, index=xs)
dams_clip.plot(ax=ax, color="Red", linewidth=0.5, markersize=150, zorder=3)
sns.move_legend(ax, loc='center left', bbox_to_anchor=(1.01, 0.5), ncol=1, title='Sizes')

sns.despine()
plt.tight_layout()
plt.show()

extending the legend

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.