krz/omaha-metro-blotter
Archive of police activity and ALPR surveillance across the Omaha metro.
clone: git clone https://gitbay.org/krz/omaha-metro-blotter.git
main: notebooks/db_exploration.ipynb · raw
1{
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "metadata": {},
6 "source": [
7 "# Omaha Incidents"
8 ]
9 },
10 {
11 "cell_type": "markdown",
12 "metadata": {},
13 "source": [
14 "## Data Exploration\n",
15 "\n",
16 "Let\"s explore the data a little bit to see what kind of analysis and visualizations we want to implement."
17 ]
18 },
19 {
20 "cell_type": "markdown",
21 "metadata": {},
22 "source": [
23 "### Set up environment\n",
24 "\n",
25 "Start by installating and importing the necessary packages. "
26 ]
27 },
28 {
29 "cell_type": "code",
30 "execution_count": null,
31 "metadata": {},
32 "outputs": [],
33 "source": [
34 "# Install packages, if needed\n",
35 "# !pip3 install ipykernel\n",
36 "# !pip3 install --upgrade pandas plotly dash \"nbformat>=4.2.0\""
37 ]
38 },
39 {
40 "cell_type": "code",
41 "execution_count": null,
42 "metadata": {},
43 "outputs": [],
44 "source": [
45 "# Import packages\n",
46 "import pandas as pd\n",
47 "import numpy as np\n",
48 "import sqlite3\n",
49 "import plotly.express as px\n",
50 "import plotly.graph_objects as go\n",
51 "import plotly.io as pio"
52 ]
53 },
54 {
55 "cell_type": "markdown",
56 "metadata": {},
57 "source": [
58 "### Load Data\n",
59 "\n",
60 "To load the data, we need to connect to the SQLite3 database file and query it for the data we want."
61 ]
62 },
63 {
64 "cell_type": "code",
65 "execution_count": null,
66 "metadata": {},
67 "outputs": [],
68 "source": [
69 "# Connect to the database\n",
70 "connection = sqlite3.connect(\"../raw_data/ingress.db\")\n",
71 "cursor = connection.cursor()"
72 ]
73 },
74 {
75 "cell_type": "code",
76 "execution_count": null,
77 "metadata": {},
78 "outputs": [],
79 "source": [
80 "# If exists, delete extra header rows\n",
81 "# delete_headers = \"DELETE FROM incidents WHERE rb = 'RB Number'\"\n",
82 "# cursor.execute(delete_headers)"
83 ]
84 },
85 {
86 "cell_type": "code",
87 "execution_count": null,
88 "metadata": {},
89 "outputs": [],
90 "source": [
91 "# Grab all data\n",
92 "select_all = \"SELECT * FROM incidents\"\n",
93 "df = pd.read_sql_query(select_all, connection)\n",
94 "df.head()"
95 ]
96 },
97 {
98 "cell_type": "markdown",
99 "metadata": {},
100 "source": [
101 "### Data Cleaning\n",
102 "\n",
103 "We will clean up the data before we use: inserting NaN, converting types, etc."
104 ]
105 },
106 {
107 "cell_type": "code",
108 "execution_count": null,
109 "metadata": {},
110 "outputs": [],
111 "source": [
112 "# Replace empty cells in [lat, lon] with NaN\n",
113 "df = df.replace(r'^\\s*$', np.nan, regex=True)\n",
114 "df.head()"
115 ]
116 },
117 {
118 "cell_type": "code",
119 "execution_count": null,
120 "metadata": {},
121 "outputs": [],
122 "source": [
123 "# Convert date col to datetime format\n",
124 "df[\"date\"] = pd.to_datetime(df[\"date\"])\n",
125 "df"
126 ]
127 },
128 {
129 "cell_type": "markdown",
130 "metadata": {},
131 "source": [
132 "### Plotting\n",
133 "\n",
134 "Let's test a plot that will show us the top categories of incidents."
135 ]
136 },
137 {
138 "cell_type": "code",
139 "execution_count": null,
140 "metadata": {},
141 "outputs": [],
142 "source": [
143 "# test plotting by sorting & plotting top 5 crime categories\n",
144 "s = dff.value_counts(subset=[\"description\"])\n",
145 "t = s.nlargest(5)\n",
146 "t.head()\n",
147 "t.plot(kind=\"bar\", title=\"Top 5 Incident Categories\")"
148 ]
149 },
150 {
151 "cell_type": "markdown",
152 "metadata": {},
153 "source": [
154 "### Data Filtering\n",
155 "\n",
156 "To reduce the workload in this rest of this notebook, I am filtering just for one description and a range of dates.\n",
157 "\n",
158 "If you are doing a lot of analysis, I recommend modifying the query at the beginning to only the pull the data you need instead of filtering after querying."
159 ]
160 },
161 {
162 "cell_type": "code",
163 "execution_count": null,
164 "metadata": {},
165 "outputs": [],
166 "source": [
167 "# Create a smaller dataframe based on a selected date and description\n",
168 "start_date = \"2023-01-01\"\n",
169 "end_date = \"2023-12-31\"\n",
170 "description = \"INJURY\"\n",
171 "\n",
172 "dff = df[(df['date'] > start_date) & (df['date'] < end_date)]\n",
173 "dff = dff.reset_index()\n",
174 "dff = dff[dff.description == description]\n",
175 "\n",
176 "dff_grouped = dff.groupby(by=\"date\").count()\n",
177 "dff_grouped = dff_grouped.reset_index()\n",
178 "\n",
179 "print(dff.head())\n",
180 "print(dff_grouped.head())"
181 ]
182 },
183 {
184 "cell_type": "code",
185 "execution_count": null,
186 "metadata": {},
187 "outputs": [],
188 "source": [
189 "dff.size"
190 ]
191 },
192 {
193 "cell_type": "code",
194 "execution_count": null,
195 "metadata": {},
196 "outputs": [],
197 "source": [
198 "dff.info()"
199 ]
200 },
201 {
202 "cell_type": "markdown",
203 "metadata": {},
204 "source": [
205 "### Mapping\n",
206 "\n",
207 "Let's create a geo map of the crime data."
208 ]
209 },
210 {
211 "cell_type": "code",
212 "execution_count": null,
213 "metadata": {},
214 "outputs": [],
215 "source": [
216 "fig = px.scatter_mapbox(\n",
217 " dff,\n",
218 " lat=\"lat\",\n",
219 " lon=\"lon\",\n",
220 " color=\"description\",\n",
221 " hover_name=\"description\",\n",
222 " hover_data=[\"date\", \"time\"],\n",
223 " title=\"Incident Count by Coordinates\",\n",
224 " center={\"lat\": 41.257160, \"lon\": -95.995102},\n",
225 " zoom=10\n",
226 ")\n",
227 "\n",
228 "# fig.update_layout(showlegend=False)\n",
229 "fig.update_layout(mapbox_style=\"open-street-map\")\n",
230 "fig.update_layout(margin={\"r\": 0, \"t\": 0, \"l\": 0, \"b\": 0})\n",
231 "fig.update_layout(mapbox_bounds={\"west\": -180, \"east\": -50, \"south\": 20, \"north\": 90})\n",
232 "fig.show()"
233 ]
234 },
235 {
236 "cell_type": "code",
237 "execution_count": null,
238 "metadata": {},
239 "outputs": [],
240 "source": [
241 "# Optionally, save the figure to an HTML file\n",
242 "# pio.write_html(fig, file=\"test.html\", auto_open=True)"
243 ]
244 },
245 {
246 "cell_type": "markdown",
247 "metadata": {},
248 "source": [
249 "## Wrapping Up\n",
250 "\n",
251 "To finish, remember to close your database connections and save any data you need."
252 ]
253 },
254 {
255 "cell_type": "code",
256 "execution_count": null,
257 "metadata": {},
258 "outputs": [],
259 "source": [
260 "# clean up and close out the database\n",
261 "connection.commit()\n",
262 "connection.close()"
263 ]
264 }
265 ],
266 "metadata": {
267 "kernelspec": {
268 "display_name": "Python 3 (ipykernel)",
269 "language": "python",
270 "name": "python3"
271 },
272 "language_info": {
273 "codemirror_mode": {
274 "name": "ipython",
275 "version": 3
276 },
277 "file_extension": ".py",
278 "mimetype": "text/x-python",
279 "name": "python",
280 "nbconvert_exporter": "python",
281 "pygments_lexer": "ipython3",
282 "version": "3.11.7"
283 }
284 },
285 "nbformat": 4,
286 "nbformat_minor": 4
287}