Plotly snippets#

import plotly.express as px
p = px.scatter(...)

Basic configurations#

Fix static image export in windows#

The kaleido package from pypi doesn’t work properly.

In order for the png renderer to work you need to install this version of kaleido (for AMD processors):

pip install https://github.com/plotly/Kaleido/releases/download/v0.1.0.post1/kaleido-0.1.0.post1-py2.py3-none-win_amd64.whl

General theming#

import plotly.io as pio

Force notebook renderer#

pio.renderers.default = "notebook"

Dark mode#

pio.templates.default = "plotly_dark"

Changing default font#

import plotly.io as pio
import plotly.graph_objects as go

# Create a custom template
pio.templates["my_template"] = go.layout.Template(
  layout=dict(font_family="MathJax")
)
# Apply the template
pio.templates.default = "plotly_white+my_template"

Layout patterns#


Centered title#

p.update_layout(title_x=0.5)

Supress margins#

no_margins = dict(l=0, r=0, t=0, b=0)
(...)
p.update_layout(margins=no_margins)

Floating legends#

floating_legend = dict(
    yanchor="top",
    y=0.99,
    xanchor="left",
    x=0.01
)
(...)
p.update_layout(legend=floating_legend)

Centered horizontal legend with transparent background#

p.update_layout(
    legend=dict(
      orientation="h",
      yanchor="top",
      y=1.05,  # 0.98,
      xanchor="center",
      x=0.5,
      title=None,
      bgcolor="rgba(0,0,0,0)",
  ),
)

No margins & floating legends#

px_custom = {
    "margin": no_margins,
    "legend": floating_legend,
}
p.update_figure(**px_custom)

Subplots#

Create a subplot figure object#

from plotly.subplots import make_subplots

fig = make_subplots(
    rows=nrows,
    cols=ncols,
    # You have to explicitely write the type of plot in the specs
    specs=[{"type": "mapbox"} for _ in range(nrows*ncols)]
    horizontal_spacing=...,
    vertical_spacing=...,
    # You can name the subplots here
    subplot_titles=["Sp1", "Sp2", ...]
)

Then, you have to add traces with the row, col parameters (starts at 1)

fig.add_trace(
    px. (or) go. ...,
    row=1,
    col=1,
)

Colors#

Make a projection of a palette on specific points#

px.colors.sample_colorscale(
    # Color scale
    px.colors.sequential.Bluered,
    # Coordinates of the projection (array-like) [0; 1]
    [0.3, 0.4, ...]
)

A-posteriori modification of trace colors#

Traces are found in fig.data as a list.

Colors can be interpreted with Hex and RGB(A) notations.

for data in fig.data:
    data.line.update(
      color="rgb(0, 255, 255)"
    )

General figure modification#


Format percents in text#

fig.update_traces(
  # Rounds and add %
  textemplate="%{text:.2f}%"
)

Completely changing the hover data#

You can completely change the hover format with the following approach:

  • If you want to use variables inside the formatting:

px.area(
    (...),
    custom_data=["my_col_1", "my_col_2"],
)

Then you can change the format:

px.area(
    (...)
).update_traces(
    hovertemplate="<br>".join([
        # Use the first column from custom_data, bolded
        "<b>%{customdata[0]}</b>",
        # Use the value of the X axis, even if the name is not "x"
        "The x-axis equals to %{x}",    
        # Use the value of the Y axis, even if the name is not "y"
        "The y-axis equals to %{y}",
    ])
)

Note: Case of multiple Y plots

The case of multiple Y plots is as following:

fig = px.area(
  (...),
  y=["var1", "var2", "var3"]
)

You may want to include this variable name in the hover template.

You can only do this with manual tweaking of figure data.

The customdata is stored as a numpy array, you need to change it manually.

The shape of customdata is (number_of_rows, number_of_custom_columns)

import numpy as np

for trace in fig.data:
  trace["customdata"] = np.hstack([
    customdata := trace["customdata"],
    # Replace trace.name with any data you want to display
    np.repeat([trace.name], customdata.shape[0]).reshape(-1, 1)
  ])

Changing ticks spacing#

p.update_layout(xaxis=dict(dtick=12))

Manual change of ticks#

p.update_xaxes(
    tickmode = 'array',
    tickvals = {array_like},
    tick
)

Putting xticks at the top#

p.update_xaxes(side="top")

Fixing narrow boxplots when using colors#

Bug fixed in version 4.8.

Workaround: add boxmode="overlay" in the arguments.


Removing “=” in facets#

p.for_each_annotation(
    lambda t: t.update(text=t.text.split("=")[1])
)

Add ABline#

Unfortunately, traces will be present in the legend, but there is a way to avoid that (in the code).

Because of the ABline, there will be a zoom out, so you will also need to either set x/y ranges, or set the ABline points more intelligently.

p.add_trace(
    go.Scatter(
        x=[0, 1],
        y=[0, 1],
        mode="lines",
        # If you want to fill to bottom
        fill="tozeroy",
        # Line color (Here invisible)
        marker_color="rgba(255, 255, 255, 0)",
        # Fill color (Here white with 20% opacity)
        fillcolor="rgba(255, 255, 255, 0.2)",
    ).update(showlegend=False),
    # If you use a facet_row/column, apply to each subplot
    row="all",
    col="all",
)

Adding custom prefix or suffix to ticks#

p.update_traces(ticksuffix="%")
p.update_traces(tickprefix="Hello")

Change text position#

When plotting you can add labels by using text=. The position can be modified by using :

p.update_traces(textposition='top center')

Change opacity#

p.update_traces(opacity=.5)

Specific color mapping#

Specific color mappings can be made with dictionnaries : {modality: color, ...}

One easy trick to outline specific modality is to create a function that highlights a specific modality.

highlight = lambda data, mod: {mod: "red", **{s: "blue" for s in data.source.unique() if s != mod}}

This can be called directly in the creation of the plot:

px.scatter(
    dataframe,
    x="x",
    y="y",
    color="some_3rd_dimension",
    color_discrete_map=highlight(dataframe, "some_3rd_dimension")
)

Change colorbar title#

fig.update_coloraxes(colorbar=dict(title="This is my colorbar title"))

Remove colorbar#

fig.update_coloraxes(showscale=False)

When multiple subplots#

Don’t match all axis#

p.update_yaxes(matches=None)
(or)
p.update_xaxes(matches=None)

Show ticks on all subplot’s axis#

p.for_each_yaxis(lambda yaxis: yaxis.update(showticklabels=True))
(or)
p.for_each_xaxis(lambda xaxis: xaxis.update(showticklabels=True))

Barplots#

Stack bars on top of each other#

p.update_layout(barmode="stack")

Stack multiple bars in the same place#

p.update_layout(barmode="overlay")

Plotting large number of points#

When plotting a large number of points (several millions points), Plotly scatterplots suffers a lot.

We can use a complementary library datashader

import datashader as ds

Datashader is about the rasterisation of plots. Instead of plotting all single points, it computes a density image that can be later shown with px.imshow

Example:

cvs = ds.Canvas(
  plot_width= ..., # Number of horizontal pixels
  plot_height= ..., # Number of vertical pixels
)
agg = csv.points(
  dataframe,
  x_column_name,
  y_column_name,
) # Creates the numpy array containing density values

# Here we want to exclude pixels where there are no points
zero_mask = agg.values == 0
# For big data reasons, we may want to look at the logarithm of density
agg.values = np.log10(
  agg.values, where=np.logical_not(zero_mask)
)
agg.values[zero_mask] = np.nan
# plot the final image
fig = px.imshow(
  agg,
  origin="lower",
  labels={"color": "Log10(count)"},
)
fig.update_traces(hoverongaps=False, opacity=.9)
fig.update_layout(coloraxis_colorbar=dict(title='Count', tickprefix='1.e'))
fig.show()

Snippet for large scatterplot#

def big_scatter(
    data: pd.DataFrame,
    x: str,
    y: str,
    plot_width=400,
    plot_height=400,
    **plotly_kwargs,
):
    cvs = ds.Canvas(plot_width=plot_width, plot_height=plot_height)
    agg = cvs.points(data, x, y)
    zero_mask = agg.values == 0
    agg.values = np.log10(
        agg.values, where=np.logical_not(zero_mask)
    )
    agg.values[zero_mask] = np.nan
    px.imshow(
        agg,
        origin="lower",
        labels={"color": "Log10(count)"},
        **plotly_kwargs,
    ).update_traces(hoverongaps=False, opacity=.9).update_layout(coloraxis_colorbar=dict(title='Count', tickprefix='1.e')).show()

Usage :

big_scatter(
    df,
    x="hour",
    y="error",
    plot_width=23,
    plot_height=200,
    height=800,
    title=model
)