đź“Ł Dash v1.21.0 - Plotly.js 2.0, Icicle Charts, Bar Chart Patterns, Clipboard Component, Bug Fixes

Hello everybody :wave:

I’m pleased to announce that Dash 1.21.0 is out :tada:

pip install dash==1.21.0

Dash 1.21.0 is a backwards compatible feature release. See the official changelog.

Plotly.js 2.x Series!

Dash 1.21.0 is the first Dash release to use the plotly.js 2.x.xandplotly.py 5.x.x. series. In particular, it uses plotly.js 2.2.1 and is compatible with plotly.py 5.1. These features are available in the dcc.Graph component.

:warning: Backwards-Incompatible Changes to dcc.Graph and plotly.py 5.0

The following features are being dropped or deprecated:

  • Support for Python 2.7 and 3.5 as well as for Internet Explorer 9 and 10 has been dropped.
  • The long-deprecated and un/under-documented area trace type and the legacy scatter.(r|t) attributes have been removed, meaning that the plotly.graph_objects.Area class has been removed.
    • (note the px.area() function still works and has never used the legacy area trace type, which was a polar trace type)
  • The heatmapgl and pointcloud trace types are now deprecated, as are the transforms attributes in any trace in which they appear

The following changes have been made to library defaults:

  • Plotly Express now always takes into account every value in category_orders when computing discrete mappings (color, symbol, line-dash, pattern-shapes) as well as facets, even those values which are absent in the data
  • The font size for legend and colorbar titles is slightly bigger, to match axis titles
  • In bar traces, the default textposition is now auto rather than none
  • Legend items will no longer include the long-disliked Aa text unless mode strictly equals text
  • the “modebar” which appears when the mouse cursor is over a plot no longer includes buttons for switching the hovermode between x and closest (closest is now the default) and no longer includes a button for toggling “spikelines”

This release of Dash bumps Plotly.js from 1.58.4 to 2.2.1, so we recommend that interested users check out our Plotly.js 2.0 release announcement, which includes more in-depth information to the breaking changes in the underlying library. The Plotly.js 2.0 work was partly funded by Equinor, and we thank them on behalf of the community :pray:

:checkered_flag: Bar Chart Patterns (aka Hatching or Textures)

Bar charts, histograms and polar bar charts have large markers which support not only a fill color, but as of version 5.0, also an optional pattern (also known as “hatching” or “texture”). This can be used for a variety of reasons:

  • to double-encode variables (i.e. using both color and pattern) to improve accessibility for visually-impaired end-users
  • to encode an additional variable beyond just using color
  • to make charts that are easier to print in black and white

This feature was a community contribution from @s417-lama and we thank them on behalf of the community :bowing_man: !

:ice_cube: Icicle and :fire: Flame Charts

Do you like plotly's sunburst charts but wish they were less circular, but also less nested than treemaps? Then our new icicle charts are for you! They can work in any direction: left to right (or vice versa) for nice text layout, top-down and bluish for the classic icicle look, or bottom up and reddish for the classic flame chart!

This feature was developed by our former colleagues @Kully and @mtwichan of Zyphr and we thank them and their sponsors at Virgin Hyperloop! :heart:

:1st_place_medal: Explicit Legend-Item Ordering

A commonly-requested feature is the ability to reorder a figure’s legend items without needing to change the order in which traces appear in the data object. The new legendrank attribute addresses this need: the default rank is 1000, meaning that any trace can be sent to the bottom of the list if it is the only one with a rank greater than 1000, and the same goes for moving traces to the top of the list with ranks less than 1000. Ties are broken using the current position-in-data logic.

This feature was anonymously sponsored, and we thank our sponsors :bowing_man: !

:racehorse: Faster JSON serialization with orjson

One core performance bottleneck in Dash is that components and their properties (like dcc.Graph's figure) are automatically, internally serialized to a JSON string format for rendering in the browser. This can be quite slow by default for figures containing large numpy arrays or pandas data frames. In this release we have made some progress towards speeding this up, using an optional dependency on the excellent orjson library. When orjson is installed, Plotly.py will automatically leverage it to perform JSON serialization, which can result in 5x-10x performance improvements when the figure contains large arrays.

dcc.Clipboard

dcc.Clipboard is a new component that copies text to the clipboard. This was graciously contributed by community member @AnnMarieW :medal_sports:

The easiest way to trigger the copy is by using the the target_id property. No callback is required!
Place dcc.Clipboard() in the layout where you would like the copy icon located. Specify the target_id of the component with text to copy.

In this example, the content of the value prop of the dcc.Textarea() is copied to the clipboard.

app.layout = html.Div(
    [
        dcc.Textarea(
            id="textarea_id",
            value="Copy and paste here",
            style={ "height": 100},
        ),
        dcc.Clipboard(
            target_id="textarea_id",
            title="copy",
            style={
                "display": "inline-block",
                "fontSize": 20,
                "verticalAlign": "top",
            },
        ),
    ],
)

When target_id is not specified, the content of the text prop is copied to the clipboard. This works well with components like the DataTable where you may want to customized the text in a callback.

In this example, the dataframe is converted to text with pandas to_string(). See the pandas documentation for other formatting options such as including or excluding headers.

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

app.layout = html.Div(
    [
        dcc.Clipboard(
            id="DataTable_copy",
            style={"fontSize": 30, "color": "white", "backgroundColor": "grey", "height":38},
            className="button",
        ),
        dcc.RadioItems(
            id="copy_selected",
            options=[
                {"label": "Copy All", "value": "all"},
                {"label": "Copy Selected", "value": "some"},
            ],
            value="all",
            style={"display": "inline-block"},
        ),
        dash_table.DataTable(
            id="DataTable",
            columns=[{"name": i, "id": i} for i in df.columns],
            data=df.to_dict("records"),
            sort_action="native",
            page_size=10,
        ),
    ]
)


@app.callback(
    Output("DataTable_copy", "text"),
    Input("DataTable_copy", "n_clicks"),
    State("DataTable", "start_cell"),
    State("DataTable", "end_cell"),
    State("DataTable", "derived_virtual_data"),
    State("copy_selected", "value")
)
def custom_copy(_, start, end, data, copy_selected):
    dff = pd.DataFrame(data)
    if start and (copy_selected == 'some'):
        copy_cells = dff.loc[
            start["row"]: end["row"], start["column_id"]: end["column_id"]
        ]
        return copy_cells.to_string(header=False)
    else:
        return dff.to_string()  # includes headers

This component uses the Clipboard API, which is not supported by older browsers like Internet Explorer. When the Clipboard API is unavailable, the icon will not appear in the app and a warning message is written to the console.

Currently dcc.Clipboard only supports copying text to the clipboard.


Various Improvements

  • #916 html option to markdown_options prop in DataTable. This enables the use of html tags in markdown text.

  • #545. Case insensitive filtering to DataTable. This feature anonymously sponsored (thank you!)

    • New props: filter_options - to control case of all filters, columns.filter_options - to control filter case for each column
    • New operators: i=, ieq, i>=, ige, i>, igt, i<=, ile, i<, ilt, i!=, ine, icontains - for case-insensitive filtering, s=, seq, s>=, sge, s>, sgt, s<=, sle, s<, slt, s!=, sne, scontains - to force case-sensitive filtering on case-insensitive columns
  • #948 disabled_days prop to DatePickerRange and DatePickerSingle components. With this prop you can specify days that should be made unselectable in the date picker, in addition to those that fall outside of the range specified by min_date_allowed and max_date_allowed. Thank you @tcbegley for the contribution!

  • #1675 Remove the constraint that requests_pathname_prefix ends with routes_pathname_prefix. When you are serving your app behind a reverse proxy that rewrites URLs that constraint needs to be violated.

  • #1664 Fix #1649, makes the devtools readable with a dark theme. Thank you @AnnMarieW for the contribution!

  • #1640 Fix #1475, missing timing_information after certain modifications to Flask behavior. Thank you @remibaar for the contribution!

  • #907 Fixed DataTable pagination bugs. Thank you @AnnMarieW for the contribution!

    • Fixed a bug where pagination did not work or was not visible #834
    • Fixed a bug where if you are on a page that no longer exists after the data is updated, no data is displayed. #892

Previous Releases

6 Likes

Here are a few more examples of new features in this release :confetti_ball:

Disable days in DatePickerRange and DatePickerSingle components .

Thanks to @tcbegley for doing this pull request This was a frequently requested feature on the forum and it’s a nice addition to Dash.

This example makes weekends in April disabled:

image

# Example for making  weekends in April disabled:

from datetime import datetime, timedelta

import dash
import dash_core_components as dcc
import dash_html_components as html

APRIL = [datetime(2021, 4, 1) + timedelta(days=i) for i in range(30)]

app = dash.Dash()

app.layout = html.Div(
    [
        dcc.DatePickerSingle(
            disabled_days=[date for date in APRIL if date.weekday() >= 5],
            min_date_allowed=APRIL[0],
            max_date_allowed=APRIL[-1],
        )
    ],
    style={"padding": "30px"},
)

if __name__ == "__main__":
    app.run_server(debug=True)



Case insensitive filtering to DataTable

I’ve been looking forward to this functionality, and it works great! There is a new icon in the filter to select whether or not the filter is case sensitive. Here is what it looks like:

case_insensitive

# Case insensitive filter demo

import dash
import dash_html_components as html
import dash_table
import plotly.express as px

df = px.data.gapminder()

app = dash.Dash(__name__)

app.layout = html.Div(
    dash_table.DataTable(
        columns=[{"name": i, "id": i, "deletable": True} for i in df.columns],
        data=df.to_dict("records"),
        filter_action="native",
        filter_options={"case": "insensitive"},
        sort_action="native",
    ),
)

if __name__ == "__main__":
    app.run_server(debug=True)

Allow html in markdown in the DataTable.

There is now an option to “opt-in” and allow html tags to be used in markdown in the DataTable. Previously this was not allowed due to security issues. See this discussion to learn more: Writing Secure Dash Apps - Community Thread

Here are some examples using html tags in markdown. (See this pull request for the code for these examples)

image

image

Adding images using <img>. This can make it easier to control image size and add other style
image



dcc.Clipboard

Here are a few more examples of the new dcc.Clipboard component, including several options for formatting:
copy_paste

# basic copy to clipboard demo - no callback required!

import dash
import dash_html_components as html
import dash_core_components as dcc

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Textarea(
            id="textarea_id",
            value="Copy and paste here",
            style={ "height": 100},
        ),
        dcc.Clipboard(
            target_id="textarea_id",
            title="copy",
            style={
                "display": "inline-block",
                "fontSize": 20,
                "verticalAlign": "top",
            },
        ),
    ],
)

if __name__ == "__main__":
    app.run_server(debug=True)

Style the copy icon as a Bootstrap button:

image

# style the copy icon as a bootstrap button

import dash
import dash_core_components as dcc
import dash_bootstrap_components as dbc

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Container(
    [
        dcc.Textarea(
            id="textarea_id",
            value="Copy and paste here",
            style={"height": 100},
        ),
        dbc.Button(
            dcc.Clipboard(target_id="textarea_id"),
            style={"verticalAlign": "top"},
        ),
    ],
)

if __name__ == "__main__":
    app.run_server(debug=True)

Copy Icon placed inside of a scrolling Div

copy_scrollable_div

html.Div("Copy icon in top right corner of a scrollable div"),
        html.Div(
            [
                dcc.Markdown(
                    code,
                    id="code",
                    style={"width": 800, "height": 200, "overflow": "auto"},
                ),
                dcc.Clipboard(
                    target_id="code",
                    style={
                        "position": "absolute",
                        "top": 0,
                        "right": 20,
                        "fontSize": 20,
                    },
                ),
            ],
            style={
                "width": 800,
                "height": 200,
                "position": "relative",
                "marginBottom": 100,
            },
        ),

Copy to clipboard in a callback.

The previous examples, no callback was required to copy text to the clipboard. This works well for html tables as well. For the Dash DataTable, it’s necessary to use a callback. The data can be copied to the clipboard in different formats using Pandas functions. See the Pandas documentation for more information on the following: .to_text() .to_csv() or .to_excel().

#  example for copying from a DataTable to the clipboard

import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_table
from dash.dependencies import Input, Output, State
import pandas as pd

external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

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

app.layout = html.Div(
    [
        dcc.Clipboard(
            id="DataTable_copy",
            style={"fontSize": 30, "color": "white", "backgroundColor": "grey", "height":38},
            className="button",
        ),
        dcc.RadioItems(
            id="copy_selected",
            options=[
                {"label": "Copy All", "value": "all"},
                {"label": "Copy Selected", "value": "some"},
            ],
            value="all",
            style={"display": "inline-block"},
        ),
        dash_table.DataTable(
            id="DataTable",
            columns=[{"name": i, "id": i} for i in df.columns],
            data=df.to_dict("records"),
            page_size=10,
        ),
    ]
)


@app.callback(
    Output("DataTable_copy", "content"),
    Input("DataTable_copy", "n_clicks"),
    State("DataTable", "start_cell"),
    State("DataTable", "end_cell"),
    State("DataTable", "derived_virtual_data"),
    State("copy_selected", "value")
)
def custom_copy(_, start, end, data, copy_selected):
    dff = pd.DataFrame(data)
    if start and (copy_selected == 'some'):
        copy_cells = dff.loc[
            start["row"]: end["row"], start["column_id"]: end["column_id"]
        ]
        # see more options for .to_text() in pandas documentation
        return copy_cells.to_text(header=False, index=False, )
    else:
        # see more options for .to_csv() or .to_excel() in pandas documentation
        return dff.to_csv(index=False)  # includes headers

if __name__ == "__main__":
    app.run_server(debug=True)

8 Likes

Congratulations @AnnMarieW for the inclusion of your component in the official dash documentation.

2 Likes

I’m excited to use " Faster JSON serialization with orjson" in my dashboard. I hope it’ll be help alot while using larger dataframes in m dashapp.

Q: would this allow to solve:
Order non-numeric y-axis - Graphing Library - Plotly Community Forum