Color, symbols, facets and hover data

Plotly · Essentials · 10 min

Encoding extra variables and faceting is how you compare groups in one figure instead of many.

Express functions accept extra mappings beyond x and y:

  • color="continent": a categorical column creates one trace per value with distinct colors; a numeric column becomes a continuous color scale with a colorbar.
  • symbol= and line_dash= for marker shapes and dash styles;
  • size="pop" scales marker area (size_max= caps it);
  • facet_col="year", facet_row=, and facet_col_wrap=3 split the data into a grid of panels sharing axes;
  • hover_name="country" puts a bold title on the tooltip, and hover_data= adds columns, with format strings or False to hide one: hover_data={"pop": ":,", "iso_alpha": False}.

Control colors with color_discrete_sequence=px.colors.qualitative.Safe, color_discrete_map={"Asia": "red"} for exact assignments, or color_continuous_scale="Viridis" for numeric color. category_orders={"day": ["Thu", "Fri", "Sat", "Sun"]} fixes the order of categories in legends, axes and facets.

Because categorical color creates separate traces, clicking a legend entry hides that group, and double-clicking isolates it.

Facet titles default to column=value; tidy them with fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1])).

Too many facets or colors overwhelm the reader; a dozen panels or eight colors is a practical limit.

Example

import plotly.express as px

df = px.data.gapminder().query("year in [1987, 2007]")
fig = px.scatter(
    df, x="gdpPercap", y="lifeExp", log_x=True,
    color="continent", size="pop", size_max=40,
    facet_col="year", hover_name="country",
    hover_data={"pop": ":,", "continent": False},
    category_orders={"continent": ["Europe", "Asia", "Americas", "Africa", "Oceania"]},
)
fig.for_each_annotation(lambda a: a.update(text=a.text.split("=")[1]))

print(list(dict.fromkeys(t.name for t in fig.data)))
# ['Europe', 'Asia', 'Americas', 'Africa', 'Oceania']
print(len(fig.data))                     # 10: 5 continents x 2 facets
print([a.text for a in fig.layout.annotations])   # ['1987', '2007']
fig.write_html("facets.html")

Remember

  • Categorical color creates one trace per group; numeric color creates a color scale
  • facet_col/facet_row split into panels; category_orders fixes ordering
  • hover_name and hover_data decide the tooltip contents

Watch out for

  • Mapping an ID-like column with hundreds of values to color, creating hundreds of traces and a huge legend.

Check yourself

  1. You set `color='continent'` on a scatter of 5 continents. How many traces does the figure contain (no facets)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: plotly.com/python/facet-plots/