Related
I want to add a histogram to my dash app where the user can select what variable they want to use from the given dataset. There are 11 variables and I want to have the user select a variable from the drop down menu and in response, a histogram will be created for said variable. I am having ALOT of trouble understanding how the input and out works and where I should place the graph. (I would like to have it placed under the description.) Here is what I have so far:
import dash
import pandas as pd
from dash import Input, Output
from dash import dash_table
from dash import dcc
from dash import html
app = dash.Dash(__name__)
df = pd.read_csv('C:\\Users\\Programming\\diamonds.csv')
app.layout = html.Div([
html.H1("Diamonds", style={'text-align': 'center'}),
html.Div(["Choose the number of observations x*: ",
dcc.Input(id='top_of_table', value=10, type='number')]),
dash_table.DataTable(id="any_id"),
html.H1("Description", style={'text-align': 'center'}),
html.Div("This is a dataframe containing 11 variables on Diamonds.
The variables are the "
"following: price, carat, clarity, cut, color, depth, table,
x, y, z, and date."
"Range in price is $338-$18791.Carats or weight of the
diamond ranges from .2-3.0."),
])
#app.callback(
Output(component_id="any_id", component_property='data'),
Input(component_id='top_of_table', component_property='value'),
)
def diamond_function(num_observ):
df1 = df.head(10)
return df1.to_dict('records')
if __name__ == '__main__':
app.run_server(debug=True)
Any help? I really appreciate it! Thanks!
I don't have your dataframe so that I just make below code as referrence:
import dash
import pandas as pd
import plotly.express as px
from dash import Input, Output
from dash import dash_table
from dash import dcc
from dash import html
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("Diamonds", style={'text-align': 'center'}),
html.Div(["Choose the number of observations x*: ",
dcc.Input(id='top_of_table', value=10, type='number')]),
dash_table.DataTable(id="any_id"),
html.H1("Description", style={'text-align': 'center'}),
html.Div("This is a dataframe containing 11 variables on Diamonds. The variables are the "
"following: price, carat, clarity, cut, color, depth, table, x, y, z, and date."
"Range in price is $338-$18791.Carats or weight of the diamond ranges from .2-3.0."),
html.Div([
dcc.Dropdown(id='variables',
options=[{'label':x,'name':x} for x in df.sort_values('Variables')['Variables'].unique()],
value=[],
multi=False,
disabled=False,
clearable=True,
searchable=True
)
]),
html.Div([
dcc.Graph(id='figure_1',figure={})
])
])
#app.callback(Output('figure_1','figure'),
[Input('variables','value')])
def update_graph(variables):
if variables != []:
dff = df[df['Variables'] == variables]
fig = px.histogram(...)
else:
dff= df.copy()
fig = px.histogram(...)
return fig
if __name__ == '__main__':
app.run_server(debug=False)
So you will need to add dcc.Dropdown as variables and dcc.Graph to show histogram for each variable.
I'm trying to create a stacked bar chart and then change color of it by dropdown value. Below is my sample code:
import pandas as pd
import numpy as np
import plotly.express as px
import dash
from dash import html
from dash import dcc
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
app = dash.Dash(__name__,external_stylesheets=[dbc.themes.LUX]) # You can change external_stylesheets
# Make the layout with two tabs
colorscales = dir(px.colors.qualitative)
df = px.data.medals_long()
app.layout = html.Div([
dbc.Row([html.H6('Color Palette',className='text-left'),
dcc.Dropdown(id='color_range',placeholder="Color", # Dropdown for heatmap color
options=colorscales,
value='aliceblue',
multi=False,
disabled=False,
clearable=True,
searchable=True),
dcc.Graph(id='bar_chart',figure={},style={'height':500,'width':'auto'})
])
])
#app.callback(Output('bar_chart','figure'),
[Input('color_range', 'value')])
def update_color(color_range):
fig = px.bar(df, x="medal", y="count", color="nation", text="nation",
color_discrete_sequence = color_range)
return fig
if __name__ == "__main__":
app.run_server(debug=False)
But it raised an error that said:
ValueError:
Invalid value of type 'builtins.str' received for the 'color' property of bar.marker
Received value: 'a'
The 'color' property is a color and may be specified as:
- A hex string (e.g. '#ff0000')
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
- A named CSS color:
aliceblue, antiquewhite, aqua, aquamarine, azure,
I tried to change from colorscales = dir(px.colors.qualitative) to colorscales = px.colors.named_colorscales() and then change color_discrete_sequence to color_continuous_scale but it didn't work. It didn't raise error but color not change.
Thank you.
The modification is that the drop-down default should choose a qualitative color name. And the bar chart color specification needs to be written in the form px.colors.qualitative.G10.
app.layout = html.Div([
dbc.Row([html.H6('Color Palette',className='text-left'),
dcc.Dropdown(id='color_range',placeholder="Color", # Dropdown for heatmap color
options=colorscales,
value='Plotly',# update
multi=False,
disabled=False,
clearable=True,
searchable=True),
dcc.Graph(id='bar_chart',figure={},style={'height':500,'width':'auto'})
])
])
def update_color(color_range):
df = px.data.medals_long() # update
fig = px.bar(df, x="medal", y="count", color="nation", text="nation",
color_discrete_sequence = eval('px.colors.qualitative.{}'.format(color_range))) # update
return fig
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 values
colorscale='Viridis', # choose a colorscale
opacity=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.")
break
if 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 values
colorscale='Viridis', # choose a colorscale
opacity=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 go
fig = 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 html
app = 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 np
py.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 format
np.random.seed(4)
cols = list('xyz')
X = np.random.randint(50, size=(3, len(cols)))
df = pd.DataFrame(X, columns=cols)
df.iloc[0] = 0
return df
df = 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 data
new_x, new_y, new_z = np.random.randint(10, size=(3, 1))
# ploy
fig = create_figure(df) # Set as global variable or local variable as required
fig.add_trace(go.Scatter3d(x=new_x, y=new_y, z=new_z, marker=dict(size=10, color='green'), mode='markers'))
return fig
app.run_server(debug=False, use_reloader=False)
Estimated that your "tab" is referring to "browser tab" it is basically not possible with the standard renderer.
With the renderer browser it serves a one-shot server on a random port, which is shutting down immediately after the rendering is done. You can check that by reloading the graph in browser.
You can:
generate a static image and serve that yourself in a webapp (e.g. with flask) with f2.write_image("test.svg")
generate a dynamic html content by f2.show(renderer = "iframe") and serve that with e.g. flask
simply use plotly dash, look here for impressions
Try using Plotly for plotting, it has a functionality (Visibility), using that you can update your plot on button click or drop down.
The below example is for dropdown.
import pandas as pd
import numpy as np
import plotly.offline as py_offline
import plotly.graph_objs as go
from plotly import tools
py_offline.init_notebook_mode()
trace = go.Scatter(
x=[1, 2, 3],
y=[4, 5, 6]
)
fig = tools.make_subplots(rows=10, cols=1)
for k in range(10):
fig.append_trace(trace, k+1, 1)
updatemenus=list([
dict(
buttons=[],
direction = 'down',
pad = {'r': 10, 't': 10},
showactive = True,
x = 0,
xanchor = 'left',
y = 1.2,
yanchor = 'top'
),
])
lister = []
for k in range(11):
lister.append(dict(
args=['visible', [True for k in range(10)] if k == 0 else [True if (i+1) == k else False for i in range(10)]],
label='Show Trace ' + str( 'All' if k == 0 else k),
method='restyle'
))
updatemenus[0]['buttons'] = lister
fig['layout']['updatemenus'] = updatemenus
fig['layout'].update(title='subplots')
py_offline.iplot(fig, filename='simple-subplot')
I build a Plotly Dash web app to display sensor data. I want to have a map where I can select the stations and therefore I can see the time series chart.
This is my callback right now:
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'selectedData'))
def display_selected_data(selectedData):
if selectedData is None: # Plot whole Dataframe if nothing is selected.
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
return fig
else:
selectedData['points'][0]['customdata'][0] # This line shows me the name of the location and I want to add this to a list
return
I can show the location in the selected data. Now my question is, how can I add this to a list?
My goal ist to filter the dataframe like this dff2 = df[df.location.isin(selected_locations)] so that I only plot the selected locations.
My full app right now:
from jupyter_dash import JupyterDash
import plotly.graph_objs as go
from dash import Dash, dcc, html, Input, Output, State
import pandas as pd
import plotly.express as px
import json
loc = pd.read_csv('location_sensors.csv')
df = pd.read_csv('measurement.csv')
style = {'width': '50%', 'height': '500px', 'float': 'left'}
# Build small example app.
app = dash.Dash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
fig_map = px.scatter_mapbox(loc, lat="lat", lon="lon", hover_name="location",
hover_data={'location':True, 'lat':False, 'lon':False}, zoom=3, height=600,
color='location', mapbox_style="open-street-map")
fig_map.update_layout(clickmode='event+select')
app.layout = html.Div([
dcc.Graph(id='map_sensors', figure=fig_map , className='six columns'),
html.Div([dcc.Graph(
id='time_series1',
style={'height': 400}
),
])
])
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'selectedData'))
def display_selected_data(selectedData):
if selectedData is None:
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
return fig
else:
# Here I want to filter the dataframe to the selected locations.
return
if __name__ == '__main__':
app.run_server()
Locations csv data:
lat,lon,location
51.20966,4.43182,BETR801
48.83722,2.3939,FR04014
51.49467,-0.13193,London Westminster
Time series data:
https://github.com/pandas-dev/pandas/blob/master/doc/data/air_quality_long.csv
For your #app.callback decorator, I think you want your input to be clickData instead of selectionData. If you look at the first example in the documentation here, once you click a location on the map and it is greyed out, when you click it again at a later time, clickData will input a dictionary with marker information, while selectionData will input null (this means that dash will have trouble knowing when you click on a point again after it's been greyed out if you use selectionData instead of clickData)
You can then have a dynamic list that changes depending on locations the user selects and deselects. Also a very minor point, but I changed your DataFrame variable name from loc to locs since .loc is a pandas DataFrame method.
from jupyter_dash import JupyterDash
import plotly.graph_objs as go
import dash
from dash import Dash, dcc, html, Input, Output, State
import pandas as pd
import plotly.express as px
import json
locs = pd.read_csv('location_sensors.csv')
df = pd.read_csv('https://raw.githubusercontent.com/pandas-dev/pandas/master/doc/data/air_quality_long.csv')
style = {'width': '50%', 'height': '500px', 'float': 'left'}
# Build small example app.
app = dash.Dash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
fig_map = px.scatter_mapbox(locs, lat="lat", lon="lon", hover_name="location",
hover_data={'location':True, 'lat':False, 'lon':False}, zoom=3, height=600,
color='location', mapbox_style="open-street-map")
fig_map.update_layout(clickmode='event+select')
app.layout = html.Div([
dcc.Graph(id='map_sensors', figure=fig_map , className='six columns'),
html.Div([dcc.Graph(
id='time_series1',
style={'height': 400}
),
])
])
## define a list that will hold the columns of the dataframe
## this will be used to modify the px.line chart
selected_locations = list(locs['location'])
#app.callback(
Output('time_series1', 'figure'),
Input('map_sensors', 'clickData'))
def display_selected_data(clickData):
## when the app initializes
if clickData is None:
fig = px.line(data_frame=df, x='date.utc', y='value', color='location')
## when the user clicks on one of the loc points
else:
selection = clickData['points'][0]['customdata'][0]
if selection in selected_locations:
selected_locations.remove(selection)
else:
selected_locations.append(selection)
fig = px.line(data_frame=df[df.location.isin(selected_locations)], x='date.utc', y='value', color='location')
return fig
if __name__ == '__main__':
app.run_server(debug=True)
I am trying to add interactivity to a plotly 3d scatter plot which I am hosting using dash. My question contains two related parts:
(i) I would like to manually select points in my 3d scatter and change the selected points' colour to red. Selection should include click event and selection event.
(ii) I would like to be able to delete these points from the plot on pressing of a key e.g. 'delete' key.
Part (i) is similar to an example in the plotly guide https://plot.ly/python/click-events/, however, on_click is not available as a method for 3d scatter charts.
I have been attempting to use FigureWidget as that apparently provides methods to capture clicks and selections, but after 2 days I am struggling to make headway.
Example data (copy below to clipboard and run df = pd.read_clipboard(sep=','))
id,x_val,z_val,y_val
a,11.313449728149418,0.13039110880256777,0.5386387766748618
b,11.321463427315383,0.2360697833061771,1.32441455152796
c,10.127132005050942,0.23085014016641864,1.0961116175427044
d,11.639819269465233,0.0958798324712593,0.6506370305953094
e,8.892696370438149,0.08223988244819926,0.6440321391968353
f,6.711586646011124,0.3657515974938044,
g,7.095030650760687,,0.5723062047617504
h,6.4523124528415,,1.293852184258803
i,7.165105300812886,0.4151365420301895,-0.5920674079031845
j,7.480703395137295,0.14284429977557123,1.0600936940126982
k,5.570775744372319,,
l,4.358946555449826,,
I have example code below which I hope is almost there (but not quite). The idea is that 'handle_click' should capture click events. I should also handle 'selection' events, although I am not sure how to do this as 3d scatter plot does not provide a selection box or lassoo tool. With the callback, I'm not even sure how to initiate, as there is no clickData/selectionData event with 3d scatterplot that I can utilize (so [Input('subindustry-dropdown', 'value')]) is incorrect and note that subindustry-dropdown is not something that I have provided in the example, but I select my ids from a dropdown which returns a subindustry value in my development version. )
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
app.layout = html.Div([html.Div(id = '3d-scatter'),
])
# Calculate and store data
#app.callback(Output('3d-scatter', 'children'),
[Input('subindustry-dropdown', 'value')])
def chart_3d():
f = go.FigureWidget(px.scatter_3d(df, x = 'x_val', y = 'y_val', z = 'z_val', hover_name = 'company_nm'))
f.layout.clickmode = 'event+select'
f.data[0].on_click(handle_click) # if click, then update point/df.
return dcc.Graph(id = '3d_scat', figure=f)
def handle_click(trace, points, selector):
c = list(f.data[0].marker.color)
s = list(f.data[0].marker.size)
for i in points.point_inds:
c[i] = '#bae2be'
s[i] = 20
with f.batch_update():
f.data[0].marker.color = c
f.data[0].marker.size = s
return f.data[0]
Here is a solution which allows:
selecting points by clicking individually
delete selected points by pressing html button
clear selection by pressing html button
according to this issue selecting multiple points in a 3d plot is currently not supported
(The use of FigureWidget does not seem to make a difference, so I removed it)
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import pandas as pd
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
import pandas as pd
df = pd.DataFrame(columns=['x_val','z_val','y_val'])
df.loc['a'] = [11.313449728149418,0.13039110880256777,0.5386387766748618]
df.loc['b'] = [11.321463427315383,0.2360697833061771,1.32441455152796]
df.loc['c'] = [10.127132005050942,0.23085014016641864,1.0961116175427044]
df.loc['d'] = [11.639819269465233,0.0958798324712593,0.6506370305953094]
df.loc['e'] = [8.892696370438149,0.08223988244819926,0.6440321391968353]
df.loc['f'] = [6.711586646011124,0.3657515974938044,0]
df.loc['g'] = [7.095030650760687,0,0.5723062047617504]
df.loc['h'] = [6.4523124528415,0,1.293852184258803]
df.loc['i'] = [7.165105300812886,0.4151365420301895,-0.5920674079031845]
df.loc['j'] = [7.480703395137295,0.14284429977557123,1.0600936940126982]
df.loc['k'] = [5.570775744372319,0,0]
df.loc['l'] = [4.358946555449826,0,0]
def create_figure(skip_points=[]):
dfs = df.drop(skip_points)
return px.scatter_3d(dfs, x = 'x_val', y = 'y_val', z = 'z_val')
f= create_figure()
app.layout = html.Div([html.Button('Delete', id='delete'),
html.Button('Clear Selection', id='clear'),
dcc.Graph(id = '3d_scat', figure=f),
html.Div('selected:'),
html.Div(id='selected_points'), #, style={'display': 'none'})),
html.Div('deleted:'),
html.Div(id='deleted_points') #, style={'display': 'none'}))
])
#app.callback(Output('deleted_points', 'children'),
[Input('delete', 'n_clicks')],
[State('selected_points', 'children'),
State('deleted_points', 'children')])
def delete_points(n_clicks, selected_points, delete_points):
print('n_clicks:',n_clicks)
if selected_points:
selected_points = json.loads(selected_points)
else:
selected_points = []
if delete_points:
deleted_points = json.loads(delete_points)
else:
deleted_points = []
ns = [p['pointNumber'] for p in selected_points]
new_indices = [df.index[n] for n in ns if df.index[n] not in deleted_points]
print('new',new_indices)
deleted_points.extend(new_indices)
return json.dumps(deleted_points)
#app.callback(Output('selected_points', 'children'),
[Input('3d_scat', 'clickData'),
Input('deleted_points', 'children'),
Input('clear', 'n_clicks')],
[State('selected_points', 'children')])
def select_point(clickData, deleted_points, clear_clicked, selected_points):
ctx = dash.callback_context
ids = [c['prop_id'] for c in ctx.triggered]
if selected_points:
results = json.loads(selected_points)
else:
results = []
if '3d_scat.clickData' in ids:
if clickData:
for p in clickData['points']:
if p not in results:
results.append(p)
if 'deleted_points.children' in ids or 'clear.n_clicks' in ids:
results = []
results = json.dumps(results)
return results
#app.callback(Output('3d_scat', 'figure'),
[Input('selected_points', 'children'),
Input('deleted_points', 'children')],
[State('deleted_points', 'children')])
def chart_3d( selected_points, deleted_points_input, deleted_points_state):
global f
deleted_points = json.loads(deleted_points_state) if deleted_points_state else []
f = create_figure(deleted_points)
selected_points = json.loads(selected_points) if selected_points else []
if selected_points:
f.add_trace(
go.Scatter3d(
mode='markers',
x=[p['x'] for p in selected_points],
y=[p['y'] for p in selected_points],
z=[p['z'] for p in selected_points],
marker=dict(
color='red',
size=5,
line=dict(
color='red',
width=2
)
),
showlegend=False
)
)
return f
if __name__ == '__main__':
app.run_server(debug=True)