Combining go.Figure and dcc.Graph

I’m trying to create a graph that consists of two-charts-in-one, i.e. one x-axis with two y-axes. One graph is static, a (go.Figure) the other changes using sliders in a callback function(dcc.Graph). For example, the first graph could be environmental data from a csv file with the y-axis on the left and the second a sine wave — y-axis on the right — that can change amplitude, phase, frequency through the use of sliders.

——————Situation now; this works——————

y1______________
| |
| graph 1 |
||
x-axis
y2

| |
| graph 2 |
|
______________|
x-axis

—————|————Amp
———|——————Phase
———————|——Freq
sliders(callback)

—————Situation wanted; can’t get it to work——————

y1______________y2
| |
| graph 1 + 2 |
|_______________ |
shared x-axes

—————|————Amp
———|——————Phase
———————|——Freq
sliders(callback)

I can’t get this to work; is there an example somewhere that I could use that anyone knows off? Below the code. Thanks in advance.

from dash_core_components.Graph import Graph
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import plotly.graph_objects as go

app = dash.Dash(__name__)

# Prepare the asset chart
df = pd.read_csv("Environment.csv")
df.head()

df['date'] = pd.to_datetime(df['date'])

fig = go.Figure(data=go.Line(x=df['date'], y=df['Temp']))

fig.update_yaxes(type="log")
fig.update_layout(
    xaxis=dict(
        rangeslider=dict(
            visible=False
        )
    )
)

app.layout = html.Div(
    children=[
        html.Div([
            dcc.Graph(id='environ-graph', figure=fig),
            dcc.Graph(id="wave")
        ]),
        html.Div([
            html.Label("Frequency 1"),
            dcc.Slider(id='f1',
                       min=0.0006,
                       max=0.0008,
                       value=0.0007456,
                       step=0.00001)

	- -------etc-----

Hi @hapygo and welcome to the Dash Community

I can’t follow your code but perhaps this information can help you, this is the code I use to make a graph with two Y axis (one to show the stock prices and the other to show a red or green bar chart if the price is below or above the S&P price), it also show an aditional graph in the botton with the daily volumes.

It use a DataFrame (pricedf) with the column “Close” that has the daily stock close price, and column “Volume” with the daily stock volume. And a second DataTable (pricenew) that has a column “Close” that has the S&P close date and two columns “positive” and “negative” that has the delta value between the stock daily price and the S&P daily pirce.

    figura = {"data": [go.Scatter(x=pricedf.index, y=pricedf.Close, line={"color": "#97151c"}, name=ticker.upper(),
                               yaxis='y1'),
                     go.Scatter(x=pricedf.index, y=pricenew.Close, line={"color": "blue"},  name='S&P 500',
                               yaxis='y1'),
                     go.Bar(x=pricedf.index, y=pricenew.positive, marker={'color':'LightGreen'},  
                                name='% over S&P', yaxis='y2'),
                     go.Bar(x=pricedf.index, y=pricenew.negative, marker={'color':'LightSalmon'},  
                                name='% under S&P', yaxis='y2'),                           
                     go.Bar(x=pricedf.index, y=pricedf.Volume, marker={"color": "Chocolate"},  name='Volume', yaxis='y3')],
            
           "layout": go.Layout(autosize=True, height=305, font={"family": "Raleway", "size": 12},
                     title = {'text':company, 'font':{'family':"Raleway", 'color': 'DarkBlue'}},
                     title_font_size=16,
                     legend = {'orientation':'h'},
                     margin={"r": 60, "t": 25, "b": 0, "l": 40}, titlefont={"family": "Raleway", "size": 10},
                     xaxis={'anchor':'x1', "autorange": True, "showline": True, "type": "date", "zeroline": False}, 
                     yaxis={'domain':[0.25, 1], "autorange": True, "showline": True, "type": "linear", 'color':'darkblue', 
                            "zeroline": False, 'title': 'Price in USD ', 'overlaying': 'y2'},
                     yaxis2={'domain':[0.25, 1],  'title': 'Difference in %', 'side': 'right'},
                     yaxis3={'title': 'Volume', 'color':'darkblue', 'domain':[0, 0.20], 'side': 'right'})}
        
                     
    return figura

This “figura” return the figure that will be showed in the dcc.Graph component, and here is the result:

Thanks @Eduardo, but that doesn’t solve the problem. I know how to combine go.Bar and/or go.Scatter plots. The problem is that one of the plots changes when a slider is moved, i.el the y-value changes. This happens via an @ app.callback(. I can’t put the two graphs, the static one and the callback one, in one graph.

Hey @hapygo

I do not understand why can’t you put both graphics as two fig Output of the same callback using the sliders as input. :thinking:
:woozy_face:

Hi @Eduardo,
Here is the complete code:

from dash_core_components.Graph import Graph
from dash_html_components.Label import Label
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import plotly.graph_objects as go


app = dash.Dash(__name__)

df = pd.read_csv("../DashPlots/Data/CO2.csv")
df.head()

df['date'] = pd.to_datetime(df['date'])


xdata = np.arange(15132, len(df.index)+15132)

fig = go.Figure(go.Scatter(x=xdata, y=df['CO2-value']))
fig.update_layout(
    title="The top graph and sine-wave graph should be one",
    yaxis_title="CO2 value",
)


app.layout = html.Div(
    [
        html.Div([
            dcc.Graph(id="env-graph", figure=fig),
            dcc.Graph(id="wave"),
        ]),
        html.Div([
            html.Label("Frequency 1"),
            dcc.Slider(id='f1',
                       min=0.0081,
                       max=0.05,
                       value=0.017456,
                       step=0.0001)
        ]),
        html.Div([
            html.Label("Amplitude 1"),
            dcc.Slider(
                id='a1',
                min=0.0,
                max=3.0,
                step=0.1,
                value=2.15)
        ]),
        html.Div([
            html.Label("Phase 1"),
            dcc.Slider(
                id='ph1',
                min=-5,
                max=5,
                step=0.1,
                value=1.72)
        ]),
    ])


@ app.callback(
    Output('wave', 'figure'),
    [Input('f1', 'value'),
     Input('a1', 'value'),
     Input('ph1', 'value'),
     ])
def update_chart(f1, a1, phi1):
    T = 2*np.pi * xdata
    wave1 = a1*np.sin(T * f1 + phi1)
    wave = wave1 + 300

    lineplot = px.line(
        data_frame=wave
    )

    return lineplot


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

Here are some mock data

date,CO2-value,
1958-03-01,315.71,
1958-04-01,317.45,
1958-05-01,317.50,
1958-07-01,315.86,
1958-08-01,314.93,
1958-09-01,313.20,
1958-11-01,313.33,
1958-12-01,314.67,
1959-01-01,315.62,
1959-02-01,316.38,
1959-03-01,316.71,
1959-04-01,317.72,
1959-05-01,318.29,
1959-06-01,318.15,
1959-08-01,314.80,
1959-07-01,316.54,
1959-09-01,313.84,
1959-10-01,313.26,
1959-11-01,314.80,
1959-12-01,315.58,
1960-01-01,316.43,
1960-02-01,316.97,
1960-03-01,317.58,
1960-04-01,319.02,

So the top two graphs should be one; that’s what I don’t understand.
Thanks for your patience.

Hey @hapygo

I’m not an expert in plotly graph, but I found the solution.

I changed your callback graph to a go.Scatter figure and then using this example Subplots with logscale and two y axes - #2 by jmmease I added the two scatter line in one graph. Here is the code:

from dash_core_components.Graph import Graph
from dash_html_components.Label import Label
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import plotly.graph_objects as go


app = dash.Dash(__name__)

df = pd.read_csv("CO2.csv")
df.head()

df['date'] = pd.to_datetime(df['date'])


xdata = np.arange(15132, len(df.index)+15132)

fig = go.Figure(go.Scatter(x=xdata, y=df['CO2-value']))
fig.update_layout(
    title="The top graph and sine-wave graph should be one",
    yaxis_title="CO2 value",
)


app.layout = html.Div(
    [
        html.Div([
            dcc.Graph(id="env-graph", figure=fig),
            dcc.Graph(id="wave"),
            dcc.Graph(id="join"),
        ]),
        html.Div([
            html.Label("Frequency 1"),
            dcc.Slider(id='f1',
                       min=0.0081,
                       max=0.05,
                       value=0.017456,
                       step=0.0001)
        ]),
        html.Div([
            html.Label("Amplitude 1"),
            dcc.Slider(
                id='a1',
                min=0.0,
                max=3.0,
                step=0.1,
                value=2.15)
        ]),
        html.Div([
            html.Label("Phase 1"),
            dcc.Slider(
                id='ph1',
                min=-5,
                max=5,
                step=0.1,
                value=1.72)
        ]),
    ])


@ app.callback(
    Output('wave', 'figure'),
    Output('join', 'figure'),
    [Input('f1', 'value'),
     Input('a1', 'value'),
     Input('ph1', 'value'),
     ])
def update_chart(f1, a1, phi1):
    T = 2*np.pi * xdata
    wave1 = a1*np.sin(T * f1 + phi1)
    wave = wave1 + 300
    print(wave)

    lineplot = px.line(
        data_frame=wave
    )
    
    # build the same figure with go.Figure
    
    trace1 = go.Scatter(
        x=xdata, y=wave,
        name='wave'
    )
    trace2 = go.Scatter(
        x=xdata, y=df['CO2-value'],
        name='CO2',
        yaxis='y2'
    )
    data = [trace1, trace2]
    layout = go.Layout(
        title='Double Y Axis Example',
        xaxis=dict(
            title='xaxis title',
        ),
        yaxis=dict(
            title='yaxis title',
            type='log'
        ),
        yaxis2=dict(
            title='yaxis2 title',
            overlaying='y',
            side='right'
        )
    )
    fig = go.Figure(data=data, layout=layout)


    return lineplot, fig


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

And this is the result:

1 Like

Thanks @eduardo, it works indeed. Little chance that I would have found the solution myself, so many thanks.

1 Like