#!/usr/bin/env python # coding: utf-8 # Shooting Events in Philadelphia, PA (2015 to 2023) # In[55]: import carto2gpd from folium.plugins import HeatMap, MarkerCluster import folium import geopandas as gpd import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import os import osmnx as ox # Using OpenDataPhilly's API, I accessed the data on shooting events. # In[63]: # Get shooting data from Open Data Philly's API url = "https://phl.carto.com/api/v2/sql" table_name = "shootings" df = carto2gpd.get(url, table_name) df = df.loc[df['year'] != 2023] # Use only full year's worth of data df # The line plot below shows a steady amount of shooting events from 2015 to 2019, with a sporadic increase in shooting events from 2019 to 2020. # In[29]: # Organize counts of shootings by year by_year = df.groupby('year').size().reset_index(name='Count') # Set up the plot and plot the line graph sns.set_style("darkgrid") plt.figure(figsize=(10, 6)) sns.lineplot(x='year', y='Count', data=by_year, marker='o', color='#5eccab', linewidth=2.5) plt.xlabel('Year') plt.ylabel('Count') plt.title('Count of Shootings - Philadelphia, PA (2015 to 2023)') plt.gca().set_facecolor('black') plt.tick_params(colors='black') plt.xlabel('Year', color='black') plt.ylabel('Count', color='black') plt.show() # Using Folium, I plotted all of the data points, colored by the year they occurred. I leveraged marker clustering so that way not all ~14,000 points appeared on the map at once, but rather you can zoom into an area of interest. Using a tooltip, I added information about the year, age, sex, and race of the offender, if the offender was injured or deceased, and whether an officer was involved. # In[62]: import folium from folium.plugins import MarkerCluster # Filter out rows with missing geometries df2 = df.dropna(subset=['point_x', 'point_y']) # Create a map centered on the calculated location f = folium.Map(location=[39.99, -75.13], zoom_start=10, tiles='Cartodb dark_matter') # Define a colormap for different years cm = { 2015: '#546319', 2016: '#c0affb', 2017: '#e6a176', 2018: '#00678a', 2019: '#984464', 2020: '#5eccab', 2021: '#cdcdcd', 2022: '#ddcc77' } # Create a marker cluster to help with map performance and aesthetics marker_cluster = MarkerCluster().add_to(f) # Iterate over the filtered dataframe rows for index, row in df2.iterrows(): latitude = row['point_y'] longitude = row['point_x'] year = row['year'] color = cm.get(year, year) race = row['race'] sex = row['sex'] age = row['age'] officer_involved = row['officer_involved'] offender_injured = row['offender_injured'] offender_deceased = row['offender_deceased'] tooltip = f"Year: {year}
Race: {race}
Age: {age}
Sex: {sex}
Officer Involved: {officer_involved}
Offender Injured: {offender_injured}
Offender Deceased: {offender_deceased}" folium.CircleMarker( location=[latitude, longitude], radius=5, alpha=0.5, color=color, fill=True, fill_color=color, tooltip=tooltip ).add_to(marker_cluster) # Add title to the map title_html = '''

Shootings - Philadelphia, PA (2015 - 2022)

''' f.get_root().html.add_child(folium.Element(title_html)) # Fit the map bounds to include all the markers f.fit_bounds(marker_cluster.get_bounds()) # Display the map f # I decided as another visualization, I wanted to make a heat map of fatal shootings. Heat maps are another great way of visualizing density of events. I filtered the data to only include fatal shooting events, and then used Folium again to create the heat map. # In[35]: where = f"fatal = '1'" fatal = carto2gpd.get(url, table_name, where=where) # In[20]: fatal['point_y'] = fatal.point_y fatal['point_x'] = fatal.point_x coords = fatal[['point_y', 'point_x']] # eliminate rows with missing values coords = coords.dropna() # In[64]: m = folium.Map( location=[39.99, -75.13], tiles='Cartodb dark_matter', zoom_start=11) title_html = '''

Fatal Shootings - Philadelphia, PA (2015 - 2023)

''' m.get_root().html.add_child(folium.Element(title_html)) HeatMap(coords).add_to(m) m