Introducing plotly.py 4.0.0

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

Update: Version 4.0.0 final has been released. See the official announcement post at Plotly.py 4.0 is here: Offline Only, Express First, Displayable Anywhere | by Plotly | Plotly | Medium.


Introducing plotly.py 4.0.0rc1

I’m happy to announce the availability of the first release candidate of plotly.py version 4. This is a major release that includes many features and changes that we’re really excited about. For installation instructions, see the Getting Started page. Here are some of the highlights that will be discussed in more detail below:

  • “offline” only (chart-studio package split)
  • Plotly Express integration
  • New figure update methods
  • New renderers framework (or plotly.py everywhere)
  • New default theme
  • Consistent figure export API
  • More flexible subplots
  • Reduced package size (plotly-geo package split)
  • graph_objs to graph_objects
  • jupyterlab-plotly extension and JupyterLab 1.0 support
  • How you can help

“offline” only (chart-studio package split)

Prior versions of plotly.py contained functionality for creating figures in both “online” and “offline” modes. In “online” mode, figures were uploaded to the Chart Studio cloud (or on-premise) service, whereas in “offline” mode figures were rendered locally. This duality has been a common source of confusion for several years, and so in version 4 we are making some important changes to help clear this up.

First, all functionality for interacting with Chart Studio has been moved from the top-level plotly module (e.g. plotly.plotly.plot) to a new top-level chart_studio module (e.g. chart_studio.plotly.plot).

Second, this chart_studio module is no longer included in the plotly distribution package. Instead, it is included in a new optional chart-studio distribution package. See the version 4 migration guide for guidance on porting “online” code to use the new chart-studio package.

Using our legacy terminology, both the plotly distribution package and the top-level plotly module are “offline” only. They contain no logic for interacting with external Chart Studio services. The only way to interact with Chart Studio services is to install the chart-studio distribution package and call functions from the top-level chart_studio module. Because this distinction is now much clearer, we are retiring the “online”/“offline” terminology.

Plotly Express integration

In March, we released a tech preview of Plotly Express: a wrapper for plotly.py that provides a simple syntax for creating complex charts. See the announcement at ✨ Introducing Plotly Express ✨. Plotly Express is a new high-level… | by Plotly | Plotly | Medium for more background.

We have been very encouraged by the positive response that Plotly Express has received, and so for version 4 we are integrating Plotly Express into the main plotly.py distribution package. The top-level plotly_express module is now included as the plotly.express module. See the new documentation page for Plotly Express at Redirecting… for more information.

New figure update methods

One of our goals for the integration of Plotly Express was to make it easy for users to start with Plotly Express for data exploration, and then tweak and refine the resulting figures with all of the customization support built into plotly.py.

To this end, version 4 introduces a suite of new figure methods for updating figures after they have been constructed. These functions all return a reference to the calling figure, and are designed to support being chained together. We’ve also introduced “magic underscore notation” to make it easier to update nested figure properties. See the new Creating and Updating Figures page for full details.

Here is an example that creates a faceted scatter plot with OLS regression lines using the Plotly Express scatter function and then:

  • Updates the title font size using the new update_layout method with magic underscore notation (Without this notation, the title_font_size=24 argument would be replaced by title=dict(font=dict(size=24))).
  • Updates the x-axes across all facets to disable the vertical grid lines using the new update_xaxes method.
  • Updates the regression lines in each facet to have a dashed pattern using the new update_trace method. The selector argument to update_traces ensures that the update applies only to the regression line traces, and not to the scatter marker traces as well.
import plotly.express as px
iris = px.data.iris()
(px.scatter(iris, x="sepal_width", y="sepal_length", color="species",
            facet_col="species", trendline="ols", title="Iris Dataset")
 .update_layout(title_font_size=24)
 .update_xaxes(showgrid=False)
 .update_traces(
     line=dict(dash="dot", width=4),
     selector=dict(type="scatter", mode="lines"))
)

New renderers framework (or plotly.py everywhere)

Version 4 introduces a new renderers framework for displaying plotly figures in a variety of contexts. The renderers framework is a generalization of the plotly.offline.init_notebook_mode and plotly.offline.iplot functions that were the preferred way to display figures in notebook contexts in prior versions. Rather than calling iplot(fig) to display a figure, now you can do the same with by calling the fig.show() figure method. And you don’t need to pre-initialize a notebook for rendering the way you needed to before using the init_notebook_mode function.

import plotly.graph_objects as go
fig = go.Figure(
    data=[go.Bar(y=[2, 1, 3])],
    layout_title_text="A Figure Displayed with fig.show()"
)
fig.show()

In most situations, you can even omit the call to .show() and allow the figure to display itself!

import plotly.graph_objects as go
fig = go.Figure(
    data=[go.Bar(y=[2, 1, 3])],
    layout_title_text="A Figure Displaying Itself"
)
fig

In many contexts, an appropriate renderer will be chosen automatically and you will not need to perform any additional configuration. These contexts include:

  • Classic Jupyter Notebook
  • JupyterLab (provided the jupyterlab-plotly JupyterLab extension is installed)
  • Visual Studio Code notebooks
  • Nteract notebooks
  • Colab
  • Kaggle notebooks
  • Azure notebooks
  • Python interactive shell

Additional contexts are supported by choosing a compatible renderer including:

  • IPython console
  • QtConsole
  • Spyder
  • PyCharm
  • Sphinx Gallery

See the new Displaying plotly figures documentation page for information on how to configure the default renderer, and for descriptions of all of the available renderers and why you might choose each one.

Note: The legacy init_notebook_mode and iplot functions have been reimplemented on top of the renderers framework and are still supported for backward compatibility.

New default theme

Plotly.py introduced theming support in version 3.4. Since that release, we have iterated on the design of the built-in 'plotly' theme, and we made this theme the default in the tech preview of Plotly Express. In version 4, we are making this theme the default for all figures created with Plotly.py. See the new Theming and templates documentation page for information on configuring the default theme.

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

# Make figure with subplots
fig = make_subplots(rows=1, cols=2, specs=[[{"type": "bar"},
                                            {"type": "surface"}]])

# Add bar traces to subplot (1, 1)
fig.add_trace(go.Bar(y=[2, 1, 3]), row=1, col=1)
fig.add_trace(go.Bar(y=[3, 2, 1]), row=1, col=1)
fig.add_trace(go.Bar(y=[2.5, 2.5, 3.5]), row=1, col=1)
fig.add_trace(go.Bar(y=[4, 2, 3]), row=1, col=1)
fig.add_trace(go.Bar(y=[5, 1, 2]), row=1, col=1)

# Add surface trace to subplot (1, 2)
# Read data from a csv
z_data = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv")
fig.add_surface(z=z_data)

# Hide legend
fig.update_layout(
    showlegend=False,
    title_text="Version 4 Default Theme",
    height=500,
    width=800,
)

fig.show()

# Disable theme
fig.layout.template = 'plotly_white'
fig.layout.title.text = 'Version 4 White Theme'
fig.show()

# Disable theme
fig.layout.template = 'plotly_dark'
fig.layout.title.text = 'Version 4 Dark Theme'
fig.show()

# Disable theme
fig.layout.template = 'none'
fig.layout.title.text = 'Version 3 Default Theme'
fig.show()

Consistent figure export API

In version 4, we have completed a long-running effort to clean up and simplify the various ways to export figures. A figure can now be written to a file as an image, as html, or as json using the .write_image, .write_html, and .write_json figure methods respectively. The corresponding .to_image, .to_html, and .to_json figure methods can also be used to convert a figure into an in-memory representation of an image, html, or json without writing to a file. Finally, all of these methods are also available as standalone functions in the plotly.io module. See the Static image export page for more information on static image export.

More flexible subplots

The make_subplots function is used to create figures with custom subplot layouts, and it has received a lot of attention for version 4. In addition to fixing many bugs, make_subplots now supports all subplot and trace types. See the subplots documentation page for more information.

Here is an example that creates and populates a 2 x 2 subplot grid containing 4 different subplot types.

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

fig = make_subplots(
    rows=2, cols=2,
    specs=[[{"type": "xy"}, {"type": "polar"}],
           [{"type": "domain"}, {"type": "scene"}]],
)

fig.add_trace(go.Bar(y=[2, 3, 1]),
              row=1, col=1)

fig.add_trace(go.Barpolar(theta=[0, 45, 90], r=[2, 3, 1]),
              row=1, col=2)

fig.add_trace(go.Pie(values=[2, 3, 1]),
              row=2, col=1)

fig.add_trace(go.Scatter3d(x=[2, 3, 1], y=[0, 0, 0], z=[0.5, 1, 2], mode="lines"),
              row=2, col=2)

fig.update_layout(height=700, showlegend=False)

fig.show()

The make_subplots function now supports creating figures with secondary y axes.

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

# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])

# Add traces
fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[40, 50, 60], name="yaxis data"),
    secondary_y=False,
)

fig.add_trace(
    go.Scatter(x=[2, 3, 4], y=[4, 5, 6], name="yaxis2 data"),
    secondary_y=True,
)

# Add figure title
fig.update_layout(
    title_text="Double Y Axis Example"
)

# Set x-axis title
fig.update_xaxes(title_text="xaxis title")

# Set y-axes titles
fig.update_yaxes(title_text="<b>primary</b> yaxis title", secondary_y=False)
fig.update_yaxes(title_text="<b>secondary</b> yaxis title", secondary_y=True)

fig.show()

See the updated Multiple Axes documentation page for more examples of creating figures with multiple y-axes, and see the updated Subplots page for more subplot examples.

Note: The new preferred location to import make_subplots from is plotly.subplots. It is also available as plotly.tools.make_subplots for backward compatibility.

Reduced package size (plotly-geo package split)

In order to reduce the size of the core plotly distribution package, the bundled geographic shape files used by the create_choropleth figure factory have been moved to a new optional plotly-geo distribution package. This change reduces the size of the plotly source package from 27.4 MB down to 4.7 MB.

graph_objs to graph_objects

The graph_objs submodule is now available as the easier-to-pronounce graph_objects module. The graph_objs submodule is still supported for backward compatibility, but going forward we recommend using the following import statement to access the graph object classes.

import plotly.graph_objects as go

jupyterlab-plotly extension and JupyterLab 1.0 support

The @jupyterlab/plotly-extension extension for rendering plotly figures in JupyterLab has been replaced by a new jupyterlab-plotly extension that will now be maintained and updated as a part of the plotly.py project.

This extension improves the load time of notebooks that contain lots of plotly figures compared to the @jupyterlab/plotly-extension extension. This is done by displaying saved figures as static images initially. These static images are then replaced with live plotly.js figures when a user hovers over the plot.

The plotlywidget JupyterLab extension for rendering FigureWidget figures has been updated to support JupyterLab 1.0.

How you can help

If you would like to help us test the release candidate, please follow the installation instructions in the Getting Started page.

If you will be running legacy code, please reference the Version 4 migration guide.

If you think you’ve found a bug, please open an issue at Issues · plotly/plotly.py · GitHub.

If you notice an issue with the new documentation, please open an issue with our documentation project at Pull requests · plotly/plotly.py-docs · GitHub.

11 Likes

I’m getting a 404 error for this URL you posted above:

https://plotly.github.io/python-next/creating-and-updating-figures/

1 Like

@matt.siler the link should point to https://plot.ly/python/next/creating-and-updating-figures/

1 Like

Thanks @matt.siler, should be fixed now.
-Jon

1 Like

Hi @jmmease,

After installing 4.0.0rc1, I had some issues e.g. solving environment failed (my mistake was not using a testing environment :sob: ) that led me to revert to the previous version. Now i’m having issues with all previous created visualizations and getting “Error displaying widget”. I looked at the java console and have all kinds of issues (below). Any ideas where i can start the rescue process?

Thanks!

vendors~main.1c3e20f9893b3b37be8d.js:226786 Plugin '@jupyter-widgets/midicontrols:plugin' failed to 
activate.
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:226786
vendors~main.1c3e20f9893b3b37be8d.js:226787 Error: No provider for: 
jupyter.extensions.jupyterWidgetRegistry.
at JupyterLab.push.2R+v.Application.resolveRequiredService 
(vendors~main.1c3e20f9893b3b37be8d.js:226701)
at vendors~main.1c3e20f9893b3b37be8d.js:226659
at Array.map (<anonymous>)
at JupyterLab.push.2R+v.Application.activatePlugin (vendors~main.1c3e20f9893b3b37be8d.js:226659)
at vendors~main.1c3e20f9893b3b37be8d.js:226785
at Array.map (<anonymous>)
at JupyterLab.push.2R+v.Application.start (vendors~main.1c3e20f9893b3b37be8d.js:226784)
at main (main.ffe41b3c8accb24e2e48.js:2661)
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:226787
vendors~main.1c3e20f9893b3b37be8d.js:226786 Plugin 'ipysheet' failed to activate.
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:226786 
vendors~main.1c3e20f9893b3b37be8d.js:226787 Error: No provider for: 
jupyter.extensions.jupyterWidgetRegistry.
at JupyterLab.push.2R+v.Application.resolveRequiredService 
(vendors~main.1c3e20f9893b3b37be8d.js:226701)
at vendors~main.1c3e20f9893b3b37be8d.js:226659
at Array.map (<anonymous>)
at JupyterLab.push.2R+v.Application.activatePlugin (vendors~main.1c3e20f9893b3b37be8d.js:226659)
at vendors~main.1c3e20f9893b3b37be8d.js:226785
at Array.map (<anonymous>)
at JupyterLab.push.2R+v.Application.start (vendors~main.1c3e20f9893b3b37be8d.js:226784)
at main (main.ffe41b3c8accb24e2e48.js:2661)
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:226787
vendors~main.1c3e20f9893b3b37be8d.js:553570 Download the Vue Devtools extension for a better 
development experience:
https://github.com/vuejs/vue-devtools
vendors~main.1c3e20f9893b3b37be8d.js:553581 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
vendors~main.1c3e20f9893b3b37be8d.js:731411 Starting application in workspace: /lab
vendors~main.1c3e20f9893b3b37be8d.js:261100 JupyterLab extension jupyterlab-dash is activated!
github?1562934593594:1 Failed to load resource: the server responded with a status of 404 (Not Found)
vendors~main.1c3e20f9893b3b37be8d.js:522930 The JupyterLab GitHub server extension appears to be missing. If you do not install it with application credentials, you are likely to be rate limited by GitHub very quickly
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:522930
vendors~main.1c3e20f9893b3b37be8d.js:228786 Warning: Each child in an array or iterator should have a unique "key" prop.

Check the top-level render call using <CollapsibleSection>. See https://fb.me/react-warning-keys for more information.
    in ListView
printWarning @ vendors~main.1c3e20f9893b3b37be8d.js:228786
vendors~main.1c3e20f9893b3b37be8d.js:261105 Notebook added!
vendors~main.1c3e20f9893b3b37be8d.js:666011 Starting WebSocket: ws://localhost:8888/api/kernels/2f08aabd-9c97-4050-ac8d-183bfecb1e0d
vendors~main.1c3e20f9893b3b37be8d.js:261108 Notebook session ready
vendors~main.1c3e20f9893b3b37be8d.js:667375 Kernel: connected (2f08aabd-9c97-4050-ac8d-183bfecb1e0d)
vendors~main.1c3e20f9893b3b37be8d.js:261111 Notebook kernel ready
vendors~main.1c3e20f9893b3b37be8d.js:666011 Starting WebSocket: ws://localhost:8888/api/kernels/b8002eed-2719-4db9-adcb-e2359fe2e637
vendors~main.1c3e20f9893b3b37be8d.js:667375 Kernel: connected (b8002eed-2719-4db9-adcb-e2359fe2e637)
vendors~main.1c3e20f9893b3b37be8d.js:754293 Could not instantiate widget
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:754293
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module plotlywidget, semver range ^0.3.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
lab:1 Uncaught (in promise) Module plotlywidget, semver range ^0.3.0 is not registered as a widget module
vendors~main.1c3e20f9893b3b37be8d.js:754293 Could not instantiate widget
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:754293
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module plotlywidget, semver range ^0.3.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
lab:1 Uncaught (in promise) Module plotlywidget, semver range ^0.3.0 is not registered as a widget module
vendors~main.1c3e20f9893b3b37be8d.js:754293 Could not instantiate widget
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:754293
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
lab:1 Uncaught (in promise) Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
vendors~main.1c3e20f9893b3b37be8d.js:754293 Could not instantiate widget
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:754293
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
2lab:1 Uncaught (in promise) Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
vendors~main.1c3e20f9893b3b37be8d.js:863412 Error: Could not create a model.
    at promiseRejection (vendors~main.1c3e20f9893b3b37be8d.js:863412)
promiseRejection @ vendors~main.1c3e20f9893b3b37be8d.js:863412
vendors~main.1c3e20f9893b3b37be8d.js:666871 Exception opening new comm
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666871
vendors~main.1c3e20f9893b3b37be8d.js:666079 Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
(anonymous) @ vendors~main.1c3e20f9893b3b37be8d.js:666079
lab:1 Uncaught (in promise) Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module
vendors~main.1c3e20f9893b3b37be8d.js:936109 Error displaying widget
vendors~main.1c3e20f9893b3b37be8d.js:936110 Module @jupyter-widgets/base, semver range ^1.2.0 is not registered as a widget module

Issue resolved.
A general comment: existing documentation should clarify the version of each library required, as well as dependencies, its versions and order of download for the tools to work properly.

Update: version 4.0.0 is now launched! The new docs have moved to their new home at https://plot.ly/python :slight_smile:

Hearty thanks and congrats to @jmmease for his amazing work on plotly.py! :clap: :tada:

1 Like

Hey everyone,

I have installed version 4 of plotly.py however now dot completion is not working any more.
The IDE I am using is pycharm.
It currently does not work with
import plotly.graph_objects as go
however it works with plotly.express module.

edit: If I import it in python console it works fine, but not in the editor.

One question. There was the py.image.get(fig) method to get the byte object of the figure, is this still possible? I working in offline mode. But i get the error: You must be authenticated to generate an image via json.

I think this is not 100% right about everything in plotly 4.0 library being offline.
df.iplot() still shows options to export to plot.ly unlike iplot(fig). Please correct me if am wrong. Thanks.

df.iplot() does not come from the plotly package but rather from cufflinks, which is a third-party module not maintained by Plotly.