[Python, Data Mining, Открытые данные] Black [O]lives Matter: раса, криминал и огонь на поражение в США. Часть 2

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
04-Сен-2020 11:31

В первой части статьи я описал предпосылки для исследования, его цели, допущения, исходные данные и инструменты. Сейчас можно без дальнейших разглагольствований сказать гагаринское...Поехали!Импортируем библиотеки и определяем путь к директории со всеми файлами:
import pandas as pd, numpy as np
# путь к папке с исходными файлами
ROOT_FOLDER = r'c:\_PROG_\Projects\us_crimes'
Гибель от рук закона
Начнем с анализа данных по жертвам полиции. Давайте подгрузим файл из CSV в DataFrame:
# Файл с БД Fatal Encounters (FENC)
FENC_FILE = ROOT_FOLDER + '\\fatal_enc_db.csv'
# грузим в DataFrame
df_fenc = pd.read_csv(FENC_FILE, sep=';', header=0, usecols=["Date (Year)", "Subject's race with imputations", "Cause of death", "Intentional Use of Force (Developing)", "Location of death (state)"])
Заметьте сразу, что мы не грузим все поля из БД, а только необходимые нам для анализа: год, расовая принадлежность (с учетом экспертной оценки), причина смерти (здесь пока не используется, но может понадобиться в дальнейшем), признак намеренного применения силы и штат, в котором имело место событие.Здесь надо пояснить, что такое "экспертная оценка" расовой принадлежности. Дело в том, что официальные источники, откуда FENC собирает данные, не всегда указывают расу жертвы, отсюда получаются пропуски в данных. Для компенсации этих пропусков сообщество привлекает экспертов, оценивающих расу жертвы по другим данным (с определенной погрешностью). Более подробно на эту тему можете почитать на самом сайте Fatal Encounters или загрузив исходный Excel файл(во втором листе).Переименуем столбцы для удобства и очистим строки с пропущенными данными:
df_fenc.columns = ['Race', 'State', 'Cause', 'UOF', 'Year']
df_fenc.dropna(inplace=True)
Теперь нам надо унифицировать наименования расовой принадлежности для того, чтобы в дальнейшем сопоставлять эти данные с данными по преступлениям и численности населения. Классификация рас в этих источниках немного разная. БД FENC, в частности, выделяет латиноамериканцев (Hispanic/Latino), азиатов и уроженцев тихоокеанских территорий (Asian/Pacific Islander) и среднеазиатов (Middle Eastern). Нас же интересуют только белые и черные. Поэтому сделаем укрупнение:
df_fenc = df_fenc.replace({'Race': {'European-American/White': 'White', 'African-American/Black': 'Black',
                          'Hispanic/Latino': 'White', 'Native American/Alaskan': 'American Indian',
                          'Asian/Pacific Islander': 'Asian', 'Middle Eastern': 'Asian',
                          'NA': 'Unknown', 'Race unspecified': 'Unknown'}}, value=None)
Оставляем только данные по белым (теперь с учетом латино) и черным:
df_fenc = df_fenc.loc[df_fenc['Race'].isin(['White', 'Black'])]
Зачем нам поле "UOF" (намеренное использование силы)? Для исследования мы хотим оставить только случаи, когда полиция (или иные правоохранительные органы) намеренно применяли силу против человека. Мы опускаем случаи, когда человек совершил самоубийство (например, в результате осады полицией) или погиб в результате ДТП, преследуемый полицейскими. Это допущение сделано по двум причинам: 1) обстоятельства гибели по косвенным причинам часто не позволяют провести прямую причинно-следственную связь между действиями правоохранительных органов и смертью (пример: полицейский держит на мушке человека, который затем умирает от сердечного приступа; другой пример: при задержании преступник пускает себе пулю в лоб); 2) при рассмотрении действий властей расценивается именно применение силы; так, например, будущая официальная БД по применению силы (которую я упомянул в предыдущей статье) будет содержать именно данные, отражающая намеренное применение смертельной силы против граждан. Итак, оставляем только эти данные:
df_fenc = df_fenc.loc[df_fenc['UOF'].isin(['Deadly force', 'Intentional use of force'])]
Для удобства добавим полные названия штатов. Для этого я приготовил отдельный CSV, который мы и подгрузим в наш датасет:
df_state_names = pd.read_csv(ROOT_FOLDER + '\\us_states.csv', sep=';', header=0)
df_fenc = df_fenc.merge(df_state_names, how='inner', left_on='State', right_on='state_abbr')
Отобразим начальные строки командой df_fenc.head(), чтобы получить представление о датасете:RaceStateCauseUOFYearstate_namestate_abbr0BlackGAGunshotDeadly force2000GeorgiaGA1BlackGAGunshotDeadly force2000GeorgiaGA2BlackGAGunshotDeadly force2000GeorgiaGA3BlackGAGunshotDeadly force2000GeorgiaGA4BlackGAGunshotDeadly force2000GeorgiaGAНам не нужно разбирать отдельные случаи гибели, давайте агрегируем данные по годам и расовой принадлежности:
# группируем по году и расе
ds_fenc_agg = df_fenc.groupby(['Year', 'Race']).count()['Cause']
df_fenc_agg = ds_fenc_agg.unstack(level=1)
# конвертируем численные данные в UINT16 для экономии
df_fenc_agg = df_fenc_agg.astype('uint16')
В итоге получили таблицу с 2 столбцами: White (количество белых жертв) и Black (количество черных жертв), индексированную по годам (с 2000 по 2020). Давайте взглянем на эти данные в виде графика:
# белые и черные жертвы полицейских по годам (кол-во гибелей)
plt = df_fenc_agg.plot(xticks=df_fenc_agg.index)
plt.set_xticklabels(df_fenc_agg.index, rotation='vertical')
plt

Промежуточный вывод:
В количественном (абсолютном) выражении белых жертв больше, чем черных.
Разница между этими данными составляет в среднем 2.4 раза. Напрашивается справедливое заключение о том, что это связано с разницей в численности белых и черных. Что же, давайте посмотрим теперь на удельные показатели. Подгрузим данные по численности населения (по расам):
# файл CSV с данными по населению (1991 - 2018)
POP_FILE = ROOT_FOLDER + '\\us_pop_1991-2018.csv'
df_pop = pd.read_csv(POP_FILE, index_col=0, dtype='int64')
Добавим эти данные в наш датасет:
# выбираем только данные по числ-ти белых и черных за 2000 - 2018 гг.
df_pop = df_pop.loc[2000:2018, ['White_pop', 'Black_pop']]
# объединяем датафреймы, выкидываем строки с пропусками
df_fenc_agg = df_fenc_agg.join(df_pop)
df_fenc_agg.dropna(inplace=True)
# конвертируем данные по численности в целочисленный тип
df_fenc_agg = df_fenc_agg.astype({'White_pop': 'uint32', 'Black_pop': 'uint32'})
ОК. Осталось создать 2 столбца с удельными значениями, разделив количество жертв на численность и умножив на миллион (количество жертв на 1 млн. человек):
df_fenc_agg['White_promln'] = df_fenc_agg['White'] * 1e6 / df_fenc_agg['White_pop']
df_fenc_agg['Black_promln'] = df_fenc_agg['Black'] * 1e6 / df_fenc_agg['Black_pop']
Смотрим, что получилось:BlackWhiteWhite_popBlack_popWhite_promlnBlack_promlnYear2000148291218756353354104361.3302474.1795592001158353219843871357587831.6056854.4184952002161363220931389361071301.6430444.4589532003179388222018906364554761.7475994.9100992004157435223106424368038231.9497424.2658612005181452224193942371521702.0161124.8718552006212460225281460375005172.0418905.6532552007219449226368978378488641.9834875.7861712008213442227456495381972111.9432295.5763232009249478228544013385455582.0915016.4598882010219506229397472388746252.2057785.6334952011290577230838975391895282.4995787.3999362012302632231992377396231382.7242277.6218092013310693232969901399193712.9746337.7656532014264704233963128403790663.0090216.5380412015272729234940100406952773.1029196.6838222016269723234644039408933693.0812636.5780842017265743235507457413934913.1548896.4019732018265775236173020416177643.2814936.367473Последние 2 столбца - наши удельные показатели на миллион человек по каждой из двух рас. Пора посмотреть на графике:
plt = df_fenc_agg.loc[:, ['White_promln', 'Black_promln']].plot(xticks=df_fenc_agg.index)
plt.set_xticklabels(df_fenc_agg.index, rotation='vertical')
plt

Также выведем основную статистику по этим данным:
df_fenc_agg.loc[:, ['White_promln', 'Black_promln']].describe()
White_promlnBlack_promlncount (количество)19.00000019.000000mean (среднее арифм.)2.3361235.872145std (станд. отклонение)0.6151331.133677min (мин. значение)1.3302474.17955925%1.9464854.89097750%2.0915015.78617175%2.9918276.558062max (макс. значение)3.2814937.765653Промежуточные выводы:
1. В среднем от рук полиции погибает 5.9 на 1 млн. черных и 2.3 на 1 млн. белых (черных в 2.6 раз больше).2. Разброс (отклонение) в данных по черным жертвам в 1.8 раз выше, чем в данных по белым жертвам. (На графике видно, что кривая по белым жертвам гораздо более плавная, без резких скачков.)3. Максимальное количество жертв среди черных - в 2013 г. (7.7 на миллион); максимальное количество жертв среди белых - в 2018 г. (3.3 на миллион).4. Жертвы среди белых монотонно растут (в среднем на 0.1 - 0.2 в год), в то время как жертвы среди черных вернулись на уровень 2009 г. после пика в 2011 - 2013 гг.
Итак, на первый поставленный вопрос мы ответили: - Можно ли сказать, что полицейские убивают черных чаще, чем белых?- Да, это верный вывод. От рук закона черных гибнет в среднем в 2.6 раз больше, чем белых.Держа в голове эти промежуточные выводы, идем дальше - посмотрим данные по преступлениям, чтобы понять, как они соотносятся с расовой принадлежностью и жертвами от рук стражей закона.Данные по преступлениям
Загружаем наш CSV по преступлениям:
CRIMES_FILE = ROOT_FOLDER + '\\culprits_victims.csv'
df_crimes = pd.read_csv(CRIMES_FILE, sep=';', header=0, index_col=0, usecols=['Year', 'Offense', 'Offender/Victim', 'White', 'White pro capita', 'Black', 'Black pro capita'])
Здесь опять-таки используем только необходимые столбцы: год, вид преступления, классификатор и данные по количеству преступлений, совершенных черными и белыми (абсолютные - "White", "Black" и удельные на человека - "White pro capita", "Black pro capita").Взглянем на данные (`df_crimes.head()`):OffenseOffender/VictimBlackWhiteBlack pro capitaWhite pro capitaYear1991All OffensesOffender4905981.518188e-052.861673e-061991All OffensesOffender441.239337e-071.914160e-081991All OffensesOffender5081221.573958e-055.838195e-071991All OffensesOffender1551764.802432e-068.422314e-071991All OffensesOffender13194.027846e-079.092270e-08Нам пока не нужны данные по жертвам преступлений. Убираем лишние данные и столбцы:
# оставляем только преступников (убираем жертв)
df_crimes1 = df_crimes.loc[df_crimes['Offender/Victim'] == 'Offender']
# берем исследуемый период (2000-2018) и удаляем лишние столбцы
df_crimes1 = df_crimes1.loc[2000:2018, ['Offense', 'White', 'White pro capita', 'Black', 'Black pro capita']]
Получили такой датасет (1295 строк * 5 столбцов):OffenseWhiteWhite pro capitaBlackBlack pro capitaYear2000All Offenses6790.0000036510.0000182000All Offenses114580.000052301990.0008532000All Offenses44390.00002031880.0000902000All Offenses104810.00004851530.0001462000All Offenses7460.000003630.000002..................2018Larceny Theft Offenses19610.00000816690.0000402018Larceny Theft Offenses486160.000206300480.0007222018Drugs Narcotic Offenses5559740.0023542233980.0053682018Drugs Narcotic Offenses3050520.001292637850.0015332018Weapon Law Violation700340.000297583530.001402Теперь нам надо превратить удельные показатели на 1 человека в удельные на 1 миллион (так как именно эти данные используются во всем исследовании). Для этого просто умножаем на миллион соответствующие столбцы:
df_crimes1['White_promln'] = df_crimes1['White pro capita'] * 1e6
df_crimes1['Black_promln'] = df_crimes1['Black pro capita'] * 1e6
Чтобы увидеть целую картину, как соотносится количество преступлений между белыми и черными по видам преступлений (в абсолютном выражении), просуммируем годовые наблюдения:
df_crimes_agg = df_crimes1.groupby(['Offense']).sum().loc[:, ['White', 'Black']]
WhiteBlackOffenseAll Offenses4459479522323144Assault Offenses124758307462272Drugs Narcotic Offenses96245963453140Larceny Theft Offenses95639174202235Murder And Nonnegligent Manslaughter2891339617Sex Offenses833088319366Weapon Law Violation829485678861Или в виде графика:
df_crimes_agg.plot.barh()

Итак, видим, что:
  • В количественном отношении нападения, наркотики, воровство и "все преступления" сильно превалируют над преступлениями, связанными с убийством, оружием и сексом
  • В абсолютных значениях белые совершают больше преступлений, чем черные (ровно в 2 раза для категории "все преступления")
Опять понимаем, что без информации о численности никакие выводы о "криминальности" рас не сделаешь. Соответственно, посмотрим на удельные показатели:
df_crimes_agg1 = df_crimes1.groupby(['Offense']).sum().loc[:, ['White_promln', 'Black_promln']]
White_promlnBlack_promlnOffenseAll Offenses194522.307758574905.952459Assault Offenses54513.398833192454.602875Drugs Narcotic Offenses41845.75886988575.523095Larceny Theft Offenses41697.303725108189.184125Murder And Nonnegligent Manslaughter125.9430071016.403706Sex Offenses3633.7770358225.144985Weapon Law Violation3612.67140217389.163849И на графике:
df_crimes_agg1.plot.barh()

Здесь уже совсем иная картина. По всем видам преступлений (из анализируемых) черные совершают больше, чем белые. По категории "все преступления" эта разница составляет почти 3 раза.Давайте теперь оставим только категорию "все преступления" (All Offenses) как наиболее представительную, только удельные показатели по преступлениям (на миллион человек) и сгруппируем данные по годам (так как в исходных данных на каждый год может быть несколько записей - по количеству служб, предоставивших данные).
# оставляем только 'All Offenses' = все преступления
df_crimes1 = df_crimes1.loc[df_crimes1['Offense'] == 'All Offenses']
# чтобы использовать другую выборку, можем, например, оставить нападения и убийства:
#df_crimes1 = df_crimes1.loc[df_crimes1['Offense'].str.contains('Assault|Murder')]
# убираем абсолютные значения и агрегируем по годам
df_crimes1 = df_crimes1.groupby(level=0).sum().loc[:, ['White_promln', 'Black_promln']]
Полученный датасет:White_promlnBlack_promlnYear20006115.05897617697.40988220016829.70142920431.70764520027282.33324920972.83832920037857.69118222218.96650020048826.57686326308.81579920059713.82625530616.569637200610252.89431333189.382429200710566.52736234100.495064200810580.52002434052.276749200910889.26359233954.651792201010977.01721833884.236826201111035.34617632946.454471201211562.83682533150.706035201311211.11349132207.571607201411227.35459431517.346141201511564.78608831764.865490201612193.02656233186.064958201712656.26166634900.390499201813180.17189337805.202605Посмотрим на графике:
plt = df_crimes1.plot(xticks=df_crimes1.index)
plt.set_xticklabels(df_fenc_agg.index, rotation='vertical')
plt

Промежуточные выводы:
1. Белые совершают в 2 раза больше преступлений, чем черные, в абсолютном выражении, но в 3 раза меньше в относительном выражении (на миллион представителей своей расы).2. Преступность среди белых относительно монотонно растет на протяжении всего периода (выросла в 2 раза за 18 лет). Преступность среди черных также растет, но скачкообразно: с 2001 по 2006 г. резкий рост, с 2007 по 2016 она даже убывала, с 2017 года опять резкий рост. За весь период преступность среди черных выросла также в 2 раза (аналогично белым).3. Если не принимать во внимание спад среди черной преступности в 2007-2016 гг., преступность среди черных растет более быстрыми темпами, чем среди белых.
Итак, мы ответили на второй вопрос:- Представители какой расы статистически чаще совершают преступления?- Черные статистически совершают преступления в 3 раза чаще белых.Криминальность и гибель от рук полицииТеперь мы подошли к самому важному: необходимо ответить на третий поставленный вопрос, а именно "Можно ли сказать, что полиция стреляет насмерть пропорционально количеству совершаемых преступлений?"То есть надо как-то проследить корреляцию между двумя нашими наборами данных - данных по жертвам полиции и данных по преступлениям. Начнем с того, что объединим эти два датасета в один:
# объединяем датасеты
df_uof_crimes = df_fenc_agg.join(df_crimes1, lsuffix='_uof', rsuffix='_cr')
# удаляем лишние столбцы (абс. показатели по жертвам)
df_uof_crimes = df_uof_crimes.loc[:, 'White_pop':'Black_promln_cr']
Что получили?White_popBlack_popWhite_promln_uofBlack_promln_uofWhite_promln_crBlack_promln_crYear2000218756353354104361.3302474.1795596115.05897617697.4098822001219843871357587831.6056854.4184956829.70142920431.7076452002220931389361071301.6430444.4589537282.33324920972.8383292003222018906364554761.7475994.9100997857.69118222218.9665002004223106424368038231.9497424.2658618826.57686326308.8157992005224193942371521702.0161124.8718559713.82625530616.5696372006225281460375005172.0418905.65325510252.89431333189.3824292007226368978378488641.9834875.78617110566.52736234100.4950642008227456495381972111.9432295.57632310580.52002434052.2767492009228544013385455582.0915016.45988810889.26359233954.6517922010229397472388746252.2057785.63349510977.01721833884.2368262011230838975391895282.4995787.39993611035.34617632946.4544712012231992377396231382.7242277.62180911562.83682533150.7060352013232969901399193712.9746337.76565311211.11349132207.5716072014233963128403790663.0090216.53804111227.35459431517.3461412015234940100406952773.1029196.68382211564.78608831764.8654902016234644039408933693.0812636.57808412193.02656233186.0649582017235507457413934913.1548896.40197312656.26166634900.3904992018236173020416177643.2814936.36747313180.17189337805.202605Давайте вспомним, что хранится в каждом поле:
  • White_pop - численность белых
  • Black_pop - численность черных
  • White promln_uof - количество жертв полиции среди белых (на 1 млн)
  • Black promln_uof - количество жертв полиции среди черных (на 1 млн)
  • White promln_cr - количество преступлений, совершенных белыми (на 1 млн)
  • Black promln_cr - количество преступлений, совершенных черными (на 1 млн)
Наверное, можно было бы не полениться и дать этим столбцам русские названия... Но я надеюсь, читатели меня простят :)Взглянем, как соотносятся графики преступлений и жертв полиции для каждой расы. Начнем с белых - в шахматном порядке :)
plt = df_uof_crimes['White_promln_cr'].plot(xticks=df_uof_crimes.index, legend=True)
df_uof_crimes['White_promln_uof'].plot(xticks=df_uof_crimes.index, legend=True, secondary_y=True, style='g')
plt.set_xticklabels(df_uof_crimes.index, rotation='vertical')
plt

То же самое на диаграмме рассеяния:
Отметим мимоходом, что определенная корреляция есть. ОК, теперь то же для черных:
plt = df_uof_crimes['Black_promln_cr'].plot(xticks=df_uof_crimes.index, legend=True)
df_uof_crimes['Black_promln_uof'].plot(xticks=df_uof_crimes.index, legend=True, secondary_y=True, style='g')
plt.set_xticklabels(df_uof_crimes.index, rotation='vertical')
plt

И скаттерплот:
Здесь все намного хуже: тренды явно "пляшут", хотя общая тенденция все равно прослеживается: пропорция здесь явно прямая, хотя и нелинейная.Давайте воспользуемся методами матстатистики для определения величины этих корреляций, построив корреляционную матрицу на основе коэффициента Пирсона:
df_corr = df_uof_crimes.loc[:, ['White_promln_cr', 'White_promln_uof', 'Black_promln_cr', 'Black_promln_uof']].corr(method='pearson')
df_corr.style.background_gradient(cmap='PuBu')
Получаем такую картинку:White_promln_crWhite_promln_uofBlack_promln_crBlack_promln_uofWhite_promln_cr1.0000000.8854700.9499090.802529White_promln_uof0.8854701.0000000.7100520.795486Black_promln_cr0.9499090.7100521.0000000.722170Black_promln_uof0.8025290.7954860.7221701.000000Коэффициенты корреляции для обеих рас выделены жирным: для белых = 0.885, для черных = 0.722. Таким образом, положительная корреляция между гибелью от полиции и преступностью прослеживается и для белых, и для черных, но для белых она гораздо выше (статистически значима), в то время как для черных она близка к статистической незначимости. Последний результат, конечно, связан с большей неоднородностью данных как по жертвам полиции, так и по преступлениям среди черных.Напоследок для этой статьи попробуем выяснить, какова вероятность белых и черных преступников быть застреленным полицией. Прямых способом это выяснить у нас нет (нет данных по тому, кто из погибших от рук полиции был зарегистрирован как преступник, а кто как невинная жертва). Поэтому пойдем простым путем: разделим удельное количество жертв полиции на удельное количество преступлений по каждой расовой группе (и умножим на 100, чтобы выразить в %):
# агрегированные значения (по годам)
df_uof_crimes_agg = df_uof_crimes.loc[:, ['White_promln_cr', 'White_promln_uof', 'Black_promln_cr', 'Black_promln_uof']].agg(['mean', 'sum', 'min', 'max'])
# "вероятность" преступника быть застреленным
df_uof_crimes_agg['White_uof_cr'] = df_uof_crimes_agg['White_promln_uof'] * 100. / df_uof_crimes_agg['White_promln_cr']
df_uof_crimes_agg['Black_uof_cr'] = df_uof_crimes_agg['Black_promln_uof'] * 100. / df_uof_crimes_agg['Black_promln_cr']
Получаем такие данные:White_promln_crWhite_promln_uofBlack_promln_crBlack_promln_uofWhite_uof_crBlack_uof_crmean10238.0161982.33612330258.2080245.8721450.0228180.019407sum194522.30775844.386338574905.952459111.5707470.0228180.019407min6115.0589761.33024717697.4098824.1795590.0217540.023617max13180.1718933.28149337805.2026057.7656530.0248970.020541Отобразим полученные значения в виде столбчатой диаграммы:
plt = df_uof_crimes_agg.loc['mean', ['White_uof_cr', 'Black_uof_cr']].plot.bar()

На диаграмме видно, что вероятность белого преступника быть застреленным несколько выше, чем черного преступника. Конечно, этот анализ весьма условный, но все же дает какое-то представление.Промежуточные выводы:
1. Гибель от рук полиции связана с криминальностью (количеством совершаемых преступлений). При этом эта корреляция неоднородна по расам: для белых она близка к идеальной, для черных далека от идеальной.2. При рассмотрении совмещенных диаграмм гибели от полиции и преступности видно, что фатальные встречи с полицией растут "в ответ" на рост преступности, с лагом в несколько лет (особенно видно по данным среди черных). Это согласуется с логическим предположением о том, что власти "отвечают" на преступность (больше преступлений -> больше безнаказанности -> больше стычек с представителями закона -> больше смертельных исходов).3. Белые преступники немного чаще встречают смерть от рук полиции, чем черные. Однако эта разница почти несущественна.
Итак, ответ на третий вопрос:- Можно ли сказать, что полиция стреляет насмерть пропорционально количеству совершаемых преступлений?- Да, такая корреляция наблюдается, хотя она неоднородна по расам: для белых почти идеальная, для черных - почти неидеальная.В следующей части статьи посмотрим на географическое распределение анализируемых данных по штатам США.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_python, #_data_mining, #_otkrytye_dannye (Открытые данные), #_python, #_pandas, #_data_science, #_black_lives_matter, #_open_source, #_rest, #_api, #_big_data, #_politsija (полиция), #_ssha (сша), #_python, #_data_mining, #_otkrytye_dannye (
Открытые данные
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 16-Май 22:54
Часовой пояс: UTC + 5