Using go.Surface in Dash

I am trying to incorporate a go.Surface graph in a dash app using this code below:

def surface_plot(df):
    X, Y = df.columns.values.astype(float), df.index.values.astype(float)
    fig = go.Figure(data=[go.Surface(x=X, y=Y, z=df.values, colorscale="Blues")])
    return fig 

It works correctly when used as a function (without making it a part of the dash app). But when I make it a part of the dash app, it uses the default color scale and doesn’t show the color bar as well. Alongside the surface plot the dash app also shows another plot (heatmap) which works as intended. Not sure what’s wrong with go.Surface.


HI @Amulya it will be quite hard to help you with the information provided. Could you add the code for the app or create a MRE?

Here goes the dash code:

app.layout = html.Div(children=[
                        html.Div(children=[components.description(), 
                                           components.dropdown_menu_day(dict_data),
                                           *components.make_break(1), 
                                           components.dropdown_menu_sample(), 
                                           *components.make_break(1), 
                                           components.dropdown_type_plot()
                        ],style={'width': '25%', 'display': 'inline-block','vertical-align':'top',
                                 'background-color': 'rgb(250, 250, 250)','text-align': 'center', 'height': '100vh'}),
                        html.Div(children=[*components.make_break(1), 
                                        html.Div([dcc.Graph(id='plotter', style={'height': '95%'})],
                                                 style={'padding':'10px', 'height': '90%', 'width': '90%','margin':'auto','background-color':'white','text-align': 'center'}),
                    ],style={'width': '75%', 'display': 'inline-block','background-color': 'rgb(240, 240, 240)', 'height': '100vh'})
                      ])

@callback(Output(component_id='dropdown_sample', component_property='options'),
          Input(component_id='dropdown_day', component_property='value'))
def dropdown_values(value):
    return [{'label':i.split(' ')[1], 'value': i} for i in dict_data[value].keys()]


@callback(
          Output(component_id='plotter', component_property='figure'),
          Input(component_id='dropdown_day', component_property='value'),
          Input(component_id='dropdown_sample', component_property='value'),
          Input(component_id='dropdown_plot', component_property='value')
          )
def figure(day, sample, plot_type):
    df_temp = dict_data[day][sample]
    if plot_type == 'Heatmap':
        return heatmap(df_temp)
    elif plot_type == 'Surface plot':
        return surface_plot(df_temp)

As I mentioned, the heatmap works as intended. The surface plot is the one having the issue.

So your main problem is, that the colorscale Blues is not used and the colorbar is not shown?

Yes. Indeed

Are you sure, the dtype of your df.columns is numeric? Looking at the surface plot it seems, that a categorical axis is being used.

Yes, they are numeric.

I think it’s a problem with your data since I can’t reproduce this.

from dash import Dash, dcc, html, Input, Output
import plotly.graph_objects as go
import pandas as pd

# Read data from a csv
data = pd.read_csv(
    filepath_or_buffer='https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv',
    index_col=0
)


def surface_plot(df):
    X, Y = df.columns.values.astype(float), df.index.values.astype(float)
    fig = go.Figure(data=[go.Surface(x=X, y=Y, z=df.values, colorscale="Blues")])
    return fig


app = Dash(__name__)

app.layout = html.Div(
    [
        html.Button(id='btn', children='click'),
        html.Div(id='graph_container')
    ]
)


@app.callback(
    Output('graph_container', 'children'),
    Input('btn', 'n_clicks'),
    prevent_initial_call=True
)
def show_plot(_):
    return dcc.Graph(figure=surface_plot(data))


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

This one works perfectly for me. I can’t figure out whats causing the issue

Can you share your data?

I cant share the entire data… the entire data is a nested dictionary with dataframes. This one is one of the dataframes.

This works with my example code, just change the csv path.

Yeah, it works for me as well. But not sure, what went wrong in my initial code

I think this line might cause the problem. dict_data is a global variable, I guess. Do you use this for reading only or are you changing dict_data anywhere?

It’s only used to read the file. The strange thing is the heatmap works as intended whereas, the surface doesn’t

Okay it looks like rendering issues to me. When I make the surface plot the the default value… it shows the colorbar and correct color map.

Now, as I switch the plots and again get back to the surface plot, it changes

Is this a bug or rendering issue?

Again, I can’t reproduce this:

from dash import Dash, dcc, html, Input, Output
import plotly.graph_objects as go
import pandas as pd

# Read data from a csv
data = pd.read_csv(
    filepath_or_buffer='example.csv',
    index_col=0
)


def surface_plot(df):
    X, Y = df.columns.values.astype(float), df.index.values.astype(float)
    fig = go.Figure(data=[go.Surface(x=X, y=Y, z=df.values)])
    return fig


def heatmap_plot(df):
    X, Y = df.columns.values.astype(float), df.index.values.astype(float)
    fig = go.Figure(data=[go.Heatmap(x=X, y=Y, z=df.values, colorscale="Blues")])
    return fig


app = Dash(__name__)

app.layout = html.Div(
    [
        dcc.Dropdown(
            id='drop',
            options=['heatmap', 'surface']
        ),
        html.Div(
            id='graph_container',
            children=dcc.Graph(id='graph')
        )
    ]
)


@app.callback(
    Output('graph', 'figure'),
    Input('drop', 'value'),
    prevent_initial_call=True
)
def show_plot(selection):

    return {
        'heatmap': heatmap_plot(data),
        'surface': surface_plot(data)
    }[selection]


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

Dash: 2.14.1
Plotly: 5.15.0

Okay not sure what it is but if I make the heatmap and the surface plot of the same colorscale, it works as expected. But if I make the heatmap of ‘blues’ color scale and leave the surface plot to its default color, the surface plot takes the ‘blues’ on the second run, leaving the colorbar to its default color. I just don’t understand this weird behavior.

So the fix for now is to make sure they have the same color scale.

1 Like