I am creating a 3D scatter plot based off a pandas dataframe, and then I want to re-draw it with slightly updated data whenever the user presses a button in my program. I almost have this functionality working, except the updated figure is drawn via a new opened tab, when really I just want my origin existing figure to be updated.
Here is my code. First I initialize the plot with 'version 1' of the data, then I set up a simple while loop to wait for the user to request an update. Then ideally once they enter input to ask for the update, I just re-draw everything in the same tab that is open. But instead a new tab is opened (which redraws the data correctly at least).
fig = go.Figure(data=[go.Scatter3d(x=df['x'],y=df['y'],z=df['z'],mode='markers', marker=dict(size=4,color=df['y'], # set color to an array/list of desired valuescolorscale='Viridis', # choose a colorscaleopacity=0.3))])# Column max and mins for plotting:xmax = df_1.max(axis=0)['x']; xmin = df_1.min(axis=0)['x']ymax = df_1.max(axis=0)['y']; ymin = df_1.min(axis=0)['y']zmax = df_1.max(axis=0)['z']; zmin = df_1.min(axis=0)['z']fig.update_layout(scene = dict(xaxis = dict(nticks=4, range=[xmin,xmax],),yaxis = dict(nticks=4, range=[ymin,ymax],),zaxis = dict(nticks=4, range=[zmin,zmax],),))f2 = go.FigureWidget(fig)f2.show()#fig.show()while True:choice = input("> ")choice = choice.lower() #Convert input to "lowercase"if choice == 'exit':print("Good bye.")breakif choice == 'w':print("W, moving forward")cube_origin = cube_origin + np.array([0.1,0,0])df_cube = createCubeMesh(cube_size, cube_density, cube_origin)new_df = df_scene_orig.copy()new_df = new_df.append(df_cube)fig = go.Figure(data=[go.Scatter3d(x=new_df['x'],y=new_df['y'],z=new_df['z'],mode='markers', marker=dict(size=4,color=new_df['y'], # set color to an array/list of desired valuescolorscale='Viridis', # choose a colorscaleopacity=0.3))])f2 = go.FigureWidget(fig)f2.show()
I based my code on another answer that said to use go.FigureWidget(fig)
, but it doesn't seem to work as intended.
Edit
Instead of me using f2.show()
at the end, I just want a simple thing analogous to f2.update()
that redraws.
This is the case you want.
Everywhere in this page that you see fig.show(), you can display the same figure in a Dash application by passing it to the figure argument of the Graph component from the built-in dash_core_components package like this:
import plotly.graph_objects as gofig = go.Figure(data=[go.Scatter(mode="markers+text",x=[10, 20],y=[20, 25],text=["Point A", "Point B"])],layout=dict(height=400, width=400, template="none")
)import dash
import dash_core_components as dcc
import dash_html_components as htmlapp = dash.Dash()
app.layout = html.Div([dcc.Graph(figure=fig)
])app.run_server(debug=True, use_reloader=False)
reference: https://plotly.com/python/figure-introspection/
Help you write a code that is closest to your needs:
import plotly as py
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import pandas as pd
import numpy as nppy.offline.init_notebook_mode(connected=True)app = JupyterDash('SimpleExample')
app.layout = html.Div([dcc.Dropdown(id='dropdown', options=[{'label': 'W', 'value': 'W'},{'label': 'exit', 'value': 'exit'}],value='exit'),dcc.Graph(id='graph-court')])def random_data():# sample dataframe of a wide formatnp.random.seed(4)cols = list('xyz')X = np.random.randint(50, size=(3, len(cols)))df = pd.DataFrame(X, columns=cols)df.iloc[0] = 0return dfdf = random_data()def create_figure(df):fig = go.Figure(data=[go.Scatter3d(x=df['x'], y=df['y'], z=df['z'], mode='markers', marker=dict(size=10,color=df['y'],colorscale='Viridis',opacity=0.3))])# Column max and mins for plotting:xmax = df.max(axis=0)['x']xmin = df.min(axis=0)['x']ymax = df.max(axis=0)['y']ymin = df.min(axis=0)['y']zmax = df.max(axis=0)['z']zmin = df.min(axis=0)['z']fig.update_layout(scene=dict(xaxis=dict(nticks=4, range=[xmin, xmax], ),yaxis=dict(nticks=4, range=[ymin, ymax], ),zaxis=dict(nticks=4, range=[zmin, zmax], ), ))fig = go.FigureWidget(fig)return fig@app.callback(Output('graph-court', 'figure'),[Input('dropdown', 'value')])
def update_figure(selected_value):selected_value = selected_value.lower() # Convert input to "lowercase"if selected_value == 'exit':print("Good bye.")new_x, new_y, new_z = [], [], []else:print("W, moving forward")# new datanew_x, new_y, new_z = np.random.randint(10, size=(3, 1))# ployfig = create_figure(df) # Set as global variable or local variable as requiredfig.add_trace(go.Scatter3d(x=new_x, y=new_y, z=new_z, marker=dict(size=10, color='green'), mode='markers'))return figapp.run_server(debug=False, use_reloader=False)