Showing a relationship split by group in one call, with legend included, is seaborn's core productivity win.
sns.scatterplot and sns.lineplot are the relational plots. Beyond x and y, they accept three semantic mappings:
hue: color. Categorical columns get a qualitative palette; numeric columns get a sequential colormap.style: marker shape (scatter) or dash pattern (line), always categorical.size: marker size or line width, usually numeric; tune the range withsizes=(20, 200).
Mapping the same column to both hue and style gives redundant encoding, which keeps the chart readable in grayscale.
The order of categories in colors and legend follows the data unless you pass hue_order=[...], or make the column a pandas Categorical with ordered categories. Pin the order explicitly in reports so colors do not shift between charts.
lineplot sorts by x before connecting points (sort=True), so unsorted input is fine. If there are several y values at the same x, it aggregates them, drawing the mean with a confidence band; that behavior is covered in the aggregation lesson.
legend="brief" (default for numeric hue) shows a sample of values; "full" lists every value; False hides it. Move it later with sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1)).
Large scatter plots suffer from overplotting; use alpha=0.3, smaller s=, or switch to a 2D histogram (sns.histplot(x=..., y=...)).
Example
import matplotlib
matplotlib.use("Agg")
import seaborn as sns
import pandas as pd
import numpy as np
rng = np.random.default_rng(0)
n = 120
df = pd.DataFrame({
"area": rng.uniform(30, 150, n),
"city": rng.choice(["Oslo", "Rome", "Lima"], n),
"rooms": rng.integers(1, 6, n),
})
df["price"] = df["area"] * 4 + df["rooms"] * 20 + rng.normal(0, 40, n)
ax = sns.scatterplot(data=df, x="area", y="price", hue="city", style="city",
size="rooms", sizes=(20, 160),
hue_order=["Lima", "Oslo", "Rome"], alpha=0.8)
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
texts = [t.get_text() for t in ax.get_legend().get_texts()]
print(texts[:4]) # ['city', 'Lima', 'Oslo', 'Rome']
ax.figure.savefig("homes.png", bbox_inches="tight")Remember
hue,styleandsizeadd up to three extra variables to a scatter or line plot- Fix category order with
hue_orderor an ordered Categorical - Use
sns.move_legendto reposition seaborn legends
Watch out for
- Mapping a high-cardinality column (hundreds of ids) to hue, producing an unreadable legend.