I'm currently working on my first web-application. I recently started following tutorials on python and on some modules. Most of the questions I have I'm able to figure by looking them up, exceptfor this one. I can't figured it out.
Goal is as follows:
- I'm trying to show a graph with a line-chart which dynamically updates by the user input. The user can choose multiple items from a multiple value dropdown.
If I add multiple 'go.Scatters' I can add multiple lines to the graph, except then the trace in the graph is static. So, I tried to write a for loop in go.Scatter that adds more go.Scatters for each item in data_name. Each time I try I got syntax erros. I tried to add values to the data list in dcc.Graph. Each time i failed.
The questions I've are the following:
- Where can i add the for loop to add traces to the data list?
- How should the for loop be structed?
I'm just a beginner so every advice is welcome :-)
Thanks in advance.
If i'm not clear enough on the question, please let me know.
My code is the following:
import dash
import dash_core_components as dcc
import dash_html_components as html
from pandas_datareader.data import DataReader
import time
from collections import deque
import plotly.graph_objs as go
import random
app = dash.Dash('vehicle-data')
max_length = 50
#times = deque(maxlen=max_length)
times = [1,2,3,4,5,6,7,8,9,10,11,12]
oil_temps = [11,12,13,14,15,16,17,18,19]
intake_temps = [11,12,13,14,15,16,17,18,19]
coolant_temps = [11,12,13,14,15,16,17,18,19]
#rpms = deque(maxlen=max_length)
#speeds = deque(maxlen=max_length)
#throttle_pos = deque(maxlen=max_length)
data_dict = {"NH Utrecht":oil_temps,
"NH Amsterdam": intake_temps,
"NH Schiller": coolant_temps
#"NH Sparrenhorst":rpms,
#"Amsterdam":speeds,
#"Overig":throttle_pos
}
app.layout = html.Div([
html.Div([
html.H2('Hotel',
style={'float': 'left',
}),
]),
dcc.Dropdown(id='vehicle-data-name',
options=[{'label': s, 'value': s}
for s in data_dict.keys()],
value=['NH Utrecht'],
multi=True
),
html.Div(children=html.Div(id='graphs'), className='row'),
dcc.Interval(
id='graph-update',
interval=100),
], className="container",style={'width':'98%','margin-left':10,'margin-right':10,'max-width':50000})
#app.callback(
dash.dependencies.Output('graphs','children'),
[dash.dependencies.Input('vehicle-data-name', 'value')],
events=[dash.dependencies.Event('graph-update', 'interval')]
)
def update_graph(data_names):
graphs = []
#update_obd_values(times, oil_temps, intake_temps, coolant_temps, rpms, speeds, throttle_pos)
if len(data_names)>2:
class_choice = 'col s12 m6 l4'
elif len(data_names) == 2:
class_choice = 'col s12 m6 l6'
else:
class_choice = 'col s12'
html.Div(children=
graphs.append(dcc.Graph(
figure=go.Figure(
data = [
go.Scatter(
x=times,
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156],
name='Hotel Okura',
marker=go.Marker(
color='rgb(55, 83, 109)'
),
type='scatter',
connectgaps=True
),
],
layout=go.Layout(
title='Hotel comparison data',
showlegend=True,
legend=go.Legend(
x=1.0,
y=1.0
),
margin=go.Margin(l=40, r=0, t=40, b=30)
),
),
style={'height': 300},
id='my-graph'
)
))
return graphs
external_css = ["https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css"]
for css in external_css:
app.css.append_css({"external_url": css})
external_js = ['https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js']
for js in external_css:
app.scripts.append_script({'external_url': js})
if __name__ == '__main__':
app.run_server(debug=True)
The for loop I used is the following (which obviously doesn't work because i try to add a graph in stead of a data time. But data.append doesn't work):
for data_name in data_names:
graph.append(go.Scatter(
x=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
name='Voorbeeld',
marker=go.Marker(
color='rgb(255, 153, 0)'
),
type='scatter'
))
Related
I'm trying to create a Dash App that has a scatterplot and timeseries, where the user can select data points on either graph and they highlight on both. This is similar to the "Crossfiltering" example in the Dash documentation (https://dash.plotly.com/interactive-graphing) but one key difference is that I'm looking for the union of each graph's selection rather than the intersection.
To further complicate - I have a second callback linked to a dropdown and "next" button with the intent that both can be used to change the underlying dataset used in the graphs by filtering on the "ID" column in the original dataset. I've set it up to store the filtered dataframe as a JSON object in a DCC.Store called "asset_df" that can then be pulled as an input for the callback that updates the graphs.
In its current state:
App loads and plots the dataset for the first ID in the list - this works as intended
Clicking the "Next" button or selecting a difference ID from the dropdown updates both graphs with the new dataset - this works as intended
Selecting data on either graph highlights those points on both graphs - this is where it breaks
It doesn't return a callback error or any error messages, but the graphs don't get updated with the selected points highlighted. I believe part of the issue is that the callback for updating the graphs seems to be firing twice, with the second firing returning a blank dataset for "selectedData" for both graphs.
Questions I'm hoping the community can help me with:
- Am I collecting/storing/recalling the asset_df correctly using the DCC.Store and my callback inputs/outputs?
- Why is the callback linked to the display_selected_data function getting called twice when a selection is made on either plot?
If you see any other issues with the code (I'm a beginner so no doubt there are many) please let me know, especially if they may be contributing to the issue described above.
Thank you!
dataset available here: SampleData
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, dash_table, ctx
import numpy as np
import pandas as pd
import json
from dash.dependencies import Input, Output
import plotly.express as px
from base64 import b64encode
import io
import collections
df_raw = pd.read_csv(PATH_TO_DATA)
df_raw.set_index('PCTimeStamp', inplace=True, drop=False)
asset_col = "Asset"
asset_list = df_raw[asset_col].unique().tolist()
X2_col = "X2_Variable"
timestamp_col = "PCTimeStamp"
Y1_col = "Y1_Variable"
app = JupyterDash(__name__)
app.layout = html.Div([
html.Button('Next',
id='next_button',
n_clicks=0),
dcc.Dropdown(asset_list,
value=asset_list[0],
id='dropdown'),
dcc.Graph(id='scatter',
config={'displayModeBar': True}),
dcc.Graph(id='timeseries',
config={'displayModeBar': True}),
dcc.Store(id='asset_df')
])
def get_figure(df, x_col, y_col, asset_col, selectedpoints):
if x_col == 'PCTimeStamp':
fig = px.scatter(df, x=df[x_col], y=df[y_col], text=df.index, color=df[asset_col])
fig.update_traces(selectedpoints=selectedpoints,
customdata=df.index, mode='markers+lines',
line_color='grey',
marker={'color': 'grey', 'size': 5},
unselected={'marker': {'opacity': 0.3, 'color': 'grey'}, 'textfont': {'color': 'grey'}},
selected={'marker': {'opacity': 1.0, 'color': 'yellow'}, 'textfont': {'color': 'yellow'}})
elif x_col == X2_col:
fig = px.scatter(df, x=df[x_col], y=df[y_col], text=df.index, color=df[asset_col])
fig.update_traces(selectedpoints=selectedpoints,
customdata=df.index, mode='markers',
marker={'color': 'grey', 'size': 10},
unselected={'marker': {'opacity': 0.3, 'color': 'grey'}, 'textfont': {'color': 'grey'}},
selected={'marker': {'opacity': 1.0, 'color': 'yellow'}, 'textfont': {'color': 'yellow'}})
else:
print("something's wrong...")
fig.update_layout(margin={'l': 20, 'r': 0, 'b': 15, 't': 5}, dragmode='select', hovermode=False)
return fig
#app.callback(
Output('asset_df', 'data'),
Output('next_button', 'n_clicks'),
Output('dropdown','value'),
Input('dropdown', 'value'),
Input('next_button', 'n_clicks'),
prevent_initial_call=False
)
def create_asset_df(value, n_clicks):
starting_pos=0
if "next_button" == ctx.triggered_id:
new_position = n_clicks
n_clicks = n_clicks
elif "dropdown" == ctx.triggered_id:
new_position = asset_list.index(value)
n_clicks = new_position
else:
new_position = starting_pos
n_clicks = 0
df_asset = df_raw[df_raw[asset_col] == asset_list[new_position]]
df_asset = df_asset[[asset_col, X2_col, Y1_col, timestamp_col]]
df_json = df_asset.to_json()
return df_json, n_clicks, asset_list[new_position]
#app.callback(
Output('scatter', 'figure'),
Output('timeseries', 'figure'),
Input('scatter', 'selectedData'),
Input('timeseries', 'selectedData'),
Input('asset_df', 'data'),
prevent_initial_call=False
)
def display_selected_data(selection1, selection2, df_json):
print("selection1:")
print(selection1)
print("selection2:")
print(selection2)
df_asset = pd.read_json(df_json)
print("df_asset:")
print(df_asset)
for selected_data in [selection1, selection2]:
if selected_data and selected_data['points']:
selectedpoints = np.union1d(selectedpoints,
[p['customdata'] for p in selected_data['points']])
print('selectedpoints:')
print(selectedpoints)
fig1 = get_figure(df_asset, X2_col , Y1_col, asset_col, selectedpoints)
fig2 = get_figure(df_asset, timestamp_col, Y1_col, asset_col, selectedpoints)
return fig1,fig2
if __name__ == '__main__':
app.run_server(port=8081,debug=True)
I'd like for an image to display at the point I'm hovering over in the 3D plot (shown below) or next it. sHow can I do this? Currently, I'm using plotly to create 3D plots like the one below. It has the hover feature but only for text. Is there some other python program that allows for images to be displayed instead of text?
Thanks.
The following code is in response to Derek's comment
Traceback (most recent call last):
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel_launcher.py", line 16, in <module>
app.launch_new_instance()
File "C:\Users\User\anaconda3\lib\site-packages\traitlets\config\application.py", line 663, in launch_instance
app.initialize(argv)
File "<decorator-gen-125>", line 2, in initialize
File "C:\Users\User\anaconda3\lib\site-packages\traitlets\config\application.py", line 87, in catch_config_error
return method(app, *args, **kwargs)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 567, in initialize
self.init_sockets()
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 271, in init_sockets
self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 218, in _bind_socket
return self._try_bind_socket(s, port)
File "C:\Users\User\anaconda3\lib\site-packages\ipykernel\kernelapp.py", line 194, in _try_bind_socket
s.bind("tcp://%s:%i" % (self.ip, port))
File "zmq/backend/cython/socket.pyx", line 550, in zmq.backend.cython.socket.Socket.bind
File "zmq/backend/cython/checkrc.pxd", line 26, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Address in use
According to this thread, displaying images on point hover isn't possible in plotly-python but might be built out in the future.
However, you can accomplish this in plotly-dash because this library allows you to modify the html using callbacks.
Following the example here, I created some sample data and a go.Scatter3d figure that matches your use case a bit more:
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
if __name__ == "__main__":
app.run_server(debug=True)
EDIT: I am also including an answer that allows you to run a dash app from a jupyter notebook based on this sample notebook:
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = JupyterDash(__name__)
server = app.server
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
app.run_server(mode="inline")
New to Plotly Dash.
I’m working through the tutorial on my simple example. Learning how to update a graph when new data is added to the data frame (two data frames in this case) and how to connect this with the dropdown that I have on my dashboard.
I want my graphs to get updated with new data on each page load or page refresh (as I will have only a few updates per day.)
This is the code I'm working on:
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
data = [['Blue', 20], ['Red ', 12], ['Green', 33]]
df = pd.DataFrame(data, columns=['Color', 'Number'])
data1 = [['A', 10, 88], ['B ', 50, 45], ['C', 25, 120]]
df1 = pd.DataFrame(data1, columns=['Letter', 'Column1', 'Column2'])
fig = px.bar(df, x=df['Color'], y=df['Number'])
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
html.Div([
html.Label(['Choose a graph:'], style={'font-weight': 'bold'}),
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'Colors', 'value': 'graph1'},
{'label': 'Letters', 'value': 'graph2'},
],
value='graph1',
style={"width": "60%"}),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output('graph', 'figure'),
[Input(component_id='dropdown', component_property='value')]
)
def select_graph(value):
if value == 'graph1':
return fig
else:
return fig1
if __name__ == '__main__':
app.run_server(debug=True)
Any help would be greatly appreciated. Thanks in advance.
As documented in the section on live update, you should be able to achieve the desired behaviour by defining a function that creates the layout,
def layout():
return html.Div(...)
and assigning this function as the app layout,
app.layout = layout # note no (), you must assign the function itself, not the layout
I want to make an interactive map with dash. So I added a Scattermapbox to visualize some data.
To select which data is shown, I added a slider. and used a callback to redraw the map.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import pandas as pd
import plotly.graph_objects as go
df = pd.DataFrame({'place_no': [1, 1, 1, 2, 2, 2],
'lat': [50.941357, 50.941357, 50.941357, 50.932171, 50.932171, 50.932171],
'lon': [6.957768, 6.957768, 6.957768, 6.964412, 6.964412, 6.964412],
'year': [2017, 2018, 2019, 2017, 2018, 2019],
'value': [20, 40, 60, 80, 60, 40]})
def get_map(df_map):
fig = go.Figure(go.Scattermapbox(
lat=df_map['lat'],
lon=df_map['lon'],
mode='markers',
marker=go.scattermapbox.Marker(
size=df_map['value']
),
))
fig.update_layout(
mapbox_style="open-street-map",
mapbox={'center': go.layout.mapbox.Center(lat=50.936600, lon=6.961497), 'zoom': 11}
)
return fig
app = dash.Dash()
app.layout = html.Div([
dcc.Graph(id='map',
figure=get_map(df[df['year'] == 2017])),
dcc.Slider(id='year-picker',
min=2017,
max=2019,
marks={2017: {'label': 2017}, 2018: {'label': 2018}, 2019: {'label': 2019}}
),
html.Div(id='shown-week', style={'textAlign': 'center'})
], )
#app.callback(
Output(component_id='map', component_property='figure'),
[Input(component_id='year-picker', component_property='value')]
)
def update_map(selected_year):
filtered_df = df[df['year'] == selected_year]
fig = get_map(filtered_df)
return fig
if __name__ == '__main__':
app.run_server()
This worked out fine for now. And I get exact the result I expect.
But if move or zoom the map, and then select new data with the slider, the center and the zoom goes back to the initial values, which is very inconvenient.
Is there any way to get the center and zoom of the current view, so I can add it in the update-layout method?
Or is there any other way to save the current view?
Have you tried the uirevision property? If you set this property to a constant value when you update the figure, i.e.
fig['layout']['uirevision'] = 'some-constant'
the zoom etc. should stay the same. For details, see the documentation.
I am trying to handle a callback by return a simple Graph based on Drop-Down and Date-Picker Inputs after Click Submit Button....
So to explain in deep,
Example:
As for the below Image, let's imagine that this is the final result I need to be looks like:
So Here's a simple of Data-base looks like I use as the Below Image:
Ok Lets Explain In-Deep:
Now I need to Out-Put a Simple Graph That's Shows a simple line with the values based on the below Input:
1st Drop-Down contains the CellName values like in the Database
2nd Drop-Down Contains the list of specific Column Names I want to out-put in the Graph.
3rd Date-Picker Contains all the dates which was in the Date Column in the previous DB Picture.
Finally I want Click this button to Generate the Graph.
So Here's My Code as the below:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from sqlalchemy import create_engine
import datetime
from datetime import datetime as dt
from dash.dependencies import Input, Output
# connect db
engine = create_engine('mssql+pyodbc://WWX542337CDCD\SMARTRNO_EXPRESS/myDB?driver=SQL+Server+Native+Client+11.0')
cursor = engine.raw_connection().cursor()
start = datetime.datetime(2019, 12, 2)
end = datetime.datetime(2019, 12, 15)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
gsm_kpis = pd.read_sql('SELECT * FROM [myDB].[dbo].[mnm_rotterdam_5_daily_details-20191216081027]',
engine)
gsm_kpis_raw = pd.read_sql('SELECT Voice_SetupFailRate_2G, '
'Voice_DropRate_2G, Packet_SetupFailRate_2G, '
'OutgHandover_SuccesRate_2G, Voice_ErlHr_2G, Packet_DLMBytes_2G, Packet_ULMBytes_2G, '
'Availability_2G FROM [myDB].[dbo].[mnm_rotterdam_5_daily_details-20191216081027]',
engine)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)
availble_cell = gsm_kpis['CellName'].unique()
app.layout = html.Div([
dcc.Dropdown(
id='cell-name-xaxis-column',
options=[{'label': i, 'value': i} for i in availble_cell],
value='11063'
),
dcc.Dropdown(
id='myColumns',
options=[{'label': col, 'value': col} for col in gsm_kpis_raw.columns],
value='Voice_SetupFailRate_2G'
),
dcc.DatePickerRange(
id='my-date-picker-range',
min_date_allowed=dt(1995, 8, 5),
max_date_allowed=dt(2030, 9, 19),
initial_visible_month=dt(2019, 10, 5),
start_date=dt(2019, 10, 1),
end_date=dt(2020, 1, 1)
),
html.Div(id='output-container-date-picker-range'),
html.Button('Submit', id='button'),
dcc.Graph(
style={'height': 300},
id='my-graph'
)
])
#app.callback(
Output('my-graph', 'figure'),
[Input('cell-name-xaxis-column', 'value'),
Input('myColumns', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name, date_value):
dff = gsm_kpis[gsm_kpis['Date'] == date_value]
return {
'data': [dict(
x = dff[dff['Date'] == xaxis_column_name]['Value'],
y = dff[dff['Date'] == yaxis_column_name]['Value'],
text = dff[dff['Date'] == yaxis_column_name]['Cell Name'],
mode = 'line',
line = {
'size': 15,
'opacity': 0.5
}
)],
}
if __name__ == '__main__':
app.run_server(debug=True)
and this is the Error I find:
TypeError: update_graph() missing 1 required positional argument: 'date_value'
I think there's a problem with handeling the Call-Back function...
Anyone Could help me how Can I handle this?
I hope everything will be clear...
Note: not Important to use the Submit button
In this part:
#app.callback(
Output('my-graph', 'figure'),
[Input('cell-name-xaxis-column', 'value'),
Input('myColumns', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name, date_value):
You have not made an Input for date_value, but as your date selection control is a range picker, you'll need to supply a start and end Input, as follows:
#app.callback(
Output('my-graph', 'figure'),
[Input('cell-name-xaxis-column', 'value'),
Input('myColumns', 'value'),
Input('my-date-picker-range', 'start_date'),
Input('my-date-picker-range', 'end_date')])
def update_graph(xaxis_column_name, yaxis_column_name, start_date, end_date):
And change your update_graph code as appropriate.