I am very new to python coding. With Dash - Plotly, I have plotted sensor data onto a GEO map, below the map is the histogram, which shows the frequency of sensor-observations per hour. There are three drop-down entries to filter the data based on date-picker from a calendar, a sensor picker and an hour picker.
Representation of my dashboard
The sample code of Dash-Plotly takes three CSV files for the example shown in the image above. I took my own CSV and made sure to alter it according to the format of the example CSV's. Which resulted into the following data format (two row sample):
**Date/Time Lat Lon**
2019-03-25 04:00:00 -10,80948998827914 24,19160777427344
2019-03-25 04:05:00 -10,798684405083584 24,16288145431259
My problem: Everything seems to work. Except when I select any date past (say) 7th of the month. Then I get three errors, which for the life of me I don't understand why, because nothing has changed except for the CSV:
EDIT 1: As an example use-case: in my CSV I have sensor observations for the date '2019-03-23'. So on the webpage when I select the date: March 23, 2019. I get the following errors:
- Callback error updating total-rides.children - IndexError: list index out of range
File "/Frontend/app.py", line 262, in update_total_rides
len(totalList[date_picked.month - 4][date_picked.day - 1])
- Callback error updating histogram.figure - IndexError: list index out of range
File "/Frontend/app.py", line 322, in update_histogram
[xVal, yVal, colorVal] = get_selection(monthPicked,dayPicked, selection)
File "/Users/Mieer/Desktop/DSP_Frontend/app.py", line 231, in **get_selection**
yVal.append(len(totalList[month][day][totalList[month][day].index.hour== i]))
- Callback error updating map-graph.figure - IndexError: list index out of range
File "/Frontend/app.py", line 419, in update_graph
listCoords = getLatLonColor(selectedData, monthPicked,dayPicked)
File "/Users/Mieer/Desktop/DSP_Frontend/app.py", line 382, in **getLatLonColor**
listCoords = totalList[month][day]
My Question: How can one solve the range issue of the list? As for all I know, all that was altered in the code was the datafile which is full of enough data of the right format as I benchmarked the example CSV for which everything worked. Any point into the right direction is appreciated, I am learning as I go.
EDIT 2: the strange thing is, that the code hasn't changed, only the CSV has been replaced with one that considers the exact same format, but with way less records. I have added the callback-graph:
EDIT 3: I am starting to think that it might be due to the way the list are checked on whether they are empty or not. Because: How do I check if a list is empty?
EDIT 4: Since the calendar (date-picker) works for any chosen date, and does not result in the three errors mentioned before with the original data-set of Plotly itself. I tried concatenating the original data with my own sensor locations. But somehow, this doesn't work because Plotly than only takes in one of the two location-areas. Either that of the original Dataset (which is New York) or my own dataset which is Africa.
Below my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import numpy as np
from dash.dependencies import Input, Output
from plotly import graph_objs as go
from plotly.graph_objs import *
from datetime import datetime as dt
app = dash.Dash(
__name__, meta_tags=[{"name": "viewport", "content": "width=device-width"}]
)
server = app.server
# Plotly mapbox public token
mapbox_access_token = "pk.eyJ1IjoicGxvdGx5bWFwYm94IiwiYSI6ImNqdnBvNDMyaTAxYzkzeW5ubWdpZ2VjbmMifQ.TXcBE-xg9BFdV2ocecc_7g"
list_of_fixed_sensors = {
"sensor_1_2": {"lat": -10.736196, "lon": 24.060188},
"sensor_1_3": {"lat": -10.736196, "lon": 24.106700},
"sensor_1_6": {"lat": -10.736196, "lon": 24.246292},
# Initialize data frame
df1 = pd.read_csv(
"/Users/ME/Desktop/Frontend/sensor_points.csv",
dtype=object,
)
df = pd.concat([df1], axis=0)
df["Date/Time"] = pd.to_datetime(df["Date/Time"], format="%Y-%m-%d %H:%M")
df.index = df["Date/Time"]
df.drop("Date/Time", 1, inplace=True)
totalList = []
for month in df.groupby(df.index.month):
dailyList = []
for day in month[1].groupby(month[1].index.day):
dailyList.append(day[1])
totalList.append(dailyList)
totalList = np.array(totalList)
# Layout of Dash App HTML
app.layout = html.Div(
children=[
html.Div(
className="row",
children=[
# Column for user controls
html.Div(
className="four columns div-user-controls",
children=[
html.Img(
className="logo", src=app.get_asset_url("dash-logo-new-.png")
),
html.H2("DASHBOARD - Park Monitoring"),
html.Div(
className="div-for-dropdown",
children=[
dcc.DatePickerSingle(
id="date-picker",
min_date_allowed=dt(2019, 3, 1),
max_date_allowed=dt(2019, 12, 31),
initial_visible_month=dt(2019, 3, 1),
date=dt(2019, 3, 1).date(),
display_format="MMMM DD, YYYY",
style={"border": "0px solid white"}
)
],
),
# Change to side-by-side for mobile layout
html.Div(
className="row",
children=[
html.Div(
className="div-for-dropdown",
children=[
# Dropdown for locations on map
dcc.Dropdown(
id="location-dropdown",
options=[
{"label": i, "value": i}
for i in list_of_fixed_sensors
],
placeholder="Select a location",
)
],
),
html.Div(
className="div-for-dropdown",
children=[
# Dropdown to select times
dcc.Dropdown(
id="bar-selector",
options=[
{
"label": str(n) + ":00",
"value": str(n),
}
for n in range(24)
],
multi=True,
placeholder="Select certain hours",
)
],
),
],
),
html.H1(id="total-rides"),
html.H1(id="total-rides-selection"),
html.H1(id="date-value"),
],
),
# Column for app graphs and plots
html.Div(
className="eight columns div-for-charts bg-grey",
children=[
dcc.Graph(id="map-graph"),
html.Div(
className="text-padding",
children=[
"Select any of the bars on the histogram to section data by time."
],
),
dcc.Graph(id="histogram"),
],
),
],
)
]
)
# Gets the amount of days in the specified month
# Index represents month (0 is January, 1 is February, ... etc.)
daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
# Get index for the specified month in the dataframe
monthIndex = pd.Index(["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"])
# Get the amount of rides per hour based on the time selected
# This also higlights the color of the histogram bars based on
# if the hours are selected
def get_selection(month, day, selection):
xVal = []
yVal = []
xSelected = []
colorVal = [
"#F4EC15",
"#DAF017",
"#BBEC19",
"#9DE81B",
"#80E41D",
"#66E01F",
"#4CDC20",
"#34D822",
"#24D249",
"#25D042",
"#26CC58",
"#28C86D",
"#29C481",
"#2AC093",
"#2BBCA4",
"#2BB5B8",
"#2C99B4",
"#2D7EB0",
"#2D65AC",
"#2E4EA4",
"#2E38A4",
"#3B2FA0",
"#4E2F9C",
"#603099",
]
# Put selected times into a list of numbers xSelected
xSelected.extend([int(x) for x in selection])
for i in range(24):
# If bar is selected then color it white
if i in xSelected and len(xSelected) < 24:
colorVal[i] = "#FFFFFF"
xVal.append(i)
# Get the number of rides at a particular time
yVal.append(len(totalList[month][day][totalList[month][day].index.hour == i]))
return [np.array(xVal), np.array(yVal), np.array(colorVal)]
# Selected Data in the Histogram updates the Values in the DatePicker
@app.callback(
Output("bar-selector", "value"),
[Input("histogram", "selectedData"), Input("histogram", "clickData")],
)
def update_bar_selector(value, clickData):
holder = []
if clickData:
holder.append(str(int(clickData["points"][0]["x"])))
if value:
for x in value["points"]:
holder.append(str(int(x["x"])))
return list(set(holder))
# Clear Selected Data if Click Data is used
@app.callback(Output("histogram", "selectedData"), [Input("histogram", "clickData")])
def update_selected_data(clickData):
if clickData:
return {"points": []}
# Update the total number of observations
@app.callback(Output("total-rides", "children"), [Input("date-picker", "date")])
def update_total_rides(datePicked):
date_picked = dt.strptime(datePicked, "%Y-%m-%d")
return "Total number of observations: {:,d}".format(
len(totalList[date_picked.month - 4][date_picked.day - 1])
)
# Update the total number of observations from selected bar in histogram
@app.callback(
[Output("total-rides-selection", "children"), Output("date-value", "children")],
[Input("date-picker", "date"), Input("bar-selector", "value")],
)
def update_total_rides_selection(datePicked, selection):
firstOutput = ""
if selection is not None or len(selection) is not 0:
date_picked = dt.strptime(datePicked, "%Y-%m-%d")
totalInSelection = 0
for x in selection:
totalInSelection += len(
totalList[date_picked.month - 4][date_picked.day - 1][
totalList[date_picked.month - 4][date_picked.day - 1].index.hour
== int(x)
]
)
firstOutput = "Total observations for selected time: {:,d}".format(totalInSelection)
if (
datePicked is None
or selection is None
or len(selection) is 24
or len(selection) is 0
):
return firstOutput, (datePicked, " - showing hour(s): All")
holder = sorted([int(x) for x in selection])
if holder == list(range(min(holder), max(holder) + 1)):
return (
firstOutput,
(
datePicked,
" - showing hour(s): ",
holder[0],
"-",
holder[len(holder) - 1],
),
)
holder_to_string = ", ".join(str(x) for x in holder)
return firstOutput, (datePicked, " - showing hour(s): ", holder_to_string)
# Update Histogram Figure based on Month, Day and Times Chosen
@app.callback(
Output("histogram", "figure"),
[Input("date-picker", "date"), Input("bar-selector", "value")],
)
def update_histogram(datePicked, selection):
date_picked = dt.strptime(datePicked, "%Y-%m-%d")
monthPicked = date_picked.month - 4
dayPicked = date_picked.day - 1
[xVal, yVal, colorVal] = get_selection(monthPicked, dayPicked, selection)
layout = go.Layout(
bargap=0.01,
bargroupgap=0,
barmode="group",
margin=go.layout.Margin(l=10, r=0, t=0, b=50),
showlegend=False,
plot_bgcolor="#323130",
paper_bgcolor="#323130",
dragmode="select",
font=dict(color="white"),
xaxis=dict(
range=[-0.5, 23.5],
showgrid=False,
nticks=25,
fixedrange=True,
ticksuffix=":00",
),
yaxis=dict(
range=[0, max(yVal) + max(yVal) / 4],
showticklabels=False,
showgrid=False,
fixedrange=True,
rangemode="nonnegative",
zeroline=False,
),
annotations=[
dict(
x=xi,
y=yi,
text=str(yi),
xanchor="center",
yanchor="bottom",
showarrow=False,
font=dict(color="white"),
)
for xi, yi in zip(xVal, yVal)
],
)
return go.Figure(
data=[
go.Bar(x=xVal, y=yVal, marker=dict(color=colorVal), hoverinfo="x"),
go.Scatter(
opacity=0,
x=xVal,
y=yVal / 2,
hoverinfo="none",
mode="markers",
marker=dict(color="rgb(66, 134, 244, 0)", symbol="square", size=40),
visible=True,
),
],
layout=layout,
)
# Get the Coordinates of the chosen months, dates and times
def getLatLonColor(selectedData, month, day):
listCoords = totalList[month][day]
# No times selected, output all times for chosen month and date
if selectedData is None or len(selectedData) is 0:
return listCoords
listStr = "listCoords["
for time in selectedData:
if selectedData.index(time) is not len(selectedData) - 1:
listStr += "(totalList[month][day].index.hour==" + str(int(time)) + ") | "
else:
listStr += "(totalList[month][day].index.hour==" + str(int(time)) + ")]"
return eval(listStr)
# Update Map Graph based on date-picker, selected data on histogram and location dropdown
@app.callback(
Output("map-graph", "figure"),
[
Input("date-picker", "date"),
Input("bar-selector", "value"),
Input("location-dropdown", "value"),
],
)
def update_graph(datePicked, selectedData, selectedLocation):
zoom = 10.5
latInitial = -10.736196
lonInitial = 24.060188
bearing = 0
if selectedLocation:
zoom = 13.0
latInitial = list_of_fixed_sensors[selectedLocation]["lat"]
lonInitial = list_of_fixed_sensors[selectedLocation]["lon"]
date_picked = dt.strptime(datePicked, "%Y-%m-%d")
monthPicked = date_picked.month - 4
dayPicked = date_picked.day - 1
listCoords = getLatLonColor(selectedData, monthPicked, dayPicked)
return go.Figure(
data=[
# Data for all rides based on date and time
Scattermapbox(
lat=listCoords["Lat"],
lon=listCoords["Lon"],
mode="markers",
hoverinfo="lat+lon+text",
text=listCoords.index.hour,
marker=dict(
showscale=True,
color=np.append(np.insert(listCoords.index.hour, 0, 0), 23),
opacity=0.5,
size=5,
colorscale=[
[0, "#F4EC15"],
[0.04167, "#DAF017"],
[0.0833, "#BBEC19"],
[0.125, "#9DE81B"],
[0.1667, "#80E41D"],
[0.2083, "#66E01F"],
[0.25, "#4CDC20"],
[0.292, "#34D822"],
[0.333, "#24D249"],
[0.375, "#25D042"],
[0.4167, "#26CC58"],
[0.4583, "#28C86D"],
[0.50, "#29C481"],
[0.54167, "#2AC093"],
[0.5833, "#2BBCA4"],
[1.0, "#613099"],
],
colorbar=dict(
title="Time of<br>Day",
x=0.93,
xpad=0,
nticks=24,
tickfont=dict(color="#d8d8d8"),
titlefont=dict(color="#d8d8d8"),
thicknessmode="pixels",
),
),
),
# Plot of fixed sensors on the map
Scattermapbox(
lat=[list_of_fixed_sensors[i]["lat"] for i in list_of_fixed_sensors],
lon=[list_of_fixed_sensors[i]["lon"] for i in list_of_fixed_sensors],
mode="markers",
marker=dict(size=8, color='white', symbol='square', opacity=0.2),
hoverinfo="text",
text=[i for i in list_of_fixed_sensors],
),
],
layout=Layout(
autosize=True,
margin=go.layout.Margin(l=0, r=35, t=0, b=0),
showlegend=False,
mapbox=dict(
accesstoken=mapbox_access_token,
center=dict(lat=latInitial, lon=lonInitial),
style="dark",
bearing=bearing,
zoom=zoom,
),
updatemenus=[
dict(
buttons=(
[
dict(
args=[
{
"mapbox.zoom": 10.5,
"mapbox.center.lon": "24.060188",
"mapbox.center.lat": "-10.736196",
"mapbox.bearing": 0,
"mapbox.style": "dark",
}
],
label="Reset Zoom",
method="relayout",
)
]
),
direction="left",
pad={"r": 0, "t": 0, "b": 0, "l": 0},
showactive=False,
type="buttons",
x=0.45,
y=0.02,
xanchor="left",
yanchor="bottom",
bgcolor="#323130",
borderwidth=1,
bordercolor="#6d6d6d",
font=dict(color="#FFFFFF"),
)
],
),
)
if __name__ == "__main__":
app.run_server(debug=True)
from Dash Plotly - How to go about solving IndexError: list index out of range', when only the data-source is altered?
No comments:
Post a Comment