Reputation: 1061
I have a nested dictionary that I am trying to plot. The user selects a date, using dcc.SingleDatePicker, which then gets stored using dcc.Store. That date value is used to open and modify files on a server, with the data from the files being saved in a dictionary. Then, the user is able to select several different options to display different types of time series data, which are all keys in the nested dictionary. Here is some sample data:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
from dash.exceptions import PreventUpdate
import pandas as pd
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
final_dict={'model': {'weight': {'available_crop': {'Temperature': array([[0.23393528, 0.80483263, 0.90419904, 0.66941807, 0.08487945,
0.16660603, 0.20534141, 0.55365599, 0.93233112, 0.77310289]]),
'Total Snow': array([[0.36390678, 0.36823791, 0.45548315, 0.74191525, 0.85447262,
0.81513686, 0.31969021, 0.18464316, 0.73136418, 0.73813535]]),
'Total Precip': array([[0.49129492, 0.56115172, 0.16307671, 0.55762181, 0.54295637,
0.04989331, 0.52099107, 0.60878435, 0.93648063, 0.20966815]]),
'time': DatetimeIndex(['2020-10-06 00:00:00', '2020-10-06 06:00:00',
'2020-10-06 12:00:00', '2020-10-06 18:00:00',
'2020-10-07 00:00:00', '2020-10-07 06:00:00',
'2020-10-07 12:00:00', '2020-10-07 18:00:00',
'2020-10-08 00:00:00', '2020-10-08 06:00:00'],
dtype='datetime64[ns]', freq='6H'),
'24H Delta Precip': array([[0.42823413, 0.77462331, 0.8945953 , 0.42222802, 0.5682175 ,
0.65584275, 0.10777537, 0.79867524, 0.67943749, 0.25435023]]),
'24H Delta Temp': array([[0.15606615, 0.80767068, 0.86591964, 0.74255143, 0.72830876,
0.94198463, 0.8370685 , 0.10685995, 0.59854699, 0.33812799]])}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
model_list=['EC ENS', 'EC OP', 'GFS ENS', 'GFS OP']
crop_list=['corn', 'soybeans', 'winterwheat', 'springwheat']
weight_list=['Production', 'Area']
column_list=['Temperature', 'Total Snow', 'Total Precip']
controls = dbc.Card(
[dcc.Store(id='date_output'),
dbc.FormGroup([
dcc.DatePickerSingle(
id='my-date-picker-single',
min_date_allowed=dt.date(2018, 4, 17),
max_date_allowed=dt.date(2020, 10, 6),
initial_visible_month=dt.date(2020, 10, 6),
date=dt.date(2020, 10, 6)
),
],
),
dbc.FormGroup(
dcc.RadioItems(
id='hour_radio',
options=[{'label': '00z', 'value': '00z'},
{'label': '12z', 'value': '12z'}],
value='00z',
labelStyle={'display': 'inline-block'},
inputStyle={"margin-left": "15px","margin-right": "5px"}
),
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
options=[{'label': k, 'value': k} for k in model_list],
value=model_list[0],
clearable=False
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
options=[{'label': i.title(), 'value': i} for i in crop_list],
value=crop_list[0],
clearable=False
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
options=[{'label': i, 'value': i} for i in weight_list],
value=weight_list[0],
clearable=False
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
options=[{'label': i, 'value': i} for i in column_list],
value=column_list[0],
clearable=False
),
]
),
dbc.FormGroup(
dcc.RadioItems(
id='units_radio',
options=[{'label': 'Metric', 'value': 'Metric'},
{'label': 'Imperial', 'value': 'Imperial'}],
value='Metric',
labelStyle={'display': 'inline-block'},
inputStyle={"margin-left": "20px","margin-right": "5px"}
),
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row([
dbc.Col([html.Div(id = 'plot_title')],)
]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
]),
dbc.Row([
dbc.Col([html.Div(id = 'date_picker')],)
]),
])
],),
],
fluid=True,
)
@app.callback(
Output('date_output', 'data'),
[Input('my-date-picker-single', 'date'),
Input('hour_radio', 'value')])
def update_output(data, radio2):
if radio2=='00z':
data = pd.to_datetime(data)
elif radio2=='12z':
data = pd.to_datetime(data)
data=data+dt.timedelta(hours=12)
return data
@app.callback(
Output('crop-graph', 'figure'),
[Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd', 'value'),
Input('units_radio', 'value'),
Input('date_output', 'data')])
def make_graph(model, weight, available_crops, vals, radio, data):
date_string = pd.to_datetime(data).strftime('%Y%m%d_%H')
#use the date object that was stored using dcc.Store to format and modify files from machine. End up with final_dict, which is in form final_dict[model][weight][available_crops][vals] (see sample data above), where vals is a dictionary with each key representing a selection from the column_dd dropdown.
fig = make_subplots(specs=[[{"secondary_y": True}]])
if radio=='Metric':
if weight == 'Production':
df_delta_prod_temp = pd.concat([pd.DataFrame(final_dict[model][weight][available_crops]['time'], columns=['time']), pd.DataFrame(final_dict[model][weight][available_crops]['24h Delta Temp'], columns=['24h Delta Temp'])], axis=1).set_index('time').resample('24h').mean().reset_index()
df_delta_prod_precip = pd.concat([pd.DataFrame(final_dict[model][weight][available_crops]['time'], columns=['time']), pd.DataFrame(final_dict[model][weight][available_crops]['24h Delta Precip'], columns=['24h Delta Precip'])], axis=1).set_index('time').resample('24h').mean().reset_index()
if vals=='Temperature':
fig.add_trace(go.Scatter(x=final_dict[model][weight][available_crops]['time'], y=final_dict[model][weight][available_crops][vals]-273,
mode = 'lines', line=dict(color='red', width=4),
hovertemplate='Date: %{x|%d %b %H%M} UTC<br>Temp: %{y:.2f} C<extra></extra>'), secondary_y=True)
fig.add_trace(go.Bar(x=df_delta_prod_temp['time'],
y=df_delta_prod_temp['24h Delta Temp'],
marker_color = "white", opacity=1,hovertemplate='Date: %{x|%d %b}<br>Delta: %{y:.2f} C<extra></extra>'),
secondary_y=False)
fig.update_yaxes(title_text="<b>Temp (C)<b>", color='red', secondary_y=True)
fig.update_yaxes(title_text="<b>24hr Forecast Change (C)</b>", secondary_y=False)
print(fig)
return fig
However, this code doesn't have any errors, but won't plot. Here is the print out of fig
Figure({
'data': [],
'layout': {'height': 800,
'paper_bgcolor': '#272B30',
'plot_bgcolor': '#272B30',
'showlegend': False,
'template': '...',
'width': 1500,
'xaxis': {'anchor': 'y', 'domain': [0.0, 0.94]},
'yaxis': {'anchor': 'x', 'automargin': True, 'domain': [0.0, 1.0], 'showgrid': False, 'zeroline': False},
'yaxis2': {'anchor': 'x',
'automargin': True,
'overlaying': 'y',
'showgrid': True,
'side': 'right',
'zeroline': False}}
})
I don't understand why the if
statement for vals
is not working.
Edit: When printing final_dict[model][weight][available_crops][vals]
I get this error:
TypeError: unhashable type: 'dict'
Upvotes: 1
Views: 333
Reputation: 522
vals
cannot be a dictionary. This is why you are getting the TypeError: unhashable type: 'dict'
error when trying to print.
Basically, this does not work because the keys have to be hashable. As a general rule, only immutable objects (strings, integers, floats, frozensets, tuples of immutables) are hashable (though exceptions are possible).
To use a dict as a key you need to turn it into something that may be hashed first. If the dict you wish to use as key consists of only immutable values, you can create a hashable representation of it like this:
key = frozenset(dict_key.items())
Refrence: https://stackoverflow.com/a/13264725/11622114
Upvotes: 1