đź“Ł Announcing Plotly.py 5.15.0 - Label Aliases, Labels on Shapes, Texttemplate on Shape Labels, Multiple Legends, and Patterns

We are happy to announce that Plotly.py 5.15.0 is now available for download via pip and conda! For up-to-date installation and upgrading instructions, please see our Getting Started documentation page and if you run into trouble, check out our Troubleshooting Guide.

To start working with Plotly v5.15.0, using pip:

  • pip install plotly==5.15.0

Or with conda:

  • conda install -c plotly plotly=5.15.0

This is a combined announcement for Plotly.py versions 5.14.0 and 5.15.0 and highlights some of the main updates. See the full changelog for more info.

:arrow_right: Label Aliases

If you’ve ever wanted to change the labels displayed on axes without changing the underlying data, you can now do this with label aliases. In the following heatmap, the underlying data has country codes in the LOCATION column. Using labelalias on the y axis, we swap out the codes (e.g. “FRA”) for the full country names (e.g. “France”). It also works on colorbars. Here we’ve also made a small adjustment to the colorbar text, swapping out the “10” value on the colorbar for “10%”.

import plotly.graph_objects as go
import pandas as pd


df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/add-commutes-data/unemployment_data_europe_oecd.csv"
)


filtered_df = df[df["LOCATION"].isin(df["LOCATION"].unique()[:7])]

fig = go.Figure(
    data=go.Heatmap(
        x=filtered_df["TIME"],
        y=filtered_df["LOCATION"],
        z=filtered_df["Value"],
        colorscale="Viridis_r",
        colorbar=dict(labelalias={10: "10%"}),
    ),
    layout=go.Layout(title="% Unemployment Rate by Country"),
)
fig.update_yaxes(
    title="Country",
    labelalias=dict(
        DEU="Germany",
        FRA="France",
        FIN="Finland",
        DNK="Denmark",
        CZE="Czech Republic",
        BEL="Belgium",
        AUT="Austria",
    ),
)


fig.show()

This feature was anonymously sponsored: thank you to our sponsor!

:label: Labels on Shapes

Plotly.py now supports adding labels to shapes. In the following example, we add a rectangle and line shape to a graph and then add a label to both. Each label is customized with a textposition and font size. See the documentation for more examples.

import plotly.express as px

df = px.data.stocks(indexed=True)
fig = px.line(df)

fig.add_shape(
    type="rect",
    x0="2018-09-24",
    y0=0,
    x1="2018-12-18",
    y1=3,
    line_width=0,
    label=dict(text="Decline", textposition="top center", font=dict(size=20)),
    fillcolor="lightgreen",
    opacity=0.25,
)

fig.add_shape(
    type="line",
    x0=min(df.index),
    y0=1,
    x1=max(df.index),
    y1=1,
    line_width=2,
    line_dash="dot",
    label=dict(
        text="Jan 1 2018 Baseline",
        textposition="end",
        font=dict(size=20),
        yanchor="top",
    ),
)

fig.show()

Thanks to the Volkswagen Center of Excellence for Battery Systems for sponsoring development!

:writing_hand: Texttemplate on Shapes

The text on shapes can now dynamically update based on properties of the shape with texttemplate. In the texttemplate you have access to raw variables based on the shape definition (x0, x1, y0, and y1) as well as some calculated variables, including width, height, length, and slope.

In the following example, we use the calculated variable dy (which is y1-y0) in the first subplot to show the change on the y axis from one point on the graph to another. On the second subplot, we use another calculated variable, dx (which is x1-x0), to show the change on the x axis. Dates axes return values in milliseconds, so we divide by 86400000 to get the number of days:

import plotly.graph_objects as go
from plotly.subplots import make_subplots

import pandas as pd

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/add-commutes-data/unemployment_data_europe_oecd.csv"
)


filtered_df = df[df["LOCATION"].isin(df["LOCATION"].unique()[8:20])]
filtered_one_country = filtered_df.loc[df['LOCATION']=='IRL']

fig = make_subplots(rows=1, cols=2)

fig.add_trace(
go.Scatter(x=filtered_one_country["TIME"], y=filtered_one_country["Value"],
), row=1, col=1)

fig.add_trace(
    go.Heatmap(
        x=filtered_df["TIME"],
        y=filtered_df["LOCATION"],
        z=filtered_df["Value"],
        colorscale="Viridis_r",
    ),
    row=1, col=2
)


fig.add_shape(
    type="line",
    xref="x2",
    x0="2009-01-01",
    y0=4.5,
    x1="2015-06-01",
    y1=4.5,
    line_width=4,
    label=dict(texttemplate="Days above 10%: %{dx/86400000}", font=dict(size=12, color="white")),
    fillcolor="black",
)

fig.add_shape(
    type="line",
    xref="x",
    x0="2012-09-01",
    y0=16.1,
    x1="2020-04-01",
    y1=5,
    line_width=4,
    label=dict(texttemplate="Change from peak: %{dy:.2f}", textposition="top center", font=dict(size=10)),
    fillcolor="grey",
)

fig.update_layout(title_text="Subplots with Texttemplate")
fig.show()

It also works on new shapes. In the following example, we enable drawing lines on the figure (in the config of fig.show, we add drawline to modeBarButtonsToAdd, which adds the drawline button to the modebar), and define a texttemplate for shapes that shows the calculated variable dy.

For more detail on the calculated variables available, see the variables in shape label text section in the docs.

new_shape

import plotly.graph_objects as go
from plotly import data

df = data.stocks()

fig = go.Figure(
    data=go.Scatter(
        x=df.date,
        y=df.GOOG,
        line=dict(color="#4285F4"),
    ),
    layout=go.Layout(
        yaxis=dict(title="Price in USD"),
        newshape=dict(
            label=dict(texttemplate="Change: %{dy:.2f}"),
        ),
        plot_bgcolor="#FAF1F1",
        title={
            "text": "Google Share Price 2018/2019",
            "font": {"color": "#333333", "size": 20},
        },
    ),
)


fig.show(
    config={
        "modeBarButtonsToAdd": [
            "drawline",
        ]
    }
)

Thanks to the Volkswagen Center of Excellence for Battery Systems for sponsoring development!

:bar_chart: Multiple Legends

Plotly.py now supports multiple legends. In this example, we assign “legend2” to the two traces on the second subplot by setting legend="legend2". We then position the first legend at "0.4" on the x axis, to move it beside the first subplot. For more on the options available for configuring legends, see the layout reference docs.

import plotly.graph_objects as go
from plotly import data
from plotly.subplots import make_subplots

df = data.gapminder()

subplot_1_countries = ["Germany", "France", "United Kingdom"]
subplot_2_countries = ["United States", "Canada"]

fig = make_subplots(
    rows=1,
    cols=2,
    horizontal_spacing=0.2,
    shared_yaxes=True,
)

for country in subplot_1_countries:
    filtered_data = df.loc[(df.country.isin([country]))]
    fig.add_trace(
        go.Scatter(x=filtered_data.year, y=filtered_data.lifeExp, name=country),
        row=1,
        col=1,
    )

for country in subplot_2_countries:
    filtered_data = df.loc[(df.country.isin([country]))]
    fig.add_trace(
        go.Scatter(
            x=filtered_data.year,
            y=filtered_data.lifeExp,
            name=country,
            legend="legend2",
        ),
        row=1,
        col=2,
    )

fig.update_layout(
    title="Life Expectancy Subplots with Multiple Legends",
    plot_bgcolor="#E6F1F6",
    legend={
        "x": 0.4,
    },
)

fig.show()

This feature was anonymously sponsored: thank you to our sponsor

:straight_ruler: Container Reference Support for Colorbar and Legends

Plotly.py 5.15.0 adds xref and yref to legend and colorbar to support container referenced positioning. To use container references, set xref and/or yref to “container”. For more on positioning and the different types of coordinates see the figure structure page in the docs.

import plotly.graph_objects as go
import pandas as pd

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/add-commutes-data/commute_modes_2021_census.csv"
)

fig = go.Figure()

for mode in df["Mode"]:
    legend_item_visibility = "legendonly" if mode == "Car, truck or van" else True

    fig.add_trace(
        go.Bar(
            x=df.columns[1:],
            y=df.loc[df["Mode"] == mode, df.columns[1:]].values[0],
            name=mode,
            visible=legend_item_visibility,
        )
    )

fig.update_layout(
    title="Commute by mode of transport as % of all commutes",
    legend={
        "xref": "container",
        "yref": "container",
        "y": 0.5,
        "bgcolor": "Gold",
    },
)

fig.show()

Thanks to Gamma Technologies for sponsoring the related development.

:chart_with_upwards_trend: Patterns on More Graph Types

Patterns were already available on a number of graph types, but are now also available on pie, funnelarea, sunburst, icicle and treemap traces. Patterns are useful if you want to double-encode variables to improve accessibility for visually-impaired end-users, encode an additional variable beyond just using color, or make charts that are easier to print in black and white.

Here’s an example of using patterns on multiple pie charts. We add the pattern when creating each trace by setting the pattern on the marker:

import plotly.graph_objects as go
from plotly.subplots import make_subplots
from plotly import data

import pandas as pd

df = data.election()[:3]

fig = make_subplots(
    rows=1,
    cols=3,
    subplot_titles=df["district"],
    specs=[[{"type": "domain"}, {"type": "domain"}, {"type": "domain"}]],
)

for i, row in df.iterrows():
    labels = ["Coderre", "Bergeron", "Joly"]
    values = [row["Coderre"], row["Bergeron"], row["Joly"]]

    fig.add_trace(
        go.Pie(
            labels=labels,
            values=values,
            marker=dict(
                colors=["Gold", "MediumTurquoise", "LightGreen"],
                pattern=dict(
                    shape=[
                        "-",
                        "/",
                        ".",
                    ],
                    solidity=0.9,
                ),
            ),
            name="",
        ),
        row=1,
        col=1 + i,
    )

fig.show()

See additional examples for sunburst, funnelarea, icicle, and treemaps.

:heart: Powered by Plotly.js 2.24.1

Plotly.py 5.15.0 is built with Plotly.js 2.24.1. The examples above, except the pattern examples, will work with Dash 2.10, which uses Plotly.js 2.23.2. The patterns feature on pie, funnelarea, sunburst, icicle and treemap traces will be available in Dash 2.11.

:package: Get it now!

To sum up: Plotly.py 5.15.0 is out and if you’re excited about any of the above features, head on over to our Getting Started documentation page for full installation instructions!

:reminder_ribbon: In Case You Missed It: Previous Announcements

  • Plotly.py 5.13, 5.12, 5.11, 5.10, and 5.9
    • Grouped scatter support
    • New marker and angle attributes
    • Persistent selections
    • Sankey links with arrows
    • Rounded corners on treemaps
    • Map bounds and clusters
  • Plotly.py 5.8
    • Better Autocompletions with Type Hints
    • Minor Ticks
  • Plotly.py 5.7
    • Patterns on areas
    • Tick label steps (Plotly 5.6)
    • Text on histograms and heatmaps (Plotly 5.5)
    • Smith charts (Plotly 5.4)
    • Legend group click (Plotly 5.3)
  • Plotly.py 5.2 and 5.1
    • Trendlines
    • ECDF Plots
    • Markers on Lines
    • Sharper WebGL
    • Legend Group Titles
  • Plotly.py 5.0
    • A combined, federated JupyterLab Extension
    • Bar Chart Patterns (aka Hatching or Textures)
    • Icicle and Flame Charts
    • Explicit Legend-Item Ordering
    • Faster JSON serialization with orjson
10 Likes

Thanks @liamc

A couple of days ago I got a few issues with the multiple legends positioning, more recently, it seemed that after I added a second legend on the chart, the legendorder switched by default to reverse. I had to specify traceorder=“normal” to have it in the normal order.
I created this topic:

I still have to provide a MRE, I will do it asap.

Dumb question, but since plotly.py is built from Plotly.js 2.24.1; are these features already available in Plotly.js 2.24.1? Is there a simple list or summary that identifies features available .js that are not in .py, or vice versa?

HI @allsyntax, that’s right, all these features are available in Plotly.js 2.24.1.
The latest Plotly.js is 2.24.2, which has a few bug fixes not yet in Plotly.py.
The best place to see the changes that are in Plotly.js that will be in a future Plotly.py release is the Plotly.js changelog

And every time we update Plotly.py with a new Plotly.js we mention it in the Plotly.py changelog

2 Likes

I have updated to the new plotly 5.15.0 version and tried the (very helpful) labelalias argument, and it is not working.
Even the example you provide in the description of this new arguments, shows the original labels on the plot:

Is multiple legends only applicable for subplots?

hi @alonsht
I can try your code and see if I get the same error on my computer.

Are you using the exact same code from @liamc 's announcement post?

Ok i noticed something weird
If I run:
fig.show(renderer=“browser”)
it is rendered via plotly.js 2.24.1 and labelalias works
while the fig.show() renders it in vscode using plotly.js 2.13.3
using the new version of plotly with Streamlit, it renders it using plotly.js 2.18.1
in the last two options - the labelalias does not show any errors, but it does not work in the plot.

All within the same code.

I’m not sure how Plotly is incorporated/used within streamlit. But if you use Dash 2.11.0 or Plotly 2.15.0, you should be able to run this code with no errors, especially using the simplified fig.show()

No, @Vaishali
You’ll see in the docs how multiple legends are added to one plot as well.

as I described earlier,
with fig.show() within vscode, it is rendered using plotly js 2.13.3 (even though 2.15.0 is installed with pip)
and i get the plot without the labelalias

when doing the same thing but with fig.show(renderer=“browser”) instead of fig.show(), it is opened in the browser where it is viewed with plotly js 2.18.1 and the labelalias works

I still couldn’t figure out how the plotly.js version is decided