17  Layering Disparity Scores with Other Measures: Washington, DC Gun Violence Prevention

Author

Rebecca John (Urban Institute)

The web version of the Spatial Equity Data Tool (SEDT) generates analysis and visualizations of the demographic and geographic distributions of a user’s uploaded resource dataset. However, these data outputs need not stand alone. The SEDT API (described in Chapter 10) allows users to perform custom analysis using the outputs of the SEDT in their preferred programming language. Below, we demonstrate how to layer a specific measure of interest on top of the tool’s disparity scores using Python and data from the District of Columbia.

Case and Policy Background

The DC Office of Gun Violence Prevention (DC OGVP) supports the coordination of Building Blocks DC, a whole-government public health strategy comprising different offices and agencies focused on preventing gun violence using place- and person-based approaches. Recognizing that violence is a multifaceted public health issue that requires a holistic strategy, Building Blocks DC supports programs that aim to reduce and prevent gun violence using a number of tactics, including community violence intervention, improving and protecting physical spaces, and addressing trauma and behavioral health. In this case study, we will focus on programs intended to create pathways to economic opportunity.

Research has found that social determinants of health, including economic opportunity and wealth, are related to gun violence (Kim 2017). To address this, the District works to increase access to economic opportunity and remove barriers to employment through programs like the Office of Neighborhood Safety and Engagement’s (ONSE) Pathways program and the Department of Employment Service’s (DOES) Project Empowerment, which equip participants with training focused on job and life skills, subsidized employment, and support in finding and keeping permanent employment.

Why Use the SEDT

To most effectively target outreach efforts for these employment skills programs, DC OGVP is interested in finding areas of the city that have a high need for these programs and in understanding the demographic groups most affected by gun violence. Building on areas the District already has selected as high priority, such as the ONSE Priority Communities, we focus on identifying areas that would benefit the most from employment-centered outreach efforts based on data from the American Communities Survey (ACS).

Using the SEDT, we can identify areas that experience particularly high levels of gun-related crimes relative to their share of the DC population using geographic disparity scores and deepen the analysis by layering those results with unemployment data. Combining SEDT outputs with unemployment data, we can locate areas that have disproportionately high levels of violence and unemployment, compare these areas to ONSE’s Priority Communities, and identify areas in the city that could be candidates for the expansion of these programs.

About the Data

The datasets that we will use in the SEDT API call are

  1. gun violence incidents in DC, and
  2. census tract-level unemployment rate.

Later, we will also use the following DC polygon data for comparison:

  1. ONSE Priority Communities

The 2023 gun violence data come from DC Crime Cards, a public source of DC police data. DC OGVP often works with this information on crime as the baseline for exploring other datasets. The data have columns for latitude, longitude, census tract GEOID, and crime description, among other measures. We will filter by offense type to focus on violent crime for our analysis. Since the crime data come with latitude and longitude columns, no additional preprocessing is required to use it with the API.

The unemployment data come from the US Census Bureau’s ACS. We acquired this data using the Census API. The data have columns for census tract GEOID, the number of unemployed people, and the number of people in the labor force. We use these columns to calculate the unemployment rate by census tract.

The ONSE Priority Communities are DC neighborhoods that have been identified as in need of ONSE’s work, which includes the employment-based Pathways program mentioned earlier, as well as programming involving community violence intervention and family and survivor support. We will use these Priority Communities to represent areas that are already considered high priority and receiving gun violence prevention support.

The Office of the Attorney General hosts Cure the Streets, which is a program that uses a data-driven public health strategy to interrupt, treat, and stop the spread of gun violence. While we do not include the Cure the Street service areas in the rest of this case for simplicity, we used these polygon data during our exploratory analysis process. Cure the Street’s data dashboard can be found here.

Using the SEDT API for Custom Analysis

Using the SEDT API, we can upload DC crime data to find areas that have disproportionately high levels of violence via the SEDT geographic disparity scores. We can also assess the composition of communities most affected by violence across the city as a whole with the demographic disparity scores.

After generating SEDT disparity scores with the API, we can compare the areas with high geographic disparity scores with ONSE Priority Communities. Lastly, we will layer unemployment data to find areas that have both high geographic disparity scores and high levels of unemployment:

  1. Select baseline dataset for comparison
  2. Upload crime data to SEDT API to obtain geographic and demographic disparity scores
  3. Assess disparity scores
  4. Compare results with ONSE Priority Communities
  5. Identify areas with both high geographic disparity and high unemployment

Select Baseline Dataset for Comparison

The SEDT methodology compares the distribution of uploaded data, in our case, instances of gun-related crime, with the distribution of a baseline dataset from the ACS. The API constructs geographic disparity scores and demographic disparity scores based on this comparison. You can read more about the construction of these scores in Chapter 3.

The ACS baseline dataset we choose to compare with our dataset of interest will affect the interpretation of our analysis. The tool has a number of baseline datasets as default options, including the total population, the population of children, and the population living under the federal poverty level. However, with the API, users have the option to upload custom datasets for comparison depending on their analysis. In our case, we are interested in identifying the areas that have disproportionately high levels of gun violence. The question then becomes, “Disproportionate to what?”

If we choose to compare the crime data to the total population as our baseline dataset, the geographic disparity scores would tell us the difference between the percentage of all gun-related crime that occurred in a particular census tract and the percentage of the total DC population living in that same census tract. That is, high disparity scores would tell us that the percentage of DC crime that occurred in a census tract is disproportionately high compared with its share of the overall DC population. Since we are interested in unemployment, a user might consider uploading the population of unemployed residents as a custom baseline dataset in the API call.

A helpful question to ask when choosing a baseline dataset is, “Which population is most affected by my data?” For example, if we were looking at playgrounds, we would choose a baseline population of children. Since gun violence affects everyone, we choose to use total population as our baseline dataset. To gain insight into the unemployed population, we can layer that data on top of the tool’s output. This allows us to identify the areas with the most disproportionately high levels of gun-related violence in the District overall and then find the overlap with areas with the highest levels of unemployment.

Code
import requests
import json
import pandas as pd
import geopandas as gpd
import time

pd.set_option('display.max_columns', None)

# upload link for SEDT API
upload_url = "https://equity-tool-api.urban.org/api/v1/upload-user-file/"

payload = {"resource_lat_column": "LATITUDE",
           "resource_lon_column": "LONGITUDE",
           "geo": "city",
           "acs_data_year": "2022"}

# load DC crime data
crime_cards = pd.read_csv("data/dc-ogvp/dc-crimes-search-results.csv")
# filter to gun-related crimes
gun_crime = crime_cards.query('offensekey == "violent|assault w/dangerous weapon" | offensekey == "violent|homicide"')
# output gun-related crime data
gun_crime.to_csv('data/dc-ogvp/gun_crime.csv', index=False)
# reference resource file for API call
resource_file = open('data/dc-ogvp/gun_crime.csv')

# API call
r = requests.post(
  upload_url, 
  data = payload, 
  files = {"resource_file": resource_file}
)
response_string = r.content.decode("utf-8")
response_dict = json.loads(response_string)
file_id = response_dict["file_id"]

# check whether SEDT has finished processing request
status_url = "https://equity-tool-api.urban.org/api/v1/get-output-data-status/"
full_url = status_url + file_id + "/"
response = requests.get(full_url)
content = json.loads(response.content)

# view the output in a more clean way:
content_dump = json.dumps(content, indent = 4)
# wait for SEDT to finish running
time.sleep(10)

# get response
output_data_url = "https://equity-tool-api.urban.org/api/v1/get-output-data/"
full_url = output_data_url + file_id + "/"
response = requests.get(full_url)
content = json.loads(response.content)

# create demographic disparity DataFrame
dem = content["results"]["result"]["demographic_bias_data"]
dem_df = pd.DataFrame(dem)
# create geographic disparity DataFrame
geo = content["results"]["result"]["geo_bias_data"]["features"]
geo_df = gpd.GeoDataFrame.from_features(geo)

Assess and Plot Demographic Disparity

Code
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import os
import numpy as np

# define repeated colors
yellow = '#fdbf11'
blue = '#1696d2'

# create dictionary of labels to match with column names
label_dict = {
  "pct_no_internet": "Without Internet Access",
  "pct_under_200_poverty_line": "Under 200% of Poverty Line",
  "pct_all_other_races": "All other races",
  "pct_less_hs_diploma": "Without a High School Diploma",
  "pct_black": "Black",
  "pct_white": "White",
  "pct_asian": "Asian",
  "pct_children": "Children",
  "pct_seniors": "Seniors",
  "pct_veterans": "Veterans",
  "pct_unins": "Uninsured",
  "pct_disability": "With a Disability",
  "pct_renters": "Renters",
  "pct_limited_eng_hh": "Limited English Proficiency",
  "pct_bach": "With Bachelors Degree",
  "pct_under_poverty_line": "Under the Poverty Line",
  "pct_cb_renter_hh": "Cost Burdened Renter Households",
  "pct_unemp": "Unemployed",
  "pct_labor_force": "In Labor Force",
  "pct_hs_diploma": "With HS Diploma",
  "pct_more_than_30_min_commute": "More than 30 Minute Commute",
  "pct_workers_no_vehicle_in_household": "Workers with no Vehicle in Household",
  "pct_renter_extreme_rent_burden": "Extremely Rent-Burdened Renters",
  "pct_people_in_state_prison": "People in State Prison",
  "pct_pov_children": "Children in Poverty",
  "pct_under18_white_alone": "White (non-Latinx) Children",
  "pct_under18_hisp": "Latinx Children",
  "pct_pov_black_alone": "Black Residents (non-Latinx) Living in Poverty",
  "pct_hisp": "Latinx",
  "pct_under18_all_other_races_alone": "Children of Another Race or Ethnicity (non-Latinx)",
  "pct_under18_limited_eng_hh": "Households with Children and Limited English Proficiency",
  "pct_pov_all_other_races_alone": "Residents in Poverty of Another Race or Ethnicity (non-Latinx)",
  "pct_pov_hisp": "Latinx Residents Living in Poverty",
  "pct_pov_less_than_hs": "Residents Living in Poverty without a HS Diploma",
  "pct_pov_unemployed": "Unemployed Residents in Poverty",
  "pct_pov_asian_alone": "Asian Residents (non-Latinx) Living in Poverty",
  "pct_pov_veterans": "Veterans Living in Poverty",
  "pct_under18_disability": "Children with a Disability",
  "pct_under18_unins": "Uninsured Children",
  "pct_under18_asian_alone": "Asian (non-Latinx) Children",
  "pct_pov_unins": "Uninsured Residents Living in Poverty",
  "pct_under18_pov": "Children Living in Poverty",
  "pct_pov_seniors": "Seniors Living in Poverty",
  "pct_pov_bach": "With Bachelor's Degree Living in Poverty",
  "pct_pov_disability": "Residents with a Disability Living in Poverty",
  "pct_pov_white_alone": "White (non-Latinx) Residents Living in Poverty",
  "pct_under18_black_alone": "Black (non-Latinx) Residents Living in Poverty"
}

# sort by demographic disparity scores
dem_df = dem_df.sort_values(by = "diff_data_city")
# filter
dem_df = dem_df[~dem_df['census_var'].str.startswith('pct_pov')]
dem_df = dem_df[~dem_df['census_var'].str.startswith('pct_under18')]

# create y-axis range
y_range = range(1, len(dem_df.index)+1)
# set figure size
plt.figure(figsize=(5.3, 4.8))

# create horizontal lines for lollipop chart
plt.hlines(y = y_range, xmin = 0, xmax = dem_df['diff_data_city'], color = 'grey', alpha = 0.4, zorder = 1)

# set plot colors based on statistical significance
plot_colors = np.where((dem_df['diff_data_city'] < 0) & (dem_df['sig_diff'] == True), yellow, np.where((dem_df['diff_data_city'] > 0) & (dem_df['sig_diff'] == True), blue, "grey"))

# create dots for lollipop chart
plt.scatter(dem_df['diff_data_city'], y_range, color = plot_colors, alpha = 1, label = dem_df['census_var'])

# create column with labels for y-axis
dem_df['y_label'] = dem_df['census_var'].replace(label_dict)

# create y-axis
plt.axvline(x = 0, color = 'black')
y_labels = dem_df['y_label']
# add y-axis labels
plt.yticks(y_range, y_labels)
# add overall plot title
plt.title('Demographic Disparity Analysis')
# add x-axis label
plt.xlabel('Demographic Disparity Score (%)')

# create legend for statistical significance color differences
custom_legend = [
  Line2D([0], [0], marker = 'o', color = 'w', markerfacecolor = yellow, markersize = 8, label = 'Underrepresented'),
  Line2D([0], [0], marker = 'o', color = 'w', markerfacecolor = blue, markersize = 8, label = 'Overrepresented'),
  Line2D([0], [0], marker = 'o', color = 'w', markerfacecolor = 'grey', markersize = 8, label = 'Not Statistically Significant')
]
plt.legend(handles = custom_legend, loc = 'lower right', fontsize = 'small')

# display plot
plt.show()

Note

We use “Latinx” to refer to people of Latino or Hispanic origin as asked by the Census. We recognize that this gender-neutral term may not be the preferred identifier for many, but we use it to remain inclusive of gender nonconforming and nonbinary people.

Evaluating the demographic disparity scores, we can see that areas with higher concentrations of Black residents are exposed to gun-related violence at a disproportionately high rate compared with the overall population in the District. When reviewing demographic disparity scores, it is important to understand the role of structural inequities driving current disparities. Structural racism and historic disinvestment provide important context for our disparity scores on gun-related crime. Race is a social construct, and “no racial group is inherently more violent than any other.” (Chaplin et al. 2022). For this reason, DC OGVP is interested in looking at the role of economic factors in gun-related crime. Reviewing the demographic disparity scores, we can see that residents living under 200 percent of the federal poverty level are also disproportionately exposed to gun-related crime in their neighborhoods.

In general, having this demographic disparity data on community outcomes is important in order to conduct racial equity impact assessments, especially given incidents of gun violence is a racial equity indicator in the DC Racial Equity Action Plan. While the demographic disparity scores provide insight into racial and ethnic groups’ levels of exposure to incidences of gun violence in the District, they do not show which areas of DC experience particularly high level of gun violence. The geographic disparity scores provide this insight.

Assess and Plot Geographic Disparity

Using the SEDT API output, we can plot the geographic disparity scores on a map to visualize the areas with the highest positive disparity scores, indicating a disproportionately high exposure to gun violence compared with their share of the total population. While many areas with high geographic disparity scores appear throughout Southeast DC, a historically marginalized quadrant of the District, some key areas in Northeast and Northwest DC are also highlighted on the map.

Code
from census import Census
from dotenv import load_dotenv
import os
import matplotlib
import folium
import matplotlib.colors as mcolors
import branca.colormap as bcm

# load environment variables from .env file
load_dotenv()
# get Census API key from environment variable
api_key = os.getenv('CENSUS_API_KEY')
# initialize Census API
c = Census(api_key)

# load unemployment data
dc_unemployment = c.acs5.state_county_tract(
  fields=['B27011_008E', 'B27011_002E'],
  state_fips='11',
  county_fips='001',
  tract='*',
  year=2022
)
# convert to DataFrame
dc_unemployment_df = pd.DataFrame(dc_unemployment)
# rename columns for clarity
dc_unemployment_df.rename(
  columns={'B27011_008E': 'unemployed', 'B27011_002E': 'labor_force'}, 
  inplace=True
)

# calculate unemployment rate
dc_unemployment_df['unemployment_rate'] = dc_unemployment_df['unemployed'] / dc_unemployment_df['labor_force']
# format GEOID for merge
dc_unemployment_df['GEOID'] = '11001' + dc_unemployment_df['tract'].astype(str)

# output unemployment data
dc_unemployment_df.to_csv('data/dc-ogvp/dc_unemployment.csv', index=False)

# prepare DC unemployment data
dc_unemployment = pd.read_csv("data/dc-ogvp/dc_unemployment.csv")
dc_unemployment["GEOID"] = dc_unemployment["GEOID"].astype(str) 
# merge unemployment data with geographic disparity scores on census tract (GEOID)
combined_df = geo_df.merge(dc_unemployment, on = "GEOID")

# round for map legend and tooltips
combined_df.loc[:, 'diff_pop'] = round(combined_df['diff_pop'] * 100, 2)
combined_df.loc[:, 'unemployment_rate'] = round(combined_df['unemployment_rate'] * 100, 1)
# prepare data for mapping
combined_gdf = combined_df.set_crs(crs = 4326, allow_override = True)

# define center of map
latitude = 38.89511 
longitude = -77.03637

# create base map
m = folium.Map(location=[latitude, longitude], tiles="CartoDB positron", zoom_start=11)

# read in DC border for map outline
dc_border = gpd.read_file('data/dc-ogvp/Washington_DC_Boundary.geojson')
# create DC border layer
dc_map_outline = dc_border.explore(
  m = m,
  color = 'none',
  edgecolor = 'black',
  interactive = False,
  style_kwds={'fillOpacity': 2, 'color': 'black', 'weight': 1}
)

# normalize data to create diverging colormap centered at 0
vmin = combined_gdf["diff_pop"].min()
vmax = combined_gdf["diff_pop"].max()
norm = mcolors.TwoSlopeNorm(vmin=vmin, vcenter=0, vmax=vmax)
# load PuOr color map
cmap = plt.get_cmap('PuOr')

# create column with PuOr hexcodes based on normalized data
combined_gdf['norm_hex'] = combined_gdf['diff_pop'].apply(lambda x: mcolors.rgb2hex(cmap(norm(x))))

# create function to style each feature
def style_function(feature):
    return {
        'fillColor': feature['properties']['norm_hex'], # color fill scale
        'color': 'black', # outline color
        'weight': 0.3, # outline weight
        'fillOpacity': 0.7, # outline opacity
    }

# add disparity score layer with the custom style
folium.GeoJson(
  combined_gdf,
  style_function=style_function,
  tooltip=folium.GeoJsonTooltip(
    fields=["tract", "diff_pop", "unemployment_rate"],
    aliases=['Census Tract ID:', 'Geographic Disparity Score (%)', 'Unemployment Rate (%)']
  )
).add_to(dc_map_outline)

# label interval for custom legend
intervals = [round(i * 0.2, 1) for i in range(int(vmin / 0.2), int(vmax / 0.2) + 1)]
if 0 not in intervals:
  intervals.append(0)
intervals = sorted(intervals)
# corresponding colors for legend label intervals
colors = [mcolors.rgb2hex(cmap(norm(i))) for i in intervals]

# create custom legend
legend = bcm.LinearColormap(
  colors=colors,
  vmin=vmin,
  vmax=vmax,
  index=intervals,
  caption='Geographic Disparity Score (%)'
)

# add to map
legend.add_to(m)

# display map
m
Make this Notebook Trusted to load map: File -> Trust Notebook

Comparison with ONSE Priority Communities

We have seen that SEDT geographic disparity scores allow us to examine the distribution of gun violence compared with the distribution of the DC population. To identify areas with the most disproportionately high levels of gun violence, we isolate the tracts with the highest geographic disparity scores. The map below plots the census tracts with geographic disparity scores above the 80th percentile in purple.

Code
# load ONSE priority areas
onse_priority_file = 'data/dc-ogvp/onse-priority-communities.geojson'
priority_areas = gpd.read_file(onse_priority_file)
priority_areas_gdf = priority_areas.set_crs(crs = 4326, allow_override = True)

# find the 20% of tracts with the most extreme geographic disparity scores
pctile_80_geog_disp_score = combined_df["diff_pop"].quantile(.80)
top_geog_disp = combined_df[(combined_df["diff_pop"] > pctile_80_geog_disp_score)]
# prepare data for mapping
top_geog_disp[["GEOID", "diff_pop", "unemployment_rate"]]
top_geog_disp_gdf = top_geog_disp.set_crs(crs = 4326, allow_override = True)

# define center of map
latitude = 38.89511 
longitude = -77.03637

# create base map
m = folium.Map(location=[latitude, longitude], tiles="CartoDB positron", zoom_start=11)

# read in DC border for map outline
dc_border = gpd.read_file('data/dc-ogvp/Washington_DC_Boundary.geojson')
# create DC border layer
dc_map_outline = dc_border.explore(
  m = m,
  color = 'none',
  edgecolor = 'black',
  interactive = False,
  style_kwds={'fillOpacity': 2, 'color': 'black', 'weight': 0.7}
)

# add map layer with top geographic disparity quintile areas
top_geog_disp_map = top_geog_disp_gdf.explore(
  m = dc_map_outline,
  color = "#7A5DA3",
  tooltip = ["tract", "diff_pop", "unemployment_rate"],
  tooltip_kwds = {'aliases': ['Census Tract ID:', 'Geographic Disparity Score (%)', 'Unemployment Rate (%)']},
  style_kwds={'fillOpacity': 0.7, 'color': 'black', 'weight': 0.3}
)

# ONSE priority area map
priority_areas_gdf.explore(
  m = top_geog_disp_map,
  tooltip = ["Title"],
  tooltip_kwds = {'aliases': ['ONSE Priority Community']},
  color = yellow,
  style_kwds={'fillOpacity': 0.7, 'color': 'black', 'weight': 0.3}
)

# add custom legend
legend_html = '''
<div style="position: fixed; 
            bottom: 50px; left: 50px; width: 200px; height: 42px; 
            border:2px solid grey; z-index:9999; font-size:14px;
            background-color:white; opacity: 0.8;">
    <i style="background: #7A5DA3; width: 18px; height: 18px; float: left; margin-right: 8px;"></i> High Geographic Disparity<br>
    <i style="background: #fdbf11; width: 18px; height: 18px; float: left; margin-right: 8px;"></i> ONSE Priority Community
</div>
'''
# add legend to map
m.get_root().html.add_child(folium.Element(legend_html))

# display map
m
Make this Notebook Trusted to load map: File -> Trust Notebook

While reviewing this map, DC subject-matter experts were initially surprised that census tract 10700, encompassed within Downtown DC, was included in the areas with disproportionately high gun violence. Upon further review, it became clear the high geographic disparity score was driven by the low residential population in this commercial area, rather than the level of gun-related crime. While this census tract does not pass through our filter for high unemployment rate shown in the next section, this observation highlights the importance of ground-truthing analysis from the tool and gut checking outliers with population levels.

As previously discussed, several offices within DC government have already highlighted specific communities as high-priority neighborhoods to target for service. ONSE Priority Communities are among these. On the map above, these areas are plotted in yellow. These ONSE Priority Communities have been selected based on community-sourced knowledge and local police data, among other factors. The map demonstrates a strong overlap between the ONSE Priority Communities and the areas with high SEDT geographic disparity scores. This makes sense as data on gun violence is a key factor in the analysis underlying their creation.

ONSE and other DC offices coordinate a wide array of violence prevention efforts, all addressing the multifaceted issue of gun violence from different angles. We are particularly interested in highlighting possible areas that are not currently targeted by these programs but that could benefit from programming focused on boosting employment and economic opportunity. Merging the geographic disparity scores with Census unemployment data adds our key measure to the analysis and enables us to find our areas of interest.

Layering Unemployment Data with Geographic Disparity

Building on our analysis thus far, we can use unemployment data to more directly target areas that would benefit from Building Blocks DC’s and ONSE’s employment-centered interventions. So far, we have identified areas that have disproportionately high levels of gun violence. By isolating the census tracts with the highest levels of unemployment and their intersection with the tracts with the highest disparity scores, we can identify new priority areas to inform potential expansion efforts of Pathways and Project Empowerment.

We can accomplish this by collecting unemployment data from the Census API, filtering for the census tracts with unemployment rates higher than the 80th percentile, and plotting their overlap with those census tracts with geographic disparity scores above the 80th percentile from the previous section. By mapping the areas with the most extreme geographic disparity scores and the highest levels of unemployment, we can drill down to the subset of priority areas that would benefit most from these specific economic opportunity programs. On the map below, these areas of interest are plotted in blue.

Code
# find the 20% of tracts with the highest unemployment rates
pctile_80_unemployment = combined_df["unemployment_rate"].quantile(.80)
# merge with highest disparity scores to find intersection
top_quintile_both = combined_df[(combined_df["diff_pop"] > pctile_80_geog_disp_score) & (combined_df["unemployment_rate"] > pctile_80_unemployment)]
top_quintile_both[["GEOID", "diff_pop", "unemployment_rate"]]
# output new target areas
top_quintile_both.to_csv("data/dc-ogvp/potential_target_areas.csv")

# round for map legend and tooltips
top_quintile_both.loc[:, 'diff_pop'] = round(top_quintile_both['diff_pop'], 2)
top_quintile_both.loc[:, 'unemployment_rate'] = round(top_quintile_both['unemployment_rate'], 1)
# prepare data for mapping
top_crime_unemployment_gdf = top_quintile_both.set_crs(crs = 4326, allow_override = True)

# define center of map
latitude = 38.89511 
longitude = -77.03637

# create base map
m = folium.Map(location=[latitude, longitude], tiles="CartoDB positron", zoom_start=11)

# read in DC border for map outline
dc_border = gpd.read_file('data/dc-ogvp/Washington_DC_Boundary.geojson')
# create DC border layer
dc_map_outline = dc_border.explore(
  m = m,
  color = 'none',
  edgecolor = 'black',
  interactive = False,
  style_kwds={'fillOpacity': 2, 'color': 'black', 'weight': 0.7}
)

# base map layer with top unemployment/crime quintile areas
crime_unemployment_map = top_crime_unemployment_gdf.explore(
  m = dc_map_outline,
  color=blue,
  tooltip=["tract", "diff_pop", "unemployment_rate"],
  tooltip_kwds={'aliases': ['Census Tract ID:', 'Geographic Disparity Score (%)', 'Unemployment Rate (%)']},
  style_kwds={'fillOpacity': 0.7, 'color': 'black', 'weight': 0.3}
)

# ONSE priority area map
priority_areas_gdf.explore(
  m=crime_unemployment_map,
  tooltip=["Title"],
  tooltip_kwds={'aliases': ['ONSE Priority Community']},
  color=yellow,
  style_kwds={'fillOpacity': 0.7, 'color': 'black', 'weight': 0.3}
)

# custom legend
legend_html = '''
<div style="position: fixed; 
            bottom: 50px; left: 50px; width: 200px; height: 62px; 
            border:2px solid grey; z-index:9999; font-size:14px;
            background-color:white; opacity: 0.8;">
    <i style="background: #1696d2; width: 18px; height: 18px; float: left; margin-right: 8px;"></i> High Geographic Disparity<br>
    <span style="margin-left: 26px;">& High Unemployment</span><br>
    <i style="background: #fdbf11; width: 18px; height: 18px; float: left; margin-right: 8px;"></i> ONSE Priority Community
</div>
'''
# add legend to map
m.get_root().html.add_child(folium.Element(legend_html))

# display the map
m
Make this Notebook Trusted to load map: File -> Trust Notebook

Overall, our analysis reaffirms that the ONSE Priority Communities are located in areas with disproportionately high levels of gun-related crime and have need for violence prevention services. As we filter by unemployment, we notice some of the previously overlapping ONSE Priority Communities are no longer included in our new areas of interest. This indicates that these areas were likely chosen for their need for other nonemployment-based ONSE programs, such as violence interruption efforts. To find new areas of interest for potential program expansion, we filter to those areas that have high geographic disparity scores, high unemployment rates, and are not current ONSE Priority Communities. Filtering in this way shows additional areas in Northeast and Southeast DC that could benefit from joint community and government dialogue about how and what employment-centered programs could be expanded or improved in those communities.

Conclusion

Using the Spatial Equity Data Tool API, we find the census tracts that have the most disproportionately high levels of violence and high levels of unemployment, lie, predominately but not exclusively, in East and Southeast DC. By identifying the locations and demographics of these areas, DC OGVP can consider the deployment of resources to these target areas to bolster outreach and find new areas to serve.

This Python-based case study demonstrates that it is possible to layer the disparity scores created by the SEDT with other key metrics to deepen analysis. If you have identified geographic areas of interest, this case study offers a guide in using SEDT API output for comparison.

We thank the hard-working DC government teams in the Office of Gun Violence Prevention, the Office of Neighborhood Safety and Engagement, and the Office of the Attorney General for their thoughtful and engaging partnership during the development of this case study.