The Systems Approach: Police Brutality in the Context of Race

June 28, 2020  •  1 Comment

In an effort to better educate myself and practice some of the things I'm learning in grad school, I spent some time looking at the data behind a topic at the forefront of our current social landscape. I'm going to try to keep a good chunk of this post purely factual and just presenting my analysis of the data.

The first question(s) any systems analysis requires is a) is this really the root of the problem? and b) is there even really a problem here? Let's start by taking a look at the problem definition as provided by the 'customer' [1].

  • America's justice system disparagingly targets persons of color, resulting in a significant difference between brutality persons of color experience as opposed to whites.

Alright let's get to the good stuff. After a bit of hunting, I found a reputable source of data from the Bureau of Justice Statistics [2]. At this step, it is important for the systems analyst to evaluate the data collection methodology. Part of why I find this data reputable is the detail provided by the statisticians on this very point. That plus the high response rate from a random sample of police encounters plus normalization for non-responses is a great indicator. Onwards!

Now I'm left with over 500 variables for over 60,000 separate cases. At this point it's time to start identifying relevant metrics. My goal is to look for metrics that will identify the extent or existence of a disparity in police responses based on race normalized for population. I'm going to look for race (obviously), use of force, civilian complaints, reason for contact and outcome. You can look at my code below to see which points I felt were relevant and plucked out of the data. I looked at four subsets of the data.

Street Stops

Unfortunately this subset sample size was not as large as I'd hoped. There were 107 encounters with white respondents and 16 with those of color. I'll point out some of the larger disparities I saw in the data. Zero of the black respondents felt the police were stopping them to help them while 20% of white respondents felt the police were helping them. 70% of white respondents felt the police had a valid reason to stop them compared to 43.8% of black respondents. 84% of white respondents felt the stop lasted an appropriate amount of time compared to 56% of black respondents. Even though both categories felt they complied at rates of 40-50%, white respondents felt the officer was considerate to them at a rate three times higher than black respondents. Interestingly, both categories thought 40% of their interactions were fair, however four times as many interactions were helpful to white respondents than black respondents and three times as many black respondents did not trust the police. Black respondents felt the police used excessive force at twice the rate of white respondents.

Traffic Stops

The sample size here is quite a bit larger at 3177 white respondents and 364 black respondents. The disparities are a little less exaggerated here probably as a result of that. Blacks were stopped at the twice the rate of their white peers for what the police claimed to be 'just a record check'. Blacks were also threatened with the use of force at four times the rate of whites and felt that the police used excessive force on them at three times the rate compared to white correspondents. However, black respondents were five times as likely to resist arrest and 3 times as likely to disobey officers.

The only interesting relation out of the police behavior survey is that persons of color are four times as likely to file a complaint against the police officer they interacted with, at a rate of 1.07%. The data in the use of force survey was also rather sporadic.

Clearly there's a problem here that needs to be addressed. I know this is a tricky topic to discuss, I hope I did it well. There's a LOT more I could do with this data, but I'm gonna go sit on my couch and watch TV now. Hope you learned something like I did!

 

Code

import csv
from collections import defaultdict
import time
import json
import pickle

###################################################################################################
# Data Loading
###################################################################################################

"""
# Creating dictionary from data table
start = time.time()
with open('34276-0001-Data.tsv','r') as infile:
    firstline = True
    reader = csv.reader(infile,delimiter='\t')
    cases = []
    for row in reader:
        if firstline:
            keys = [val for val in row]
            myDict=defaultdict(dict)
            firstline = False
        else:
            firstval = True
            for val in row:
                if firstval:
                    case_num = val
                    cases.append(case_num)
                    firstval = False
                    index = 1
                else:
                    myDict[case_num][keys[index]] = val
                    index += 1

end = time.time()
with open('dict.json','w') as f:
    f.write(json.dumps(myDict)) #json works better for dicts
    f.close()
with open('cases.txt','wb') as fp:
    pickle.dump(cases,fp) #pickling list
print('Elapsed time to import datafile and write to file: ' + str(end-start))
"""
#Takes forever to run on the raspberry pi, so ran this on my desktop, dumped json to import on 'development server'
start = time.time()
with open('dict.json') as json_file:
    myDict = json.load(json_file)
with open('cases.txt','rb') as fp:
    cases = pickle.load(fp)
end = time.time()
print('Elapsed time to import files and store as variables: ' + str(end-start))    

###################################################################################################
# Data Mining Function
###################################################################################################
def data_mine(data,cases,data_points,export_file):
# first data_points entry has to be defining characterisitic (i.e. for street stop, VG34 signifies this is a street stop case)
# all other inputs are self-explanatory
    result_dict = defaultdict(dict)
    for i in range(20):
        RACE = i+1 # RACE Codes go from 1-20
        result_dict[str(RACE)]['Total'] = 0 #total cases
        for data_point in data_points:
            result_dict[str(RACE)][data_point] = 0

    for case in cases:
        if data[case][data_points[0]] != '-9': #Case is relevant
            result_dict[data[case]['RACE']]['Total'] += 1
            for data_point in data_points:
                if data[case][data_point] == '1':
                    result_dict[data[case]['RACE']][data_point] += 1

    #write to csv
    with open(export_file,'w') as f:
        f.write('RACE,Total,')
        for data in data_points:
            f.write(data+',')
        f.write('\n')
        for i in range(20):
            RACE = i+1
            f.write(str(RACE)+','+str(result_dict[str(RACE)]['Total'])+',')
            for data in data_points:
                f.write(str(result_dict[str(RACE)][data])+',')
            f.write('\n')
    return result_dict

###################################################################################################
# Street Stops - 2011
###################################################################################################
# Street stop variable groups are groups 3 and 4, with 4 signifiying the outcome
data_points = ['V34','V39','V41','V47','V66','V68','V69','V84','V85',\
               'V93','V95','V98','V100','V104','V118','V120','V146','V148',\
               'V150','V152','V154','V156','V162','V163','V180','V181','V182']
"""
    VG5_6[str(RACE)]['V34']  #reason for stopping
    VG5_6[str(RACE)]['V39']  #suspect you of something
    VG5_6[str(RACE)]['V41']  #match description
    VG5_6[str(RACE)]['V47']  #provide you help
    VG5_6[str(RACE)]['V66']  #valid reason for stopping?
    VG5_6[str(RACE)]['V68']  #police officer white?
    VG5_6[str(RACE)]['V69']  #police officer black?
    VG5_6[str(RACE)]['V84']  #did stop last appropriate amount of time?
    VG5_6[str(RACE)]['V85']  #ticketed?
    VG5_6[str(RACE)]['V93']  #warning?
    VG5_6[str(RACE)]['V95']  #did you comply?
    VG5_6[str(RACE)]['V98']  #considerate officer?
    VG5_6[str(RACE)]['V100'] #was the officer fair?
    VG5_6[str(RACE)]['V104'] #officer was helping?
    VG5_6[str(RACE)]['V118'] #you don't trust the police?
    VG5_6[str(RACE)]['V120'] #police were hassling you?
    VG5_6[str(RACE)]['V146'] #threatened with force?
    VG5_6[str(RACE)]['V148'] #pushed?
    VG5_6[str(RACE)]['V150'] #handcuffed?
    VG5_6[str(RACE)]['V152'] #hit/kicked?
    VG5_6[str(RACE)]['V154'] #pepper sprayed?
    VG5_6[str(RACE)]['V156'] #stun gun?
    VG5_6[str(RACE)]['V162'] #necessary?
    VG5_6[str(RACE)]['V163'] #excessive?
    VG5_6[str(RACE)]['V180'] #injured
    VG5_6[str(RACE)]['V181'] #get medical attention
    VG5_6[str(RACE)]['V182'] #arrested?
"""

VG5_6 = data_mine(myDict,cases,data_points,'street_stops.csv')


###################################################################################################
# Traffic Stops - 2011
###################################################################################################
# Traffic stop variable groups are groups 5 and 6, with 6 signifiying the outcome
data_points = ['V189','V191','V192','V209','V210','V216','V248','V249','V279','V283','V285','V287',\
               'V289','V291','V293','V295','V297','V299','V301','V303','V304','V305','V306','V312',\
               'V322','V333','V324','V333']
"""
    VG5_6[str(RACE)]['V189'] #how many officers? (this point doesnt really matter but tells us if this is a traffic stop case or not)
    VG5_6[str(RACE)]['V191'] #officer white?
    VG5_6[str(RACE)]['V192'] #officer black?
    VG5_6[str(RACE)]['V209'] #appropriate amount of time?
    VG5_6[str(RACE)]['V210'] #given a reason?
    VG5_6[str(RACE)]['V216'] #just a record check?
    VG5_6[str(RACE)]['V248'] #legimate traffic stop?
    VG5_6[str(RACE)]['V249'] #given a ticket?
    VG5_6[str(RACE)]['V279'] #shouting?
    VG5_6[str(RACE)]['V283'] #threaten arrest?
    VG5_6[str(RACE)]['V285'] #threaten ticket?
    VG5_6[str(RACE)]['V287'] #threaten force?
    VG5_6[str(RACE)]['V289'] #pushing/grabbing?
    VG5_6[str(RACE)]['V291'] #handcuff?
    VG5_6[str(RACE)]['V293'] #kick/hit?
    VG5_6[str(RACE)]['V295'] #sprayed?
    VG5_6[str(RACE)]['V297'] #electroshocked?
    VG5_6[str(RACE)]['V299'] #gun pointed?
    VG5_6[str(RACE)]['V301'] #police use other force?
    VG5_6[str(RACE)]['V303'] #aggressive behavior?
    VG5_6[str(RACE)]['V304'] #were these actions necessary?
    VG5_6[str(RACE)]['V305'] #was force excessive?
    VG5_6[str(RACE)]['V306'] #did you disobey officer?
    VG5_6[str(RACE)]['V312'] #resist handcuff/search?
    VG5_6[str(RACE)]['V322'] #injured?
    VG5_6[str(RACE)]['V323'] #required medical attention?
    VG5_6[str(RACE)]['V324'] #arrested?
    VG5_6[str(RACE)]['V333'] #legitimate reason for frisk/search?
"""
VG5_6 = data_mine(myDict,cases,data_points,'traffic_stops.csv')


###################################################################################################
# Police Behavior - 2011
###################################################################################################
# Police Behavior survey is group 8
data_points = ['V347','V348','V350']
"""
    VG8[str(RACE)]['V347'] #appropriate behavior?
    VG8[str(RACE)]['V348'] #filed complaint?
    VG8[str(RACE)]['V350'] #treated respectfully?
"""
VG8 = data_mine(myDict,cases,data_points,'police_behavior.csv')

###################################################################################################
# Use of Force - 2008
###################################################################################################
# Use of force is variable group 11
data_points = ['V357','V365','V367','V369','V371','V373','V375','V378','V379','V381','V383','V385',\
               'V385','V396','V397','V399','V401','V405']
"""
    VG11[str(RACE)]['V357'] #threaten force?
    VG11[str(RACE)]['V365'] #push or grab?
    VG11[str(RACE)]['V367'] #kick or hit?
    VG11[str(RACE)]['V369'] #pepper spray?
    VG11[str(RACE)]['V371'] #electroshock?
    VG11[str(RACE)]['V373'] #point gun?
    VG11[str(RACE)]['V375'] #use other force?
    VG11[str(RACE)]['V378'] #excessive?
    VG11[str(RACE)]['V379'] #injured?
    VG11[str(RACE)]['V381'] #gunshot?
    VG11[str(RACE)]['V383'] #broken bones/teeth knocked out?
    VG11[str(RACE)]['V385'] #internal injury?
    VG11[str(RACE)]['V396'] #arrested?
    VG11[str(RACE)]['V397'] #handcuff?
    VG11[str(RACE)]['V399'] #disobey?
    VG11[str(RACE)]['V401'] #try to get away?
    VG11[str(RACE)]['V405'] #resist arrest?
"""
VG11 = data_mine(myDict,cases,data_points,'use_of_force.csv')




























References

[1] https://blacklivesmatter.com/what-we-believe/

[2] https://www.bjs.gov/index.cfm?ty=dcdetail&iid=251


Comments

Pat R(non-registered)
Hey man this is very neat, thank you for sharing. What are these data composed of? I assume that the information on police behavior is submitted by the person stopped, but then are things like resisting arrest or disobeying the officer also self-reported? Thanks again for putting these thoughts together.
No comments posted.
Loading...


Subscribe

* indicates required
/ ( mm / dd )

Subscribe
RSS
Archive
January February March April May June (7) July (1) August (1) September October November (1) December (1)
January February March April (1) May June (1) July August September (7) October (1) November December
January February March April May June July August September October November December
January (1) February March April May June July August September October November December
January February March April May June July August September October November December
January February March April May June July August September October November December
January February March April May June July August September October November December