I am trying to make my dash datatable interactive with my Mapbox. So when I click on “A” highlighted as the image shown below, it should show me the latitude/longitude of the point on mapbox. Correct me if I am wrong but I need to use the callback function - clickData. But I tried a few times using clickData but it did not work. Was wondering if there is any code I can refer to or any website out there that talks about dash datatable interactive with clickData . Thank you!
This is my table:
This is my coding for mapbox:
fig = px.scatter_mapbox(df4_1.to_dict('records'), lat="Latitude", lon="Longitude", hover_name="Name", hover_data=["Id"],color_discrete_sequence=["purple"], zoom=9, height=450)
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
clickData is not a property of a dash datatable, what you have, is an active_cell property. There are examples on how to use the active cell to update plots. The caveat is that you don't get the value inside of the selected cell, but the row, and column id. This active_cell is not reliable if you have sorting enabled, or editing.
They give an example on how to get the value if you have a static datasource(df), but If you don't have a static datasource then you need to use a sort of data sharing solution between callbacks like a dcc.Store. https://dash.plotly.com/datatable/interactivity
Anyway at this point the problem just increased in complexity so I would suggest a different simpler model:
In that case I would say to separate the mapbox and the datatable entirely and just have a text input that generates the plot and have a simple callback:
#put this before your mapbox
dcc.Input(
id="text_input_id",
type="text,
placeholder="input name of mapbox")
#app.callback(
Output("out-all-types", "children"),
Input("text_input_id", "value"),
)
def render_mapbox(val):
data=get_data(val) #do your stuff, get the data, make the plot
fig= ....
return fig
Related
(My first question and post here)
Here's my situation:
I have to program a data displaying software on Python dash/plotly. And I am stuck at the part where I have to transfer data from one page of my app.py to the other one (for now offline). Both two Dash layout pages are defined in app.py. The structure is basically the same like the one the third example code in that dash/plotly docu page (https://dash.plotly.com/urls), so I have url_bar_and_content_div defined and assigned to app.layout, and I have layout_page1 and layout_page2, as well as app.validation_layout in which all these layouts are listed in the *html.Div()... *
(I can't show concrete code because it's for my internship)
I am able to redirect from page 1 to page 2, right now I'm coding the Divs and callbacks of page 2. On page 2, I want to submit and store data of any file I work with, preferably in a pd.DataFrame or in a dict. And I'm picturing it more or less, that I use an html.Button() to "Submit", which after being clicked finally saves the data, "transfers" it to page 1 and redirects the user to page 1 too. And so here's where I am stuck now: How do I transfer that saved/submitted data to layout_page_1? How do I even make page 2 store and submit data in a df or smth using callbacks to begin with?? I know that using global variables in callbacks is not possible, and I have also ofc looked at the dash docu page for this matter (https://dash.plotly.com/sharing-data-between-callbacks). It hasn't really helped me though, like I didn't know where I should approach my issue with this information, and I also didn't really understand many things. I have never used the dcc.Store()-component before, I am not a pro/expert at Python programming, and this is my first internship in which I am working with Dash for the first time...
So how do I approach this problem? Do I have to add dcc.Store() to both layout-Divs? Do they have to have the same id, must they have to be named "memory","local" or "session", or is it irrelevant? How would I "connect" those two Store-components then, how can I make these two layouts "communicate" with each other and enable smooth data transition from page 2 to page 1 and vice versa??
I'd be so much thankful if someone here would help me out on this, I hope I'll get a response soon, because I have to finish that task in a few weeks. Thanks in advance and cheers!
Edit: #PeterWood Alright, so again, I'll explain what I have to do and I'll use some code to illustrate my problem better!
I have my main app program which I always execute, and it contains the following layouts:
app = dash.Dash(__name__)
url_bar_and_content_div = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
]) # just like in https://dash.plotly.com/urls
layout_page1 = html.Div([ ...
,html.Div(id="data-display",
children=[],...),
"""this is basically the place where the data from page 2 should be loaded (no plot, just saved text and value information)
"""
])
layout_page2 = html.Div([
...
dcc.Textarea(id,...),
dash_table.DataTable( id=...,... ), # contains values and data which I want to work with/save
html.Button(id,...children='Submit', ....) # the Submit Button
...
])
# index layout
app.layout = url_bar_and_content_div
# complete layout
app.validation_layout = html.Div([
url_bar_and_content_div,
layout_page1,
layout_page2
])
# **Again** , the layout-structure is the **same** as that one in the 3rd code example in https://dash.plotly.com/urls
# callbacks etc.
So page 2 consists of a DataTable which mainly consists of text descriptions and values (of many types, str, float, int, NaN etc.). Whenever I click on a line/row on the DataTable, its text gets displayed on the Textarea-field above. And when I click on the "Submit"-button, all the displayed text and values should then be finally "saved" and somehow redirected to page 1 and displayed on the 'data-display'-component (see code again).
My question: How do I do that? How do I program a temporary df which saves text from layout_page2 and displays it on layout_page1?
Here the callback which does the data display on dcc.Textarea when a not-empty DataTable-cell gets clicked:
#app.callback([Output('textarea-p2','value'), # the Text-block where the text from DataTable gets shown
Output('datatable-p2','selected_cells'),
Output('datatable-p2','active_cell')]
[Input('datatable-p2','active_cell')],
[State('datatable-p2','data'),
State('textarea-p2','value')])
def update_data_display(active_cell, data_cells, displayed_text):
if dash.callback_context.triggered[0]['prop_id'].split('.')[0] == 'datatable-p2':
...
if not pd.isnull(data_cells[row][col]):
content = text on that 'row' of DataTable + '\n' # work with active_cells and a df = pd.DataFrame(data_cells)
...
displayed_text += content
return displayed_text, [], None
#"[], None" so that every time I click on a cell it gets unmarked again, just for clarification
What should I change on that callback update_data_display for the next step of sending the Text-block-data to 'data-display' on page 1?
And also how should my callback on the "Submit"-button look like then?
Should both of my layout_pagex have a dcc.Store()-component? So how do I proceed with it now?
Currently, plotly express treemap shows only label inside treemap. How to include the value alongside the label?
That's why I don't like express, there are too many limitations and to make these kinds of changes you have to access the trace either way. From my point of view it is better and more code-transparent to use plain plotly instead.
That being said, you can access the textinfo attribute of the trace to do this. From the reference:
Determines which trace information appear on the graph.
Any combination of "label", "text", "value", "current path", "percent root", "percent entry", "percent parent" joined with a "+" OR "none".
Taking an example from the site:
df = px.data.tips()
fig = px.treemap(df, path=['day', 'time', 'sex'], values='total_bill')
# this is what I don't like, accessing traces like this
fig.data[0].textinfo = 'label+text+value+current path'
fig.layout.hovermode = False
fig.show()
Also take a look at the texttemplate attribute for formatting options.
I'm working on a project that involves exporting charts from SPSS. The value labels are supposed to be visible on the pie charts. Here's what I get, here's what I want.
The code is all working (see below how I do it). I just haven't found a way to make those "value labels" show up on the pie charts without going through the Chart Editor (double click on the chart in SPSS Viewer).
What I want is to have those labels on my pie charts like when I would click "Show Data Labels" as shown here. Is there any way to achieve this?
I'm accessing the charts in my code like this:
#in a SYNTAX file
* Encoding: UTF-8.
OMS
/DESTINATION
FORMAT=OXML
XMLWORKSPACE="my_ws"
VIEWER=YES
IMAGES=YES
IMAGEFORMAT=PNG
CHARTFORMAT=IMAGE.
BEGIN PROGRAM python.
import spss
spss.Submit("""FREQUENCIES VARIABLES=Sex
/PIECHART PERCENT
/ORDER=ANALYSIS.""")
imgs = spss.EvaluateXPath('my_ws', '/outputTree',
'//command[#command="Frequencies"]/chartTitle/chart/#imageFile' )
image=spss.GetImage( 'my_ws', imgs[-1] )
END PROGRAM.
OMSEND.
I don't think that's possible using SPSS syntax directly. The command syntax reference says about FREQUENCIES, subcommand PIECHART:
"PERCENT. [...] Percentage is displayed when you request values in the Chart Editor."
I've done that and it didn't add new code to the output document which makes me think that switching on percentages in the plot of this subcommand is not available through syntax.
You can however make a custom chart template as shown here.
Hence your code should be somthing like:
OMS
/DESTINATION
FORMAT=OXML
XMLWORKSPACE="my_ws"
VIEWER=YES
IMAGES=YES
IMAGEFORMAT=PNG
CHARTFORMAT=IMAGE.
BEGIN PROGRAM python.
import spss
spss.Submit("""set ctemplate
'<TEMPLATELOCAION>/<TEMPLATENAME>.sgt'.
FREQUENCIES VARIABLES=<VARIABLENAME>
/PIECHART PERCENT
/ORDER=ANALYSIS.
set ctemplate none.""")
imgs = spss.EvaluateXPath('my_ws', '/outputTree',
'//command[#command="Frequencies"]/chartTitle/chart/#imageFile' )
image=spss.GetImage( 'my_ws', imgs[-1] )
END PROGRAM.
OMSEND.
I am working with the DASH library, and my app now has multiple tabs, but I need to make each tab have a different URL, has anyone accomplish that?
If you want to change the URL shown in the address bar when a new tab is selected, you can add a dcc.Location component and use dcc.Link components to select your tabs. Your dcc.Location component is the input for your tab change.
The example in the official docs does this:
https://dash.plot.ly/urls
As suggested by #papalagi here, the only method that worked for me is to use two dcc.Location components (one that works as "input" url and one as "output" url) and write two callbacks:
One that is fired when the url (dcc.Location) is changed and updates the selected tab (dcc.Tabs)
Another one that is fired when the selected tab changes (dcc.Tabs) and updates the displayed url (dcc.Location)
Since this scenario implies a circular dependency, it explains the need two dcc.Location objects.
Looking at code, I implemented a solution where each tab has its own hash within the same page but this approach could be changed to update whole paths instead of hashes.
Firstly, in the layout of the app I include two dcc.Location components.
app.layout = dhc.Div([
dcc.Location(id='url', refresh=False),
dcc.Location(id='url-output', refresh=False),
dhc.Div(id='page-content')
])
Then, I write the callbacks:
#app.callback(
inputs=[Input('url', 'hash')],
output=Output('main-tabs', 'value'))
def update_tab(hashh):
print('>>> UPDATE TAB', hashh)
return hash_tabs_value_dict.get(hashh.lstrip('#'), 'tab-1')
#app.callback(
inputs=[Input('main-tabs', 'value')],
output=Output('url-output', 'hash'))
def update_tab_hash(tab_value):
print('>>> UPDATE HASH', tab_value)
return '#' + tabs_value_hash_dict.get(tab_value, '')
P.S. In hash_tabs_value_dict and tabs_value_hash_dict I have a couple of dictionaries that store the mapping between the tabs values ('tab-1', 'tab-2', ...) and the desired values that I want to show.
I have a figure with some glyphs, but only want tooltips to display for certain glyphs. Is there currently a way to accomplish this in Bokeh?
Alternatively, is there a way to plot two figures on top of each other? It seems like that would let me accomplish what I want to do.
Thanks to this page in Google Groups I figured out how this can be done.
Link here
Edit 2015-10-20: looks like the google group link doesn't work anymore unfortunately. It was a message from Sarah Bird #bokehplot.
Edit 2017-01-18: Currently this would add multiple hover tool icons to the tool bar. This may cause problems. There is already an issue filed at github here. Alternatively, try #tterry's solution in the answer below.
Essentially you need to (bokeh version 0.9.2):
not add hover in your tools when you create the figure
create glyphs individually
add glyphs to your figure
set up the hover tool for this set of glyphs
add the hover tool to your figure
Example:
import bokeh.models as bkm
import bokeh.plotting as bkp
source = bkm.ColumnDataSource(data=your_frame)
p = bkp.figure(tools='add the tools you want here, but no hover!')
g1 = bkm.Cross(x='col1', y='col2')
g1_r = p.add_glyph(source_or_glyph=source, glyph=g1)
g1_hover = bkm.HoverTool(renderers=[g1_r],
tooltips=[('x', '#col1'), ('y', '#col2')])
p.add_tools(g1_hover)
# now repeat the above for the next sets of glyphs you want to add.
# for those you don't want tooltips to show when hovering over, just don't
# add hover tool for them!
Also if you need to add legend to each of the glyphs you are adding, try using bokeh.plotting_helpers._update_legend() method. github source Eg:
_update_legend(plot=p, legend_name='data1', glyph_renderer=g1_r)
You need to name your glyph with the name= attribute on the glyph that you are interested in having the hover tool active for and then set that name in the hover tool's names= attribute. (Note the name= attribute of the fig.line glyph in the example below.
hover = HoverTool( mode='vline', line_policy='nearest', names=['ytd_ave'],
tooltips=[
("Week Number", "#WeekNumber"),
("OH for the Week", "#OverHead{0.00}%"),
("OH Average", "#AveOverHead{0.00}%"),
("Non-Controllable Hours", "#NonControllableHours{0.0}"),
("Controllable Hours", "#ControllableHours{0.0}"),
("Total Hours", "#TotalHours{0.0}"),
]
)
fig = Figure(title='Weekly Overhead', plot_width=950, plot_height=400,
x_minor_ticks=2, tools=['pan', 'box_zoom', 'wheel_zoom', 'save',
'reset', hover])
ch = fig.vbar('WeekNumber', top='ControllableHours', name='Over Head',
color='LightCoral', source=sources, width=.5)
nch = fig.vbar('WeekNumber', bottom='ControllableHours', top='TotalOHHours',
name='Non-Controllable Over Head', color='LightGray',
source=sources, width=.5)
bh = fig.vbar('WeekNumber', bottom='TotalOHHours', top='TotalHours',
name='Project Hours', color='LightGreen', source=sources,
width=.5)
ave = fig.line('WeekNumber', 'AveOverHead', source=sources, color='red',
y_range_name='Percent_OH', name='ytd_ave')
Will Zhang's answer will work, but you would end up with multiple hover tools. If this is undesirable, you can add renderers to an existing hover tool:
from bokeh import plotting
from bokeh.models import HoverTool, PanTool, ResetTool, WheelZoomTool
hover_tool = HoverTool(tooltips=[('col', '#x'),('row', '#y')]) # instantiate HoverTool without its renderers
tools = [hover_tool, WheelZoomTool(), PanTool(), ResetTool()] # collect the tools in a list: you can still update hover_tool
plot = plotting.figure(tools=tools)
plot.line(x_range, y_range) # we don't want to put tooltips on the line because they can behave a little strange
scatter = plot.scatter(x_range, y_range) # we assign this renderer to a name...
hover_tool.renderers.append(scatter) # ...so we can add it to hover_tool's renderers.
So the differences here:
You can create your glyph in a high level way using the plotting interface and this will still work.
You don't have to create a new HoverTool (unless you want different tooltips) each time, just add it to the existing tool's renderers.
UPDATE from maintainters: hover IS now supported on both lines and images
OBSOLETE:
Hover is not currently supported for image type glyphs and line glyphs. So, using one of these glyphs in combination with glyphs that support hover tool tip, might be a work around.
See:
http://docs.bokeh.org/en/latest/docs/user_guide/objects.html#hovertool