Changed behaviour in dash 2.18.2 using numpy 1.26.4 when reading figure into callback on linux

I have experienced a changed behaviour using dash 2.18.2 and numpy 1.26.4.

Before, when creating a figure and returning it to a dcc.Graph from a callback, I could simply use my numpy arrays and pass it to x and y of the traces of the figure.
Because when loading the figure into a callback as state afterwards, x and y were simply python lists.

Now, BUT JUST WHEN RUNNING ON LINUX, the behaviour is different: When reading the figure into a callback after returning it to a dcc.Graph with numpy arrays, x and y will be dicts when accessing the traces of the figure and look like this:

{
dtype
bdata
_inputArray (the actual set of values that I originally passed to the x attribute of the trace)
}

This behaviour was very unexpected for me and has caused exceptions in my app, because I expected x and y to be lists when reading a figure as state and working with it. It is still like this on Windows. Now I have to cast my numpy arrays to lists before passing it to x and y of the traces before I return the figure from the callback.

Is this new behaviour intentional or is it a bug?

Thanks in advance

Hey @dashuser2 hard to tell. There could be a lot of reasons for a changed behavior.

First of all, which versions did you use before? I guess you updated dash, as the current version of numpy is 2.2.3.

I’m on ubuntu 22.04, could you provide an example which reproduces the behavior you mention? I could check on my side.

Hi @AIMPED, thanks for the quick reply.

We have found out that this behaviour happens due to a new plotly version.

Plotly 5.24.1 creates a list from a numpy array in HTML and Plotly 6.0.0 creates a dict from a numpy array as described in my issue. The generated HTML and JS code works in both cases.

It seems like dash still converts this dict back to a list on windows, but on linux, it just reads the dict as it is when reading the figure as a state.

Minimal example:

# numpy dict:
# numpy 1.26.4
# plotly 6.0.0

# list:
# numpy 1.26.4
# plotly 5.24.1

x = np.array([1.1, 1.2, 1.3])
y = np.array([3.5, 33, 2.1])

fig = go.Figure()
fig.add_trace(
go.Scattergl(
    x=x,
    y=y
  )
)
fig.write_html("test.html")

I’m not sure how all this relates.

You mention dash, the code provided exports a plotly figure to a html-file.

Maybe you could provide more code and/ or explanations of the issue.

No problem, please run this dash example below and press “Print Data”.

Output with plotly 5.24.1 installed:
X values: [1, 2, 3, 4, 5]
Y values: [2, 1.5, 3.5, 2, 5]

Output with plotly 6.0.0 installed:
X values: {‘dtype’: ‘f8’, ‘bdata’: ‘AAAAAAAA8D8AAAAAAAAAQAAAAAAAAAhAAAAAAAAAEEAAAAAAAAAUQA==’, ‘_inputArray’: {‘0’: 1, ‘1’: 2, ‘2’: 3, ‘3’: 4, ‘4’: 5, ‘bdata’: ‘AAAAAAAA8D8AAAAAAAAAQAAAAAAAAAhAAAAAAAAAEEAAAAAAAAAUQA==’, ‘dtype’: ‘f8’, ‘shape’: ‘5’}}
Y values: {‘dtype’: ‘f8’, ‘bdata’: ‘AAAAAAAAAEAAAAAAAAD4PwAAAAAAAAxAAAAAAAAAAEAAAAAAAAAUQA==’, ‘_inputArray’: {‘0’: 2, ‘1’: 1.5, ‘2’: 3.5, ‘3’: 2, ‘4’: 5, ‘bdata’: ‘AAAAAAAAAEAAAAAAAAD4PwAAAAAAAAxAAAAAAAAAAEAAAAAAAAAUQA==’, ‘dtype’: ‘f8’, ‘shape’: ‘5’}}

Dash example:

import dash
from dash import dcc, html, Input, Output, State
import plotly.graph_objs as go
import numpy as np

# Initialize the Dash app
app = dash.Dash(__name__)

# Create some sample data using numpy
x_data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
y_data = np.array([2.0, 1.5, 3.5, 2.0, 5.0])

# Define the layout of the app
app.layout = html.Div([
    dcc.Graph(id='scatter-plot'),
    html.Button('Generate Plot', id='generate-button', n_clicks=0),
    html.Button('Print Data', id='print-button', n_clicks=0)
])

# Callback to generate the ScatterGL plot
@app.callback(
    Output('scatter-plot', 'figure'),
    Input('generate-button', 'n_clicks')
)
def update_figure(n_clicks):
    # Create a ScatterGL trace
    trace = go.Scattergl(
        x=x_data,
        y=y_data,
        mode='markers',
        marker=dict(size=10, color='blue')
    )
    
    # Create the figure
    fig = go.Figure(data=[trace])
    fig.update_layout(title='ScatterGL Plot', xaxis_title='X Axis', yaxis_title='Y Axis')
    
    return fig

# Callback to print the x and y data from the figure
@app.callback(
    Output('print-button', 'children'),
    Input('print-button', 'n_clicks'),
    State('scatter-plot', 'figure')
)
def print_data(n_clicks, figure):
    if n_clicks > 0 and figure is not None:
        # Extract the x and y data from the figure
        x_values = figure['data'][0]['x']
        y_values = figure['data'][0]['y']
        
        # Print the x and y values
        print("X values:", x_values)
        print("Y values:", y_values)
    
    return "Print Data"

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