I can imagine this question has been asked a lot, but I can´t find a needed approach:
I built a dashboard in streamlit and everything works fine. I want to show data based on some filters:
Date = st.sidebar.multiselect(
"Please select the month:",
options=df["Date"].unique(),
default=month
)
Type = st.sidebar.multiselect(
"Please select the type:",
options=df["Type"].unique(),
default=df["Type"].unique()
)
df_selection = df.query(
"Date == #Date & Type == #Type"
)
In column "Type" there are only three types: "A", "B", "C". Some months have only types "A" and "B" and some all three.
If I select the months with only "A" and "B" streamlit throws an error: KeyError: 'C' and
File "app.py", line 106, in <module>
c = typen.loc["C"]
I avoided it with:
if 'C' in types.index:
c = types.loc["C"]
else:
c = 0
a = types.loc["A"]
b = types.loc["B"]
I tried to do it for other types ("A" and "B") but it does not really work. Maybe I need a combination of for loop and try & catch to make multiselect more flexible.
So basically I need a way to scan my DataFrame for missing data based on filters, ignore them or replace with 0 and return the existing ones.
But somehow I can´t come further and need an advice :)
I try to do a dashboard with streamlit, but I do not succeed to impact the graph with the filter. You can see what i did below :
year = st.sidebar.multiselect(
"select the year : ",
options=df2["Year"].unique(),
default=df2["Year"].unique(),
)
df_selection = df2.query("Year == #year")
fig = px.bar(df2, x="dpe", y="Percentage", color="signature",title="<b> DPE repartition in function of the year <b>")
fig.update_layout(plot_bgcolor="rgba(0,0,0,0)")
If you have any solution for this problem let me know thanks in advance.
You need to use st.plotly_chart() to display your bar graph.
try adding the following after
fig.update_layout(plot_bgcolor="rgba(0,0,0,0)")
st.plotly_chart(fig)
this should resolve the issue.
Can anyone let me know why my code is not updating the graph with data when I select drop-down value? (entire GitHub code in link in comments below)
def filterPollutants(selected_pollutants):
if selected_pollutants:
dff = df.loc[df.pollutant_abb.isin([selected_pollutants])]
else:
dff = df
bar_fig = {
"data": [
go.Bar(
x=dff["U_SAMPLE_DTTM"],
y=dff["DISPLAYVALUE"],
)
],
"layout": go.Layout(
title="Sampling for Local Limits",
# yaxis_range=[0,2],
yaxis_title_text="Metals mg/L",
),
}
From testing the code at the Github link you shared I think the problem is in this line where you filter your data set in the callback:
dff = df.loc[df.pollutant_abb.isin([selected_pollutants])]
The problem with this line is that the value of selected_pollutants inside the callback is of the form ['SELECTED_VALUE_FROM_DROPDOWN'] and not 'SELECTED_VALUE_FROM_DROPDOWN'. This is because your dropdown has multi set to True.
But because of this your isin filter doesn't work, since you're essentially doing this:
dff = df.loc[df.pollutant_abb.isin([['SELECTED_VALUE_FROM_DROPDOWN']])]
instead of this:
dff = df.loc[df.pollutant_abb.isin(['SELECTED_VALUE_FROM_DROPDOWN'])]
So the fix could be to remove the list surrounding selected_pollutants:
dff = df.loc[df.pollutant_abb.isin(selected_pollutants)]
I am creating a word search app using Dash by Plotly - I have seen some other similar questions to mine out there, but none seem to hit my direct point. I want to have a user enter a query into a Dash object, in my case a dcc.Input, and have that input create a DataFrame (or a dt.DataTable if someone can explain how to further manipulate those properly). Most the examples on Dash's website have a pre-built DataFrame, if not pre-built, no examples show an #app.callback creating a DataFrame.
So... step by step where I am
Here is my app.layout. I want to pass an input that creates a DataFrame/table. Then, pass that resulting table to some graphs (starting with one for simplicity).
app.layout = html.Div([
html.H2('Enter a text query'),
html.H6('Searching multiple words will create an AND statement where \
\n |valve leak| will return records with valve and leak. Or, \
\n you can use " " to search for specific phrases like "valve leak".'),
dcc.Input(id='searchId', value='Enter Search Term', type='text'),
html.Button('Submit', id='button', n_clicks=0),
dcc.Graph(id='tableGraph', figure='fig'),
html.Button('Update Graph', id='graph', n_clicks=0),
dt.DataTable(style_cell={
'whiteSpace': 'normal',
'height': 'auto',
'textAlign': 'left'
}, id='queryTable',
)
])
Here is the first search callback. Right now, I am attempting to use a global df to 'export' the DataFrame from the function. A problem is that Dash does not really allow DataFrame returns (or does it? not really sure how to extract my search DataFrame). This does output the table properly via data, columns
#app.callback(
[Output(component_id='queryTable', component_property='data'),
Output(component_id='queryTable', component_property='columns')],
[Input(component_id='button', component_property='n_clicks')],
[State('searchId', 'value')]
)
def update_Frame(n_clicks, value):
if n_clicks > 0:
with index.searcher() as searcher:
parser = QueryParser("content", index.schema)
myquery = parser.parse(value)
results = searcher.search(myquery, limit=None)
#print(results[0:10])
print("Documents Containing ", value, ": ", len(results), "\n")
global df
df = pd.DataFrame([i['date'], i['site'], i['ticket'], i.score, i['docId'],i['content']] for i in results)
df.columns=['Reported Date', 'Site','Ticket ID', 'Score', 'Document ID', 'Content']
columns = [{'name': col, 'id': col} for col in df.columns]
data = df.to_dict(orient='records')
return data, columns
Now, if I had the DataFrame, I would pass it to another callback to manipulate and create figures. My attempt is to assign the global df in a new callback, but that does not work.
#app.callback(
Output(component_id='tableGraph', component_property='figure'),
[Input(component_id='graph', component_property='n_clicks')]
)
def updateFig(n_clicks):
if n_clicks > 0:
frame = df
frame = frame.sort_values(by='Reported Date')
#fig = px.line(df, x='Reported Date', y='Score', title=value)
frame['Avg'] = frame['Score'].rolling(window=10).mean()
# Test
abc = frame.loc[frame['Site'] =='ABC']
# real
fig = go.Figure()
fig.add_trace(go.Scatter(x=abc['Reported Date'], y=abc['Score'],
mode='markers',
marker_color='BLUE',
name='ABC',
text="Site: " + abc['Site'].map(str) + " " + "Ticket: "+ abc['Ticket ID'].map(str)))
# There is a good bit more of figure trace stuff here, but I am shortening it.
print(fig)
return fig
It seems that Python is recognizing the correct frame, and when I print fig the console shows what looks to be the correct Dash object. However, no figure appears on the actual test website. My main question is: How can I pass a variable to a Dash object and ultimately a callback to create an initial DataFrame to pass to further Dash objects?
Thank you for reading a long question
You could use dcc.Store. The dcc.Store component works like a session based storage. For your case you would have two callbacks then.
First define the Store component in your Frontend section:
dcc.Store(id='memory')
The first callback where you output the genereated data into the dcc.Store component.
#app.callback(Output('memory', 'data'), [Input('button', 'n_clicks')])
The second callback where you fetch the data from the storage to show graphs/plots or anything else
#app.callback(Output('queryTable', 'data'), [Input('memory', 'data')])
If I understand correctly, your user input from dcc.Input is used as a search query and that generates your main dataframe lets say op_df.
Edit:
Not sure of what exactly you are generating in your df, but a psuedo code to give you some pointers:
def generate_df(user_input):
created_dict = {'col': user_input, 'value': user_input * 3}
op_df = pd.DataFrame(created_dict)
return op_df
Now to display this op_df, you can make use of plotly graph_object's `dataTables. Here is the official documentation for dataTables. As an example, in your layout part, you would have :
dcc.Graph(
id='main_table',
figure=mn_table,
style = {'width':'50%', 'height':'30%'} #
)
And you can then generate mn_table as:
mn_table = go.Figure(data=[go.Table(
header=dict(fill_color='white', line_color='black'),
cells=dict(values=[op_df['Col_name'], op_df['Values']],
fill_color='white',
align='left',
font_size=16,
line_color='black',
height=25
))
])
Later in the callback you can pass in the user input and call the function(generate_df) that calculates or generates your op_df.
Edit2:
Psuedo code for callback:
#app.callback(Output('main_table', 'figure'),
[Input('user_ip', 'value')]
def refresh_df(user_input):
new_table = generate_df(user_input)
return new_table
I am trying to use a Holoviz Panel dropdown widget value to query a dataframe. The dataframe however does not reflect the change in the dropdown value. I added a markdown widget to check if the change in the dropdown value is being captured - It seems to be. However, I can't figure out how to update the dataframe. I am a complete beginner to programming, just trying to learn. Any help is appreciated.
import pandas as pd
import panel as pn
pn.extension()
# Dataframe
df = pd.DataFrame({'CcyPair':['EUR/USD', 'AUD/USD' ,'USD/JPY'],
'Requester':['Client1', 'Client2' ,'Client3'],
'Provider':['LP1', 'LP2' ,'LP3']})
# Dropdown
a2 = pn.widgets.Select(options=list(df.Provider.unique()))
# Query dataframe based on value in Provider dropdown
def query(x=a2):
y = pn.widgets.DataFrame(df[(df.Provider==x)])
return y
# Test Markdown Panel to check if the dropdown change returns value
s = pn.pane.Markdown(object='')
# Register watcher and define callback
w = a2.param.watch(callback, ['value'], onlychanged=False)
def callback(*events):
print(events)
for event in events:
if event.name == 'value':
df1 = query(event.new)
s.object = event.new
# Display Output
pn.Column(query, s)
Output Image
Inspired by the self-answer, the following code produces a select box containing the list of providers and a dataframe filtered on that selection. It was tested on Panel version 0.13.1.
Note that the watch=True suggestion in the self-answer wasn't necessary.
import pandas as pd
import panel as pn
pn.extension()
# Dataframe
df = pd.DataFrame({
'CcyPair':['EUR/USD', 'AUD/USD' ,'USD/JPY'],
'Requester':['Client1', 'Client2' ,'Client3'],
'Provider':['LP1', 'LP2' ,'LP3']
})
# Dropdown
providers = list(df.Provider.unique())
select_widget = pn.widgets.Select(options=providers)
# Query dataframe based on value in Provider dropdown
#pn.depends(select_widget)
def query(x):
filtered_df = pn.widgets.DataFrame(df[df.Provider==x])
return filtered_df
# Display Output
pn.Column(select_widget, query)
Figured it out, turned out I just needed to add #pn.depends above my query function. Once I added pn.depends(a2, watch=True), the dataframe was filtered based on a2 input. The callback and watcher were unnecessary.