20 марта The Guardian и Архив насилия с применением огнестрельного оружия (GVA) опубликовали новый набор общенациональных данных за 2015 год, который отображает убийства с применением огнестрельного оружия на микроуровне — вплоть до местных переписных участков. Вы можете использовать эти данные для анализа того, как убийства с применением огнестрельного оружия группируются в районах вашего города или штата».

The Guardian и GVA заслуживают похвалы за то, что они сделали эти данные доступными для общественности. В этой статье мы рассмотрим некоторые инструменты, разработанные сообществами программного обеспечения с открытым исходным кодом, которые можно использовать для анализа этих данных. Но мы также увидим, что при использовании общедоступных данных мы должны с самого начала устранять несоответствия и несовершенства в этих данных.

В статье Guardian, указанной выше, вы можете найти ссылки на пять файлов со следующими именами:

  • guardian-gva-shr-data-dictionary.csv
  • gva_release_2015_grouped_by_city_and_ranked.csv
  • gva_release_2015_grouped_by_tract.csv
  • gva_release_2015_raw_incidents.csv
  • UCR-1985-2015.csv

Тот факт, что файлы отображаются в формате CSV (значения, разделенные запятыми), означает, что их можно открыть в программах для работы с электронными таблицами, таких как Microsoft Excel или LibreOffice Calc. Но это также означает, что файлы могут обрабатываться языками динамического программирования, такими как Perl 5.

В дальнейшем мы будем предполагать, что у вас есть доступ к программным инструментам, обычно используемым в среде программирования Unix. У вас есть такая среда в операционных системах Linux или FreeBSD или — под капотом — в Mac OS X (Darwin). (Подобные инструменты, вероятно, существуют в современных средах Windows, но у меня нет личного опыта работы с ними.) В частности, мы предполагаем, что вы немного знакомы с Perl 5 и знаете, как устанавливать расширения. на Perl из Комплексной сети архивов Perl (CPAN). Мы также предполагаем, что вы немного знакомы с GitHub.

Зачем нам переводить данные CSV в структуру данных Perl?

Вы можете использовать программу электронных таблиц для вычисления новых столбцов в данных Guardian/GVA. Например, в файле gva_release_2015_grouped_by_tract.csv указано количество случаев насилия с применением огнестрельного оружия и количество погибших в результате таких инцидентов для каждого района переписи. Чтобы определить коэффициент таких происшествий или смертельных исходов, мы можем захотеть вычислить столбец, в котором содержится количество происшествий на 100 000 населения. Мы, безусловно, могли бы сделать это в программе для работы с электронными таблицами.

Однако, если бы мы хотели объединить данные из двух отдельных файлов CSV или если бы мы хотели объединить некоторые данные Guardian/GVA с данными из другого источника, нам бы понадобились средства искажения данных, выходящие за рамки электронной таблицы. мог предложить. Perl — идеальный выбор для такой обработки данных.

Какие файлы мы скачали?

Вы можете загрузить файлы, просто щелкнув правой кнопкой мыши ссылку на странице Guardian, указанную выше. (В этой статье мы будем обсуждать эти файлы в том состоянии, в котором их загрузил автор 21 марта 2017 года. Вполне возможно, что содержимое этих файлов может измениться при внесении небольших исправлений или обновлений. Поэтому мы храните эти файлы в том виде, в каком они появились 21 марта, в нашем репозитории GitHub.)

Во время загрузки файлы будут перемещаться в любой каталог в вашей системе, который вы указали для этой цели в своем браузере. В качестве альтернативы вы можете открыть терминальное приложение вашей системы и ввести команды в командной строке ('$' в приведенных ниже примерах), чтобы создать дерево каталогов специально для получения этих файлов.

$ mkdir -p gva/inputs
$ cd gva/inputs
$ wget https://interactive.guim.co.uk/2017/feb/09/gva-data/gva_release_2015_raw_incidents.csv \
https://interactive.guim.co.uk/2017/feb/09/gva-data/gva_release_2015_grouped_by_city_and_ranked.csv \
https://interactive.guim.co.uk/2017/feb/09/gva-data/gva_release_2015_grouped_by_tract.csv \
https://interactive.guim.co.uk/2017/feb/09/gva-data/UCR-1985-2015.csv \
https://uploads.guim.co.uk/2017/03/20/guardian-gva-shr-data-dictionary.csv \
https://interactive.guim.co.uk/2017/feb/09/gva-data/UCR-1985-2015.csv

Вот как будет выглядеть вывод wget в вашем терминале:

Чтобы увидеть список всех файлов, вы можете использовать команду ls -l. Его вывод будет выглядеть следующим образом:

Чтобы получить представление о том, как устроен каждый из этих файлов, мы можем использовать утилиту file внутри небольшого вызова оболочки в командной строке.

$ for f in `ls *.csv`; do file $f; done

Переведя это на английский язык, мы получим: Для каждого файла CSV, найденного в этом каталоге, вызовите утилиту file для файла. Вывод утилиты file поступает на стандартный вывод (STDOUT)», т. е. на ваш терминал. Там это будет выглядеть так:

В этот момент мы знаем, что у нас есть проблема. Файлы имеют разные разделители строк. Один заканчивается терминаторами «CRLF» (возврат каретки/перевод строки), обычно встречающимися в средах DOS и Windows. Три заканчиваются терминаторами «CR», встречающимися в операционных системах Macintosh до OS X. В одном из перечисленных файлов нет упоминания о конкретных разделителях строк, что предполагает наличие разделителей строк «LF», типичных для сред программирования Unix.

Эти проблемы не являются серьезными. В основном они влияют на то, как выбранный вами текстовый редактор будет отображать файлы после открытия. На этом этапе вы можете преобразовать все файлы в стандартный формат.

$ ls *.csv | \
    xargs perl -pi'.orig' -e 's{(\015?\012$|\015)}{\012}g'

В переводе на английский это гласит: Получите каждый файл CSV в этом каталоге и обработайте его с помощью однострочной программы Perl, которая (а) создает резервную копию исходного файла с расширением .orig; и (b) перезаписывает файл со всеми разделителями строк, преобразованными в стандарт Unix 'LF'. Вывод команды ls становится выводом (или передается в) команды xargs. Команда xargs, в свою очередь, говорит: Выполните сразу следующую команду — однострочную команду perl — для каждого из файлов, которые были переданы в xargs.

Если бы мы перечислили файлы в нашем каталоге inputs/, мы бы получили следующее:

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

Как нам преобразовать файл CSV в структуру данных Perl?

Настоящим убойным приложением для языка программирования Perl является упомянутый выше архив общедоступных библиотек и расширение, известное как CPAN. Библиотеки CSV обычно имеют в своих именах Text-CSV. Возможно, наиболее часто используется просто Text-CSV; мы будем использовать модуль Text::CSV из этого дистрибутива.

01-load.pl

Мы начнем с написания программы на Perl 5, которая, учитывая базовое имя одного из файлов данных, предоставленных Guardian/GVA, считывает данные этого файла в массив массивов: одна запись для каждой строки в CSV-файле. (Каждую из следующих программ вы можете скачать из нашего репозитория GitHub.)

Чтобы запустить эту и последующие программы, установите расширения Text-CSV и Data-Dump из CPAN. Прочитав код каждой программы, вы можете проверить свое понимание кода, пройдя тест по этой программе, размещенный на GitHub.

Первым файлом, который мы попытаемся обработать, будет UCR-1985-2015.csv. Поскольку 01-load.pl выводит весь вывод в STDOUT, мы вызовем программу с именем файла, а затем перенаправим STDOUT в файл, который мы назовем out1.

$ perl 01-load.pl UCR-1985-2015.csv > out1

out1 содержит следующее:

02-load.pl

Не была бы наша структура данных более полезной, если бы у каждой записи (кроме заголовка) было имя, по которому мы могли бы обращаться к ней? И не была бы наша структура данных еще более полезной, если бы в каждой записи мы помечали каждое поле именем столбца?

Да, обе функции были бы полезны. Для этого мы хотим преобразовать данные CSV в другую структуру данных Perl: многоуровневый хэш или хэш хэшей. Мы делаем это в нашей следующей версии программы.

В 02-load.pl мы предполагаем, что можем использовать значение первого столбца в каждой строке — столбец «Агентство» из строки заголовка — в качестве имени (или key или уникальный идентификатор) для каждой строки.

Мы вызываем эту программу и перенаправляем ее STDOUT в файл с именем out2.

$ perl 02-load.pl UCR-1985-2015.csv > out2

out2 содержит следующее:

Но подождите минутку! 01-load.pl сообщил нам, что у нас есть:

291 rows in UCR-1985-2015.csv (excluding header row)

Но 02-load.pl сообщил нам другое:

280 rows extracted from UCR-1985-2015.csv

Как мы можем объяснить это несоответствие?

03-load.pl

Когда мы построили хэш из данных CSV с ключом «Агентство», мы получили на 11 элементов меньше, чем ожидалось. Возможно, в файле CSV есть более одной строки с одинаковым значением «Агентство». Другими словами, возможно, ключи не являются действительно уникальными! Давайте посмотрим, так ли это.

В 03-load.pl мы повторно считываем данные CSV, но теперь с целью создания небольшой таблицы, которая позволяет нам искать те значения для «Агентство», которые появляются несколько раз в CSV-данные.

С этого момента мы собираемся жестко закодировать базовое имя файла, который мы обрабатываем, непосредственно в программе Perl. Мы делаем это, потому что наша программа теперь сосредоточена на выявлении и исправлении грязных данных в одном конкретном CSV-файле, а не на простой загрузке содержимого любого заданного CSV-файла в массив Perl или хэш. Таким образом, мы можем вызвать программу как:

$ perl 03-load.pl > out3

out3 содержит следующее:

Этот отчет подтверждает нашу гипотезу — в основном. Например, вполне возможно иметь одну запись для полицейского управления Канзас-Сити, Миссури, и другую запись для полицейского управления Канзас-Сити, Канзас. Самая известная Пеория находится в Иллинойсе, но есть еще одна в Аризоне!

Единственным очевидным звеном в этих данных является Департамент полиции Гонолулу. Конечно, не может быть двух городов в Соединенных Штатах, каждый из которых называется Гонолулу!

04-load.pl

В следующей версии нашей программы, 04-load.pl, мы допускаем возможность того, что заданная строка может быть "Агентством" более чем в одном городе. Мы распечатаем название агентства и соответствующий город и штат, но только там, где есть две строки с одинаковым названием агентства.

Это вызывается как:

$ perl 04-load.pl > out4

out4 содержит следующее:

Похоже, что у нас действительно есть две записи для полицейского управления Гонолулу, Гавайи. Затем мы должны задаться вопросом: являются ли эти две записи точными копиями друг друга или в них есть несколько столбцов с разными данными?

На данный момент самым простым решением является использование обычной утилиты Unix grep для просмотра строк Гонолулу во входном файле.

$ grep -n Honolulu inputs/UCR-1985-2015.csv

Очевидно, что данные в двух записях, начиная с 31-го поля («2011_raw_murder_num»), различаются. В идеале мы должны сообщить об этом несоответствии поставщику данных. А пока, ради целесообразности, мы просто будем иметь вторую запись, перезаписывающую первую.

05-load.pl

05-load.pl представляет окончательную версию программы, которую мы будем использовать для анализа файла UCR-1985-2015.csv. В этой программе мы создадим отдельную структуру данных Perl для хранения одной записи — первой записи Гонолулу — которую мы собираемся выбросить из нашего набора данных. Затем мы создадим очень простой отчет, в котором перечислены все агентства в порядке убывания уровня убийств в 2014 году (на 100 000 населения) в их городах.

Это вызывается как:

$ perl 05-load.pl > out5

out5 содержит следующее:

Далее в out5 находится таблица, в которой агентства перечислены в порядке убывания количества убийств в 2014 году:

Мы знаем, что в некоторых городах больше насилия с применением огнестрельного оружия, чем в других, поэтому мы не удивимся городам с самым высоким рейтингом в этом списке. Но действительно ли возможно, чтобы, скажем, в Уичито, штат Канзас, целый год не было совершено ни одного убийства с применением огнестрельного оружия?

Нет, это неправдоподобно. Если мы посмотрим на наш отчет более внимательно, мы увидим, что есть много крупных городов, где уровень убийств в 2014 году был указан как нулевой. Вот некоторые из них:

Можно только сделать вывод, что либо некоторые полицейские управления не сообщили свои данные о насилии с применением огнестрельного оружия за 2014 год в ФБР (источник данных в UCR-1985-2015.csv), либо данные между ФБР и Guardian /GVA произошла ошибка табуляции.

Чему мы научились?

  1. Мы узнали, как использовать общие служебные программы с открытым исходным кодом для определения характеристик общедоступных файлов данных. К этим программам относятся: file; грэп; лс; перл; получить; xargs.
  2. Мы научились писать программы на Perl 5, которые с помощью расширений Perl, доступных на CPAN, позволяют нам преобразовывать данные CSV в структуры данных, которые затем можно использовать для изучения новых взаимосвязей между данными.
  3. Мы узнали, что способ структурирования общедоступных данных в файлах не обязательно оптимален. Возможно, нам придется создать собственные уникальные идентификаторы для каждой записи. Возможно, нам придется обнаруживать частичное или полное дублирование записей.
  4. Мы узнали, что неполные записи в общедоступных данных могут исказить наше понимание социальных реалий, которые, как утверждается, описывают данные.

Так что слава Guardian и GVA за то, что они сделали эти данные доступными, но предостережение относительно пределов полезности данных как таковых.

Следующие шаги

  1. Вам рекомендуется написать программы на Perl, подобные представленным в этой статье, для анализа данных CSV в других файлах, предоставленных Guardian и GVA.
  2. Вам предлагается пройти тесты, доступные на GitHub, соответствующие каждой из программ Perl, представленных здесь. Если вы хотите получить ответ от автора этой статьи, сохраните результаты теста в текстовом формате и прикрепите их к электронному письму, отправленному jkeenan в cpan dot org.
  3. Вам рекомендуется использовать общедоступные данные для понимания социальных и политических проблем, но вы должны осознавать возможность ограничений и расхождений в этих данных.