diff options
author | Christian Cleberg <hello@cleberg.net> | 2024-01-24 13:38:15 -0600 |
---|---|---|
committer | Christian Cleberg <hello@cleberg.net> | 2024-01-24 13:38:15 -0600 |
commit | 07fa226874a2d6a9502926038714f91cb087bb03 (patch) | |
tree | 14d7fe9b2de30cc525f7ba3453f62117a8b4f592 | |
parent | 1627615c291024f159f6699768fbc13662e83bbd (diff) | |
download | omaha-incidents-07fa226874a2d6a9502926038714f91cb087bb03.tar.gz omaha-incidents-07fa226874a2d6a9502926038714f91cb087bb03.tar.bz2 omaha-incidents-07fa226874a2d6a9502926038714f91cb087bb03.zip |
add line graph and date range selector
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | app.py | 95 | ||||
-rw-r--r-- | assets/styles.css | 31 | ||||
-rw-r--r-- | screenshots/dashboard.png | bin | 2332467 -> 0 bytes | |||
-rw-r--r-- | screenshots/dashboard_01.png | bin | 0 -> 2140416 bytes | |||
-rw-r--r-- | screenshots/dashboard_02.png | bin | 0 -> 412054 bytes |
6 files changed, 100 insertions, 31 deletions
@@ -1,4 +1,4 @@ -# Omaha Incidents +# Omaha Crime Mapping & Analysis Data from the Omaha police department, used to analyze and visualize statistics. @@ -22,4 +22,5 @@ sqlite2rest serve ./raw_data/ingress.db ## Screenshots - + + @@ -1,7 +1,9 @@ from dash import Dash, html, dcc, callback, Output, Input, dash_table import plotly.express as px import pandas as pd +import numpy as np import sqlite3 +from datetime import datetime, date # Connect to database and query all incidents connection = sqlite3.connect("./raw_data/ingress.db") @@ -9,44 +11,51 @@ cursor = connection.cursor() query = "SELECT * FROM incidents;" df = pd.read_sql_query(query, connection).sort_values(by="description") -# Create custom YEAR column to use in dropdown -df['year'] = df['date'].str[-4:] +# Replace empty cells with NaN +df = df.replace(r'^\s*$', np.nan, regex=True) + +# Convert date column to datetime +# TODO: Create a combined datetime column in the db to load/convert here +df["date"] = pd.to_datetime(df["date"]) # Configure HTML layout app = Dash(__name__) app.layout = html.Div(children = [ - html.H1(children="Omaha Police Incidents", style={"textAlign":"center"}), + html.H1(children="Omaha Crime Mapping & Analysis", style={"textAlign":"center"}), html.Div([ - html.H2(children="Totals per Category and Year", style={"textAlign":"center"}), - dcc.Dropdown(df.sort_values("description").description.unique(), "INJURY", id="dropdown"), - dcc.Dropdown(df.sort_values("year").year.unique(), "2023", id="year-dropdown"), - html.Hr(), - dash_table.DataTable(data=df.to_dict("records"), page_size=5, id="table"), - html.H2(children="Map of Incidents per Category and Year", style={"textAlign":"center"}), - dcc.Graph(id="map-graph") + html.Div(className="flex", + children = [ + html.P("Crime:"), + dcc.Dropdown(df.sort_values("description").description.unique(), "INJURY", id="dropdown"), + ] + ), + html.Div(className="flex", + children = [ + html.P("Date Range:"), + dcc.DatePickerRange( + id='date-picker-range', + min_date_allowed=date(2015, 1, 1), + max_date_allowed=date(2023, 12, 31), + start_date=date(2015, 1, 1), + end_date=date(2023,12,31) + ), + ] + ), + dcc.Graph(id="map-graph"), + dcc.Graph(id="line-graph"), + dash_table.DataTable(data=df.to_dict("records"), page_size=5, id="table") ]) ]) -# Create table -@callback( - Output("table", "data"), - Input("dropdown", "value"), - Input("year-dropdown", "value") -) -def update_table(description, year): - dff = df[df.year == year] - dff = dff.reset_index() - dff = dff[dff.description == description] - return dff.to_dict("records") - # Create map @callback( Output("map-graph", "figure"), Input("dropdown", "value"), - Input("year-dropdown", "value") + Input('date-picker-range', 'start_date'), + Input('date-picker-range', 'end_date') ) -def update_map(description, year): - dff = df[df.year == year] +def update_map(description, start_date, end_date): + dff = df[(df['date'] > start_date) & (df['date'] < end_date)] dff = dff.reset_index() dff = dff[dff.description == description] @@ -54,7 +63,6 @@ def update_map(description, year): dff, lat="lat", lon="lon", - color="description", hover_name="description", hover_data=["date", "time"], title="Incident Count by Coordinates", @@ -69,5 +77,40 @@ def update_map(description, year): return fig +# Create line graph +@callback( + Output("line-graph", "figure"), + Input("dropdown", "value"), + Input('date-picker-range', 'start_date'), + Input('date-picker-range', 'end_date') +) +def update_line(description, start_date, end_date): + dff = df[(df['date'] > start_date) & (df['date'] < end_date)] + dff = dff.reset_index() + dff = dff[dff.description == description] + dff = dff.groupby(by="date").count() + dff = dff.reset_index() + + fig = px.line( + dff, + x="date", + y="description", + ) + + return fig + +# Create table +@callback( + Output("table", "data"), + Input("dropdown", "value"), + Input('date-picker-range', 'start_date'), + Input('date-picker-range', 'end_date') +) +def update_table(description, start_date, end_date): + dff = df[(df['date'] > start_date) & (df['date'] < end_date)] + dff = dff.reset_index() + dff = dff[dff.description == description] + return dff.to_dict("records") + if __name__ == "__main__": app.run(debug=True) diff --git a/assets/styles.css b/assets/styles.css index d014936..97fd316 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -4,6 +4,31 @@ body { margin: 2rem auto; } -hr { - margin: 1rem auto; -}
\ No newline at end of file +.dash-table-container { + overflow: scroll; +} + +.dash-table-container, +.dash-graph { + margin: 2rem 0; + max-width: 100%; +} + +.flex { + display: flex; + flex-direction: row; + align-items: center; +} + +.flex p { + margin-right: 1rem; + flex-shrink: 0; +} + +.flex .dash-dropdown { + width: 100%; +} + +.flex #date-picker-range { + width: 100%; +} diff --git a/screenshots/dashboard.png b/screenshots/dashboard.png Binary files differdeleted file mode 100644 index 5a7c6aa..0000000 --- a/screenshots/dashboard.png +++ /dev/null diff --git a/screenshots/dashboard_01.png b/screenshots/dashboard_01.png Binary files differnew file mode 100644 index 0000000..b0587e4 --- /dev/null +++ b/screenshots/dashboard_01.png diff --git a/screenshots/dashboard_02.png b/screenshots/dashboard_02.png Binary files differnew file mode 100644 index 0000000..620f2fb --- /dev/null +++ b/screenshots/dashboard_02.png |