8. Using dual-axes in Plotly#

Charts with two y-axes, also known as dual-axis charts, are a type of data visualization that allows two different datasets with distinct scales to be represented on the same graph. Each y-axis corresponds to a different dataset, typically placed on opposite sides of the chart (left and right), while the x-axis is shared. This design enables users to compare two related but distinct metrics simultaneously. For instance, a dual-axis chart might plot revenue on one y-axis and the number of units sold on the other, helping illustrate how sales volume correlates with revenue trends.

These charts are particularly useful when the datasets being compared have different units or magnitudes but share a common temporal or categorical dimension. They are effective in cases where understanding the relationship between the two datasets is crucial, such as analyzing temperature and energy consumption, stock price movements and trading volume, or marketing spend and customer acquisition rates. Dual-axis charts are valuable tools for identifying correlations and trends while keeping the visualization compact and focused.

However, they should be used cautiously, as the presence of two y-axes can easily lead to misinterpretation. Differences in scaling can exaggerate or understate relationships, potentially misleading viewers if the axes are not clearly labeled or appropriately aligned. Additionally, dual-axis charts are not ideal for datasets with no meaningful connection, as forcing them into the same visualization can create confusion rather than clarity. Alternative visualizations, such as separate charts or scatter plots, may be better for uncorrelated datasets. When using dual-axis charts, it is essential to ensure transparency in axis scaling and provide clear legends to maintain accuracy and comprehension.

How to do it#

  1. Import the plotly.graph_objects modules as go; and the function make_subplots from plotly.submodules

import plotly.graph_objects as go
from plotly.subplots import make_subplots
  1. Create a Figure object which has a secondary y-axis and inspect it. As you can see the secondary y-axis is still not visible without adding traces to the figure object

fig = make_subplots(specs=[[{"secondary_y": True}]]) # Create figure with secondary y-axis
fig.update_layout(title="Double Y Axis Example") # Add figure title
fig.show()
type(fig)
plotly.graph_objs._figure.Figure
  1. Usa the method add_trace to add two different traces into the Figure object and make sure one of them will use the secondary axis. We can do this by using go functions such as go.Scatter which allows us to specify the axis via the argument secondary_axis. Thus, we add two scatter plots, and set secondary_axis = True for one of them

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

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

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

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

fig.show()
  1. Customise the labels for the Figure axex by using the methods update_xaxes and update_yaxes

fig = make_subplots(specs=[[{"secondary_y": True}]])

fig.update_layout(title_text="Double Y Axis Example")

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

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

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

There is more#

import yfinance as yf
import datetime as dt
import numpy as np
start = dt.datetime(2019, 12, 30)
end = dt.datetime(2024, 10, 31)

btc = yf.download('BTC-USD', start=start, 
                  end=end, interval="1wk")
[*********************100%***********************]  1 of 1 completed
YF.download() has changed argument auto_adjust default to True

btc = btc.assign(returns=btc['Close'].pct_change(1))
btc = btc.assign(returns_sign=np.sign(btc["returns"]))
colors = {-1.0: "rgba(255,0,0, 0.75)", 1.0: "rgba(0,153,76, 0.75)"}
# Create figure
fig = make_subplots(specs=[[{"secondary_y": True}]],
                    )
# Add traces
fig.add_trace(
    go.Bar(x=btc.index, y=btc.returns, marker=dict(color=btc.returns_sign.map(colors)),
           name="Bitcoin weakly % change"),
    secondary_y=False)

fig.add_trace(
    go.Scatter(x=btc.index, y=btc.Close, mode='lines', line_color='rgba(0, 102 , 204, 1.0)',
               name="BTC/USD DOLLAR"),
    secondary_y=True, )

# Update Layout
fig.update_layout(title_text="Bitcoin Weakly Movement", template="plotly_white", height=600, width=1000)

fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="<b></b>USD", secondary_y=True)
fig.update_yaxes(title_text="<b></b>Percent", secondary_y=False)

fig.update_layout(dict(yaxis2={'anchor': 'x', 'overlaying': 'y', 'side': 'left', 'range': [0, 80000],
                               'domain': [0, 1], 'position': 0.5},
                       yaxis={'anchor': 'x', 'range': [-0.5, 0.5],
                              'domain': [0.0, 1.0],
                              'side': 'right'}))

fig.show()