📣 Dash 2.11.0 Released - Dash in Jupyter, Locked Flask versions, and dcc.Graph Updates

Update : version 2.13.0 has been released since this was posted.

We’re excited to announce that Dash 2.11.0 has been released :rocket:

pip install dash==2.11.0

Official Changelog :arrow_forward: Dash v2.11.0

Highlights :arrow_down_small:

Dash in Jupyter Environments

Dash 2.11 and later supports running Dash apps in classic Jupyter Notebooks and in JupyterLab without the need to update the code or use the additional JupyterDash library. If you are using an earlier version of Dash, you can run Dash apps in a notebook using JupyterDash.

Display Modes

There are 4 display modes. When you run an app in a notebook cell, it displays inline by default.

Inline Mode -

Code for sample app:
from dash import Dash, html, dash_table, dcc, callback, Output, Input
import pandas as pd
import plotly.express as px

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')

app = Dash(__name__)

# App layout
app.layout = html.Div([
    html.Div(children='My Dash App'),
    html.Hr(),
    dcc.RadioItems(options=['pop', 'lifeExp', 'gdpPercap'], value='lifeExp', id='my-final-radio-item-example', inline=True),
    dash_table.DataTable(data=df.to_dict('records'), page_size=6),
    dcc.Graph(figure={}, id='my-final-graph-example')
])

@callback(
    Output(component_id='my-final-graph-example', component_property='figure'),
    Input(component_id='my-final-radio-item-example', component_property='value')
)
def update_graph(col_chosen):
    fig = px.histogram(df, x='continent', y=col_chosen, histfunc='avg')
    return fig

# Run the app
if __name__ == '__main__':
    app.run()

External Mode -
By using external mode, you can configure it to display a link that allows you to view the app.

app.run(jupyter_mode="external")

Tab mode -
Use tab mode to automatically open the app in a new browser tab:

app.run(jupyter_mode="tab")

Jupyterlab mode -
In jupyterlab mode, the notebook displays the app in a separate tab in JupyterLab.

app.run(jupyter_mode="jupyterlab")

JupyterLab version 3 is required to run a Dash app in a Jupyterlab mode.

Please note that Multi-page apps using Dash Pages are not supported in notebooks.

You can read more about Dash in Jupyter customization features and troubleshooting in the docs.


Flask and Werkzeug upper bound version

Since day 1, Dash has used Flask as its web server. We’re incredibly grateful to have this excellent package to build on, and grateful for all the hard work of the folks maintaining it! And in the spirit of openness and our place as just one library of many in the Python ecosystem, we’ve always required only a minimum version for Flask, so that if you integrate Dash with a larger Flask app you can use whatever version of Flask you want, whatever version your host app already uses.

Unfortunately, this has caused us problems: a new version of Flask (or its matched WSGI utility Werkzeug) changes something we were using, either in Dash itself or in one of the many add-on libraries, either in the open-source world or in our enterprise offerings, and apps that were previously working suddenly fail.

So in order to make all of those standalone Dash apps more robust and maintainable, we’ve decided to lock Flask and Werkzeug to versions that we know work with Dash and all the other packages maintained by Plotly.

Starting from Dash v2.10.2, we’ve set Flask and Werkzeug version upper bound to <2.3.

If you specifically ask for the latest Flask (any newly-released version of Flask that we haven’t yet approved) alongside Dash with no version, you’ll get pushed back to dash==2.9.3 , the last version without an upper bound on Flask. To avoid this and still pin a Flask version, we recommend setting a minimum Dash version at least 2.10, so that instead of getting the older Dash you’ll be alerted to the conflict:

> pip install "dash>=2.10" "Flask>=2.3"
Collecting dash>=2.10
  Downloading dash-2.10.0-py3-none-any.whl (10.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.3/10.3 MB 17.3 MB/s eta 0:00:00
Collecting Flask>=2.3
  Using cached Flask-2.3.2-py3-none-any.whl (96 kB)
INFO: pip is looking at multiple versions of dash to determine which version is compatible with other requirements. This could take a while.
ERROR: Cannot install Flask>=2.3 and dash==2.10.0 because these package versions have conflicting dependencies.

The conflict is caused by:
    The user requested Flask>=2.3
    dash 2.10.0 depends on Flask<=2.2.3 and >=1.0.4

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

Plotly.py & dcc.Graph Updates

Updated Plotly.js to from version 2.20.0 to version 2.24.2.

The version of Plotly.js that is built in here is the same one that is bundled with the recently released Plotly.py 5.15.0, so we recommend that you upgrade to Plotly 5.15.0 to get the full benefit of all of these libraries working together.

pip install plotly==5.15.0

Official Changelog :arrow_forward: Plotly v5.15.0

There have been many developments in the world of Plotly.py and dcc.Graph recently. To highlight just a few:

:arrow_right: dcc.Graph - 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()

: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()

:writing_hand: Texttemplate on Shapes and Shape Formulas

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 .

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",
        ]
    }
)

slopes

: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()

To learn how to add multiple legends to a single plot, see the adding-multiple-legends docs.

: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()

: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 .


Notable Bug Fixes, Additions & Minor Changes

Dash:

  • #2530 Merge JupyterDash repository with Dash.
    • Add jupyter_mode argument to app.run, defaults to inline and configurable via jupyter_dash.default_mode.
    • Add prefixed arguments from JupyterDash to app.run: jupyter_width, jupyter_height, jupyter_server_url.
  • Set an upper bound to Flask and Werkzeug versions at <2.3 because we expect the Dash ecosystem to be incompatible with the next minor release of Flask (this excludes the current latest Flask release 2.3.x). We will raise the upper bound to <2.4 after we fix incompatibilities elsewhere in the Dash ecosystem.
  • #2538 Update JS dependencies in dcc, html, dash-table, dash-renderer, and dash
  • #2540 Add include_pages_meta=True to Dash constructor, and fix a security issue in pages meta tags #2536.
  • #2555 Fix browser back button when removing one of multiple location components from layout, fix #1312
  • #2565 Fix sorting for > 10 pages, fix #2564
  • #2545 Fix typescript objectOf generation.
  • #2548 Fix component as props callback triggering other callbacks not in response, fix #2487.
  • #2508 Fix error message, when callback output has different length than spec
  • #2207 Fix object of components support.
  • #2500 Passing customdata by click for scattermapbox, fix #2493
  • #2513 Raise error when iterating over patch objects, fix #2512
  • #2489 Fix location change event handling when Location objects are removed from the layout. Event handlers would not be removed and eventually change props of a random DOM element, fix #1346
  • #2498 Fix error when caching callbacks which return Patch objects by making Patch objects picklable
  • #2491 Fix clientside inline function name not found, fix #2488

Plotly:

  • #2474 Update plotly js to 2.24.2 from 2.23.2
    • Feature release 2.24.0 add pattern to pie, funnelarea, sunburst, icicle and treemap traces
    • Patch release 2.24.1 and 2.24.2
  • #2533 and #2538 Update Plotly.js to v2.23.2 from v2.20.0.
    • Feature release 2.23.0 adds legend/colorbar xref/yref.
    • Feature release 2.22.0 adds legend references to traces.
    • Feature release 2.21.0 adds label.texttemplate to parametric shapes.
    • Patch releases 2.23.1 and 2.23.2 fix heatmap rendering on iOS and Safari when zsmooth is set to false and shape text when drawing a new shape.

Previous Releases

:mega: Dash 2.9.2 Released - Partial Property Updates, Duplicate Outputs, dcc.Geolocation, Scatter Group Attributes & More
:mega: Dash 2.7 Released - Directional Arrows Feature, Map Bounds, and DataTable Filter Text
:mega: Dash 2.6 Released - Background Callbacks, Unit Testing, Persistent Selections, Dropdown Features
:mega: Dash 2.5 Released - Easier Multi-Page Apps, Component Properties
:mega: Dash 2.4 Released - Improved Callback Context, Clientside Promises, Typescript Components, Minor Ticks
:mega: Dash 2.3.0 Release - MathJax and fillpattern option in scatter trace
:mega: Dash 2.2.0 Release - Adds ticklabelstep to axes, and added dash.get_asset_url
:mega: Dash 2.1.0 Release - Autogenerated IDs and reärranged keyword arguments in Dash components

5 Likes

Thanks a lot! This is a great release.

I loved the integration of JupyterDash, and it works great on my local machine.

I wasn’t able to run it online however (Kaggle, JupyterHub, TLJH).

Is there a special setup for that?

Happy to share more details, but I first wanted to know if this is a known thing, or if I’m missing something.

hi @eliasdabbas
Thank you for bringing this up.

There are lots of different Jupyter-derived environments that all behave a little bit differently, so if we haven’t specifically tested Dash in your environment it might not work. We’ve tested Dash inside Jupyter notebook and Jupyter Lab running locally and on Dash Enterprise, as well as Google Colab. Databricks support is available with Dash Enterprise. Other environments - including JupyterHub, TLJH, JupyterLite, Kaggle, Sagemaker, VS Code, the list goes on - might work or we may be able to support in the future with help from the community. However, be aware that such fully-managed notebook environments may change their internals at any time and break Dash support, in ways that we have no control over.

1 Like

Thanks for the labelalias, I did need this so much! Same for the multiple legend, this feature is great :slight_smile:

1 Like