Second y axis on a subplot

Hello guys,
I eight subplots on one chart.
For the 8th, I need to add the second yaxis. I hope to follow all relevant guides and discussions, however I stil get this issue: KeyError: ‘yaxis9’

Could you please let me know what it’s wrong with this syntax?
fig[‘layout’][‘yaxis8’].update(anchor = ‘x8’, side = ‘left’)
fig[‘layout’][‘yaxis9’].update(anchor = ‘x8’, overlaying= ‘y8’, side= ‘right’)

In the trace definition, I assign one trace to y8, the next two traces to y9.

@empet: I’ve seen your guide (https://plot.ly/~empet/14352/defining-subplots-with-two-y-axes-in-eac/#/) as well as other discussion, however it doesn’t help me to tackle the issue.

Many thanks,
Vaclav

@vaclavku Here is a simpler example that appends a new yaxis to the last cell, to imitate your case:

from plotly import tools as tls
import plotly.graph_objs as go

trace1 = go.Scatter(
    x=[1, 2, 3],
    y=[2, 4, 3]
)
trace2 = go.Scatter(
    x=[20, 30, 40],
    y=[7, 5, 3]
)
trace3 = go.Scatter(
    x=[2, 3, 4],
    y=[60, 70, 80]
)
trace4 = go.Scatter(
    x=[40, 50, 60, 70],
    y=[70,  90, 50, 65])

trace5 =go.Scatter(
                x=[40, 50,60, 70],
                y=[11, 15, 27, 23]
                  )
fig = tls.make_subplots(rows=2,
                        cols=2,
                        print_grid=True,
                        horizontal_spacing=0.15,
                        vertical_spacing=0.12
                         )
fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 1, 2)
fig.append_trace(trace3, 2, 1)
fig.append_trace(trace4, 2, 2)   
fig.append_trace(trace5, 2, 2) 

with fig.batch_update():
    fig.data[4].update(yaxis='y5')
    fig.layout.update(width=600, height=500,
                      yaxis5=dict(overlaying='y4',
                                  side='right',
                                  anchor='x4',
                                  range=[10, 30],
                                  showgrid=False,
                                  title='right title'),
                      hovermode='closest')
fw = go.FigureWidget(fig)

figsbplts

Hello @empet,
OK, many thanks. That sounds interesting and very usefull, however I do have more complex structure and can’t find a simpler way how to adopt it.
First, I’ve very dymanic list of traces (data) and the ones relevant for the second y have different number as well as the position.
Is it some posible to state similar expression:
with fig.batch_update():
fig.TRACES_names.update(yaxis=‘y5’)
fig.layout.update(width=600, height=500,
yaxis5=dict(overlaying=‘y4’,
side=‘right’,
anchor=‘x4’,
range=[10, 30],
showgrid=False,
title=‘right title’),
hovermode=‘closest’)
fw = go.FigureWidget(fig)

Second, how shell I use the FigureWidget in Dash?

Many thanks, Vaclav

Note: For your curiousity:
Inside the update callback, I’ve these two functions:
trace_price = price(asset_tic_adj)
fig.append_trace(trace_price,5,3)

trace_volume = volume(asset_tic_adj)
for trace in trace_volume: 
    fig.append_trace(trace,5,3)    

The functions call the global ones which return the traces (it states that price is on the left and the volume is on the right Y).

What does it return the KeyError: ‘yaxis9’?
IMHO: This part ’ fig.TRACES_names.update(yaxis=‘y5’)’ is defined in the trace:
trace = go.Scatter(
x=asset_tic_adj[‘date’], y=asset_tic_adj[‘close’], mode=‘lines’, showlegend=False, name=‘line’, opacity = 0.9,
line = dict(width= 0.5, color = ‘#800000’), yaxis = ‘y8’
)

or:

trace1 = go.Bar(
    x=asset_tic_adj['date'], y=asset_tic_adj['volume'], showlegend=False, opacity = 0.9, 
        marker=dict(
        color='#81CC00',
        line=dict(
            color='#81CC00',
            width=1.5,
        )
        ),
        yaxis='y9'
    )

Why can’t I use the logic here:
fig[‘layout’][‘yaxis8’].update(anchor = ‘x8’, side = ‘left’)
fig[‘layout’][‘yaxis9’].update(anchor = ‘x8’, overlaying= ‘y8’, side= ‘right’)

@vaclavku I didn’t notice that your project is a dash one, because the question wasn’t related to dash.

  1. You can update your initial fig. The conversion to go.FigureWidget isn’t necessary. I did it for displaying faster the figure.
  2. In this case (i.e. no conversion to go.FigureWidget) you can perfom the updates on dicts if you prefer this form:
fig['data'][4].update(yaxis='y5')
fig['layout'].update(width=600, height=500,
                      yaxis5=dict(overlaying='y4',
                                  side='right',
                                  anchor='x4',
                                  range=[10, 30],
                                  showgrid=False,
                                  title='right title'),
                      hovermode='closest')

I’m not sure if I undesrstood your last question. It seems that you assigned the yaxis='y9' in your trace definition. In my example the axes assignments are performed by the lines of code:

fig.append_trace(trace1, 1, 1)
fig.append_trace(trace2, 1, 2)
fig.append_trace(trace3, 2, 1)
fig.append_trace(trace4, 2, 2)   
fig.append_trace(trace5, 2, 2)

If you print the fig after these appends you’ll see:

print(fig)
(Scatter({
     'uid': 'c88b9eea-8be1-4a61-86df-c768bc47b513', 'x': [1, 2, 3], 'xaxis': 'x', 'y': [2, 4, 3], 'yaxis': 'y'
 }), Scatter({
     'uid': '0a188393-2cfc-4fb0-99b3-feab35697970', 'x': [20, 30, 40], 'xaxis': 'x2', 'y': [7, 5, 3], 'yaxis': 'y2'
 }), Scatter({
     'uid': '16eac9fd-f525-4f22-9209-2b82c415e697', 'x': [2, 3, 4], 'xaxis': 'x3', 'y': [60, 70, 80], 'yaxis': 'y3'
 }), Scatter({
     'uid': 'f216bc77-a156-4734-93bf-52d6cb8789e6',
     'x': [40, 50, 60, 70],
     'xaxis': 'x4',
     'y': [70, 90, 50, 65],
     'yaxis': 'y4'
 }), Scatter({
     'uid': 'fc125392-8abe-4b48-b49b-19f2efc8cd99',
     'x': [40, 50, 60, 70],
     'xaxis': 'x4',
     'y': [11, 15, 27, 23],
     'yaxis': 'y4'
 }))

Hence both trace4 and trace5 are referenced to xaxis4, yaxis4. That’s why is necessary the following update:

fig['data'][4].update(yaxis='y5') # or equivalently fig.data[4].update(yaxis='y5')

But if you don’t define the subplots via tls.make_subplots, but specifying in each trace its xaxis and yaxis,
no update is necessary on fig['data'][4].

You should update only fig[‘layout’].
The traces whose yaxis have not set side='right' in layout, are supposed to have side='left' by default.

Hello @empet
OK, great for your time. Very appreciated.
However, I still have the issue with the update of data.

  1. This version ‘fig[data][-2:].update(yaxis=‘y9’)’ returns:
    NameError: name ‘data’ is not defined

  2. This version ‘fig.data[-2:].update(yaxis=‘y9’)’ returns:
    AttributeError: ‘tuple’ object has no attribute ‘update’

What goes wrong in my minds?

Many thanks,
Vaclav

The full update part is like this:
fig[data][-2:].update(yaxis=‘y9’)
fig[‘layout’].update(height=1800,
width=1500.54,
paper_bgcolor=colors[‘white’],
plot_bgcolor= colors[‘white’],
font=dict(size=12,
color= colors[‘dark’]),
title=‘Basic trading indicators’,
yaxis9=dict(overlaying=‘y8’,
side=‘right’,
anchor=‘x8’),
)
fig[‘layout’][‘margin’] = {‘b’: 20, ‘r’: 25, ‘l’: 30, ‘t’: 40}
fig[‘layout’][‘xaxis3’].update(anchor = ‘y3’,
rangeslider = dict(visible = False))
fig[‘layout’][‘xaxis4’].update(anchor = ‘y4’,
rangeslider = dict(visible = False))

@vaclavku What Python version are you running? I posted code that works with the last version 3.10. In order to see what’s wrong with your code, it’s necessary to see how you defined the subplots and traces appended to each cell. Morever please don’t paste here fragments of non-formatted Python code. If you addressa questions on stackoverflow such a code is not accepted.

Hence, please update your plotly library:

pip install plotly --upgrade

and follow either my example posted yesterday here or the two notebooks I uploaded a time ago on Plotly cloud, to answer the same question on subplots with two yaxes.

hello @empet,
many thanks. Very appreciated.
Your examples work well. It’s just not possible to update two traces in one shot [-2:0].
Now, it works fine if I update it one by one.

Cheers,
Vaclav