/
Author: Сьюзен Макгрегор
Tags: языки программирования компьютерные технологии программирование информатика язык программирования python
ISBN: 978-5-9775-1846-8
Year: 2024
Text
ОРЕ1ЫУв
^Ну®
Обработка
данных на Ру I Коп.
Оата УУгапдНпд
и Эата ОиаНту
Начните работу с чтения, очистки
и анализа данных
Материалы
на шимиЬкиги
Сьюзен Макгрегор
Ргас1ка1 Ру11юп Оа1а МгапдПпд
апс! Оа1а ОиаНГу
8и$ап Е. МсСге^ог
Веушд • Во5(оп • ЕагпЬат • 5еЬа$1оро1 • Токуо
ОБЕШГ
Сьюзен Макгрегор
Обработка
данных на Ру1Ноп.
0а1а М/гапдПпд
и Оата ОиаНТу
Начните работу с чтения, очистки
и анализа данных
Санкт-Петербург
«БХВ-Петербург»
2024
УДК 004.43
ББК 32.973.26-018.1
М15
Макгрегор С.
М15 Обработка данных на Руйюп. Ба1а АУгап§Нп§ и Ба1а (^иаШу: Пер. с англ. —
СПб.: БХВ-Петербург, 2024. — 432 с.: ил.
18ВХ 978-5-9775-1846-8
Книга посвящена первичной обработке данных (Па1а АУгап§1ш§) на РуШоп и
оценке их качества (Па1а (^иаШу). Материал содержит основополагающие концепции,
экспертные советы и ресурсы, необходимые для первичной обработки, извлечения,
оценки и анализа данных. Все темы раскрыты на простых и наглядных примерах
из практики. Даны необходимые и достаточные сведения о языке программирова-
ния РуШоп 3.8+ для чтения, записи и преобразования данных из различных источ-
ников, а также для обработки данных в больших масштабах. Приведены лучшие
практики документирования и структурирования кода. Описан сбор данных из
файлов, веб-страниц и АР1. Рассмотрены приемы проведения базового статистиче-
ского анализа наборов данных, а также наглядные и убедительные способы визуа-
лизации и представления данных. Изложение рассчитано как на новичков по обра-
ботке данных, так и на профессионалов.
Электронный архив на сайте издательства содержит цветные иллюстрации к книге.
Для специалистов по обработке данных
УДК 004.43
ББК 32.973.26-018.1
Научный редактор:
Архитектор решений, 1Т-компания «Яндекс»
Дмитрий Бардин
Группа подготовки издания:
Руководитель проекта
Зав. редакцией
Перевод с английского
Редактор
Компьютерная верстка
Оформление обложки
Евгений Рыбаков
Людмила Гаулъ
Михаила Райтмана
Наталья Смирнова
Натальи Смирновой
Зои Канторович
© 2023 ВНУ
АиШопхей Киззхап 1гапз1а1юп оГ Ше Еп§йз11 есИйоп о Г Ргас1гса1 Ру1коп йсПа 1Угап§Пп§ апй И сП а ОиаШу
18ВХ 9781492091509 © 2022 8изап МсОге^ог.
ТЫз 1гапз1айоп 18 риЬНзйес! апс! зоШ Ьу репшззюп оГО’КеШу МесИа, 1пс., Мией о\упз ог сопйо1з а11
1о риЬНзй апс! зе11 Ше заше.
Авторизованный перевод с английского языка на русский издания
Ргасйса! Рубит Неба \Угап§Ип§ апс1 йсНа ОиаШу
18ВХ 9781492091509 © 2022 8изап МсОге^ог.
Перевод опубликован и продается с разрешения компании-правообладателя О’КеШу МеШа, 1пс.
"БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20.
18ВХ 978-1-492-09150-9 (англ.)
18ВУ 978-5-9775-1846-8 (рус.)
© Зизап МсОге§ог, 2022
© Перевод на русский язык, оформление.
ООО "БХВ-Петербург", ООО "БХВ", 2023
Содержание
Введение.................................................................11
Для кого предназначена эта книга?........................................12
Поедете сами или возьмете такси?.........................................12
Кому не следует читать эту книгу?........................................13
Что ожидать от этой книги................................................13
Типографские соглашения..................................................14
Использование примеров кода..............................................15
Цветные иллюстрации......................................................16
Возможности онлайнового обучения от компании О’КеШу......................16
Как связаться с нами.....................................................16
Благодарности............................................................17
ГЛАВА 1. Введение в первичную обработку (выпас) и качество данных........19
Что такое выпас данных?..................................................20
Что такое качество данных?...............................................22
Целостность данных...................................................23
Соответствие данных..................................................23
Почему мы выбрали РуШоп?.................................................25
Универсальность......................................................25
Доступность..........................................................25
Удобочитаемость......................................................26
Сообщество...........................................................26
Альтернативы языку РуЙюп.................................................27
Написание и исполнение программ на языке РуЙюп...........................27
Работа с кодом РуЙюп на локальном устройстве.............................30
Введение в работу с командной строкой................................30
Установка языка Руйюп, среды 1иру1ег МоТеЬоок и редактора кода.......33
Работа с кодом Руйюп в режиме онлайн.....................................39
Не11о АУогМ!.............................................................39
Создание файла автономного сценария РуЙюп при помощи А1ош............39
Создание блокнота кода РуЙюп в 1иру1ег МоТеЬоок......................41
Создание блокнота кода Руйюп в Ооо§1е Со1аЬ..........................42
Создаем программу........................................................43
В файле автономного сценария.........................................43
В блокноте...........................................................43
Исполняем программу.....................................................43
В файле автономного сценария........................................43
В блокноте..........................................................44
Документирование, сохранение и управление версиями своего кода..........44
Документирование....................................................44
Сохранение..........................................................46
Управление версиями.................................................46
Заключение..............................................................56
ГЛАВА 2. Введение в РуНюп...............................................57
”Части речи” языков программирования....................................58
Существительные ~ переменные........................................59
Важно ли конкретное имя?............................................61
Наилучшие практики для именования переменных........................62
Глаголы ~ функциям..................................................62
Применение пользовательских функций.................................67
Библиотеки: занимаем пользовательские функции у других программистов.68
Структуры управления: циклы и условные операторы........................69
Циклы...............................................................69
Условные операторы..................................................72
Понимание ошибок........................................................77
Синтаксические ошибки...............................................78
Ошибки времени исполнения...........................................79
Логические ошибки...................................................83
Отправляемся в путь с данными СШ В1ке...................................85
Начинаем с создания псевдокода......................................86
Масштабирование.....................................................92
Заключение..............................................................93
ГЛАВА 3. Понимание качества данных......................................95
Оценка соответствия данных..............................................97
Достоверность данных................................................98
Надежность данных...................................................100
Репрезентативность данных...........................................101
Оценка целостности данных...............................................104
Необходимые, но недостаточные.......................................106
Важные..............................................................108
Достижимость........................................................111
Улучшение качества данных...............................................114
Очистка данных......................................................115
Аугментация данных..................................................115
Заключение..............................................................116
ГЛАВА 4. Работа с файловыми и канальными данными
на языке РуОюп.........................................................117
Структурированные и неструктурированные данные.........................119
Работа со структурированными данными...................................123
Файловые табличные форматы данных..................................124
Выпас табличных данных посредством языка РуШоп.....................126
Выпас реальных данных: понимание безработицы...........................133
ХЬБХ, ОЭБ и все остальные..........................................136
Данные фиксированной ширины........................................143
Канальные данные — интерактивные обновления через Интернет.........147
Выпас канальных данных средствами языка РуЙюп......................150
Формат 1БО№ данные следующего поколения............................160
Работа с неструктурированными данными..................................165
Текст на основе изображений: доступ к данным в формате РЭР.........165
Выпас РЭР-данных, используя РуШоп...................................166
Обращение к таблицам РЭР посредством ТаЬи1а........................171
Заключение.............................................................171
ГЛАВА 5. Доступ к интернет-данным.......................................173
Доступ к веб-данным ХМЕ и ЗБОХ.........................................175
Знакомство с АР1-интерфейсами..........................................178
Базовые АР1-интерфейсы на примере поисковой системы....................179
Специализированные АР1-интерфейсы: добавление простой
аутентификации.....................................................181
Получение ключа для АР1-интерфейса РКЕЭ............................181
Использование ключа АР1 для запроса данных.........................182
Чтение документации по АР1-интерфейсу..................................183
Защита своего ключа АР1 при использовании сценариев РуЙюп..............186
Создание файла учетных данных......................................188
Использование учетных данных в отдельном сценарии..................189
Основы работы с файлом .§Ш§поге....................................190
Специализированные АР1-интерфейсы: работа с протоколом ОАиШ............193
Получение учетной записи разработчика Тмлйег.......................194
Создание приложения и учетных данных Тмлйег........................196
Кодирование ключа АР1 и ключа секрета АР1..........................201
Запрос токена доступа и данных из АР1-интерфейса Тмлйег............202
Этические нормы при работе с АР1-интерфейсами..........................206
Извлечение веб-данных: источник данных последней надежды...............207
Осторожно извлекаем данные с веб-сайта УГПТ........................210
Использование средств инспектирования браузера.....................211
Решение РуЙюп для извлечения данных из веб-страницы:
библиотека ВеаиШп! Боир............................................214
Заключение.............................................................218
ГЛАВА 6. Оценка качества данных........................................219
Пандемия и программа РРР...............................................221
Оценка целостности данных..............................................222
Имеют ли данные известное происхождение?...........................223
Актуальны ли данные?...............................................223
Полные ли данные?..................................................224
Хорошо ли данные аннотированы?.....................................236
Являются ли данные крупномасштабными?..............................242
Непротиворечивы ли данные?.........................................244
Многомерны ли наши данные?.........................................248
Атомарны ли данные?................................................250
Понятны ли данные?.................................................250
Размерностно структурированы ли данные?............................252
Оценка соответствия данных.............................................253
Достоверность данных...............................................253
Надежность данных..................................................257
Репрезентативность данных..........................................258
Заключение.............................................................259
ГЛАВА 7. Очистка, преобразование и дополнение данных...................261
Выбор подмножества данных системы СШ В1ке..............................262
Простое разбиение..................................................263
Регулярные выражения: супермощное средство сопоставления строк.....265
Создание дат.......................................................270
Удаление хлама из файлов данных........................................272
Декодирование дат Ехсе1................................................276
Создание настоящих данных С8У из данных фиксированной ширины...........279
Исправление разнообразности написаний..................................282
Тернистый путь к "простым” решениям....................................288
Опасные подводные камни................................................290
Дополнение данных......................................................292
Заключение.............................................................294
ГЛАВА 8. Структурирование и рефакторинг кода...........................296
Обзор пользовательских функций.........................................296
Многократное использование кода....................................297
Аккуратное и понятное документирование.............................297
Недостаточная функциональность по умолчанию........................298
Область видимости......................................................298
Определение параметров функции.........................................301
Доступные опции....................................................302
Предоставление аргументов..........................................303
Возвращаемые значения..................................................303
Работа со стеком.......................................................305
Рефакторинг для получения удовольствия и прибыли.......................306
Функция для определения рабочих дней...............................306
Опрятные метаданные....................................................309
Использование рубос для документирования сценариев и пользовательских
функций................................................................317
О полезности аргументов командной строки...............................321
Отличия во взаимодействии со сценариями в автономных файлах и блокнотах.325
Заключение.............................................................325
ГЛАВА 9. Введение в анализ данных......................................327
Вся суть — в контексте.................................................328
Одинаковые, но не совсем...............................................329
Что типично? Оценка центральной тенденции..............................329
Что это значит?....................................................330
Поразмыслим нестандартно: выявляем выбросы.............................332
Визуализация для анализа данных........................................332
Какова форма наших данных? Учимся понимать гистограммы.............336
Вопрос за $2 миллиона..................................................346
Пропорциональный ответ.................................................359
Заключение.............................................................362
ГЛАВА 10. Представление данных.........................................364
Основы визуального красноречия.........................................365
Сформулируйте свои данные..............................................367
Диаграммы, графики и картограммы — вот это да!.........................368
Круговые диаграммы.................................................369
Линейчатые и столбчатые диаграммы..................................372
Линейные диаграммы.................................................377
Диаграмма рассеяния................................................380
Картограммы........................................................383
Элементы красноречивых визуальных эффектов.............................386
"Мелкие” детали действительно имеют значение.......................386
Доверяйте своим глазам (и экспертам)...............................387
Выбор масштаба.....................................................388
Выбор цветовой гаммы...............................................389
Прежде всего делайте аннотации!....................................389
От базового к красивому: настройка визуализации с помощью зеаЬот и та1р1оШЬ... 390
Выйдите за рамки основ.................................................395
Заключение.............................................................396
ГЛАВА 11. За пределами РуНюп...........................................397
Дополнительные инструменты для анализа данных..........................398
Программы для работы с электронными таблицами......................398
ОрепКейпе..........................................................399
Дополнительные инструменты для обмена и представления данных........402
Редактирование изображений в форматах }РО, РЖ} и О1Р.............402
Программное обеспечение для редактирования ЗУ О и других векторных
форматов.........................................................402
Размышления об этике................................................404
Заключение..........................................................405
Приложение А. Другие ресурсы по программированию на РуСЬоп..........406
Официальная документация РуЙюп......................................406
Установка ресурсов Руйюп............................................407
Где искать библиотеки............................................407
Следите за остротой своих инструментов..............................408
Где получить больше информации......................................409
Приложение Б. Еще несколько слов о СИ...............................410
Вы запускаете команду §й ризй/риП и оказываетесь в странном текстовом
редакторе...........................................................410
Ваша команда ризй/риП отклоняется...................................412
Выполните команду §й ри11........................................412
Краткое руководство по Ой...........................................414
приложение В. Поиск данных..........................................416
Репозитории данных и АР1............................................416
Эксперты по предметным вопросам.....................................417
Запросы ГО1А/Г......................................................418
Кастомные методы сбора данных.......................................419
приложение Г. Ресурсы для визуализации и информационного дизайна....421
Основополагающие книги по визуализации информации...................421
Краткое руководство, за которым вы потянетесь.......................422
Источники вдохновения...............................................422
Об авторе...........................................................423
Колофон.............................................................424
Предметный указатель................................................425
Введение
Добро пожаловать! Если вы взяли в руки эту книгу, вы, вероятно, один из многих
миллионов людей, заинтересованных процессами и возможностями, связанными с
”данными” — этой невероятной, неуловимой новой ”валютой”, которая меняет наш
образ жизни: как мы живем, работаем и даже общаемся друг с другом. Например,
большинство из нас осознают то обстоятельство, что данные, собираемые в процес-
се использования наших электронных устройств, используются для формирования
рекламы, рекомендуемого для прослушивания или просмотра различного аудиови-
зуального материала, а также для выбора результатов поиска — что разместить в
начале списка. Но при этом многие люди могут не знать, что им вполне доступны
инструменты и навыки для доступа, преобразования и получения информации на
основе данных. Цель этой книги — помочь таким людям, в число которых можете
входить и вы, научиться пользоваться данными инструментами.
Данные могут быть доступными или полезными не только для крупных компаний
или государственных статистических организаций. Умение получать доступ к дан-
ным, понимать их и извлекать из них полезную информацию является ценным на-
выком не только для специалистов по работе с данными. В настоящее время сред-
ства для эффективного использования данных более доступны, чем когда-либо.
Серьезную работу с данными можно осуществлять, используя бесплатное программ-
ное обеспечение и языки программирования, при этом вам не потребуется дорого-
стоящее оборудование. Например, все упражнения в этой книге были рассчитаны
для выполнения на ноутбуке СйгошеЪоок стоимостью менее 500 долларов. Более
того, можно просто использовать бесплатные онлайновые платформы, получая
доступ к ним посредством бесплатного интернет-подключения.
Цель этой книги — предоставить новичкам в области обработки и анализа данных
руководство и уверенность, необходимые, чтобы начать исследовать мир данных —
сначала получая доступ к ним, а затем оценивая их качество. Обеспечив эти фун-
даментальные навыки, далее мы перейдем к рассмотрению базовых методов анали-
за и представления данных для извлечения из них значащей информации. В даль-
нейших главах мы рассмотрим основные навыки, необходимые для выполнения
подробного и содержательного анализа данных и визуализации полученных ре-
зультатов, используя очищенную версию исходных данных.
Для кого предназначена эта книга?
Эта книга предназначена для самых что ни на есть начинающих — вам нужны
лишь самые базовые знания работы с компьютерами (как загрузить файл, запустить
на исполнение программу, копировать, вставлять и т. и.), открытость к новому обу-
чению и желание экспериментировать. Особенно я рекомендую эту книгу тем, кого
пугают данные или программирование, кто думает, что он "слаб в математике”, или
кто считает работу с данными или обучение программированию слишком трудной
для себя задачей. Я почти десять лет обучал людей, которые не считали себя техни-
чески подкованными, именно тем навыкам, которые рассматриваются в этой книге,
и никогда среди них не было ни одного, для кого овладение этим материалом было
бы действительно не по силам. Исходя из моего опыта, самое трудное в програм-
мировании и работе с данными — это не сложность преподаваемого материала, а
качество его преподавания1. Я благодарна всем моим студентам, вопросы которых
в течение многих лет безмерно помогли мне в отыскании способов лучшего пред-
ставления этого материала, а также за возможность поделиться тем, чему я научи-
лась от них. И хотя никакая книга не может по-настоящему заменить поддержку,
предоставляемую живым преподавателем, я надеюсь, что данная книга, по крайней
мере, даст вам инструменты, необходимые для овладения базовыми знаниями, и,
возможно, вдохновит вас на развитие этих навыков до следующего уровня.
Книга будет полезна для тех, кто уже имеет некоторый опыт по первичной обра-
ботке (выпасу) данных, но достигли предела возможностей средств электронных
таблиц, и для тех, кто планирует расширить диапазон форматов данных, поддаю-
щихся легкому доступу и манипулированию. Материал будет полезен и для тех,
кто обладает навыками программирования фронтенда (используя, например,
1ауа8спр1 или РНР) и ищет способа начать работать с языком РуЙюп.
Поедете сами или возьмете такси?
В предисловии к своей книге Рго^гат ог Ве Рго^гаттед, (Программируй сам или
запрограммируют тебя) из серии ОК Воокз теоретик медиа Дуглас Рушкофф (Вои§-
1аз Кизйкой) сравнивает программирование с вождением автомобиля. Если вы не
научитесь программированию, пишет он, то вы будете вечным пассажиром в циф-
ровом мире, которого ”с одного места в другое перевозят в такси. Но только это
такси без окон, и если водитель говорит вам, что в стране только один супермаркет,
то у вас нет выбора, кроме как верить ему”.
”Вы можете поручить задачу программирования другим, — продолжает Рушкофф,
— но тогда вам нужно верить им, что их программы в действительности делают то,
1 Долгое время большой преградой была установка требуемых инструментов. Теперь же для этого
нужно лишь подключение к Интернету.
что вы хотите, и таким образом, который отвечает вашим насущным интересам”.
В настоящее время последнее предположение все в большей степени стоит под во-
просом.
За годы моего преподавания я спросила у нескольких сотен студентов, верили ли
они, что водить автомобиль может научиться любой человек, и они всегда давали
положительный ответ на этот вопрос. В то же самое время мне повстречалось очень
мало людей, которые по-настоящему верят, что научиться программированию мо-
жет любой человек. Но вождение транспортного средства в действительности на-
много более сложная задача, чем программирование. Почему же тогда многие из
нас полагают, что программирование будет для них ”слишком трудным”?
В этом для меня проявляется настоящая сила аналогии Рушкоффа, поскольку его
”безоконное такси” не только скрывает внешний мир от пассажира, но также скры-
вает "водителя” от прохожих. Верить в то, что любой может научиться водить ав-
томобиль, легко по той причине, что мы каждый день видим, как это делают самые
разные люди.
Но когда речь заходит о программировании, нам редко выпадает возможность уви-
деть тех, кто ”за рулем”. Это означает, что наше представление о том, кто может и
должен заниматься программированием, достаточно ошибочно. Поскольку, если вы
можете водить автомобиль или, скажем, расставить знаки препинания в предложе-
нии, я обещаю, что вы также можете и научиться программированию.
Кому не следует читать эту книгу?
Как говорилось ранее, эта книга предназначена для начинающих. Поэтому основ-
ные ее разделы полезны для новичков в области анализа или визуализации данных
и вряд ли заинтересуют читателей, обладающих опытом программирования на язы-
ке РуЙюп или на другом языке, направленном на работу с данными. К счастью, из-
дательство О'КеШу предоставляет большое количество специализированных книг
по сложным темам и библиотекам языка РуЙюп, включая книги Ру1коп /ог Ба1а
Апа1у818 (Язык РуЙюп для анализа данных), автор Уэс Маккинни (\Уе§ МсКтпеу), и
Ру1коп Оа1а Зсгепсе НапАЪоок (Настольная книга по работе с данными на языке
РуЙюп), автор Джейк ВандерПлас (1аке УапбегРкз).
Что ожидать от этой книги
Содержимое данной книги следует изучать в порядке его изложения, поскольку
понятия и упражнения каждой главы основаны на материале предыдущих глав. Но
на всем ее протяжении упражнения представлены двумя способами: в виде ”блок-
нотов” с кодом и в виде ”автономных” файлов программ — листингов. Этот метод
преследует двойную цель. Во-первых, он позволяет читателям использовать любой
предпочитаемый ими или удобный для них подход, а во-вторых, предоставляет
способ для сравнения этих двух методов взаимодействия с кодом на языке РуЙзоп,
управляемым данными. Исходя из моего личного опыта, ”блокноты” с кодом на
языке РуЙзоп чрезвычайно полезны для быстрого создания рабочих программ, но
этот метод может стать утомительным в случае желания многократного исполне-
ния какой-либо надежной программы. Так как часто код в одном из этих форматов
нельзя просто скопировать и вставить в другой формат, в репозитории (ЗйНиЪ для
данной книги программы предоставляются в обоих этих форматах. Файлы данных
также предоставляются посредством службы Ооо§1е Ипуе. По мере решения уп-
ражнений вы сможете использовать предпочитаемый вами формат, а также у вас
будет возможность лично увидеть разницу между кодом в этих двух форматах.
Хотя язык РуЙзоп является основным используемым в этой книге инструментом,
эффективную первичную обработку и анализ данных можно облегчить, используя
ряд инструментов, от текстовых редакторов (программы, используемые для напи-
сания кода) до электронных таблиц.
По этой причине некоторые упражнения в этой книге кроме языка РуЙзоп исполь-
зуют другие бесплатные инструменты и/или инструменты с открытым исходным
кодом. Во всех таких случаях предоставляется объяснение причины использования
конкретного инструмента, а также достаточные инструкции для выполнения задачи
примера.
Типографские соглашения
В книге используются следующие типографские соглашения:
Курсив
Этим шрифтом выделяются новые термины.
Полужирный шрифт
Используется для выделения адресов электронной почты, элементов интерфейса.
Моноширинный шрифт
Выделены листинги программ. Внутри параграфов используется для обозначе-
ния элементов программы, таких как имена переменных, функции и типы дан-
ных.
Моноширинный шрифт (полужирный)
Обозначает команды или другой текст, который вводится пользователем.
Моноширинный шрифт (курсив)
Используется для обозначения текста, который нужно заменить предоставляе-
мыми пользователем значениями или значениями, определяемыми контекстом.
Этот элемент означает совет или предложение.
Данный элемент обозначает общее примечание.
Этот элемент обозначает предупреждение или предостережение.
Использование примеров кода
Дополнительный материал для книги (примеры кода, упражнения и т. п.) можно
загрузить по адресу ййр8://§ййиЬ.сот/Ргасйса1Ру€йопВа€а\Угап§1т§Ап(19иа1йу.
Вопросы по техническим аспектам примеров кода или проблемам с их использова-
нием можно задать, отправив сообщение электронной почтой по адресу
Ьоокдие8йоп@оге11у.сот.
Код в этой книге предназначен для того, чтобы помочь вам развить свои навыки.
Предоставляемые в этой книге примеры кода можно использовать в своих про-
граммах и документации, без необходимости спрашивать нашего разрешения, если
только речь не идет о значительном объеме кода. Например, использование не-
скольких фрагментов кода из этой книги в своей программе разрешения не требует,
а вот для продажи или распространения примеров из книг издательства О’КеШу
разрешение необходимо. Цитирование текста из этой книги, включая код, разреше-
ния не требует, но включение значительного объема кода из книги в документацию
своего продукта требует разрешения.
При цитировании материалов из наших книг мы будем признательны за предостав-
ление ссылки на источник, но обычно не требуем этого. Формат такой ссылки мо-
жет включать название, имя автора, издателя и 18ВМ книги. Например: ”Ргасйса1
РуШоп Ва1а \^гап§1т§ апб Ва1а РиаШу Ъу 8и§ап Е. МсОге§ог (О’КеШу). Соруп§Ы
2022 8и§ап Е. МсОге§ог, 978-1-492-09150-9”.
Если вы считаете, что использование примеров кода выходит за рамки добросове-
стного использования или указанного выше разрешения, вы можете уточнить этот
вопрос, обратившись к нам по адресу регт1881оп8@оге111у.сош.
Цветные иллюстрации
РВЕ-файл с цветными рисунками из книги можно скачать по ссылке:
Ьйр8://ир.ЬЬу.ги/9785977518468.г1р, а также со страницы книги на сайте ЬЬу.ги.
Возможности онлайнового обучения
от компании О'КеШу
Свыше 40 лет компания О’КеШу МеШа предоставляет средства обучения в техноло-
гической и предпринимательской областях, необходимые компаниям для достиже-
ния успехов в своей деятельности.
Наша уникальная сеть экспертных специалистов и новаторов делятся своими зна-
ниями и опытом посредством книг, статей и нашей онлайновой обучающей плат-
формы. Онлайновая система обучения компании О’КеШу предоставляет доступ (по
требованию) к учебным курсам в режиме реального времени, доскональным мето-
дам обучения, интерактивным средам кодирования, а также к огромному собранию
печатного и видеоматериала от самой компании и от свыше 200 других изда-
тельств. Чтобы получить дополнительную информацию, посетите наш веб-сайт по
адресу Ьйр://оге111у.сош.
Как связаться с нами
Если у вас есть какие-либо замечания или вопросы по этой книге, вы можете задать
их издателю по следующему адресу:
О’КеШу МеШа, 1пс.
1005 Огауеп81ет Н1§11\уау МогШ
8еЪа81оро1, СА 95472
800-998-9938 (в США или Канаде)
707-829-0515 (международный или местный)
707-829-0104 (факс)
Для этой книги создан веб-сайт, который содержит список ошибок, где приводятся
примеры и предоставляется другая дополнительная информация. Веб-сайт находится
по адресу кйр8:/Лулулу.оге111у.сот/11Ьгагу/у1елу/ргасйса1-руЙ1оп-с1а1:а/9781492091493.
Свои комментарии о книге и технические вопросы присылайте электронной почтой
по адресу Ьоокдие8Йоп8@оге111у.сош.
Чтобы узнать последние новости и информацию о наших книгах и курсах, посетите
наш веб-сайт по адресу Ьир://оге111у.сош.
Мы на БасеЪоок: Ъир://ГасеЬоок.сот/оге111у.
И в Т\уШег: Ьйр^АлуШег.сош/огеШутеШа.
А также на УоиТиЪе: Ьйр://лулулу.уои€иЬе.сот/оге111утеД1а.
Благодарности
Как я уже упоминала ранее, эта книга появилась благодаря моим студентам, кото-
рые в процессе обучения были достаточно смелыми, чтобы попробовать что-то но-
вое или изучить различные вопросы. Процесс написания этой книги (не говоря о
самом ее тексте) был безмерно улучшен моим редактором, Джеффом Блайалем
(ЛсГГ В1е!е1), который своей дружелюбностью, гибкостью и легкостью подхода
умерил мои крайности, оставляя при этом место для моего личного стиля. Я также
благодарна за содержательные и любезные комментарии моим рецензентам: Джо-
анне С. Као (1оаппа 8. Као), Анн Боннер (Аппе Воппег) и Рэнди Ау (Капбу Ан).
Кроме этого, я хочу сказать спасибо Джесс Хаберман (1е88 НаЪегшап), которая по-
могла мне подготовить материал, а также Джаклин Казил (1аские1те Кахй) и Кат-
рин Джармул (КаЙтаппе 1агти1), которые помогли мне познакомиться с Джесс.
Дополнительно я хочу поблагодарить Джаннетт Уинг (1еаппейе АУт§) и Клифа
Штейна (С11й? 81ет), чья заинтересованность в этой работе помогла сделать книгу
увлекательнее. И конечно же, я хочу поблагодарить моих друзей и родственников
за их заинтересованность и поддержку, особенно в тех случаях, когда у них не бы-
ло ни малейшего представления о том, о чем я вела речь.
Наконец, я хочу поблагодарить всех членов моей семьи (включая детей, которые
еще слишком молоды, чтобы прочитать это) за их поддержку даже в те дни, когда
мной овладевали печаль и грусть. Вы делаете мою работу стоящей и важной.
ГЛАВА 1
Введение в первичную обработку
(выпас) и качество данных
Кажется, что в настоящее время данные предоставляют ответ на все случаи жизни.
Например, мы используем данные в обзорах товаров и ресторанов, чтобы решить,
что купить и куда пойти покушать. Компании используют данные о наших дейст-
виях в Интернете — что мы читаем, смотрим и выбираем, для принятия решения,
какой контент создавать и какую рекламу показывать. Агентства по подбору кадров
используют данные для принятия решения, каким соискателям работы назначать
собеседование. Ну и, конечно же, государственные учреждения используют данные
для принятия широкого круга решений, от распределения средств на строительство
дорог до выбора школы для вашего ребенка. Данные, будь они в виде простой таб-
лицы личных финансов или массива чисел для систем искусственного интеллекта,
проникают во все аспекты нашей жизни. Именно во всепроникающем воздействии
данных на наши ежедневные действия и возможности состоит причина, почему
первичная обработка данных является жизненно важным навыком для любого за-
интересованного в понимании принципов работы управляемых данными систем и
влиянии на эти системы. Умение оценивать и улучшать качество данных необхо-
димо для улучшения работы этих систем, которые зачастую содержат достаточно
серьезные изъяны.
Но поскольку термины первичная обработка данных или выпас данных1 (ба!а
мтап§1т§) и качество данных (баи диаШу) могут истолковываться разными людь-
ми по-разному, мы начнем эту главу с краткого обзора трех главных тем, рассмат-
риваемых в этой книге: выпас данных, качество данных и язык программирования
РуЙюп. Цель этого обзора — пояснить мой подход к этим темам, для того чтобы вы
смогли решить, подходит ли вам эта книга. После этого мы уделим некоторое вре-
мя на рассмотрение логистики доступа к программным инструментам и другим ре-
сурсам, которые будут нам нужны для решения упражнений в этой книге. Хотя все
упоминаемые в этой книге ресурсы предлагаются на бесплатной основе, во многих
книгах и руководствах по программированию автоматически предполагается, что
их читателям для разработки программ будут доступны собственные компьютеры,
1 ЬПр8://гоЬоШгеат8.сс/Ыо§/80-йшкс1уа-ро1:ег-п-§гатту-1-етЬе(1(1т§
часто довольно дорогие. Но поскольку я по-настоящему верю в то, что любой же-
лающий может научиться выполнять выпас данных, используя язык программиро-
вания РуЙюп, я хотела обеспечить возможность использования материала в этой
книге даже при отсутствии доступа к собственному полнофункциональному ком-
пьютеру. С этой целью все решения в этой и последующих главах были разработа-
ны и протестированы на ноутбуке типа СйгошеЪоок. Их также можно исполнять,
используя бесплатные онлайновые инструменты на своем собственном компьютере
или компьютере для общего пользования, например, в школе или публичной биб-
лиотеке. Я надеюсь, что демонстрация высокого уровня доступности не только
знаний, но также и средств для выпаса данных, воодушевит вас на дальнейшее ис-
следование этой захватывающей и полезной деятельности.
Что такое выпас данных?
Выпас данных (ба1а мтап§1т§) — это процесс преобразования ”сырых” или ”обна-
руженных” данных в форму, которую можно использовать для получения знаний и
смысла. В основе всех усилий по выпасу данных лежит вопрос: есть ли что-то в
мире, что вы хотите исследовать. Конечно же, если вы решили заниматься по этой
книге с целью научиться программированию, тогда выпас данных будет хорошей
начальной точкой. Но я настоятельно советую не пытаться переходить напрямую к
программированию, не разобравшись с процессами обеспечения качества данных,
излагаемых в последующих главах. И хотя обладание навыками программирования
может быть полезным для выпаса данных, для полноты успеха в этой области тре-
буется намного больше, чем просто знания, как получать доступ к данным и мани-
пулировать ими. Как иллюстрируется в этой книге, большинство доступных дан-
ных имеет не особенно хорошее качество, поэтому выпас данных невозможно вы-
полнять, не принимая решений, влияющих на суть полученных данных. Пытаться
выполнять выпас данных, не принимая во внимание качество данных, сродни по-
пытке вести автомобиль без рулевого управления: вы можете доехать куда-либо, и
достаточно быстро, но, скорее всего, не туда, куда вам нужно. Если вы собираетесь
тратить время на разбор и анализ данных, надо убедиться, что это по крайней мере
будет стоить затраченных усилий.
Однако не менее важно и то, что нет лучшего способа освоить новый навык, чем
делать это во взаимосвязи с чем-либо, что вы очень хотите сделать правильно. Та-
кая личная заинтересованность позволит вам пережить неизбежные моменты разо-
чарования от неудач. Это что-либо не обязательно должно быть чем-то особенно
важным, а просто вопросом, например, о ваших любимых видеоиграх, музыкаль-
ных группах, типах чая, вашей школе, вашем квартале, вашей жизни в социальных
сетях, экономике, политике, вере или финансах. То есть это может быть вопрос о
чем угодно, важно, что это должно по-настоящему интересовать вас.
Определившись с интересующим вас вопросом, можно начинать процесс выпаса
данных. В зависимости от специфики проекта, конкретные шаги этого процесса
может потребоваться корректировать (или повторять), в целом процесс выпаса
данных содержит следующие шаги:
1. Обнаружение или сбор данных.
2. Обзор данных.
3. Очистка, стандартизация, преобразование и/или дополнение данных.
4. Анализ данных.
5. Визуализация данных.
6. Предоставление данных.
Надо помнить, что время и усилия, необходимы для реализации каждого из этих
шагов, могут быть разными. Например, если вы хотите ускорить исполнение какой-
либо задачи по выпасу данных, которую вы уже выполняете на работе, то у вас уже
может быть в наличии набор данных и вы, в основном, можете знать, в чем она со-
стоит. С другой стороны, если вы пытаетесь выяснить объем финансов, которые
городские власти расходуют в вашем городе, сбор данных может стать наиболее
трудоемкой частью вашего проекта.
Кроме этого, несмотря на то, что шаги в предыдущем списке изложены в пронуме-
рованном порядке, процесс выпаса данных представляет собой скорее цикл, нежели
линейный набор шагов. Зачастую, по мере получения дополнительных знаний о
значении и контексте используемых данных, вам нужно будет возвратиться к пре-
дыдущим шагам. Например, при анализе крупного набора данных могут встретить-
ся неожиданные комбинации или значения данных, вынуждающие вас ставить под
вопрос предположения, которые вы могли сделать до этого. Это почти всегда будет
означать необходимость поиска дополнительной информации (или из исходного
источника данных, или из полностью новых источников), чтобы понять, что в дей-
ствительности происходит, прежде чем вы сможете перейти к анализу или визуали-
зации данных. Наконец, хотя в вышеприведенном списке это и не указано явно,
было бы более точным начинать каждый шаг в нем с "Исследование и”, например,
"Исследование и обзор данных". Тогда как части "первичной обработки" нашей
работы будут в основном фокусироваться на имеющихся у нас наборах данных,
часть "обеспечение качества" почти полностью сводится к исследованию и контек-
сту, и оба эти аспекта являются неотъемлемыми составляющими каждого этапа
процесса выпаса данных.
Если на данном этапе у вас возникают трудности с пониманием всего этого, не пе-
реживайте. Примеры в этой книге построены на основе настоящих наборов данных,
и по мере освоения процессов кодирования и оценки данных все это будет стано-
виться для вас все более естественным. Если в процессе работы над своим собст-
венным проектом выпаса данных вы начнете испытывать затруднения, просто по-
стоянно напоминайте себе о вопросе, на который вы пытаетесь получить ответ. Это
не только напомнит вам, почему вы морочитесь с пониманием всех мельчайших
подробностей форматов данных и ключей доступа АР1-интерфейсов2, но также по-
зволит вам интуитивно перейти к следующему шагу в процессе выпаса данных,
будь то визуализация данных или просто выполнение дополнительных исследова-
ний с целью улучшения контекста и качества данных.
Что такое качество данных?
Окружающий нас мир содержит большие объемы самых разнообразных данных,
получить доступ к которым и организовать их извлечение можно разными метода-
ми. Но не все данные являются равными. Понимание качества данных является
важной частью выпаса данных, поскольку любые знания, полученные на основе
данных, хороши лишь настолько, насколько хороши эти данные3. Поэтому, если вы
пытаетесь использовать данные, чтобы получить какие-либо значимые знания об
окружающем мире, сначала нужно удостовериться в том, что используемые вами
данные правильно отображают соответствующий аспект мира. Как мы увидим в
последующих главах (в частности, в главах 3 и 6), задача по улучшению качества
данных почти никогда не является такой четко определенной, как опрятно поме-
ченные аккуратные строки и столбцы данных, с которыми вы будете работать.
Это объясняется тем, что, несмотря на такие термины, как ”машинное обучение” и
”искусственный интеллект”, вычислительные инструменты могут только следовать
заданным им указаниям, используя предоставленные им данные. А даже самые
сложные и абстрактные данные всегда по своей сути человеческие, поскольку они
являются результатом человеческих решений, что измерять и как. Кроме этого, да-
же наиболее продвинутые современные компьютерные технологии делают ”пред-
сказания” и принимают ”решения” посредством метода, который, по сути, сводится
к крупномасштабному поиску структур данных, совпадающих с образцами в опре-
деленных выборках данных, которые предоставляются людьми, "тренирующими”
эти системы. У компьютеров не возникает никаких оригинальных идей и у них не
бывает творческих озарений; они в своей основе плохо справляются со многими
задачами, которые люди решают на интуитивном уровне (например, объяснение
сути дискуссии или сюжета рассказа). С другой стороны, компьютеры преуспевают
в области повторяющихся вычислений, которые они выполняют очень быстро, без
устали, не отвлекаясь на посторонние раздражители и не теряя интерес к выпол-
няемой работе. Иными словами, компьютеры фантастически дополняют человече-
ские способности к анализу и мышлению, но они могут только усилить, но никак
не заменить их.
Это означает, что задача обеспечения качества данных, чтобы получить в результа-
те их обработки значащие результаты, возлагается на людей, выполняющих сбор и
анализ данных. Тема качества данных подробно рассматривается в главе 3, а пока я
2 Эти аспекты рассматриваются более подробно в главах 4 и 5, соответственно.
3 В мире вычислений это часто описывается как "мусор на входе — мусор на выходе".
хочу представить две отдельные (но одинаково важные) оси для оценки качества
данных: целостность данных (дай т!е§п1у) и соответствие данных (баШ Й1) кон-
кретной задаче или вопросу.
Целостность данных
Для наших целей оценка целостности набора данных осуществляется, используя
значения и описания составляющих его данных. Например, если набор данных со-
держит значения измерений, выполненных в течение периода времени, то выясня-
ется, выполнялись ли эти измерения через регулярные или нерегулярные времен-
ные интервалы. Представляют ли значения непосредственные отдельные измерения
или же только средние значения? Существует ли словарь данных, содержащий
подробную информацию о том, каким образом осуществлялся сбор или запись дан-
ных или как их следует интерпретировать (например, предоставляя релевантные
единицы измерения)? В общем, полные, неделимые и качественно аннотированные
(среди прочих аспектов) данные считаются обладающими более высокой целостно-
стью, поскольку эти характеристики позволяют выполнять более широкий диапа-
зон однозначных анализов. Но в большинстве случаев в конкретном наборе данных
будет недоставать определенного количества размерностей целостности данных,
означая, что задача по выяснению его ограничений и по его улучшению возлагается
на вас. Решение этой задачи часто означает улучшение данного набора данных по-
средством нахождения других наборов данных, которые могут дополнить, расши-
рить или поместить его в контекст. Но почти всегда это состоит в выходе за рамки
”данных” любого типа и обращении за помощью к экспертам: людям, которые раз-
работали эти данные, собрали их, работали с ними ранее или обладают большими
знаниями в предметной области, задачу в которой эти данные должны помочь ре-
шить.
Соответствие данных
Но даже если набор данных имеет превосходную целостность, его можно считать
высококачественным только тогда, когда он подходит и для решения конкретной
задачи. Предположим, что нас интересует, в какой из городских станций проката
велосипедов сети СШ В1ке было взято в прокат и возвращено наибольшее количе-
ство велосипедов за данные сутки. Хотя АР1-интерфейс реального времени веб-
сайта сети СШ В1ке содержит данные с высоким уровнем целостности, эти данные
плохо подходят для предоставления ответа на конкретный вопрос, какая из станций
этой сети имела наибольший оборот за определенный день. В данном случае было
бы лучше попытаться получить ответ на этот вопрос, используя историю поездок
этой сети проката.
Конечно же, задача соответствия данных может быть решена так просто только в
редких случаях; чаще нам приходится выполнять большой объем работы по повы-
шению уровня целостности данных, прежде чем мы сможем быть уверены в том,
что наш набор данных в действительности соответствует конкретному вопросу или
задаче. Но избежать этих расходов времени невозможно, поскольку ”срезание уг-
лов” при решении задачи целостности или соответствия данных неизбежно отрица-
тельно скажется на качестве и соответствии работы по выпасу данных в целом.
В действительности, вред, создаваемый современными вычислительными система-
ми, во многих случаях связан с проблемами с соответствием данных. Например,
использование данных, описывающих одно явление (например, доход) для получе-
ния ответа о потенциально связанных, но фундаментально других явлениях (на-
пример, достижениях в образовании), может вести к искаженным заключениям о
происходящем в мире, иногда с разрушительными последствиями. Конечно же, в
некоторых случаях использования таких посреднических измерений избежать не-
возможно. Например, для предоставления неотложной медицинской помощи будет
оправдан первичный диагноз на основе наблюдаемых симптомов пациента, пока не
будут получены результаты более подробных тестов. Такие промежуточные этапы
иногда приемлемы на индивидуальном уровне, но разрыв между косвенными изме-
рениями и настоящим явлением увеличивается прямо пропорционально масштабу
данных и системы, в которой эти данные используются. В таких случаях в конеч-
ном итоге мы получим чрезвычайно искаженное представление явления, которое
мы хотим прояснить при помощи нашей первичной обработки и анализа данных.
К счастью, существует несколько способов защиты от ошибок такого типа, которые
мы подробно рассмотрим в главе 3.
Распаковка системы СОМРА8
Один из громких примеров вреда, который может быть причинен использо-
ванием неверных данных в крупномасштабной вычислительной системе, был
продемонстрирован несколько лет тому назад группой журналистов из неком-
мерческой расследовательской новостной организации РгоРиЪНса4. В серии
МасШпе Шаз (Машинная предвзятость) своих публикаций эти журналисты ис-
следовали разные предсказания возможности повторного совершения преступ-
ления черными и белыми заключенными, рассматриваемые для условно-
досрочного освобождения, выдаваемые алгоритмическим инструментом СОМРА8
(Соггесбопа! Ойепбег Мапа§ешеп1 Ргой1т§ Гог АЙегпабуе 8апсбоп8 — Испра-
вительно-управленческое профилирование правонарушителей для определения
целесообразности применения альтернативного наказания). Этот инструмент
генерировал более высокие значения вероятности повторного совершения пре-
ступления для черных правонарушителей, чем для белых с подобным уголов-
ным прошлым. В значительной степени это обуславливалось тем, что данные,
используемые для предсказания (или "моделирования”) риска повторного пра-
вонарушения, рассматривали количество арестов как косвенный коэффициент
склонности к совершению правонарушений.
4 Разглашение: многие из штатных сотрудников организации РгоРпЬНса, включая ведущего репортера
этой серии, в прошлом были коллегами автора.
Но модель арестов с самого начала проявляла предвзятость против черных, т. е.
черные арестовывались за такие ''преступления” (например, за добирание на
работу пешком), которые не предъявлялись белым. Поэтому создаваемые этим
инструментом оценки степени риска также были предвзятыми5.
И можно с легкостью найти другие примеры возможности нанесения большого
вреда вследствие использования некачественного соответствия данных. Поэто-
му оценка данных как на их целостность, так и на соответствие задаче, является
важной частью процесса выпаса данных: если используются неподходящие
данные, то полученные на их основе результаты могут не просто быть непра-
вильными, но даже нанести вред.
Почему мы выбрали РуНюп?
Если вы читаете эту книгу, то по всей вероятности вы уже знаете о программирова-
нии на языке РуЙюп и даже можете быть довольно уверенными в том, что это пра-
вильный инструмент для начала или продолжения работы в области выпаса дан-
ных. Но даже если это так, я полагаю, что будет иметь смысл выполнить краткий
обзор, что делает язык РуЙюп и чем он подходит для того типа работы по первич-
ной обработке и обеспечению качества данных, которой мы будем заниматься в
этой книге. Если вы ничего не знаете о языке РуЙюп, считайте это введением в рас-
смотрение факторов, делающих его одним из наиболее мощных и популярных со-
временных языков программирования.
Универсальность
Наверное, одной из самых сильных сторон языка РуЙюп является его универсаль-
ность: его можно с легкостью использовать для доступа к АР1-интерфейсам, извле-
чения данных из веб-страниц, выполнения статистических анализов и генерирова-
ния значащих визуализаций. Другие языки программирования могут выполнять
некоторые из этих задач, но очень немногие из них могут справиться с ними всеми
так хорошо, как это делает РуЙюп.
Доступность
Согласно одному из создателей языка РуЙюп, Гвидо ван Россуму ((Эшбо Уап
Ко88шп), целью создания этого языка было получить "язык программирования, ко-
торый можно понимать как обычный английский язык". В этом языке используют-
5 Серия публикаций "МасЫпе В1а8" вызвала значительное обсуждение в научном сообществе, некото-
рые члены которого не были согласны с определением предвзятости, используемым организацией
РгоРиЬНса. Но важен тот факт, что эти разногласия породили полностью новую область научных
исследований: беспристрастность и прозрачность в машинном обучении и интеллекте.
ся английские ключевые слова в тех случаях, когда многие языки сценариев (на-
пример, язык К и 1ауа8спр1) используют знаки препинания. В результате этого для
англоязычных пользователей изучение языка РуШоп может быть легче и более ин-
туитивно, чем изучение других языков написания сценариев.
Удобочитаемость
Одна из центральных доктрин языка РуШоп гласит, что "удобочитаемость важна”.
В большинстве языков программирования визуальная организация кода не влияет
на его функционирование, и ход исполнения программы определяется специаль-
ными символами. В противоположность им язык РуШоп полагается на отступы и
табуляцию для задания хода исполнения, в результате чего неправильная расста-
новка этих символов будет вызывать сообщения об ошибке. Такой подход требует
некоторого привыкания, но он обеспечивает определенный уровень удобочитаемо-
сти исходного кода программ на языке РуШоп, позволяющий более легкое понима-
ние кода, написанного другими (или, что более уместно, — собственного кода, по
истечении некоторого времени после его написания). Другим аспектом удобочи-
таемости кода является комментирование и прочее документирование своей рабо-
ты, которое рассматривается более подробно далее в этой главе в разд. "Докумен-
тирование, сохранение и управление версиями своего кода".
Сообщество
У РуШоп очень большое и активное сообщество пользователей, многие из которых
помогают создавать и поддерживать "библиотеки” кода, расширяющие возмож-
ности, которые вы можете быстро выполнить с помощью вашего собственного кода
на РуШоп. Например, популярные высокоразвитые библиотеки ЫшпРу и Рапбаз по-
зволяют выполнять очистку и анализ данных, а библиотеки Ма1р1оШЪ и 8еаЪогп
позволяют создавать визуализации. А еще более мощные библиотеки 8с1кй-Ьеат и
И БИК позволяют решать задачи даже в таких сложных областях, как машинное
обучение и обработка текстов на естественных языках. Многие из этих библиотек
используются в рассматриваемых в этой книге основных принципах выпаса данных
с использованием языка РуШоп. Но с их помощью можно получить намного более
продвинутые результаты, используя всего лишь несколько строк кода. Люди, кото-
рые разрабатывают эти библиотеки, часто ведут блоги, создают видеоруководства и
предоставляют примеры кода, которые можно использовать для расширения воз-
можностей ваших программ на языке РуШоп.
Большое сообщество пользователей языка РуШоп и их энтузиазм помогают легко и
быстро найти решения по наиболее распространенным (и даже не очень распро-
страненным) проблемам и ошибкам, с которыми вам придется столкнуться, исполь-
зуя публикуемые в Интернете решения. В результате поиск ошибок в коде РуШоп
может быть более легким, чем для более специализированных языков с сообщест-
вами пользователей меньшего размера.
Альтернативы языку Ру(Ноп
Хотя язык РуШоп имеет много достоинств, выпас данных можно выполнять и с по-
мощью других средств. Далее приводится краткий обзор некоторых из этих инст-
рументов, включая объяснение причины, по которой я отдала предпочтение именно
языку РуЙюп.
Язык К
Язык программирования К является, наверное, наиболее близким конкурентом
языка РуЙюп для работы с данными. Этот язык используется многими организа-
циями и командами разработчиков для выпаса данных, продвинутого статисти-
ческого моделирования и визуализации. Но по таким характеристикам, как дос-
тупность и удобочитаемость, этот язык несколько уступает языку РуЙюп.
Язык
Язык (81шр1е Риегу Ьап§иа§е — простой язык запросов) предназначен для
работы с данными баз данных. Этот язык обладает мощными полезными воз-
можностями, но требует, чтобы обрабатываемые данные были в специальном
формате, что сразу же ограничивает его полезность для выпаса данных.
Язык 8са1а
Язык программирования 8са1а хорошо подходит для работы с наборами данных
большого размера, но его кривая обучения намного круче, чем у языка РуЙюп.
Кроме этого, у него намного меньше сообщество пользователей. То же самое
относится и к языку 1ийа.
Языки и С/С++
Хотя эти языки программирования высокоуниверсальны и имеют большие со-
общества, они не обладают свойствами естественного языка и удобочитаемости,
присущими языку РуЙюп. Кроме этого, они больше направлены на разработку
программного обеспечения, нежели на первичную обработку и анализ данных.
Язык Луа8спр1
Язык 1ауа8спр1 неоценим в веб-среде, и многие популярные средства визуали-
зации (например, ЭЗ) созданы, используя его разновидности. В то же самое вре-
мя, этот язык не обладает таким же обширным набором возможностей анализа
данных, как язык РуЙюп, и, в целом, программы на нем исполняются более мед-
ленно.
Написание и исполнение программ
на языке РуНтои
Для выполнения упражнений в этой главе вам нужно знать, как работать с инстру-
ментами РуЙюп для написания и исполнения программ на этом языке. Также реко-
мендуется организовать систему для резервного копирования и документирования
кода, чтобы не потерять свою работу вследствие ошибочного нажатия клавиши6, и
чтобы можно было с легкостью напомнить себе, что и как делает ваш код, даже по-
сле значительного перерыва в работе с ним. Поскольку для решения этих задач су-
ществует много разных наборов инструментов, я рекомендую начать с ознакомле-
ния с материалом следующих разделов, а затем выбрать подход или комбинацию
подходов, наилучшим образом подходящих вашим предпочтениям и ресурсам.
Сначала вам нужно будет принять ключевое решение, хотите ли вы работать толь-
ко в онлайновом режиме (т. е. используя инструменты и службы, доступные через
Интернет) или же вы хотите и можете работать без доступа к Интернету, установив
эти инструменты на локальной машине.
В зависимости от обстоятельств, мы все пишем по-разному: стиль и структура со-
общения электронной почты будут, скорее всего, иными, чем 8М8-сообщения, а
формат и тон сопроводительного письма заявления о приеме на работу будет пол-
ностью другим, нежели в этих сообщениях. Лично я, в зависимости от решаемой
задачи, также использую разные письменные инструменты. Например, для текстов,
над которыми я работаю совместно с моими сослуживцами и коллегами, я исполь-
зую онлайновые инструменты, но для написания книг и других сочинений я пред-
почитаю пользоваться предельно простым текстовым редактором, установленным
на моем устройстве. А для контрактов и других важных документов, которые мы
хотим защитить от изменения посторонними лицами, применяются более специ-
фичные форматы, например РОГ.
Так же как и естественные человеческие языки, язык программирования РуЙюп мож-
но использовать в документах разных типов, каждый из которых поддерживает слег-
ка иной стиль написания, тестирования и исполнения кода. Основными средствами
работы с кодом на языке РуЙюп являются блокноты и автономные сценарии. Тогда
как для первичной обработки, анализа и визуализации данных можно использовать
документы обоих этих типов, они слегка отличаются своими сильными сторонами и
требованиями. Поскольку преобразование между этими форматами требует некото-
рой модификации исходного документа, упражнения в этой книге предоставляются в
обоих этих форматах. Я применила этот подход не только для того, чтобы предоста-
вить вам возможность выбора наиболее легкого и полезного для вас формата, но
также для того, чтобы вы могли сравнить их и увидеть своими глазами воздействие,
оказываемое на код процессом трансляции. Далее приводится краткий обзор типов
документов кода РуЙюп, чтобы помочь вам сделать начальный выбор.
Блокнот
Блокнот (по!еЪоок) РуЙюп представляет собой интерактивный документ, позво-
ляющий исполнять фрагменты кода, используя в качестве интерфейса окно веб-
браузера. В этой книге мы будем использовать инструмент 1иру1ег для создания,
редактирования и исполнения наших блокнотов РуЙюп7. Ключевым достоинст-
6 Не забывайте, что в языке РуЙзоп даже неправильно поставленный пробел может вызвать ошибку.
7 Это программное обеспечение можно также использовать для создания блокнотов на языке К и
других языках написания сценариев.
вом использования блокнотов для программирования на языке РуШоп является
то, что они предоставляют простой способ для написания, документирования и
исполнения исходного кода на языке РуШоп — все в одном месте. Этот метод
может быть подходящим для тех, кто предпочитает программировать в стиле
”указал и щелкнул” или для кого важно работать исключительно в онлайновом
режиме. К тому же одни и те же блокноты кода РуШоп можно использовать с
минимальными изменениями, как в онлайновой среде разработки, так и на ло-
кальном устройстве. Это означает, что этот подход может быть удобным для
вас, если: 1) у вас нет устройства, на котором можно было бы установить соот-
ветствующее программное обеспечение, или 2) у вас есть такое устройство, но
вы также хотите иметь возможность работать со своим кодом, когда этого уст-
ройства нет при себе.
Файлы автономных сценариев
Файл автономного сценария РуШоп представляет собой любой текстовый файл,
содержащий исходный код на языке РуШоп. Такие файлы можно создавать при
помощи любого простого текстового редактора, но я настоятельно рекомендую
использовать редактор, предназначенный специально для работы с кодом, на-
пример редактор АШш. Я пошагово рассмотрю установку редактора кода далее в
этой главе в разд. "Установка языка Ру1коп, среды <1иру1ег Уо1еЪоок и редакто-
ра кода". Тогда как у вас есть большая свобода выбора программы для написа-
ния и редактирования кода, исполнять файлы автономных сценариев РуШоп,
созданные посредством выбранного вами редактора, можно только на устройст-
ве (например, компьютере или смартфоне), на котором установлен язык про-
граммирования РуШоп. Распознать файлы автономных сценариев РуШоп можно
по их расширению .ру. Хотя подход с использованием файлов автономных сце-
нариев поначалу может казаться более ограничивающим, в действительности он
может иметь некоторые преимущества. В частности, для исполнения этих фай-
лов не требуется подключение к Интернету, а также не нужно загружать данные
в облачное хранилище. Тогда как оба эти аспекта присущи и локальному испол-
нению блокнотов кода РуШоп, для исполнения файлов автономных сценариев
РуШоп также не нужно ожидать запуска какой-либо программы. На компьютере
с установленным языком РуШоп файлы автономных сценариев исполняются
сразу же после их запуска из командной строки (этот вопрос рассматривается
чуть далее). Это особенно полезно в тех случаях, когда нужно регулярно испол-
нять какой-либо сценарий на языке РуШоп. Возможность исполнения фрагмен-
тов кода в блокнотах РуШоп независимо друг от друга может делать этот подход
чуть более удобным. Но то обстоятельство, что код в файлах автономных сцена-
риев РуШоп всегда исполняется ”с нуля”, может помочь вам избежать ошибок
или непредсказуемых результатов, которые могут возникнуть в результате ис-
полнения фрагментов кода в блокноте в неправильном порядке.
Конечно же, вам совсем не обязательно выбирать только один из этих подходов.
Благодаря их интерактивному и удобочитаемому формату большинство пользова-
телей находят блокноты РуШоп особенно полезными для исследования и объясне-
ния данных. А файлы автономных сценариев РуШоп лучше подходят для получения
доступа к данным и их очистки и преобразования, поскольку, например, один и тот
же код в них можно более быстро и легче исполнять с разными наборами данных.
Наверное, более важным критерием будет вопрос, хотите ли вы работать в онлай-
новом или локальном режиме. Если у вас нет устройства, на которое можно уста-
новить язык РуЙзоп, вам придется работать только с облачными блокнотами кода; в
противном случае можно использовать на своем устройстве или блокноты кода
РуЙзоп, или файлы автономных сценариев, или то и другое. Как упоминалось ранее,
блокноты кода РуЙзоп, которые можно использовать или в онлайновом режиме, или
локально, а также файлы автономных сценариев РуЙзоп предоставляются для всех
упражнений в этой книге. Это делается для того, чтобы позволить вам наибольшую
гибкость в их выполнении, а также для того, чтобы предоставить вам возможность
сравнить решение одной и той же задачи, используя каждый из этих подходов.
Работа с кодом Ру№оп на локальном устройстве
Чтобы понимать и исполнять код на языке РуЙзоп, вам нужно установить этот язык
на свое устройство. В зависимости от используемого вами устройства, это можно
сделать, загрузив и запустив на исполнение установочный файл или же исполнив
соответствующую команду в текстовом интерфейсе, называющимся командной
строкой (при использовании локально установленного языка РуЙзоп вам рано или
поздно придется работать с этим интерфейсом). Нашей целью является установка
по крайней мере версии 3.9 языка РуЙзоп8. После установки на своем устройстве
языка РуЙзоп необходимо для работы с блокнотами кода установить программу
среды разработки 1иру1ег Мо1еЪоок, а также редактор кода (далее в этой главе при-
водятся инструкции по установке редактора кода А1от). Если вы планируете рабо-
тать только в онлайновом режиме, то для ознакомления с информацией, как при-
ступить к работе в этом режиме, можете пропустить следующий материал и перей-
ти сразу же к разд. "Работа с кодом Ру1коп в режиме онлайн ".
Введение в работу с командной строкой
Если вы планируете работать с языком РуЙзоп локально, то вам нужно научиться
использовать интерфейс командной строки (который также называется термина-
лом или консолью), который представляет собой способ подачи команд компьютеру
в виде текстовых строк. В принципе, все, что можно делать посредством команд-
ной строки, можно делать и при помощи мыши, однако командная строка особенно
удобна для установки кода и программного обеспечения (особенно библиотек
РуЙзоп, которые мы будем использовать на всем протяжении этой книги), а также
8 Номера версий языка РуШоп последовательно возрастают по мере его изменения и обновления с
течением времени. Первая цифра (в данном случае 3) обозначает основной номер версии, а вторая (в
данном случае 9) — дополнительный. Точка в обозначении версии не является десятичным разде-
лителем, и дополнительный номер версии может быть больше, чем 9. Поэтому вполне возможно, что
в будущем мы увидим версию, например, 3.12.
для создания резервных копий и исполнения кода. Хотя к работе с командной стро-
кой нужно немного привыкнуть, для выполнения многих задач программирования
этот метод более быстрый и прямолинейный чем использование мыши. Я буду
предоставлять инструкции для использования, как командной строки, так и мыши,
в случаях, допускающих использование обоих методов. Таким образом, у вас будет
возможность выбрать тот метод, который для вас будет более удобным для реше-
ния конкретной задачи.
Для начала работы откроем интерфейс командной строки (или, как его еще назы-
вают, терминал) и с его помощью создадим папку для хранения всей нашей работы
по выпасу данных. Для СйгошеЬоок, шасО8 или 1лпих, выполните поиск по
’ЧегштаГ’ и в результатах поиска выберите приложение Тегшша!. Для \^тс1о\У8
выполните поиск по ”ро\уег81зеП” и выберите программу УУшсКуоз Роууег8йе11.
а Для использования Хлпих на устройстве СйгошеЬоок нужно открыть меню
настроек операционной системы Сйгоше, щелкнув по значку шестеренки
в меню 8€аг€ или выполнив поиск по ”8еИт§8” в строке запуска программ.
В открывшемся окне настроек щелкните по небольшому значку пингвина,
обозначенному ’Ътих (Ве1а)”, и следуйте соответствующим инструкциям.
Чтобы продолжить работу, может потребоваться перезагрузить устройство.
Прежде чем создать нашу папку, рассмотрим вкратце несколько основных команд
для работы с файловой системой.
18
Сокращение слова (список). Выводит список файлов и папок в текущей пап-
ке. Это текстовая версия содержимого, отображаемого в окне Гтйег (или про-
водника \^тс1о\У8).
сс! название_папки
Сокращение от скап^е (Нгес1огу (изменить директорию/папку). Выполняет пере-
ход из текущей папки в папку, указанную в параметре название_папки, при усло-
вии, что целевая папка находится в списке, выводимом командой 1з. Это эквива-
лентно двойному щелчку мышью по значку папки в окне Гшйег (или проводни-
ка \^тс1о\У8).
ед ../
Та же самая команда для перехода в другую папку, но параметр ../ означает
папку высшего уровня, содержащую текущую папку.
са у
Еще одна команда для перехода в другую папку, но параметр ~/ означает корне-
вую (или домашнюю) папку.
пксНг название_папки
Сокращение от таке сИгес1огу (создать директорию/папку). Создает в текущей
папке новую папку с названием название_папки. Это эквивалентно выбору после-
довательности команд Создать | Папку в контекстном меню окна Гшйег (или
проводника А^тс1о\У8) с последующим присвоением названия созданной папке.
При работе с командной строкой необязательно указывать полное назва-
ние файла или папки. Можно просто ввести несколько начальных симво-
лов названия (не забывая при этом, что в Ыпих названия чувствительны к
регистру), а затем нажать клавишу <ТаЬ>. В результате система насколь-
ко возможно автоматически заполнит остальные символы.
Предположим, что текущая папка содержит два файла, один из которых
называется х18_раг8ш§.ру, а другой — х18х_раг8ш§.ру. Чтобы запустить
один из этих файлов, в командной строке можно начать вводить команду
руЫтоп х1, а затем нажать клавишу <ТаЬ>. В результате система автомати-
чески завершит название файла, но всего лишь до части руМтоп х15. Это
объясняется тем, что система не знает, какой из двух возможных файлов
мы имеем в виду, и нам нужно указать его самим, добавив символ для
первого файла или ”х” для второго, а затем снова нажать клавишу <ТаЬ>.
При открытии окна терминала текущей папкой становится корневая папка данного
пользователя. На Ыпих, шахО8 и \Ыпс1о\У8 такой папкой в большинстве случаев
будет папка, называющаяся по имени текущего пользователя, а не область рабочего
стола, отображающаяся при входе в систему. Поначалу это может немного сбивать
с толку, поскольку список файлов и папок, выводимый при первом исполнении ко-
манды в окне терминала, будет, скорее всего, выглядеть для вас незнакомым. Но
это легко исправить, просто выполнив переход на рабочий стол, введя в терминал
команду:
сб -/ОезкЬэр
и нажав клавишу <Еп1ег>.
На СйгошеЬоок программы РуШоп (а также другие требующиеся нам программы)
можно исполнять только из папки Ыпих, поэтому в действительности у вас не по-
лучится перейти на рабочий стол и нужно будет запустить терминал (т. е. интер-
фейс командной строки).
Теперь выполните в терминале следующую команду:
пксНг ба1:а_мгапд11пд
В результате должна создаться папка <1а1:а_мтап§т§. Если же что-то пошло не так, и
папка не была создана, еще раз проверьте правильность ввода команды создания
папки. Также убедитесь, что вы находитесь на рабочем столе, проверив, что слева
от символа командной строки ($ — в СйгошеЪоок, % — в шасО8 и > — в \Ыпс1о\У8)
стоит слово Оезк^ор. Если нет, то снова выполните команду сб -/ОезкТюр, а затем ко-
манду создания папки пкс11г с!а1:а_мгапд11пд.
Пробелы —зло!
Почти все операционные системы позволяют использовать пробелы в назва-
нии файлов и папок, но я настоятельно рекомендую не использовать в них ни
пробелов, ни прочих знаков препинания, кроме символа подчеркивания (_).
Как мы увидим далее в главе 2. пробелы и знаки пунктуации используются в
командной строке и языке РуЙюп для обозначения специальных функцио-
нальностей. Это означает, что для использования их в названиях файлов или
папок они должны предшествоваться управляющими символами (езсаре
8ушЬо18), обычно символом обратной косой черты (\). Поэтому мы не сможем
создать папку с1а±а мгапдПпд из командной строки. В результате попытки сде-
лать это следующей командой:
пксйг с!а±а мгапдНпд
будет создано две папки, одна с названием с!а1:а, а другая с названием мгапдИпд.
Папку с пробелом в названии можно создать в проводнике файловой системы
с графическим интерфейсом, но для доступа к этой папке из командной строки
нужно будет использовать управляющий символ перед пробелом:
сс1 с1а-Ьа\ мгапдИпд/
Так что, хотя использование пробелов в названиях элементов файловой системы
в принципе возможно, от этой возможности больше проблем, чем пользы. Во из-
бежание этих проблем будет легче просто выработать привычку не использовать
пробелов и символов препинания в названиях объектов файловой системы, а
также в названиях переменных языка РуЙюп.
Теперь, когда у нас есть немного опыта работы с командной строкой, можно по-
пробовать применить этот опыт для установки и тестирования языка РуЙюп на ло-
кальной машине.
Установка языка РуНтоп, среды ^ру1ег Мо(еЬоок
и редактора кода
Ради простоты для установки требуемого нам программного обеспечения мы вос-
пользуемся диспетчером распределения программного обеспечения Мпйсопба, ко-
торый автоматически установит язык РуЙюп и среду разработки 1иру1ег Мо1еЬоок.
Даже если вы не планируете использовать блокноты кода РуЙюп в своей работе,
они достаточно популярны, поэтому будет полезным иметь возможность просмат-
ривать и исполнять блокноты других пользователей. Кроме этого, соответствующее
приложение не займет слишком много места на вашем жестком диске. Кроме уста-
новки языка Руйюп и среды разработки 1иру1ег Мо1еЪоок, диспетчер Мгшсопба так-
же создает новую функцию командной строки сопс1а для быстрого и легкого обнов-
ления этих программ9. Подробная информация о том, как выполнять эти обновле-
ния, приводится в приложении А.
Даже если вы планируете выполнять большую часть работы программирования на
языке РуЙюп, используя блокноты, я все равно рекомендую вам установить редак-
тор кода. Даже если вы никогда не напишете в нем ни одной строки кода, он будет
9 Диспетчер Мппсопба представляет собой урезанную версию диспетчера Апасопба. Мы используем
его, чтобы сэкономить дисковое пространство, не устанавливая язык программирования К и
некоторые другие инструменты, которые нам не нужны, но устанавливаются диспетчером Апасопба.
незаменим для просмотра, редактирования и даже создания файлов данных более
эффективно, чем это возможно, используя встроенные текстовые редакторы боль-
шинства устройств. Что более важно, редактор кода обладает функциональностью
выделения синтаксических элементов кода, что, по сути, является встроенным
средством проверки синтаксиса кода и данных. Хотя это может казаться не такой и
важной возможностью, в действительности она значительно ускорит процессы ко-
дирования и диагностирования, поскольку в случае ошибок в коде позволит точно
определить, где они находятся. Благодаря этой комбинации возможностей хороший
редактор кода является одним из самых важных инструментов для программирова-
ния на языке РуЙюп в частности и для выпаса данных в целом.
В этой книге используется многоплатформный бесплатный редактор кода с откры-
тым исходным кодом А1ош (Ьйр8://а€ош.1о). Редактируя параметры этого редакто-
ра, его можно с большой точностью настроить под свои требования. Упоминаемые
в этой книге цвета определенных символов или фрагментов кода относятся к ис-
пользуемой по умолчанию теме Опе Вагк редактора А1ош, но вы можете использо-
вать любые предпочитаемые вами настройки.
Для реализации последующих процессов установки программного обес-
печения вам потребуется стабильное подключение к Интернету с высо-
кой пропускной способностью и около 30-60 минут времени. Я настоя-
тельно рекомендую подключить свое устройство к сетевому источнику
питания, а не использовать питание от встроенной батареи.
Установка на устройство СЬготеЬоок
Для установки набора инструментов для выпаса данных на устройство СйгошеЪоок
первым делом нужно определить разрядность его операционной системы Сйгоше
08, т. е. является ли она 32- или 64-разрядной.
Чтобы получить эту информацию, нужно открыть меню настроек операционной
системы Сйгоше (щелкнув значок шестеренки в меню 8€аг€ или выполнив поиск по
строке ”8еШп§8” в строке запуска программ), а затем щелкнуть по ссылке АЬои€
СЬготе 08 в левой нижней части окна. В верхней части окна отобразится сообще-
ние с названием операционной системы и указанием разрядности, как показано на
рис. 1.1.
Запишите или запомните эту информацию, прежде чем продолжать установку.
6оод1е Сйготе 03
л Уоиг СИготеЬоок 1з ир 1о йаГе
® Уегз5оп 86.0.4240.112 (01Т|С1а1 ВиПй) (64-ЬП)
Рис. 1.1. Сообщение с информацией о версии
и разрядности операционной системы СЬготе 08
Установка языка РуНюп и среды разработки ЛируДег ]Чо1:еЬоок. Первым делом
загрузите версию установщика для Ыпих диспетчера Мписопба, соответствующего
разрядности вашей операционной системы Сйгоше 08 (Ьйр8://(1ос8.соп(1а.1о/еп/
1а€е8€/тш1соп(1а.к€т1#1а€е8€-тт1сопс1а-ш81:а11ег-1тк8). Затем переместите загру-
женный файл установщика (который будет иметь расширение .811) из папки
Вомлйоаск в папку Ыпих Й1ез.
Далее запустите программу терминала и исполните в нем команду Ы, чтобы убе-
диться в наличии файла установщика диспетчера Мпйсопба в папке. Если файл на
месте, запустите установщик, исполнив следующую команду (если вы помните, то
можно ввести только первые несколько символов названия файла, а затем нажать
клавишу <ТаЪ>, и остальная часть будет заполнена автоматически):
ЬазЬ _М1п1сопс1а_1п51:аПа1:1оп_’Г11епате_. зЬ
Следуйте инструкциям, выводимым в окне терминала (в частности, согласитесь с
условиями лицензии и папкой для установки диспетчера). По завершению установ-
ки выполните следующую команду:
сопс!а тпт±
После этого закройте и снова откройте окно терминала и установите среду разра-
ботки 1иру1ег Мо1еЬоок, исполнив следующую команду:
сопс1а гп51аИ диру1:ег
Ответьте ”уе8” на все последующие запросы и по завершению установки закройте
окно терминала. Установка языка РуШоп и среды разработки 1иру1ег Мо1еЪоок за-
вершена.
Установка редактора кода А€ош. Первым делом загрузите файл ЛеЪ установщика
редактора А1ош с его веб-сайта (кир8://а€от.1о) и сохраните (или переместите) его
в папку Ыпих Й1ез вашего устройства СйгогпеЬоок.
Чтобы установить программу, откройте терминал, перейдите в папку, содержащую
файл установщика, и исполните в терминале следующую команду10:
зис1о с1ркд -I аШп-атс1б4.с1еЬ
Установка будет завершена, когда в окне терминала перестанет прокручиваться
текст и возвратится приглашение на ввод команды терминала (заканчивающееся
символом $).
Можно также выполнить щелчок правой кнопкой мыши по файлу установщика и в
открывшемся контекстном меню выбрать опцию 1п8€а11 Ыпих, щелкнуть
1п8€а11 в следующем окне, а затем нажать кнопку ОК. В процессе установки в ниж-
ней правой части экрана будет отображаться индикатор хода установки, а по ее за-
вершению выведется соответствующее сообщение.
Независимо от выбранного метода установки, по ее завершению в разделе Ыпих
арр8 меню запуска команд должен появиться зеленый значок редактора кода А1ош.
10 Для 32-разрядных систем СИготе 08 это название файла может быть немного другим.
Установка на тас08
Установить диспетчер Мтшсопба на тасО8 можно двумя способами: из терминала,
используя файл установщика с расширением .811, или в графическом интерфейсе,
загрузив пакет установщика с расширением .рк§, а затем выполнив по нему двой-
ной щелчок мышью.
Установка языка РуНюп и среды разработки ЛируТег ]Чо1:еЬоок. Первым делом
откройте в браузере страницу со ссылками на файлы установщика диспетчера
Мгшсопба (Ьйр8://(1ос8.соп(1а.1о/епЛа1:е8€/тт1соп(1а.Ы:т1#1а€е8€-тт1соп(1а-1П81:а11ег-
Ипк8). Для установки из терминала загрузите файл установщика с расширением .8Й,
а для установки из графического интерфейса загрузите файл установщика с расши-
рением .рк§. (В процессе загрузки файла установщика может отобразиться сообще-
ние с предупреждением, что ТЫ8 1уре оГ Й1е сап Натт уоиг сотри^ег (Этот файл
может причинить вред вашему компьютеру). Игнорируйте его, нажав кнопку
Кеер.)
Для обоих методов установки переместите загруженный файл установщика из пап-
ки Вомлйоаск на свой рабочий стол.
Для установки диспетчера Мгшсопба при помощи терминала откройте окно терми-
нала и перейдите на рабочий стол, выполнив следующую команду:
сс1 ~/Оезк1:ор
Затем исполните команду 1з, чтобы убедиться в том, что файл .811 установщика дис-
петчера Мпйсопба действительно находится в папке рабочего стола. Если файл на
месте, то запустите установщик, исполнив следующую команду (если вы помните,
то можно ввести только первые несколько символов названия файла, а затем на-
жать клавишу <ТаЬ>, и остальная часть будет заполнена автоматически):
ЬазЬ _М1п1сопс1а_1п81:аиа1:1оп_Г11епате_. зЬ
Далее следуйте выводимым в окне терминала инструкциям:
♦ нажимая клавишу пробела, пролистайте лицензионное соглашение до конца
(пока не увидите слово (ЕМО), а затем нажмите клавишу <Еп1ег>;
♦ согласитесь с условиями лицензионного соглашения, введя уев и нажав клавишу
<Еп1ег>;
♦ согласитесь с папкой установки по умолчанию, нажав клавишу <Еп1ег>, а затем
введите уез и нажмите клавишу <Еп1ег>, чтобы принять приглашение сопс1а 1п1Л.
Наконец, закройте окно терминала.
Для установки с использованием графического пользовательского интерфейса
просто выполните двойной щелчок по файлу .рк§ установщика и следуйте выводи-
мым инструкциям.
Завершив установку диспетчера Мгшсопба, снова откройте окно терминала и вы-
полните в нем следующую команду:
сопс!а 1п11:
Теперь можно установить среду разработки 1иру1ег Мо1еЪоок. Для этого закройте и
снова откройте окно терминала и исполните следующую команду:
сопс1а гпз^аП. диру1:ег
На все выводимые запросы отвечайте уев.
Установка редактора кода А€ош. Первым делом загрузите файл установщика ре-
дактора А1ош с его веб-сайта (Ьир8://а€ош.1о), нажав большую желтую кнопку
Волуп1оас1.
В папке Вомлйоаск распакуйте файл архива а1ош-шас.х1р, а затем перетащите при-
ложение А1ош (обозначенное зеленым значком в виде стилистического изображе-
ния атома) в папку Аррйсабопз (для этого может потребоваться ввести свой па-
роль).
Установка на УУ|пс1оа/уз 10+
Для установки набора инструментов для выпаса данных на \^тбо\У8 10+ первым
делом нужно определить разрядность используемой операционной системы, т. е.
является ли она 32- или 64-разрядной.
Для этого откройте меню Пуск и нажмите в нем на значок в виде шестеренки,
чтобы открыть меню настроек. В левой части окна настроек выберите последова-
тельность команд Система | О системе. Информация о разрядности операцион-
ной системы указывается в элементе Тип системы в разделе Параметры устрой-
ства. Официальные инструкции компании Мтсгозой для решения этой задачи
приводятся в соответствующем разделе ответов на часто задаваемые вопросы
(Ъйр8://8иррогкт1сго8ОЙ.сот/еп-и8/лутДолУ8/32-Ьй-апД-64-Ьй-лутДолУ8-
ГгедиепЯу-а8ке(1-дие8йоп8-с6са9541-8(1се-4(148-0415-94аЗГаа2е13(1).
Запишите или запоните эту информацию, прежде чем продолжать установку.
Установка языка РуНюп и среды разработки ЛируДег ]Чо1:еЬоок. Первым делом
загрузите со страницы загрузок Мпйсопба (кйр8://(1ос8.соп(1а.1о/еп/1а1:е8€/
шш1соп(1а.к€ш1#1а€е8€-тт1сопс1а-т81:а11ег-Ипк8) файл установщика РуШоп 3.9, со-
ответствующий разрядности операционной системы используемого вам компьюте-
ра (32- или 64-разрядная). Запустите загруженный файл на исполнение и следуйте
выводимым инструкциям, принимая все предустановленные опции. (Можно про-
пустить просмотр рекомендуемых руководств и подписку на Апасопба Мис1еи8 в
конце процесса установки.)
По завершении установки вверху списка раздела Недавно добавленные (Кесепйу
аббеб) меню Пуск должны появиться два новых элемента: Апасопйа Рготр€
(тпнсопйаЗ) и Апасопйа Роууег8Йе11 Рготр1: (тпнсопйаЗ), как показано на рис. 1.2.
Для наших целей можно использовать любое из этих двух приложений, но лично я
рекомендую использовать интерфейс терминала Ро\уег811е11 для всей работы в этой
книге.
ВесепЙу
Апасоп^а Ргогпр!: [пл1пкоп^аЗ)
ГП Апасоп^а Роугег5Не11 Ргатр1 (тЫсо...
Рис. 1.2. Опции интерфейса Апасопс1а в меню Пуск
Завершив установку диспетчера Мпйсопба, откройте окно терминала РохуегзНеП и
выполните в нем следующую команду:
сопба гпИ
Теперь можно установить среду разработки 1иру1ег Мо1еЪоок. Для этого закройте и
снова откройте окно терминала и исполните следующую команду:
сопба гпз^аП. диру1:ег
В последующем ходе установки дайте утвердительный ответ на все запросы, вводя
с клавиатуры уев (или у).
Установка редактора кода А€ош. Первым делом загрузите файл установщика ре-
дактора А1ош с его веб-сайта (Ьир8://а€ош.1о), нажав большую желтую кнопку
Волуп1оай.
Запустите загруженный установщик на исполнение, выполнив двойной щелчок
мышью по его файлу, и следуйте выводимым инструкциям. По завершении уста-
новки должен автоматически запуститься редактор А1ош. На запрос о том, зареги-
стрировать ли редактор А1ош в качестве обработчика по умолчанию идентифика-
торов 1Ж1 а!ош://, можно дать утвердительный ответ.
Проверка успеха установки
Чтобы проверить, что установленные программные средства РуЙюп и 1иру1ег
Мо1еЪоок работают должным образом, откройте окно терминала и перейдите в нем
в папку ба1а_\угап§11п§, которую мы создали в разд. "Введение в работу с команд-
ной строкой"11'.
сб ~/Оезк1:ор/ба1:а_мгапд11пд
Затем исполните следующую команду:
ру1±оп --7егз1оп
В результате в окне терминала должна вывестись строка:
Ру1±оп 3.9.4
Это означает, что установка языка РуЙюп была успешной. Далее проверим установку
среды разработки 1иру1ег Мо1еЪоок, исполнив в терминале следующую команду:
диру1:ег по1:еЬоок
11 Если не указано обратное, то в конце всех команд терминала нужно нажимать клавишу <Еп1ег> (или
<Ке1ит>).
Если в результате откроется окно12, как показано на рис. 1.3, то установка среды
1иру1ег Мо1еЬоок была выполнена успешно.
Рис. 1.3. Средство ЗируТег ГЧоТеЬоок,
исполняющееся с пустой папкой
Работа с кодом Ру11топ
в режиме онлайн
Если вы не хотите морочиться с установкой языка РуЙюп и редактора кода на ло-
кальной машине (и если вы планируете использовать РуЙюп только при наличии
надежного подключения к Интернету с хорошей пропускной способностью), то ра-
ботать с блокнотами 1иру1ег можно в онлайновом режиме, используя средство
Ооо§1е Со1аЬ. Для этого вам нужна лишь неограниченная (ипге8йтс1ей) учетная за-
пись Ооо§1е. (Для работы с 1иру1ег Мо1еЬоок можно создать отдельную учетную
запись. Только не забудьте пароль к ней.)
Не11о УУоНсН
Теперь, когда мы имеем в своем распоряжении все необходимые средства для вы-
паса данных, мы можем попробовать написать и исполнить нашу первую програм-
му на языке РуЙюп. Мы не будем оригинальничать с решением этой задачи, а по-
следуем установившейся традиции и создадим простую программу, которая выво-
дит на экран текст НеПо \^ог1й! (Здравствуй, мир!). Для начала нам нужно создать
файл для написания и сохранения соответствующего кода.
Создание файла автономного сценария Ру№оп
при помощи А1от
Редактор кода А1ош работает так же, как и любая другая программа для редактиро-
вания текста; ее можно запустить при помощи мыши или из командной строки
(терминала).
12 Если отобразится запрос о том, как открыть данный файл, я рекомендую выбрать Ооо§1е Сйгоше.
Для запуска программы при помощи мыши нужно найти ее значок на используе-
мом устройстве:
СкготеЪоок
Значок находится в рамке Приложения Ыпих (Ыпих аррз).
Мас
Значок находится в папке Приложения (Аррйсайопз) или в средстве Ьаипсйраск
ТРМомя
Значок находится в меню Пуск или его можно найти, выполнив поиск по слову
А1от в строке поиска этого меню. Если после первой установки на АУтсклуз 10
редактора кода А1ош его значок отсутствует в меню Пуск и в результатах поис-
ка, выяснить причину этой проблемы можно с помощью диагностических ви-
деоклипов на УоиТиЪе (ййр8://уоиЫЬе.сотЛуа1сй?у=К411ГдзК9пМ).
Также редактор А1ош можно запустить, просто исполнив в окне терминала соот-
ветствующую команду:
а1:от
При первом запуске редактора на устройстве СйгошеЪоок выводится сообщение о
создании пароля для нового набора ключей СЬоозе раззууогй Гог пелу кеугш§. По-
скольку мы будем использовать редактор А1ош только для редактирования кода и
данных, нам такой набор ключей не нужен. Поэтому просто закройте это сообще-
ние, нажав кнопку Сапсе1. На компьютерах шасО8 выводится предупреждение о
том, что данная программа была загружена из Интернета. Это сообщение также
можно игнорировать.
' С *
Рог Нещ. р1м ее
ТЬе Л|йГпййй МгСиЙеа аПЛ 1Не ДИ РёГИЪпее.
Теш еикепИу СпцКу . Пи Лип (агат .1 Лкия.аГот. кз
. Пи Двиг» пгд. Пив ц. «Неге а11 6ИНиЬ-ргва1к1' А Ют рас кпд ₽5
1Ь.чрсп г» ® *№1мтс Бик» ^Неп оси;тпд А1йт
Рис. 1.4. Окно приветствия редактора кода А1от
В конечном итоге мы получим окно приветствия редактора, пример которого пока-
зан на рис. 1.4.
По умолчанию при запуске редактор кода А1ош показывает одно или несколько
вкладок приветствия. Эти вкладки можно просто закрыть обычным способом,
щелкнув по значку крестика справа от названия вкладки. (В старых версиях А1ош
этот значок появляется при наведении курсора мыши на название вкладки.) В ре-
зультате в центре окна останется только вкладка нового файла ипбйес!. При жела-
нии можно также свернуть и панель Рго]ес1 с левой стороны окна. Для этого нужно
навести курсор мыши на эту панель, а затем щелкнуть по значку <, который поя-
вится посредине правой стороны этой панели.
Прежде чем приступать к написанию какого-либо кода, сохраним наш файл там,
где мы будем знать, где найти его, — в нашей папке с!а1а_\угап§1т§. Для этого в
меню ГПе выбираем опцию 8ауе аз, в открывшемся окне навигации по файловой
системе переходим в эту папку, затем в поле названия файла ГПе пате внизу окна
вводим название файла НеПоА^огШ.ру и нажимаем кнопку 8ауе.
При сохранении файлов автономных сценариев РуЙюп в конце их на-
звания необходимо добавлять расширение .ру (это английские буквы ру,
а не русские ру). Хотя код РуЙюп будет работать и без этого расшире-
ния, его наличие позволит редактору кода А1ош выполнять выделение
синтаксических элементов кода, которое упоминалось ранее в этой гла-
ве в разд. "Установка языка Ру1коп, среды <1иру1ег Уо1еЬоок и редакто-
ра кода ". Эта возможность значительно облегчит правильное написание
кода с первого же раза.
Создание блокнота кода Ру№оп в Эиру1ег Мо(еЬоок
Как вы, возможно, заметили при тестировании установки среды разработки 1иру1ег
Мо1еЪоок в разд. "Проверка успеха установки", в этой среде используется интер-
фейс в виде окна простого браузера. В действительности, при запуске этой среды
установки посредством исполнения команды диру1:ег псйеЬоок на локальном компь-
ютере создается миниатюрный веб-сервер13. В результате можно создавать новые
файлы РуЙюп и исполнять другие команды прямо в браузере.
Первым делом запустите программу терминала и перейдите в папку Йа1а_\угап1§т§,
исполнив команду:
сс1 ~/Оезк1:ор/с1а1:а_мгапд11пд/
Затем запустите среду разработки 1иру1ег Мо1еЪоок, исполнив команду:
диру1:ег псйеЬоок
Сначала в окне терминала будут отображаться разные информационные сообщения
о ходе запуска программы, и в конечном итоге в используемом по умолчанию брау-
зере откроется новая вкладка, содержащая список папок и файлов. В данном случае
13 Не волнуйтесь, это веб-сервер, не видимый из Интернета.
этот список будет пустой. В правом конце заголовка списка нажмите меню Ке\т и в
открывшемся списке выберите опцию РуПюпЗ (1рукегпе1). Откроется новая вклад-
ка браузера, называющаяся ПиШес!. Выполните двойной щелчок мышью по назва-
нию вкладки справа от логотипа 1иру1ег в левом верхнем углу страницы и в от-
крывшемся диалоговом окне переименуйте файл блокнота на НеПоА^огМ.
Поскольку, как упоминалось ранее, при запуске ЗируТег МоТеЬоок также
запускается и локальный веб-сервер, то важно не закрывать окно тер-
минала, из которого была запущена эта среда разработки, пока вы ра-
ботаете в ней. Закрытие этого окна остановит работу локального серве-
ра, и доступ к среде будет утерян.
К счастью, блокноты ЗируТег автоматически сохраняются каждые две
минуты, поэтому даже в случае сбоя приложения объем потерянной ра-
боты не будет слишком большим. При всем этом может быть полезным
свернуть окно терминала, из которого была запущена среда разработки
ЗируТег МоТеЬоок, чтобы случайно не закрыть его в процессе работы.
Создание блокнота кода Ру№оп в Соод1е Со1аЬ
Первым делом войдите в свою учетную запись (Зоо§1е, которую вы хотите исполь-
зовать для работы по выпасу данных, а затем посетите веб-сайт Со1аЪ
(Ьйр8://со1аЬ.ге8еагсй.§оо§1е.сот). Откроется веб-страница с наложенным на нее
окном, как показано на рис. 1.5.
В правом нижнем углу наложенного окна щелкните по ссылке Ке\т по^еЬоок. От-
кроется новая страница ипбДесЮлрупЪ. Выполните двойной щелчок по названию
страницы в левой верхней части страницы и переименуйте ее на НеПоА^огШлрупЪ14.
Рис. 1.5. Домашняя страница 6оод!е Со!аЬ
14 Ранняя версия 1иру1ег ШГсЬоок называлась 1РуЙюп ШГсЬоок, что объясняет использование расши-
рения имени файла лрупЬ.
Создаем программу
Теперь создадим нашу первую программу, которая просто выводит на экран текст
”Не11о А^огШ!”. Независимо от используемого вами подхода для создания програм-
мы, код для нее одинаковый (листинг 1.1).
Листинг 1.1. Программа КеНомоНб.ру
# следующий код выводит на экран текст "Не11о 1л1ог1с1!"
ргтп^С'НеНо 1л!ог1б!")
В файле автономного сценария
Просто скопируйте (или введите вручную) в файл код в листинге 1.1 и сохраните
файл.
В блокноте
При создании нового файла блокнота РуЙюп в нем по умолчанию создается пустая
ячейка для кода. В 1иру1ег Мо1еЪоок эта ячейка обозначена меткой 1п [] слева от
нее, а в (Зоо§1е Со1аЪ — значком исполнения кода (направленным вправо треуголь-
ником). Скопируйте (или введите вручную) в эту ячейку код в листинге 1.1.
Исполняем программу
Теперь, когда мы создали и сохранили нашу программу на языке РуЙюп, мы можем
исполнить ее.
В файле автономного сценария
Запустите программу терминала и перейдите в папку ба1а_\угап§1т§, исполнив сле-
дующую команду:
сс! ~/Оезк1:ор/с1а1:а_мгапд11пд
Исполните команду 1з, чтобы убедиться в том, что папка содержит файл програм-
мы НеПоА^огМ.ру. Наконец, запустите программу на исполнение, исполнив в тер-
минале следующую команду15:
ру1±оп НеПо1л1ог1с1.ру
В результате на экране в новой строке должен отобразиться текст ”Не11о А^огШ!”,
после чего снова возвратится строка запроса команды (означая завершение испол-
нения программы).
15 Возможно, вместо ру1±оп нужно будет ввести руЩопЗ.
В блокноте
Нажмите кнопку запуска на исполнение (находится в панели инструментов в
1иру1ег Хо1еЪоок и слева от ячейки кода в (Зоо§1е Со1аЪ). Под ячейкой кода должен
отобразиться текст ”Не11о А^огШ!”.
Если все сработало, как описано, тогда я вас поздравляю с вашей первой програм-
мой на языке РуЙюп.
Документирование, сохранение
и управление версиями своего кода
Прежде чем забираться в настоящие дебри программирования на языке РуЙюп в
главе 2. нам нужно выполнить несколько приготовлений. Это может показаться вам
нудным и утомительным, но подготовка к правильному документированию своей
работы сэкономит вам в будущем десятки часов напрасного труда и убережет вас
от расстройства. Более того, аккуратное комментирование, сохранение и управле-
ние версиями своего кода является важной частью обеспечения надежной работы
первичной обработки данных. И хотя на данном этапе это не совсем увлекательное
занятие, довольно скоро все эти шаги станут для вас второй натурой (я обещаю), и
вы увидите, насколько это повысит скорость и эффективность вашей работы с дан-
ными.
Документирование
Возможно, вы заметили, что при исполнении нашей первой программы (см. лис-
тинг 1.1) на экране отобразился только текст ”Не11о \^ог1б!”, но не ее первая строка
кода. Это объясняется тем, что эта строка кода является комментарием, который
простым языком объясняет назначение данного кода. Почти все языки программи-
рования (а также некоторые типы данных) поддерживают возможность вставки
комментариев, поскольку это отличный способ предоставить любому пользовате-
лю16, исследующему ваш код, необходимый контекст и объяснение, чтобы он мог
понять назначение вашей программы или раздела кода в ней.
Хотя многие программисты имеют склонность обделять вниманием (читай игнори-
ровать) процесс комментирования своего кода, это, наверное, самая ценная при-
вычка в программировании, которую можно выработать. Комментирование не
только сэкономит вам и любому другому вашему товарищу по работе огромное
количество усилий и времени при исследовании кода РуЙюп. Это также наилучший
способ по-настоящему усвоить все, что вы изучаете о программировании. Поэтому,
хотя примеры кода в этой книге уже снабжены комментариями, я настоятельно ре-
комендую переписать их своими словами. Таким образом, если в будущем вам
16 И вам самим впоследствии.
придется снова просматривать эти файлы, они будут содержать подробный анализ
вашего понимания каждой задачи кодирования при встрече с ней.
Другим важным процессом документирования для выпаса данных является веде-
ние, как я его называю, "дневника данных”. Подобно личному дневнику, дневник
данных можно вести и упорядочивать любым предпочитаемым вами образом. Здесь
главной задачей является зафиксировать то, что вы делаете, по мере того, как вы
это делаете. Будь то просмотр различных веб-страниц в поисках данных, отправле-
ние сообщений электронной почты экспертам по интересующим вас вопросам или
разработка программы, вам нужно вести учет всех эти активностей, поскольку вы
вскоре забудете, когда и зачем вы их выполняли.
Первой записью в таком дневнике для любого проекта по выпасу данных должен
быть вопрос, на который вы пытаетесь получить ответ. Хотя это может быть и
трудно, пытайтесь сформулировать этот вопрос в одном предложении и запишите
его вверху своего дневника проекта. Почему важно сформулировать этот вопрос
одним предложением? Потому что в процессе настоящего выпаса данных вы опус-
титесь в достаточно большое количество "заячьих нор" (например, в поиске ответа
на вопрос источника ваших данных или пытаясь решить какую-либо задачу про-
граммирования), что будет легко потерять из виду, чего вы исходно пытались до-
биться и почему. Но когда этот вопрос занимает место вверху вашего дневника
данных, вы всегда легко вспомните главную задачу.
Целевой вопрос дневника данных также окажет вам значительную помощь в при-
нятии решений о том, на что тратить свое время в процессе выпаса данных. Напри-
мер, если обрабатываемый набор данных содержит незнакомые вам термины, то
следует ли пытаться выяснить значение каждого из них? Да, если это поможет по-
лучить ответ на ваш вопрос. В противном же случае может быть пора переходить к
другой задаче.
Успешно ответив на свой вопрос (хотя бы частично), вы почти наверняка обнару-
жите наличие других вопросов, на которые вы захотите ответить, Или вы захотите
ответить на этот же вопрос спустя неделю, месяц или год. Наличие вашего дневни-
ка данных под рукой поможет вам в следующий раз сделать это намного быстрее и
легче. Но это не означает, что такой подход не требует усилий. Исходя из моего
опыта, ведение дневника данных увеличивает время первого исполнения проекта
на 40%, но ускоряет его повторное исполнение (например, с новой версией набора
данных) по крайней мере в два раза. Дневник данных также можно использовать в
качестве ценного доказательства проделанной работы: если вам когда-либо потре-
буется информация о процессе, посредством которого вы получили свои результа-
ты выпаса данных, вы (или любой другой) сможете найти эту информацию в своем
дневнике данных.
Что касается формата, в котором вести свой дневник данных, то этот вопрос полно-
стью на ваше усмотрение. Некоторым нравится использовать большой объем вы-
чурного форматирования, тогда как для других достаточно простого текстового
файла. Можно даже использовать простую тетрадь — используйте то, что удобно
для вас. Тогда как ваш дневник данных будет ценным источником справочной ин-
формации при обсуждении с другими ваших данных (и процесса их обработки),
вести его нужно в подходящем именно вам формате.
Сохранение
Кроме тщательного документирования своей работы посредством комментариев в
коде и ведения дневника данных, обязательно регулярно сохраняйте ее. К счастью,
процесс сохранения, по сути, встроен в рабочий поток: блокноты выполняют со-
хранение автоматически через регулярные интервалы времени, а для исполнения
кода в файле автономного сценария сначала нужно сохранить все выполненные
изменения. Сохранение можно выполнять, как используя меню графического ин-
терфейса, так и с помощью клавиатуры, нажатием комбинации клавиш <С1г1>+<8>
(что для меня вошло в привычку). В любом случае, следует сохранять свою работу
как минимум каждые 10 минут.
Для файлов автономных сценариев будет полезным узнать, как исполь-
зуемый редактор кода указывает на наличие несохраненных изменений.
Например, в редакторе кода А1ош это обозначается небольшой цветной
точкой вместо крестика для закрытия документа (на вкладке справа от
названия файла). Если код не исполняется ожидаемым образом, про-
верьте, что вы сохранили выполненные изменения, а затем повторите
его исполнение.
Управление версиями
Подобно большинству типов письменной деятельности, написание программ явля-
ется итерационным процессом. Я всегда предпочитала написать небольшой фраг-
мент кода, протестировать его, в случае успешных результатов тестирования доба-
вить еще немного кода, снова протестировать объединенный код, и так далее. Одна
из целей такого подхода — иметь возможность легко возвратиться к более ранней
версии кода в случае, если последний добавленный код не работает должным обра-
зом17.
В то же самое время не всегда можно гарантировать, что ваш код будет в рабочем
состоянии, когда вам нужно временно прекратить работать с ним по какой-либо
причине, не имея при этом возможности протестировать его. Поэтому всегда стоит
иметь надежную рабочую версию кода, с которой можно возобновить работу над
программой. Эта задача решается посредством управления версиями кода.
17 Означает, что результаты исполнения кода отличаются от ожидаемых, при исполнении кода выда-
ются сообщения об ошибке или вообще не выдается никаких результатов.
Введение в работу с СИНиЬ
Управление версиями — это, по сути, система для резервного копирования своего
кода как на локальном компьютере, так и в облачном хранилище. В этой книге для
управления версиями мы будем использовать пользующийся огромной популярно-
стью веб-сайт (ЭйНиЪ, на котором можно бесплатно сохранять резервные копии
своего кода. Хотя с этим веб-сайтом можно взаимодействовать разными способами,
мы будем использовать для этого командную строку, поскольку таким образом код
можно быстро и надежно сохранить до следующего раза всего лишь несколькими
простыми командами.
Сгеа1е а пе\л/ герозйогу
А герозкогу сота1П5 а11 рго]есС П1ез, 1Пс1исНпд Н1е гемзюп МзЮгу. А1геас1у Ьауе а рго]ес1 герозкогу е1зе\л/Ьеге?
1троП а герозкогу.
Рерозкогу 1етр1а1е
51ай уоиг герозкогу а 1етр1а(е герозйогу'з соШепгз.
1Мо 1етр1аГе ~
Оуупег * Рерозкогу пате *
ф зизапетсд ~ / с1аГадллгапдтд_ехегс15ез
бгеас герозкогу патез аге зкоп апб тетогаЫе. Ыееб тзркайоп? Ноуу аЬои! ТпешЯу-йезха?
йезспрйоп (орйопа!)
О I I риЬНс
ПСЭ Апуопе оп (Не 1п1егпе1 сап зее Низ герозкогу. Уои сНоозе иНю сап соттк.
ф О Рпуа!е
I 1 Уои скоозе \мЬо сап зее апс! соттк ю Низ герозкогу.
1пк1аПхе гЫз герозкогу ууйЫ
5к1р Н115 з1ер к уоиТе 1троП1пд ап ех1зйпд герозкогу.
а АсШ а РЕАОМЕ ГПе
ТГпз 18 м/Ьеге уои сап м/гйе а Юпд безспрНоп (огуоиг рго]ес(. кеагп тоге.
Рис. 1.6. Создание нового репозитория на бИНиЬ.сот
Для начала вам нужно создать учетную запись на веб-сайте ОйНиЪ, установить
приложение Ой на свой компьютер, а затем подключиться к учетной записи на
СгйНиЬ.
1. Посетите веб-сайт ОйНиЪ по адресу Ьйр8://§йЬиЬ.сош и нажмите кнопку 81§п
Ир. Введите имя пользователя (скорее всего, вам придется перепробовать не-
сколько разных имен, пока не найдете еще не занятое), адрес своей электронной
почты и пароль. Не забудьте записать свой пароль или сохранить его в менедже-
ре паролей, поскольку вскоре он вам потребуется.
2. Создав учетную запись, войдите в нее и нажмите кнопку Ке\у в левой верхней
части страницы. Откроется окно Сгеа€е а пеуу герозйогу (рис. 1.6).
3. Присвойте репозиторию название. Название может быть любым по вашему ус-
мотрению, но я рекомендую использовать описательное название, например,
(1а1а_мтап§1т§_ехегс18е8.
4. Установите переключатель РгКа^е и флажок Ас1с1 а КЕАБМЕ Й1е.
5. Нажмите кнопку Сгеа€е герозйогу.
Откроется новая страница с названием репозитория с1а1а_\угап§1т§_ехегс18е8 круп-
ным шрифтом и небольшим значком карандаша в правом верхнем углу. Щелчок
мышью по этому значку открывает интерфейс редактирования — это ваш файл
КЕАВМЕ, который вы можете использовать для описания вашего репозитория. По-
скольку мы будем использовать этот репозиторий для хранения упражнений из
этой книги, мы и назовем его соответствующим образом, как показано на рис. 1.7.
РиИ 1ззива Магкв(рисе Ехр1оге
& зиьалетсд / с1а(а. игапдтд_ехегс15е5 ***« фьнияда - 1 Дя» о уйл о
ОСойе О 1миеа Г1 РЫ1 0 Асвппа 0 Рчукй 0 Босаку Ы
с1а!а 'лтапд!пд ЯЕАОмЕ тй "•1° Сагнж! сГи*»де&
О ЁШ1П1е 0 РгеЧН'Л 5рясм 5 ? * 5иПат1(| *
ЙГ ЯГЙ1па1Л|| .•Х8ГС1ЙЙ1
ТЫ я 1,5 Г ня г яря ыН-ятя Т ' 11 кп-пр яу <1я [ я игяпд11пд р<к1я зм-ят<дА1
Рис. 1.7. Обновление файла КЕАЮМЕ в репозитории ОИНиЬ
Прокрутите страницу вниз до самого конца. В левой нижней части страницы дол-
жен быть значок профиля, справа от которого находится редактируемая область с
заголовком Сошшй сЬап§е8, под которым находится текстовая строка, содержащая
текст по умолчанию Прба1е КЕАВМЕ.шб, и текстовое поле. В текстовой строке
замените текст по умолчанию кратким описанием выполненных изменений. Это
будет коммит-сообщение. Например, я ввела текст ”Аск1ес1 безспрйоп оГ геро
соп1еп18”, как показано на рис. 1.8. Затем нажмите кнопку Сошшй сЬап§е8.
После обновления содержания страницы на ней внизу начального заголовка
ба1а_\угап§11п§_ехегс18е8 должен отображаться текст, добавленный вами к основно-
му файлу. А непосредственно над этим текстом должно находиться содержимое
вашего коммит-сообщения, а также время, приблизительно истекшее после нажа-
тия вами кнопки Сошшй сйап§е8. Щелчок по тексту ”2 сошшйз” справа от этого
текста выводит на экран историю коммитов (сошшй Ы81огу), содержащую все из-
менения (на данном этапе всего лишь два), выполненные с данным репозиторием
(геро), как показано на рис. 1.9.
АйасН 61ез Ьу с!гадд1пд & бгоррюд, зе1есбпд ог разбпд 1Ьет.
СП
Соттй сЬапдез
Ас1с1ес1 йезспргюп оТ геро соп(еп(5.
АсИ ап орбопа! ех1епс1ес1 безспрбоп...
® -о- СоттИ сКгесПу Ю ГНе та!п ЬгапсЬ.
О П СгеаГе а пеиу ЬгапсЬ Тог 6118 соттИ апб 8(аП а риП гедиезГ. Ьеагп тоге аЬоиГ ри11 гедиезГз.
СоттИ сйапдез
Сапсе]
Рис. 1.8. Добавление коммит-сообщения
в изменения файла ВЕАОМЕ
Чтобы просмотреть эффект какого-либо коммита на определенный файл, щелкните
по шестизначному коду в правом конце строки с названием коммита. В результате
откроется так называемое представление (11// (сокращение от (И//егепсё) данного
файла. В левой панели будет отображаться версия файла до выполнения коммита, а
в правой — версия после коммита. (Для двухпанельного представления может по-
требоваться нажать кнопку 8р1й справа над содержимым файла.)
На этом этапе вы можете задаваться вопросом, как все это связано с резервным ко-
пированием кода, поскольку мы только нажимаем разные кнопки и редактируем
тот или иной текст. Все очень просто. Теперь, когда у нас есть репозиторий (геро)
на СгйНиЪ, мы можем создать его копию на локальной машине, а затем посредством
всего лишь нескольких команд терминала выполнять коммиты18 разрабатываемого
кода и сохранять их резервные копии на этом веб-сайте.
Для резервного копирования локальных файлов: установка и настройка сред-
ства Сй. Подобно языку РуЙюп, программное обеспечение Сгй устанавливается на
локальном компьютере и управляется командами из терминала. Поскольку управ-
ление версиями является неотъемлемой частью большинства процессов кодирова-
ния, средство (Зй встраивается в операционные системы шасО8 и Ейшх. На
А^йк1о\у8 это средство можно установить как программное обеспечение. Также его
можно установить и на устройства СйгошеЪоок, используя для этого приложение
Теггпих. Установив СгИ на свой компьютер, запустите программу терминала и ис-
полните в нем следующую команду:
д!Г --7ег51оп
На экране должна отобразиться версия установленного средства Сгй, что означает
его успешную установку на локальном компьютере. Но нам еще нужно задать имя
пользователя и электронную почту (для этих параметров можно использовать лю-
бые значения), выполнив следующие команды:
д!Г сопНд --д!оЬа! изег.епаП ваша_элекР1ронная_почпа@допатп.соР1
д1Г сопНд --д!оЬа! изег.пате ваше_имя_пользовапеля
Следующим шагом нам нужно создать ключ аутентификации на локальном устрой-
стве, чтобы при создании резервной копии на СгйНиЪ система сайта знала, что ее
создаете действительно вы, а не кто-либо, кто каким-то образом узнал ваши имя
пользователя и пароль.
Для этого нам нужно создать так называемый ключ 88Н — длинную, однозначную
строку символов, хранящуюся на локальном устройстве, посредством которой сис-
тема СгйНиЪ может идентифицировать это устройство. Этот ключ можно легко соз-
дать, исполнив в окне терминала следующую команду:
ззЬ-кеудеп -Г гза -Ь 4096 -С "ваша_элекР1ронная_почпа@с1опа1П.соР1"
Когда в ходе исполнения этой команды отобразится сообщение ”Еп1ег а Й1е ш ууЫсй
1о §ауе Ше кеу” (Введите название файла для сохранения ключа), просто нажмите
клавишу <Еп1ег>, чтобы сохранить ключ в файле по умолчанию. Таким образом
будет легче найти этот файл, когда чуть далее нам нужно будет скопировать ключ в
нашу учетную запись (ЗйНиЪ. Затем отобразится следующее сообщение:
ЕпГег раззрЬгазе (епрГу Гог по раззрЬгазе):
Введите парольную фразу (оставьте пустым, чтобы продолжить без пароля):
Обязательно введите парольную фразу. При этом не используйте тот же самый па-
роль, что для вашей учетной записи ОйНиЬ или любой другой учетной записи. Но
поскольку вам нужно будет предоставлять этот пароль при каждом сохранении ре-
18 Англ. соттИ — в данном контексте это операция, которая сохраняет самые последние изменения
кода в репозитории.
зервной копии на ОйНиЪ19, то он должен быть легко запоминаемым. Поэтому по-
пробуйте использовать, к примеру, первые три слова второго куплета вашей люби-
мой песни. В любом случае любой пароль длиной в 8-10 символов будет удовле-
творительным.
Далее нужно будет снова ввести этот пароль, чтобы подтвердить его. После этого
можно скопировать ваш ключ в вашу учетную запись (ЭйНиЪ. Это позволит системе
(ЭйНиЪ удостоверять вашу личность, сравнивая эту копию с оригиналом на вашем
локальном компьютере. Для этого сначала щелкните по значку вашего профиля в
правом верхнем углу страницы ОйНиЬ и выберите в выпадающем меню опцию
8ейш§8. Затем в левой панели щелкните по опции 88Н апД СРС Кеуз и в правой
верхней области открывшейся панели нажмите кнопку Ке\т 88Н кеу (рис. 1.10).
Откроется страница АДД пеуу.
АссоипС зеПтдз
РгоШе
АссоигИ
Арреагапсе
АссоигП зесипГу
ВННпд & р1апз
ЗесипГу 1од
ЗесипГу & апа1уз1з
ЕтаПз
МойЛсайопз
8с1пес1и1ес1 геттс1егз
88Н апс! 6Р6 кеуз
Рерозйопез
55Н кеуз
ТЬеге аге по 88Н кеуз аззоааГес! ууИ1п уоиг ассоипГ.
СЬеск они оиг дЫбе го депегаппд ЗЗН кеуз ог ггоиЫезЬоог соттоп ЗЗН ргоЫетз.
6Р6 кеуз
ТЬеге аге по СРС кеуз аззос1аГес1 у\л1Ь уоиг ассоипГ.
1_еагп Ком ю делегате а СРС кеу апб ас!с1 И (о уоиг ассоипг.
\Лд11ап1 тос1е ( Вега )
□ Аад ипз!дпе<1 соттИз аз ипуеНТ|ес1
ТЫз мШ1пс1ибе апу соттк аПлЬигеб ю уоиг ассоипг Ьиг пот з!дпес1 мйЬ уоиг СРС ог 3/М1МЕ кеу.
Моте ТМат тЫз м(111пс1ибе уоиг ех(з11пд ипз1дпеб сотткз.
1_еагп аЬоиТ мдЛап1 гтюбе.
Мем ЗЗН кеу
Мем СРС кеу
Рис. 1.10. Страница веб-сайта СИНиЬ
для создания ключей
Далее нам нужно перейти в основную папку пользователя на своем устройстве (это
папка, в которой открывается новое окно терминала) и отобразить в ней скрытые
файлы:
На устройствах СкготеЪоок
Основной папкой пользователя на этих устройствах является папка Ыпих Й1е§.
Чтобы отобразить скрытые файлы, щелкните по значку из трех вертикальных
точек в правой верхней части окна и выберите опцию 81юуу ЫДДеп Шез.
19 В зависимости от вашего устройства, этот пароль можно сохранить в так называемом брелке для
ключей (кеусйат). Дополнительную информацию по этому вопросу см. в документации на ОйНпЬ.
тасО8
Скрытые файлы можно отобразить (или скрыть уже отображающиеся), нажав
комбинацию клавиш <Сошшапс1>+<8Ый>+<.>.
В Проводнике \^тбо\У8 выполните последовательность команд меню У1еуу |
Орйоп8 | СЬап§е Го1Дег апД зеагсй орйоп8. На вкладке У1елу в разделе АДуапсеД
8ейт§8 установите флажок 8йолу ЫДДеп Ше8, Го1Дег8, апД Дпуе8.
Найдите и откройте папку (это действительно папка, а не расширение), называю-
щуюся .8811, а затем откройте (используя любой базовый текстовый редактор, на-
пример А1ош) файл 1Д_г8а.риЪ. Используя команды клавиатуры, выберите и скопи-
руйте все содержимое этого файла, а затем вставьте его в пустое текстовое поле
Кеу на странице АДД пеуу веб-сайта СйНиЪ (рис. 1.11).
Ассоигй зейтдз
РгоШе
АссоигП
Арреагапсе
АссоигП зесипгу
ВППпд & р1апз
ЗесипГу 1од
ЗесигПу & апа1уз1з
ЕтаПз
Ыоййсайопз
5сЬе<1и1ес1 гет1пбегз
58Н апб 6Р6 кеуз
РерозПопез
55Н кеуз / Ас1с1 пем/
ТК1е
Кеу
Вед'тз у/НЬ 'ззН-гза', 'есйза-з11а2-п|8(р256', ’есс1за-511а2-П15(р384', 'есб8а-зЪа2-П1з1р52Г. ’зз11-еб25519', ’зк-
есбза-з11а2-П18(р256@орепз5Ь.сот', ог ,5к-5зI^-ес^25519@ореп55I^.сот,
Ас1с1 85Н кеу
Рис. 1.11. Страница Ас1с1 пего для загрузки ключа 88Н
в учетную запись СИНиЬ
Наконец, присвойте этому ключу имя, чтобы знать, к какому устройству он отно-
сится, а затем нажмите кнопку АДД 88Н кеу. Возможно, что для завершения про-
цесса потребуется ввести пароль вашей учетной записи ОйНиЪ. Вот и все. Теперь
можно снова скрыть папки и файлы на локальном устройстве и завершить подклю-
чение учетной записи СгйНиЪ к локальному устройству и/или учетной записи Со1аЪ.
Я рекомендую использовать команды клавиатуры для копирования и
вставки ключа 88Н, поскольку важно скопировать точную последова-
тельность символов (включая пробелы) этого ключа, а при выполнении
этих операций при помощи мыши существует возможность нечаянно
прихватить или пропустить какие-либо символы. Если при попытке до-
бавления нового ключа $$Н система ОйНиЬ выдает сообщение об ошиб-
ке, попробуйте следующие два способа ее решения:
О убедитесь, что вы скопировали содержимое файла с расширением
.риЬ, а не другого файла в папке .88Й с таким же самым названием, но
без расширения;
О закройте файл, не сохраняя его, а затем повторите попытку.
Если это не устранит проблему, можно просто полностью удалить папку
.8811, а затем сгенерировать новые ключи. Поскольку ничего еще не было
загружено на веб-сайт ОйНиЬ, то не будет утеряно никакой информации.
Сводим все в одно целое. В завершение нам нужно создать на локальном компью-
тере связанную копию нашего репозитория (ЗйНиЪ. Это можно легко сделать по-
средством следующего процесса:
1. Запустите программу терминала и перейдите в папку с!а1а_мтап§1т§.
2. В своей учетной записи на веб-сайте ОйНиЬ откройте репозиторий
(1а1:а_\угап§1т§_ехегс18е8.
3. На странице репозитория нажмите кнопку Сойе в правом верхнем углу панели
репозитория.
4. В открывшемся контекстном меню С1опе выберите опцию 88Н, а затем щелкни-
те по значку буфера обмена справа от 1ЖЬ-адреса репозитория (рис. 1.12).
а зиьалетсд/йака нгалд]пд ехегс1&с5
о соде 0 П рцв гсчшзд ф дспогщ 0*^ О зесииу иэдгте ф =лпгщ&
Н ЩН1П * Г I ЦЪЗПСЛ ф в 1№ДО
аийипетсд йТ ге-ро схИйиЛз С1ОПВ
нгпч етлсм
о ЯЕДРМЕ.ЦК1 вил1ИЫ|Г.И1.И1«П1идгв«10гЯв11ч о
ф - 1 ф 0 V 0
ДЬри! ф
Та йиет,Ш|,пп, IX Гадиса
ргацйесГ
С0 Й15ПЙ1110
ЙЕАОМЕгпД
Йа(а_тапд1пд_ехегс[5е§
и«* * иду.
Ц) □омгп1сШ|С| 21Р
№ ' «МИ! ЦйВвЪ«1
ТНад & 1/16 г&рй к*Ьегц 111 кейр пгу
Раскадез
№ рвсклэи риМвГ»«1
Риссел яиг 1«й р*скпд»
Рис. 1.12. Копирование адреса размещения репозитория ОИНиЬ
5. Обратно в окне терминала введите команду дП сТопе, пробел, а затем вставьте
скопированный 1ЖЬ-адрес (или введите его с клавиатуры). Полностью эта ко-
манда должна выглядеть следующим образом:
дП сТопе дП@дПЬиЬ.сот:8и8апетсд/^а1:а_мгапдипд_ехегс18е8.ди
6. Нажмите клавишу <Еп1ег>. В процессе исполнении команды может отобразить-
ся сообщение, хотите ли вы добавить узел назначения в список известных хос-
тов. Введите уез и нажмите клавишу <Еп1ег>. Далее может отобразиться запрос
ввести ваш пароль 88Н. Введите его и снова нажмите клавишу <Еп1ег>.
7. После завершение копирования репозитория, что будет обозначено выводом слова
боле, исполните команду 1з. Среди прочих элементов выведенный список должен
содержать папку скопированного репозитория СйНиЬ с!а1:а_\угап§1т§_ехегс18е8.
8. Перейдите в эту папку, выполнив команду сб да-Ьа_игапд1гпд_ехегсгзез. Исполни-
те команду 1з. В списке должен отобразиться файл КЕАВМЕ.шб.
Вот, наконец, и все. Возможно, что этот процесс выглядит довольно сложным, но
имейте в виду, что ключ 88Н нужно создавать только один раз. Также процесс кло-
нирования для каждого репозитория нужно выполнять тоже только один раз. А для
всех упражнений в этой книге можно использовать один и тот же репозиторий.
Теперь давайте посмотрим, как все это работает в действии, добавив в наш репози-
торий файл РуЙюп. В окне Гшйег перейдите в папку ба1а_\угап§1т§. Сохраните и
закройте файл НеПоА^огМ.ру или НеПоА^огМлрупЪ, а затем перетащите его в папку
ба1а_\угап§11п§_ехегс18е8. Затем в окне терминала проверьте наличие этого файла в
данной папке, выполнив команду 1з.
Одним из последних шагов нам нужно проинформировать программу Сгй, что мы
хотим включить наш файл РуЙюп в резервную копию, которая будет сохраняться
на веб-сайте СгйНиЪ. Затем мы сохраним текущую версию файла, а затем собствен-
но загрузим его в репозиторий на (ЭйНиЬ.
Начнем этот процесс с исполнения команды дН з1а1из в окне терминала. В резуль-
тате должно отобразиться сообщение со списком неотслеживаемых файлов
Еййгаскес! Шез: (), содержащим наш файл Руйюп. Это было ожидаемо, но удосто-
вериться в этом будет не лишним. Теперь приступим к реализации вышеописанно-
го процесса сохранения резервной копии в репозитории на веб-сайте СйНиЬ. Обра-
тите внимание на то, что последующие команды выдают выходные сообщения в
терминале:
1. В окне терминала исполните команду д!1 асИ название_вашего_файла_руЬЬоп.
2. Затем исполните команду дН сопрп.1 -т "АсМтпд ту НеПо ЫогХд РуЬЬоп РПе." на-
звание_вашего_файла_руЬЬоп. Параметр -т указывает, что текст в кавычках должен
использоваться в качестве коммит-сообщения. Это эквивалент командной стро-
ки сообщения, которое мы предоставили на веб-сайте (ЭйНиЬ для файла
ВЕАПМЕ некоторое время ранее.
3. Наконец, исполните команду дН ризЬ.
Последняя команда, собственно, и загружает файл на веб-сайт ОйНиЪ. Обратите
внимание на то, что последняя команда, понятное дело, не будет работать без нали-
чия подключения к Интернету. Но предыдущие две команды можно исполнять в
любое время, а команду ризЬ — дождавшись наличия подключения. Чтобы удосто-
вериться в том, что резервная копия была создана должным образом, перезагрузите
страницу репозитория СгйНиЬ. Теперь она должна содержать ваш файл РуЙюп и
соответствующее коммит-сообщение.
Резервное копирование онлайновых файлов РуЙюп: подключение Соо§1е
Со1аЬ к СйНпЬ. Если вся работа по выпасу данных выполняется в онлайновом ре-
жиме, то средство Ооо§1е Со1аЪ можно подключить напрямую к своей учетной за-
писи ОйНиЪ. Проверьте, что вы находитесь в своей учетной записи СгйНиЬ, а затем
в новой вкладке браузера откройте страницу Ьйр8://со1аЬ.ге8еагсЬ.§оо§1е.сош/
§10шЬ. Откроется всплывающее окно с запросом выполнить вход в свою учетную
запись ОйНиЬ, а затем другое всплывающее окно с запросом Аийюпхе Со1аЬога1огу
(разрешить средство Со1аЬога1огу). После предоставления этого разрешения в левой
части всплывающего окна будет отображен список ваших репозиториев СгйНиЪ.
При выборе требуемого репозитория в нижней части окна отобразится список со-
держащихся в нем файлов блокнотов 1иру1ег.
В всплывающем окне Ооо§1е Со1аЬ отображаются только файлы блок-
нотов ЕзруТег (т. е. файлы с расширением лрупЬ). Для просмотра всех
файлов репозитория нужно перейти на веб-сайт СгйНиЬ.
Сводим все в одно целое. При работе в Сюо§1е Со1аЬ, чтобы добавить новый файл
в репозиторий СгйНиЬ, нужно выполнить последовательность команд меню ГПе |
8ауе а сору ш СйНиЬ. В результате откроется и закроется несколько всплываю-
щих окон, в которых Со1аЪ выполняет вход в вашу учетную запись СгйНиЬ, а затем
в разделе Керо8Йогу всплывающего окна Сору €о СйНиЬ можно будет выбрать
репозиторий, в котором сохранить ваш файл. В этом же окне в разделе И1е раЙ1
указывается имя сохраняемого файла, которое при желании можно изменить, а в
разделе Сонник ше88а§е можно ввести коммит-сообщение. Наконец, слева внизу
этого окна находится флажок 1пс1ис1е а 1шк €о Со1аЬога€огу (Включить ссылку на
Со1аЪога1огу). Если этот флажок установить, тогда сохраненный на ОйНиЬ файл
будет содержать метку Ореп ш Со1аЬ, щелчок по которому отроет этот файл на
веб-сайте Сюо§1е Со1аЬ. Файлы блокнотов, которые явно не сохраняются на (ЭйНиЪ
вышеописанным способом, сохраняются на вашем диске Ооо§1е Впуе в папке
Со1аЪ Мо1еЪоок§. Доступ к этим файлам можно получить, выбрав вкладку Соо§1е
Бггуе на веб-сайте Со1аЬ кйр8://со1аЬ.ге8еагсЬ.§оо§1е.сош.
Папки, директории и репозитории
Возможно, вы заметили, что на всем протяжении этой главы термины папка, ди-
ректория и даже репозиторий употребляются без большой разницы между ними.
В действительности, первые два термина обозначают одно и то же понятие, а по-
следний относится исключительно к папкам, в которых по крайней мере некото-
рые файлы отслеживаются системой Сгй.
Иными словами, со всеми этими элементами можно взаимодействовать, исполь-
зуя мышь и обычные окна Етбег (или проводника А^тбоууз), чтобы создавать,
удалять и открывать файлы. Единственная разница состоит в том, что в случае
репозитория необходимо использовать команды Сгй типа асИ или гт, чтобы со-
держать все в порядке. Мы будем рассматривать каждую из этих ситуаций более
подробно по мере их возникновения в последующих главах.
Заключение
Целью этой главы было предоставить вам общее представление о том, чему вы мо-
жете научиться в этой книге: что я имею в виду под ”выпасом20 данных” и "качест-
вом данных”, а также почему я полагаю, что язык программирования РуЙюп явля-
ется правильным инструментом для этой работы.
Кроме этого, мы рассмотрели все необходимые приготовления, чтобы начать (и
продолжать) использовать язык РуЙюп для выпаса данных. В частности, были пре-
доставлены инструкции для организации предпочитаемого вами подхода програм-
мирования: использование файлов автономных сценариев РуЙюп или блокнотов
1иру1ег на локальной машине или работа с блокнотами 1иру1ег в онлайновом режи-
ме, используя средство Сюо§1е Со1аЪ. Наконец, мы рассмотрели использование
контроля версий с любым из этих подходов для резервного копирования, докумен-
тирования и предоставления общего доступа к своей работе.
В следующей главе мы пойдем намного дальше нашей программы ”Не11о А^огШ!”,
исследуя основы языка программирования РуЙюп, и даже предпримем попытку
решить наш первый проект по выпасу данных: один день работы системы СШ В1ке
города Нью-Йорк.
20 Первичной обработкой. — Примеч. ред.
ГЛАВА 2
Введение в Ру1Ноп
Если вы можете читать этот текст, то у меня для вас есть хорошая новость: у вас не
будет никаких проблем с обучением программированию. Почему? Поскольку уро-
вень сложности компьютерных языков программирования в целом, а языка РуЙюп
в частности, намного ниже, чем естественных человеческих языков. Языки про-
граммирования разработаны людьми и предназначены для чтения, в большинстве
случаев, компьютерами. Поэтому у них более простая грамматика и меньшее коли-
чество "частей речи”, чем у естественных языков. Поэтому, если вы чувствуете себя
достаточно уверенно при чтении текста на русском языке, который известен своим
большим словарным составом и сложной орфографией, то можете быть уверенны-
ми в том, что овладеть основами языка программирования РуЙюп вполне в преде-
лах ваших способностей.
К концу этой главы вы будете владеть всеми навыками программирования на этом
языке, необходимыми для того, чтобы начать выполнять базовый выпас данных,
используя наиболее распространенные форматы данных, которые мы рассмотрим в
главе 4. Чтобы достичь этой цели, мы начнем с выполнения базовых упражнений
по программированию, охватывающих следующие области:
♦ основные ”части речи” языка РуЙюп — его базовую грамматику и синтаксис;
♦ как компьютер считывает и интерпретирует код на языке РуЙюп;
♦ как использовать "рецепты” кода, созданные другими разработчиками (и вами
самими), чтобы быстро расширить возможности своего кода.
На протяжении этой главы приводятся фрагменты кода, иллюстрирующие каждое
рассматриваемое понятие. Эти примеры также собраны в блокнотах 1иру1ег и в
файлах автономных сценариев РуЙюп на ОйНиЪ. Их можно загрузить и исполнять в
среде разработки Ооо§1е Со1аЬ или на локальном компьютере. Так как эти файлы
позволят вам увидеть код примеров этой главы в действии, я настоятельно реко-
мендую создать новый блокнот Со1аЪ или 1иру1ег или файл автономного сценария
РуЙюп и практиковаться в написании, комментировании и исполнении этого кода
самостоятельно. (Вспомнить, как это делается, можно в разд. ”Не11о ТУогШ!" главы 7.)
Вы можете подумать, что в этом нет большого смысла, но нет ничего более полез-
ного для развития навыков выпаса данных и выработки уверенности в своих спо-
собностях, чем создание файла РуЙюп с нуля, с последующим обозрением, как на-
писанный вами самими код заставляет компьютер делать то, что вы хотите, даже
если вы просто перепечатали его с другого файла. Да, таким образом вам придется
столкнуться с большим количеством сложностей, но в этом и заключается опреде-
ленная суть: в действительности качественная обработка данных состоит не в том,
чтобы научиться делать что-то ”правильно”, а в том, чтобы научиться восстанавли-
ваться после ошибок. Предоставив себе возможность делать небольшие ошибки на
начальном этапе (и научившись, как распознавать и исправлять их), вы получите
возможность по-настоящему достигнуть успеха как специалист по выпасу данных,
так и как программист. Вы никогда не сможете получить этот опыт, просто испол-
няя готовый код.
Поскольку эти небольшие ошибки имеют такую большую важность, в эту главу я
включила несколько врезок "Быстрая прокрутка", в которых описаны способы
продвижения предоставленных примеров на один шаг дальше, что часто связано с
преднамеренной ”поломкой” кода. К концу этой главы вы будете готовы объеди-
нить рассмотренные базовые понятия в полноценную программу выпаса данных,
использующую реальные данные. Для каждого примера я также предоставляю не-
сколько явных напоминаний о типах заметок, которые желательно включать в свой
дневник данных, о том, когда желательно выполнять резервное копирование на
СгйНиЪ, и тому подобное. Делаю я это для того, чтобы вы по-настоящему освоили
эти процессы, а также развили чувство, когда необходимо предпринимать эти шаги
в ходе работы над своими собственными проектами по выпасу данных.
Теперь, когда вы знаете, куда мы направляемся, приступим к работе!
"Части речи" языков программирования
Разные человеческие языки используют разные словарные составы и синтаксиче-
ские структуры, но все они обычно имеют много общих фундаментальных поня-
тий. Взглянем, например, на следующие два предложения
Му папе Зизап. // Английский
Зе п'арреИе Зизап. // Французский
Оба эти предложения выражают, по сути, одно и то же: как меня зовут. И хотя в
разных языках используются разные слова и слегка разные грамматические струк-
туры, все языки содержат такие части речи, как подлежащее, сказуемое, глаголы и
модификаторы. Они также следуют подобным грамматическим и синтаксическим
правилам в том смысле, что они структурируют идеи и слова в предложения, абза-
цы и так далее.
Многие языки программирования также имеют подобные ключевые структурные и
организационные элементы, которые грубо соответствуют подобным элементам
естественных языков. Чтобы получить лучшее представление о том, как все это ра-
ботает, начнем изучение языков программирования таким же самим способом, ка-
кой бы мы применили при изучении нового естественного языка: исследуя ”части
речи” языков программирования.
Существительные = переменные
В английском языке (как и в русском) существительные часто определяются как
любое слово, обозначающее ”лицо, понятие или предмет”. Хотя это не сверхточное
определение, оно удачно иллюстрирует, что существительным могут быть разные
сущности. В языках программирования ”существительными”, используемыми при
написании "предложений” программ, являются переменные. Но вместо людей, по-
нятий и предметов они обозначают один из пяти основных типов данных:
♦ числовой;
♦ строчный;
♦ список;
♦ словарь;
♦ булев.
Как и в случае с естественными языками, где в разных языках существительное
может быть разных типов, в различных языках программирования типы обозначае-
мых переменными данных также могут быть разными. Например, то, что в языке
РуЙюп называется списком (1181), в языках, например, С или 1ауа8спр1 называется
массивом (агггау). А объекты 1ауа8спр1 в РуЙюп называются шар или словарями
(сйсбопагу)1.
Скорее всего, вы уже можете предположить, что собой представляют некоторые
типы данных из приведенного ранее списка. В отличие от существительных в есте-
ственных языках, каждый тип данных в языке РуЙюп можно с легкостью узнать по
его форматированию и пунктуации. Поэтому, при условии уделения достаточного
внимания символам вокруг данных, нам не нужно беспокоиться о смешении раз-
ных типов данных.
Чтобы получить представление об однозначной структуре пунктуации каждого ти-
па данных РуЙюп, рассмотрим пример в листинге 2.1. При этом обязательно соз-
дайте свою копию этого кода в автономном файле или блокноте, чтобы увидеть
возможность выделения синтаксических элементов в действии. Например, числа
должны выделяться другим цветом, чем строки, так же как круглые и фигурные
скобки и комментарии (строки, начинающиеся символом #).
Листинг 2.1. Сценарий раН8_о1_8реесЬ.ру
# Число (питЬег) - это просто одна или несколько цифр
25
# Строка (зСггпд) - это комбинация любых символов в парных кавычках
"НеПо 1л!ог1с1"
1 Многие специфичные для языка типы данных, например кортеж (1пр1е) в РуШоп, не существенны
для работы по выпасу данных, поэтому мы не будем рассматривать их здесь в подробностях.
# Список (ПзС) состоит из нескольких элементов, разделенных запятыми, а сам список
# берется в квадратные скобки.
# Обратите внимание на то, что в РуГЬоп первый элемент списка считается
# в позиции 0, следующий - в позиции 1 и так далее.
# Словарь (сИс±) - это набор пар ключ:значение, разделенных запятыми,
# а весь список берется в фигурные скобки.
{"ШХе": "РгасГтса! РуГЬоп Гог ОаГа 1л1гапдПпд апс! ОаГа ОиаПГу",
"ГогпаГ": "Ьоок",
"аиГЬог": "Бизап Е. МсСгедог"
}
# Булев тип (ЬооТеап) имеет только два значения - истина (Ггие) и ложь (ГаТзе).
Тгие
Конечно же, это далеко не исчерпывающий список. Точно так же, как и естествен-
ные языки поддерживают сложные существительные (например, электростанция
или воздухозаборник), языки программирования также поддерживают создание
более сложных типов данных. Но, как мы вскоре сможем увидеть, даже эти не-
сколько простых типов данных предоставляют нам довольно обширные возможности.
В реальном мире мы также часто присваиваем имена и названия: людям, уникаль-
ным местам, предметам и понятиям, чтобы было легче ссылаться на них и обсуж-
дать их. То же самое происходит и в программировании, и точно по этой же причи-
не: присваивая имена переменным, мы можем ссылаться на них и модифицировать
специфичные данные таким образом, который будет понятным для компьютера.
Для примера переведем простое английское предложение в код РуЙюп:
ТЬе аиГЬог 1з Бизап Е. МсСгедог
Прочитав это предложение, мы свяжем имя ”8изап Е. МсСге§ог” с меткой "аийюг".
Впоследствии, если у вас спросят, кто автор этой книги, вы (будем надеяться)
вспомните эту связь и ответите: ”8изап Е. МсСге§ог”. Эквивалентное ”предложе-
ние” на языке РуЙюп показано в листинге 2.2.
Листинг 2.2. Вывод содержимого переменной в языке РуЙюп
аиГЬог = "Бизап Е. МсСгедог"
Этот код дает указание компьютеру выделить область памяти, присвоить ей имя
аиГЬог, а затем сохранить в ней строку "Бизап Е. МсСгедог". Если далее в программе
спросить у компьютера о переменной аиГЬог, он ответит нам, что она содержит
строку "Бизап Е. МсСгедог". Соответствующий код приводится в листинге 2.3.
Листинг 2.3. Печать содержимого переменной РуНтоп
# Создаем переменную с именем аиШог и присваиваем ее содержимому
# значение "Зизап Е. МсСгедог"
аиШог = "Зизап Е. МсСгедог"
# Проверяем, что компьютер "помнит" содержимое переменной 'аи^Ьог'
рг!п1:(аи1±ог)
Важно ли конкретное имя?
В листинге 2.3 я присвоила своей переменной имя аиШог, но это имя не обязательно
должно быть чем-то специфичным. В принципе, переменной можно присвоить лю-
бое имя, соблюдая только следующие три ограничения — имя переменной в языке
РуШоп не может:
♦ начинаться с цифры;
♦ содержать какие-либо знаки препинания, за исключением символа подчеркива-
ния (_);
♦ быть "зарезервированным” или ключевым словом, например ЬШгпЬсг или Воо1еап.
Например, в листинге 2.3 я могла бы с легкостью назвать переменную пус_гез1с1еп1:
(житель нью йорка) или даже Ги22у_ртпк_Ьиппу (пушистый_розовый_зайчик). Важ-
но лишь то, чтобы программист следовал трем ранее изложенным ограничениям и
чтобы при доступе к содержимому переменной ее имя указывалось точно так же,
как оно было указано при ее создании, учитывая регистр. Например, создадим но-
вый файл РуШоп, вставим в него код из листинга 2.4, а затем исполним его и по-
смотрим, какие будут результаты исполнения.
Листинге 2.4. Сценарий поип_ехашр1е§.ру
# Создаем переменную с именем пус_гез1с1еп1: и присваиваем ее содержимому значение
# "Зизап Е. МсСгедог"
пус_гезШеп1: = "Зизап Е. МсСгедог"
# Проверяем, что компьютер "помнит" содержимое переменной пус_гез1с!еп1:
рг1п1:(пус_ге51с1еп1:)
# Создаем переменную с именем ^иггуРтпкВиппу и присваиваем ее содержимому значение
# "Зизап Е. МсСгедог"
1т122уР1пкВиппу = "Зизап Е. МсСгедог"
# Проверяем, что компьютер "помнит" содержимое переменной ^иггуРтпкВиппу
рг1п1:('Ри22уР1пкВиппу)
# Но регистр имеет значение!
# Следующая строка кода выдаст сообщение об ошибке
рг1п1:(Ги22ур1пкЬиппу)
Наилучшие практики для именования переменных
Хотя все имена переменных в листинге 2.4 соответствуют всем изложенным ранее
правилам именования переменных, не все они являются особо хорошими. Как мы
увидим на протяжении всей этой книги, подобно любому другому типу писатель-
ской деятельности, написание качественного кода состоит не просто в создании
кода, который ”работает”, но также и в том, чтобы этот код был как можно более
понимаемым и удобным как для компьютеров, так и для людей. Поэтому я считаю
вопрос присвоения переменным качественных имен важным аспектом хорошего
программирования. В частности, подходящими именами переменных будут такие,
которые являются:
♦ описательными;
♦ однозначными (в рамках данного файла или программы);
♦ удобочитаемыми.
Поскольку удовлетворение первого условия часто требует нескольких слов, про-
граммисты часто прибегают к использованию одного из двух стилистических ме-
тодов, чтобы обеспечить удобочитаемость таких имен. Оба эти метода показаны в
листинге 2.4. В первом подходе отдельные слова имени переменной разделяются
символом подчеркивания, например пус_гез1с1еп1:. Во втором подходе используется
так называемый верблюжий регистр (сашеЮазе), когда все слова имени переменной
пишутся слитно, но каждое новое слово, за исключением первого, начинается с за-
главной буквы, например ГиггуРтпкВиппу. В общем, независимо от выбранного вами
стиля, его следует использовать на всем протяжении разрабатываемого проекта,
хотя смешение этих стилей никак не повлияет на работоспособность вашего кода (и
в большой мере на его удобочитаемость также). В этой книге мы будем в основном
использовать подход с подчеркиванием, который также считается более "прису-
щим” языку РуЙюп.
Глаголы а функциям
В английском языке глаголы часто описываются как ”действия” или ''состояния”.
В предыдущих примерах мы уже видели эквиваленты глагола в языке программи-
рования: знак равенства (=) и функцию рг1п±(). В английском языке, чтобы описать,
чем является что-то, используется глагол ”1о Ье” (быть). В языке программирования
РуЙюп (а также во многих других языках программирования) значение переменной
указывается после знака равенства. Поэтому знак равенства также называется опе-
ратором присваивания.
В программировании эквивалентом глаголов являются функции. Язык РуЙюп и
многие другие языки программирования содержат встроенные функции, которые
реализуют задачи (например, отображение вывода посредством функции рг1п1:()),
которые язык ”просто знает”, как делать. Методы подобны функциям, но предна-
значены для работы с определенными типами данных, и для правильной работы их
нужно вызывать с переменной конкретного типа данных. Методы для определен-
ного типа данных обычно отражают задачи общего характера, которые мы хотим
выполнять с его помощью. Поэтому, подобно тому, как большинство людей могут
ходить, разговаривать, есть, пить и брать в руки объекты, большинство языков про-
граммирования содержат строковые методы, которые могут выполнять такие опе-
рации, как соединение двух строк (что на профессиональном жаргоне называется
конкатенацией), разделение строки на составляющие подстроки и тому подобное.
Но поскольку операция разделения” на части чисел, например числа 5, не имеет
смысла, то для числовых типов данных метода разделения зрГЩ) не существует.
Какое же практическое различие между встроенными функциями и методами?
Не очень большое, за исключением способа использования этих "глаголов” в
"предложениях” языка РуЙюп, которые называются выражениями или инструкция-
ми. В случае встроенных функций мы просто указываем имя функции и передаем
ей требуемые ”ингредиенты”, называющиеся параметры, заключая их в круглые
скобки. Например, в листинге 1.1 главы 1 мы передавали функции рг!п±() строчный
параметр "НеНо НогТсН
рггп1("НеИо 1л1ог1сП")
Но в случае метода зрШ() нам нужно прикрепить этот метод к конкретной строке.
Эта строка может быть символьной константой (т. е. последовательность симво-
лов, заключенная в кавычки) или переменной, содержащей строковое значение.
Попробуйте исполнить код в листинге 2.5 в автономном сценарии или в блокноте,
и посмотрите, какой получится результат.
Листинг 2.5. Сценарий те1Ьос!_тас1пе§§.ру
# Разделяем символьную константу и отображаем результат
зрШ_мог1б = "НеПо 1л1ог1б!" .2рШ()
рг1пГ(5р1Л-|:_\л/ог1с1)
# Присваиваем переменной строковое значение,
# а затем отображаем результат вызова для нее метода зрГЩ)
мог1б_тзд = "НеПо 1л1ог1с1!"
рг1п1:(мог1с1_т5д. зрГЩ))
Обратите внимание на то, что если попытаться исполнить метод зрШ() сам по себе
или с типом данных, для которого он не предназначен, то будет возвращено сооб-
щение об ошибке. Попробуйте исполнить операторы в листинге 2.6 по отдельности
(или в двух разных ячейках блокнота) и посмотрите, какие будут результаты.
Листинг 2.6. Неправильные вызовы метода зр1И()
# Следующий оператор выдаст сообщение об ошибке, поскольку метод зрГЩ) должен
# вызываться со строковым объектом
зрШ( "НеШо 1л!ог1с1!")
# Следующее выражение выдаст сообщение об ошибке, поскольку для
# числового типа нет метода зрШ()
рггп1(5.5р1г1())
Подобно типам данных, методы и функции можно распознать по стилю и пунктуа-
ции в их представлении. В редакторе кода или блокноте кода РуЛоп встроенные
функции (например, рг1п±()) отображаются определенным цветом. Например, в ис-
пользуемой по умолчанию в редакторе кода А1ош теме Опе Эагк имена перемен-
ных отображаются светло-серым цветом, операторы наподобие = (равно) — фиоле-
товым, а встроенные функции типа рг1п±() — бирюзовым. Функции можно также
определить по используемой с ними пунктуации. В частности, любой текст, сразу
же после которого следуют две парные круглые скобки (например, рг1п1:()), будет
функцией. То же самое относится и к методам, с тем исключением, что они всегда
предшествуются значением соответствующего типа данных или именем перемен-
ной, отделенным от него точкой, например могТсИпзд.зрШО.
Язык программирования РуЙюп предоставляет довольно обширные возможности,
используя операторы, методы и встроенные функции, особенно если выполнять
такие задачи, как простые математические вычисления. Но для выпаса данных нам
нужны немного более продвинутые возможности. Точно так же, как игра на пиани-
но или в футбол можно рассматривать как ”просто” слаженную комбинацию мно-
гих более простых действий — например, движение пальцами или ногами — очень
сложные программные функции можно создавать, объединяя тщательно продуман-
ным способом сравнительно простые операторы, методы и встроенные функции.
Вот эти определяемые пользователем функции и начинают придавать нашему коду
настоящую мощь, позволяя создавать ”рецепты” кода, которые можно использовать
на многократной основе.
Например, предположим, что нам нужно распечатать одно и то же сообщение для
двух разных людей. Эту задачу можно было бы решить, используя функцию рг!п±()
тем же самым способом, как и в предыдущих примерах, как показано в листинге 2.7.
Листинг 2.7. Ьа8Ю_дгеейпд.ру
# Создаем переменную с именем аи1±ог
аи1±ог = "Зизап Е. МсСгедог"
# Создаем другую переменную с именем есйЛог
ес11±ог = "ЗеГГ В1е1е1"
# Используем встроенную функцию рг1п1:() для вывода
# индивидуализированного сообщения "НеПо..." для каждого пользователя
рггп1("НеИо "+аи1±ог)
рггп1("НеП.о "+есНЛог)
В листинге 2.7 следует обратить внимание на два обстоятельства. Во-первых, ис-
пользование функции рг!п±() справляется с поставленной задачей должным обра-
зом, что не может не радовать. По сути, наша первая цель при написании кода для
решения какой-либо задачи (включая текущую) заключается в том, чтобы он выда-
вал требуемый результат.
Но добившись этого, мы можем начинать думать о том, как каким-либо простым
способом сделать наш код более опрятным и более полезным. Используемые в пре-
дыдущем примере два оператора рг1п±() абсолютно идентичны, за исключением
передаваемой им переменной. Каждый раз, когда мы видим такое повторение в ко-
де, это индикатор, что его можно заменить собственной определяемой пользовате-
лем функцией, как показано в листинге 2.8.
Листинг 2.8. Пользовательская функция дгееСте.ру
# Создаем функцию, отображающую на экране приветствие по любому переданному ей имени
бе!2 дгее1:_те(а_пате):
рггп1("НеП.о !,+а_папе)
# Создаем переменную с именем аи1±ог
аи1±ог = "Зизап Е. МсСгедог"
# Создаем другую переменную с именем есйЛог
есПЛог = "ЗеН В1е1е1"
# Используем нашу пользовательскую функцию дгее1:_те() для вывода
# индивидуализированного сообщения "Не11о..." для каждого пользователя
дгее1:_те(аи1±ог)
дгее1:_те(еб11:ог)
С первого взгляда может показаться, что в этом подходе нет ничего особенного,
поскольку в некоторых отношениях главная часть этого кода не особенно отлича-
ется от кода предыдущего примера, но в действительности этот пример содержит
много нового. Сейчас мы рассмотрим некоторые из новых понятий, используе-
мых в этом примере. Но если все это не сразу будет вам понятно, то не пережи-
вайте, поскольку мы будем возвращаться к этим понятиям в дальнейшем мате-
риале книги.
Самой главной составляющей кода в листинге 2.8 является наша первая пользова-
тельская функция дгее1:_те(). Для ее создания мы использовали несколько разных
синтаксических структур и стилистических принципов, чтобы сообщить компью-
теру, что он должен создать эту функцию и запомнить ее для использования в
дальнейшем. Некоторые из этих принципов, например использование описательно-
го имени функции дгее1:_те(), мы уже видели при именовании переменных в пре-
дыдущих примерах. Также мы придерживались правила следования имени функ-
ции парными круглыми скобками, как это делается в случае встроенных функций и
методов.
На рис. 2.1 приводится объяснение составляющих нашей пользовательской функции
дгее1:_те(), чтобы пояснить, что происходит в каждой строке кода ее определения.
Указывает, что далее Имя Имена параметров
следует имя пользовательской предоставляют доступ
пользовательской функции функции к переменным ингредиентам
। изнутри функции
дгее1_пе(а_папе):
рггп1(“не1Ло ”+а_пате)
ь-г—1
Любой код, входящий в состав функции,
должен начинаться отступом в одну табуляцию
Рис. 2.1. Компоненты пользовательской функции
Как можно видеть на рис. 2.1, при создании пользовательской функции необходимо
предоставлять несколько указателей для компьютера:
♦ ключевое слово с!еГ (сокращение от с1фпе — определить) сообщает компьютеру,
что далее следует имя функции;
♦ парные круглые скобки, следующие сразу же за именем функции, подтвержда-
ют, что данное имя является именем функции, и служат для заключения в них
параметров (если таковые имеются);
♦ двоеточие (:) обозначает, что все последующие строки кода с отступом являются
частью функции;
♦ для доступа к переменной, передаваемого функции в качестве аргумента, ис-
пользуется имя локального для данной функции параметра, которое указывается
в парных круглых скобках, следующих за именем функции;
♦ при создании пользовательской функции можно использовать уже существую-
щие функции и методы любого типа, как встроенные, так и пользовательские. В
этом состоит ключевая стратегия для создания эффективного и гибкого кода.
Пользовательские функции используются или вызываются точно так же, как и
встроенные функции. Мы просто указываем имя функции (в данном примере это
дгее1:_те()), вставляя в круглые скобки точно такое же количество аргументов, как и
количество параметров, указанное в ее определении. Поскольку при определении
нашей пользовательской функции дгее1:_те() мы указали только один параметр
(т. е., а_пате), то при ее вызове ей нужно передавать также только один аргумент. В
противном случае мы получим сообщение об ошибке.
Быстрая прокрутка
Мы рассмотрим вопрос ошибок в коде и сопутствующих сообщений более под-
робно в разд. "Понимание ошибок" далее в этой главе. Но вы должны смело экс-
периментировать с кодом в этих примерах, чтобы увидеть, что происходит в слу-
чае каких-либо проблем с ним. Экспериментировать с кодом в листинге 2.7 мож-
но следующими способами:
О вызвать функцию, не передавая ей никаких параметров, т. е. дгее1:_те();
О вызвать функцию, передавая ей избыточные параметры, т. е. дгее1:_те(аи1±ог,
ес!1±ог);
О вызвать функцию, передавая ей параметр, не являющийся переменной, например
д гее1=_те (" Запап^Ьа");
О вызвать функцию, передавая ей параметр, не являющийся строкой, например
дгее1:_те(14).
Обратите внимание на то, что происходит при исполнении каждой из этих ва-
риаций кода. Если выдается сообщение об ошибке, попробуйте найти связь меж-
ду содержимым этого сообщения и выполненными вами изменениями в коде.
Тогда как сообщения об ошибках программ не отличаются особым качеством
описания этих ошибок, из них обычно можно почерпнуть по крайней мере часть
полезной информации о причине недовольства компьютера. Мы рассмотрим во-
прос поиска и устранения причин проблем с исполнением кода чуть далее в этой
главе, но ваши эксперименты станут хорошим началом в изучении этой области.
Применение пользовательских функций
Как вы, возможно, заметили, мне нравится рассматривать определяемые пользова-
телем или просто пользовательские функции как своего рода программные "рецеп-
ты”. Подобно кулинарным рецептам они предоставляют компьютеру переисполь-
зуемые инструкции для преобразования одного или больше "ингредиентов” в виде
сырых данных в другой, более полезный продукт. Иногда, как в случае с нашим
"рецептом” дгее1:_те(), такой параметр существует только один, но их может быть и
несколько, причем с разными типами данных. Не существует правильных или не-
правильных способов создания пользовательских функций, как не существует пра-
вильных или неправильных способов создания кулинарных рецептов. У каждого
будет свой собственный стиль. В то же самое время, что касается стратегии приня-
тия решения о составляющих конкретной пользовательской функции, может быть
полезным помнить о том, как мы обычно используем (или, возможно, даже созда-
ем) рецепты для приготовления кулинарных блюд.
Например, очевидно, что вполне возможно создать один кулинарный рецепт ”День
благодарения”, описывающий, как приготовить весь ужин для этого праздника с
первого до последнего блюда. В зависимости от вашего стиля отмечания этого
праздника, для "исполнения” этого рецепта может потребоваться от 2 до 72 часов.
Но он будет очень полезным один день в году, без надобности во все остальные
дни. И если бы вы захотели приготовить какое-либо отдельное блюдо из этого ре-
цепта для другого праздника (например, картофельное пюре для Нового года), вам
бы потребовалось просмотреть весь этот рецепт, чтобы найти и извлечь соответст-
вующие ингредиенты и инструкции только для этого блюда. Это означало бы необ-
ходимость выполнения большого объема работы, прежде чем приступать к собст-
венно приготовлению этого блюда.
Поэтому, в то время как мы хотим, чтобы наши пользовательские функции выпол-
няли задачи несколько более сложные, чем поддающиеся выполнению встроенны-
ми средствами языка, обычно мы не хотим, чтобы они были излишне сложными.
Подобно рецепту ”День благодарения”, громадные пользовательские функции (и
даже программы) ограничивают наши возможности их эффективного использова-
ния. А вот простые, сфокусированные пользовательские функции делают наш код в
общем итоге более полезным и гибким. Мы исследуем процесс создания таких
функций более пристально в главе 8.
Библиотеки: занимаем пользовательские функции
у других программистов
Если пользовательские функции можно рассматривать как программные рецепты,
тогда библиотеки являются программными поваренными книгами: большими
коллекциями пользовательских функций, созданных другими программистами,
которые можно использовать для преобразования наших ингредиентов в виде сы-
рых данных без необходимости придумывать свои собственные ”рецепты” с само-
го начала. Как упоминалось в главе 7, одной из причин, по которым мы вообще
используем язык РуЙюп, является наличие большого сообщества программистов,
которые создали и продолжают создавать полезные библиотеки функций на языке
РуЙюп. Как мы увидим в разд. "Отправляемся в путь с данными СШ В1ке" в кон-
це этой главы, использование этих библиотек предоставляет нам полезные и
мощные возможности.
Но прежде чем приступать к использованию этих возможностей, нам нужно рас-
смотреть еще две важные грамматические структуры языка РуЙюп: циклы и услов-
ные операторы.
Структуры управления:
циклы и условные операторы
Как уже обсуждалось ранее, написание кода на языке РуШоп во многом похоже на
процесс писания на естественном языке. Кроме использования нескольких основ-
ных ”частей речи”, код на языке РуШоп также пишется слева направо и сверху вниз
и читается, по сути, в таком же порядке. Но путь исполнения кода программы более
похож на книгу под условным названием "Создай свое собственное приключение",
чем на обычный очерк или статью. В частности, в зависимости от предоставленных
программистом команд, некоторые части кода могут пропускаться или исполняться
многократно на основании данных или иных факторов.
Циклы
Одной из самой распространенных целей при выпасе данных будет выполнение
каких-либо одинаковых действий над каждой записью в наборе данных. Например,
предположим, что нам нужно вычислить сумму чисел в списке:
# Создаем список, содержащий количество страниц в каждой главе
# печатной версии данной книги
раде_соип1=8 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
Выполнить эту задачу, не прибегая к программированию, можно несколькими спо-
собами: используя программу калькулятора на компьютере, физический калькуля-
тор или даже (о, ужас!) карандаш и бумагу. Также, если вы знаете, как работать с
электронными таблицами, можно было бы ввести каждое число в отдельную ячей-
ку таблицы, а затем применить функцию $11М() к этим ячейкам. Для небольших
списков любой из этих подходов будет вполне удовлетворительным, но все они
плохо поддаются масштабированию: сложение 10 чисел на бумаге или с калькуля-
тором не займет много времени, чего нельзя сказать о сложении 100 чисел. Подход
с использованием электронной таблицы несколько лучше по времени, но все равно
требует выполнения нескольких дополнительных операций: копирование и вставка
данных в таблицу и ручной выбор ячеек, подлежащих суммированию. Использова-
ние программного решения позволит избежать всех этих недостатков. Кроме этого,
независимо от количества суммируемых чисел, будь то 10 или 10 миллионов, соз-
дание соответствующей программы займет одинаковое время, а ее исполнение в
последнем случае будет лишь незначительно более длительным.
Поскольку процесс написания компьютерной программы, конечно же, относится к
более общему письменному процессу, инструкции компьютеру для решения по-
ставленной задачи можно изложить разными способами. Один из этих способов —
считывать каждое число в списке и добавлять его в промежуточную сумму, как по-
казано в листинге 2.9.
Листинг 2.9. Сценарий раде_соип(_1оор.ру
# Список значений количества страниц в каждой главе книги
раде.соипГз = [28, 32, 44, 23, 56, 32, 12, 34, 30]
# Переменная для отслеживания общего количества страниц; начальное значение равно 0
ГоГа1_раде8 = 0
# Для каждого элемента списка выполняем определенную операцию
Гог а_пипЬег 1п раде_соипГз:
# В данном случае добавляем текущее число к значению переменной ГоГа1_радез
ГоГа1_радез = ГоГа1_радез + а_пипЬег
рг1пГ(ГоГа1_радез)
Прежде чем рассматривать другие способы программного решения этой задачи,
рассмотрим по частям программу в листинге 2.9. Очевидно, что мы начинаем ее со
списка чисел. Далее мы создаем переменную ГоГа1_радез для хранения значения
общего количества страниц. Этой переменной мы присваиваем начальное значение,
равное 0 (большинство программ калькулятора выполняют эту операцию более-
менее косвенным образом). Наконец, мы начинаем перебор значений в списке:
Гог а_пипЬег 1п раде_соипГз:
Для меня лично эту строку кода легче всего понять, проговорив ее вслух, как обыч-
ное предложение: ”Для каждого элемента а_пипЬег в списке раде_соипГз выполняем
следующее”. В действительности, именно это и происходит. В частности, для каж-
дого элемента списка раде_соипГз компьютер выполняет инструкции, содержащиеся
в блоке кода внизу под оператором Гог.Лп...:, выделенным отступами. В данном слу-
чае это суммирование текущего значения переменной ГоГа1_радез и значения пере-
менной а_пипЬег, сохраняя полученный результат в этой же переменной ГоГа1_радез.
Все это довольно прямолинейно: мы уже предоставили компьютеру явным образом
значение как переменной раде_соипГз (это наш список значений), так и переменной
ГоГа1_радез. Но как насчет значения переменной а_пипЬег? Откуда оно берется и как
компьютер знает, где его найти?
Подобно оператору рг1пГ() или конструкции беГ...имя_функции():, формат Гог.Лп...:
встроен в язык РуЙюп, и по этой причине при кодировании нам не нужно предос-
тавлять ему много инструкций. В данном случае нам нужно предоставить операто-
ру Гог.Лп...: всего лишь две вещи: переменную типа список (т. е. раде_соипГз) и имя,
которое компьютер может использовать для обращения к текущему элементу спи-
ска (т. е. а_пипЬег). Назначение составляющих этого оператора показано на рис. 2.2.
В выборе имени переменной а_пипЬег, как и с именами переменных в целом, не бы-
ло ничего особенного, просто оно мне показалось достаточно описательным и удо-
бочитаемым. Важно лишь то, чтобы имя этой переменной в блоке кода (с отступом
внизу под оператором) было точно таким же, как и имя соответствующей перемен-
ной в самом операторе.
Обозначает, что будет
выполняться в цикле
обработка каждого
элемента списка
Имя уже определенного
объекта наподобие списка
(например, должным образом
преобразованного файла
со значениями,
разделенными запятыми)
Задаваемое программистом
имя переменной
для обращения
к отдельным элементам
списка в цикле
Тог а_питЬег 1п раде_соип1:5:
1о1а1_раде5 = 1о1а1_раде5 + а_гшнЬег
Любой код, входящий в состав цикла,
\ должен начинаться отступом в одну табуляцию
Рис. 2.2. Структура цикла -Гог
Быстрая прокрутка
Вот вам еще одна возможность поэкспериментировать с ошибками в коде. Мо-
дифицируйте код в листинге 2.9 следующим образом:
1. Измените имя переменной а_пипЬег только в операторе 1"ог.. Лп:.
2. Измените имя переменной а_пипЬег как в операторе -Гог...1п:, так и в коде с
отступом внизу под этим оператором (т. е. новое имя переменной должно
быть одинаковым в обоих случаях).
Посмотрите, что происходит при попытке исполнить модифицированный таким
образом код. Если при исполнении второй модификации продолжает выводиться
сообщение об ошибке, проверьте, чтобы имена переменных были абсолютно
идентичны в обоих местах. Самый надежный способ добиться этого — использо-
вание копирования и вставки.
На профессиональном языке программистов конструкция -Гог.. .1п...: называется
циклом (1оор) и присутствует в том или ином виде во всех широко применяемых
языках программирования. Циклом она называется по той причине, что для каждо-
го элемента списка каждая релевантная строка кода — в случае языка Руйюп это
все строки в блоке кода с отступом внизу под оператором Еэг.Лп...: — исполняется в
повторяющемся цикле. Это немного трудно увидеть в данном примере, поскольку
наш цикл содержит только одну строку кода, поэтому давайте добавим еще не-
сколько строк, чтобы лучше проиллюстрировать происходящее. Соответствующий
код приводится в листинге 2.10.
Листинг 2.10. Сценарий раде_соипСрпп(ои1ру
# Список значений количества страниц в каждой главе книги
раде_соип1=8 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
# Переменная для отслеживания общего количества страниц; начальное значение равно 0
ГоГа1_раде8 = 0
# Для каждого элемента списка выполняем определенную операцию
Гог а_пипЬег 1п раде_соипГз:
рг1пГ("Тор оГ 1оор!") # Начало цикла
рг1пГ("ТЬе сиггепГ 1Геп 1з:") # Текущий элемент списка:
рг1пГ(а_пипЬег)
ГоГа1_радез = ГоГа1_радез + а_пипЬег
рг1пГ("ТЬе гиппТпд ГоГаТ 1з:") # Промежуточная сумма
рг1пГ(ГоГа1_радез)
рг1пГ("ВоГГоп оГ Тоор!") # Конец цикла
рг1пГ(ГоГа1_радез)
Вы можете подумать, что для простого суммирования списка значений мы проде-
лали слишком много работы. Конечно же, данную задачу можно решить более эф-
фективным способом. В частности, язык РуШоп содержит встроенную функцию
зип(), которая принимает в качестве аргумента список значений, подлежащих сум-
мированию, и возвращает их сумму. В листинге 2.11 приводится пример использо-
вания этой функции.
Листинг 2.11. Пример использования функции §ит()
# Список значений количества страниц в каждой главе книги
раде_соипГз = [28, 32, 44, 23, 56, 32, 12, 34, 30]
# Отображаем результат исполнения функции зип()О
рг1пГ(зит(раде_соипГ5))
О Попробуйте самостоятельно добавить этот код в свою программу.
Хотя мы могли бы использовать функцию зип() с самого начала, по разным причи-
нам я этого не сделала, это дало возможность познакомить вас с циклом Гог. Во-
первых, чтобы напомнить вам, что даже простые задачи программирования можно
решить более чем одним способом. Во-вторых, операторы цикла являются жизнен-
но важной частью процесса выпаса данных (и, по сути, всего программирования), а
цикл Гог...1п...: является одним из ключевых инструментов, который мы будем
использовать для фильтрации, оценки и переформатирования данных.
Условные операторы
Цикл Гог предоставляет нам простой способ обращения к каждому элементу набора
данных, но выпас данных также требует принятия решений о том, что и как делать
с данными. Обычно это означает, что мы оцениваем некоторый аспект данных и
для определенного значения выполняем одно действие, а для других значений —
другое, или вообще не выполняем никаких действий. Например, предположим, что
мы хотим узнать, сколько глав в этой книге содержат более 30 страниц, а сколько
меньше 30. Для решения этой задачи нам нужно:
1. Проверить, больше ли чем 30 значение определенного элемента в списке
раде_соип1=5.
2. Если да, то инкрементируем на 1 значение переменной счетчика сл/ег_30.
3. В противном случае инкрементируем на 1 значение переменной счетчика ипбег_30.
К счастью, язык РуЙюп содержит встроенную грамматическую структуру для вы-
полнения точно такой оценки и принятия решения: условный оператор 1Е..е1зе.
Рассмотрим, как он работает. Для этого в листинге 2.11 модифицируем цикл Еэг,
чтобы так же проверять, больше или меньше значения 30 значение переменной ко-
личества страниц главы а пишЬег. Модифицированный код приводится в листинге 2.12.
Листинг 2.12. Сценарий раде_соип<_сопс1Июпа1.ру
# Список значений количества страниц в каждой главе книги
раде_соип1=8 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
# Создаем переменные для хранения следующих значений:
# Общего количества страниц в книге
1о1:а1_раде5 = 0
# Количества глав, содержащих более 30 страниц
ипс1ег_30 = 0
# Количества глав, содержащих менее 30 страниц
О7ег_30 = 0
# Для каждого элемента в списке раде_соип1=5
Еэг а_пипЬег 1п раде_соип1=5:
# Добавляем значение количества страниц текущей главы
# а_пипЬег к общему количеству страниц 1о1:а1_радез
1=о1:а1_раде8 = 1:о1:а1_раде8 + а_пипЬег
# Проверяем, содержит ли текущая глава больше 30 страниц
И а_пипЬег > 30:
# Если да, то инкрементируем на 1 значение переменной счетчика оуег_30.
О7ег_30 = О7ег_30 + 1
# В противном случае...
е!зе:
# ...инкрементируем на 1 значение переменной счетчика ипс1ег_30.
ипбег_30 = ипбег_30 + 1
# Отображаем полученные результаты
рг1п±(ГоГа1_раде8)
рг1пГ("МипЬег оГ сЬарГегз 07ег 30 радез:")
# Количество глав, содержащих свыше 30 страниц
рг1пГ(о7ег_30)
рг1пГ("МипЬег оГ сЬарГегз ипс!ег 30 радез:")
# Количество глав, содержащих менее 30 страниц:
рг1пГ(ипбег_30)
Как и в случае с циклом Гог, я считаю, что легче всего понимать происходящее в
условном операторе 1Г...е1зе, озвучивая его вслух как предложение (рекомендую
записать это предложение как комментарий в соответствующем месте кода): ”Если
(1Г) количество страниц текущей главы более 30, то инкрементируем на 1 значение
счетчика глав, содержащих свыше 30 страниц сл/ег_30. В противном случае (е!зе)
инкрементируем на 1 значение счетчика глав, содержащих менее 30 страниц
ипбег_30”.
Я надеюсь, что такой подход позволит вам получить некоторое представление о
происходящем на интуитивном уровне. Тем не менее я хочу немного задержаться и
рассмотреть процесс в условном операторе 1Г.. .е1зе более подробно, поскольку мы
будем постоянно прибегать к его использованию.
Прежде всего взглянем на блок кода, выделенного отступом на одну табуляцию от
левого поля. Весь этот код входит в состав цикла Гог. Выделение этого кода отсту-
пом говорит компьютеру, что он находится ”внутри” цикла Гог. Подобным образом,
код, входящий в состав каждой части условного оператора 1Г...е1зе, выделяется
отступом на одну или больше табуляций. В языке РуЙюп этот подход с выделением
частей кода отступами используется не только для придания коду опрятного вида.
Такое нарастающее выделение отступами требуется, чтобы код работал должным
образом. Неправильные отступы вызывают сообщения об ошибке2. Такой механизм
часто называется вложением (пе81т§). На рис. 2.3 приводится графическая иллюст-
рация этого процесса.
Вложение кода имеет несколько последствий, которые мы рассмотрим далее в этой
книге. Но на данном этапе главное, что нужно знать об этой практике, — это то,
что для того, чтобы строка кода ”принадлежала” функции, циклу или условному
оператору, ее нужно сместить на одну табуляцию вправо от структуры, частью ко-
торой мы хотим ее сделать. Чтобы продемонстрировать это на практике, сведем
все, что мы сделали до сих пор, в одну программу РуЙюп, содержащую цикл, ус-
ловный оператор и пользовательскую функцию. Соответствующий код приводится
в листинге 2.13.
2 Во многих других языках программирования блоки кода, входящие в состав циклов или условных
операторов, выделяются фигурными скобками. Но, как упоминалось в разд. "Удобочитаемость”
главы 7, язык РуЙзоп для этой цели полагается на отступы и табуляцию.
^ог а_пшпЬег хп рвде_соипГ8:
рггп1("лцтЬег с11ар№$ оуег 30 радев:")
рг!п1:(оуег_30)
ргтШСлигоЬег о€ сЬарьегя ипс1ег 30 радев:")
рггп1(ит!ег_36)
Рис. 2.3. Вложение кода
Листинг 2.13. Сценарий раде_соипСси8(от_Типс(юп.ру
# Список значений количества страниц в каждой главе книги
раде_соип1=5 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
# Определяем новую функцию соип1:_раде8(), которая принимает один аргумент:
# список чисел
с1е^ соип1:_раде8(раде_соип1:_1т-21:) :О
# Создаем переменные для хранения следующих значений:
# Общего количества страниц в книге
Ь^аТ-радез = 0
# Количества глав, содержащих более 30 страниц
ипс!ег_30 = 0
# Количества глав, содержащих менее 30 страниц
О7ег_30 = 0
# Для каждого элемента в списке раде_соип1_1.1.51:
1"ог а_пипЬег 1п раде_соип1_1.1.5'1::0
# Добавляем значение количества страниц текущей главы а_пипЬег к общему
# количеству страниц 1о1а1_радез
1:о1:а1_раде8 = Ь^аТ-радез + а_пипЬег
# Проверяем, содержит ли текущая глава больше 30 страниц
II2 а_пипЬег > 30:
# Если да, то инкрементируем на 1 значение переменной счетчика О7ег_30
О7ег_30 = О7ег_30 + 1
# В противном случае
е!зе:
# инкрементируем на 1 значение переменной счетчика ипс1ег_30.
ипс1ег_30 = ипс1ег_30 + 1
# Отображаем полученные результаты
рг1пГ(ГоГа!_радез)
рг1пГ("1\1ипЬег оГ сЬарГегз 07ег 30 радез:")
# Количество глав, содержащих свыше 30 страниц:
ргГпГ(о7ег_30)
рг1пГ("1\1ипЬег оГ сЬарГегз ипбег 30 радез:")
# Количество глав, содержащих менее 30 страниц
рг1пГ(ипс1ег_30)
# Исполните этот "рецепт", вызвав его функцию, передав ей в качестве параметра
# настоящий список значений количества страниц в каждой главе
соипГ_радез (раде_соипГз )О
О Мы можем заключить наш существующий код в оболочку новой функции (кото-
рая здесь называется соипГ_радез()), добавив строку определения функции и сме-
стив код на одну табуляцию вправо.
О Имя переменной списка значений для использования в цикле должно быть таким
же, как и имя переменной, указанное в круглых скобках в определении функции в
строке О.
О Функция не делает ничего до тех пор, пока не будет вызвана, т.е. исполнена. На
данном этапе нам нужно передать ей специфичный аргумент, обработку которого
мы хотим выполнять посредством этой функции.
Сравнив код в листинге 2.13 и листинге 2.12, можно увидеть, что первый код — это
тот же второй код, в который были внесены следующие три модификации:
1. Добавлен оператор определения функции с1еГ соип1_радез(раде_соип1_1л51:):.
2. Весь существующий код смещен вправо на одну дополнительную табуляцию,
сообщая таким образом компьютеру, что он принадлежит новой функции
соипГ_радез(). В редакторе кода А1ош этот отступ можно выполнить одновре-
менно для всех требуемых строк кода, выделив соответствующие строки и на-
жав клавишу <ТаЪ>. Модифицировано имя переменной списка значений, ука-
занное в операторе Гог цикла, чтобы совпадало с именем переменной, указанным
в круглых скобках в определении функции.
3. В конце выполняется вызов данной функции, передавая ей в качестве аргумента
переменную раде_соипГз. Обратите внимание на отсутствие какого бы то ни было
отступа перед оператором вызова функции соипГ_радез(раде_соипГз).
Будем надеяться, что у вас понемногу начинает вырисовываться общая картина
выполняемого процесса при использовании всех этих грамматических структур. Но
прежде чем приступать к использованию всех этих средств для выпаса данных для
реальных ситуаций, нам нужно уделить некоторое время рассмотрению, что проис-
ходит, когда в ходе исполнения кода возникает ошибка.
Понимание ошибок
Как упоминалось в главе 7, компьютеры хорошо справляются с повторяющимися
задачами, выполняя их быстро и точно. Это позволяет нам создавать программы,
хорошо поддающиеся масштабированию. Иными словами, код для суммирования
или сортировки списка в 10 элементов (как в случае с нашим списком раде_соип1:5)
можно использовать и для списка, содержащего, скажем, 10 тысяч элементов.
Но в то же самое время компьютеры по-настоящему, существенно и неисправимо
глупы. Компьютеры не могут делать никаких заключений или создавать иннова-
ции; они могут лишь выбирать тот или иной путь исполнения кода на основе инст-
рукций и данных, предоставляемых для них людьми. В результате процесс написа-
ния кода во многом похож на дачу указаний 2-3-летнему ребенку: они должны
быть в высшей степени буквальными и очень ясными, потому что в случае возник-
новения какой-либо ситуации, не упомянутой в этих инструкциях, можно ожидать
любых непредвиденных результатов их исполнения3.
Например, люди часто даже не замечают орфографические или грамматические
ошибки в письменном тексте, поскольку в большинстве случаев мы практически
автоматически сделаем умозаключение о соответствующем значении на основе
контекста предложения или абзаца. Более того, даже в случае перестановки букв во
всех словах предложения мы, как вы сейчас убедитесь, обынчо сомжем протичать
его без осбо бошьлих уислий4. В противоположность этому, если в вашем коде хотя
бы одна запятая будет не там, где она должна быть, то компьютер откажется ис-
полнять весь код, выдав соответствующее сообщение.
Поэтому ошибки при написании кода не просто неизбежны, они ожидаемы. Неза-
висимо от вашего опыта программирования, любой фрагмент вашего кода длиной
больше, чем несколько строк, может содержать ошибки того или иного типа. По-
этому, вместо того, чтобы думать о том, как избежать ошибок в своем коде, будет
намного полезнее научиться, как интерпретировать сообщения об ошибках и ис-
правлять их. В этой книге я буду иногда намеренно делать ошибки в коде (или ре-
комендовать, чтобы вы сами делали их, как я это делаю в этой главе во врезках
"Быстрая прокрутка"), чтобы они стали для вас привычными, и вы могли начать
разрабатывать свой собственный процесс поиска их причин и последующего уст-
3 Конечно же, в отличие от компьютеров, малыши способны проявлять настоящее оригинальное
мышление и способны к самостоятельному обучению.
4 Дополнительную информацию на эту тему можно найти в посте \Уогс1уагс1 (Ы1р://\уог(1уаг(1.сот/
003/09/15/й-и-сп-г(14Ь8-п18§-и-г-18Ыке-угупе-18е) блога 8со11 Ко§епЬег§.
ранения. Для начала мы рассмотрим три главных типа ошибок при программирова-
нии: синтаксические ошибки, ошибки времени исполнения и логические ошибки.
Синтаксические ошибки
Грамматические или синтаксические ошибки программирования могут быть одно-
временно самыми простыми и при этом самыми изводящими ошибками, отчасти
потому, что они происходят очень часто и компьютер постоянно закатывает скан-
далы из-за них. Упоминаемая ранее неправильно поставленная запятая является
примером синтаксической ошибки: так или иначе, в вашем коде было допущено
нарушение грамматических правил языка программирования.
Я называю такие ошибки простыми потому, что они почти всегда являются тако-
выми. Исходя из моего опыта, большинство синтаксических ошибок (и в более ши-
роком смысле, ошибки программирования) являются обычными опечатками: про-
пущенная запятая или кавычка или избыточный или недостаточный отступ строки
кода. К сожалению, многие современные программисты, с которыми мне приходи-
лось работать, находят такие ошибки особенно изводящими, как раз из-за их про-
стоты, и потому, что они чувствуют себя дураками из-за того, что допустили их.
По правде говоря, опытные программисты допускают синтаксические ошибки все
время. Самое главное, чему начинающие программисты могут научиться на своих
синтаксических ошибках, — это как не допускать, чтобы они сбивали их с курса.
Более того, одной из причин, по которой я включила ранее в эту главу врезки "Бы-
страя прокрутка ", в которых даются указания, как преднамеренно внести ошибки
в свой код, является желание продемонстрировать, насколько легко можно допус-
тить ошибку в коде, а также насколько легко ее можно исправить. В некоторых
отношениях одним из самых ценных навыков, которым вы научитесь в процессе
программирования, — это как допускать множество ошибок, но не дать этому
обескуражить себя.
Программирование как игра
Чтобы программные ошибки не вызывали особо большого расстройства, осо-
бенно на начальных стадиях обучения, к процессу программирования можно
попробовать подходить как к своего рода видеоигре, где для прохождения
трудного уровня часто требуются многократные (как минимум, десятки) по-
пытки. Иногда повторная попытка обуславливается по-настоящему трудным
противником (или задачей), но иногда вы просто совершаете ошибку и попа-
даете в ловушку (или просто забываете поставить запятую). В каждом из этих
случаев ключевой подход к успешному решению — пробовать снова, но толь-
ко до определенной степени. Тогда как определенно не стоит отказываться от
попытки решить программную задачу при первой же ошибке, в многочасовом
сеансе программирования, несомненно, можно перегореть точно так же, как и
при попытках пройти трудный уровень видеоигры.
Когда такое происходит, то возьмите перерыв по крайней мере на 20-30 минут,
прежде чем снова пытаться решить задачу. Со временем у вас начнет выраба-
тываться чувство, когда нужно сделать такой перерыв. Это еще один навык
программирования, который, в конечном счете, будет для вас бесценным.
При возникновении синтаксической ошибки мы будем знать, что это именно син-
таксическая ошибка, поскольку в последней строке (обычно многострочного) со-
общения об ошибке будет указано 8уп1:ахЕггог: описание ошибки. Но более полезной
для устранения ошибки будет та часть сообщения об ошибке, в которой указывает-
ся файл, содержащий ошибку, и строка кода этого файла, в которой она находится.
Со временем просто посмотреть на эту строку кода и поискать в ней возможные
проблемы (обычно это пропущенные знаки пунктуации: запятая, скобка, точка с
запятой, кавычки) будет достаточным для вас, чтобы определить причину ошибки.
Обычно сообщение об ошибке содержит проблемную (предположительно) строку
кода, в которой возможный пропущенный символ обозначается знаком вставки (Л).
Но иногда бывают случаи, когда место проблемы указывается неверно. Рассмот-
рим, например, код в листинге 2.14, где в одной строке данных типа сНс! пропуще-
на запятая.
Листинг 2.14. Код с преднамеренной ошибкой
1 # Хотя ошибка в действительности находится в строке 4 (пропущена запятая),
2 # в сообщении об ошибке указывается строка 5
3 Ьоок = {"Ш1е":"Ргас1:1са1 Ру1±оп 1"ог 0а1а 1л1гапд1Лпд апс! 0а1:а риаШу",
4 'Тота!:": "Ьоок"
5 "аи1±ог": "Зизап Е. МсСгедог"
6 }
Само сообщение об ошибке выглядит следующим образом:
Е1Ле "0Ьдес1:Еггог.ру", Нпе 5
"аи1±ог": "Зизап Е. МсСгедог"
8уп1:ахЕггог: 1п7а1Лс1 зуп1:ах
Как видите, хотя в коде в листинге 2.14 запятая пропущена в строке 4 после значе-
ния "Ьоок", в сообщение об ошибке указывается, что ошибка находится в строке 5.
Это объясняется тем, что транслятор понял, что имеется проблема, только в этой
точке. Но, как правило, синтаксические ошибки будут находиться в строке, указан-
ной транслятором (или в строке под ней).
Ошибки времени исполнения
В категорию ошибок времени исполнения входят проблемы любого типа, которые
возникают в процессе исполнения кода. Подобно синтаксическим ошибкам, боль-
шая часть ошибок времени исполнения, по сути, те же опечатки, такие как, напри-
мер, неправильно указанные имена переменных. В частности, когда сообщение об
ошибке содержит фразу типа: некая_переменная не была определена (уапаЫе \уаз по!
бейпеб), можно быть почти уверенным, что ваш код содержит неправильно напи-
санную переменную. Следует иметь в виду, что лишние или недостающие пробелы
также учитываются. Изучение всего сообщения об ошибке, чтобы отследить ее ис-
точник может быть несколько затруднительным, поскольку такие сообщения обыч-
но ссылаются на подробности внутренней работы языка РуЙюп в объеме, который я
лично не нахожу особо полезным. Поэтому я рекомендую скопировать имя про-
блемной переменной непосредственно из сообщения об ошибке, а затем выполнить
поиск этой переменной без учета регистра в исходном коде. (Редактор кода А1ош
осуществляет такой поиск по умолчанию.) В результате будут выделены все по-
добные (но не идентичные) варианты написания данной переменной, что ускорит
поиск несовпадающего имени.
Например, в листинге 2.15 имя переменной в определении функции дгее1:_те(а_пате)
не совпадает с именем переменной в теле определения.
Листинг 2.15. Несовпадающие имена переменной вызовут ошибку времени исполнения
# Создаем функцию, отображающую на экране приветствие по любому переданному ей имени
беГ дгее1:_те(а_пате):
рггп1("НеИо "+А_папе)
# Создаем переменную с именем аи1±ог
аи1±ог = "5изап Е. МсСгедог"
# Передаем функции дгее1:_те() аргумент аи1±ог
дгее1:_те(аи'1:Ьог)
Поскольку имя параметра в круглых скобках в определении функции всегда прева-
лирует над именами в остальных местах, исполнение кода в листинге 2.15 создает
сообщение об ошибке, показанное в листинге 2.16.
Листинг 2.16. Сообщение об ошибке времени исполнения,
вызванное несовпадающими именами переменной
ЕПе пдгее1:_те_рагате1:ег_т15та1:сЬ.ру", Ппе 10, 1п <тоби1е>
дгее1:_те(аи1±ог)
ЕПе пдгее1:_те_рагате1:ег_т15та1:сЬ.ру", Ппе 4, 1п дгее1:_те
рп.п1("НеИо "+А_папе)
МапеЕггог: д!оЬа! папе 'А_пате' по!: беПпеб
Обратите внимание на то, что как обычно бывает, последние несколько строк со-
общения содержат наиболее полезную информацию. В частности, в последней
строке сообщения говорится, что мы пытаемся использовать имя переменной
А_пате, предварительно не объявив его, а предшествующая строка содержит сам
код, где находится эта переменная. Имея эти два элемента информации плюс нашу
стратегию поиска, определить причину данного сообщения об ошибке, скорее все-
го, не займет слишком много времени.
Другой очень распространенный тип ошибки времени исполнения происходит при
попытке выполнить с определенным типом данных операцию, которую он не под-
держивает. Во врезке "Быстрая прокрутка" в конце разд. "Глаголы ~ функциям"
было предложено исполнить заведомо неправильную строку кода дгее1:_те(14). По-
пытка исполнения этого кода вызвала бы сообщение об ошибке, содержащее слово
ТуреЕггог, означающее, что какая-то часть нашего кода обратилась к типу данных,
не соответствующему ожидаемому. В данном случае проблема состоит в том, что
функция дгее1:_те() ожидает в качестве параметра строковое значение, но ей было
передано числовое значение 14.
Трудность с ошибками этого типа состоит в сложности определения точного ме-
стонахождения причины ошибки вследствие разнесенности мест в коде, где пере-
менной присваивается значение и где эта переменная (и, следовательно, ее значе-
ние) используется. Эти два процесса могут находиться в коде на значительном рас-
стоянии друг от друга, особенно с повышением уровня сложности разрабатываемой
программы. Например, в сообщении об ошибке в листинге 2.16, вызванной ошиб-
кой в коде в листинге 2.15, указываются два места в коде. Первым является строка,
в которой переменная передается функции, и, следовательно, в которой ей при-
сваивается значение:
ЕПе "дгееГ_те_рагате1ег_т1_5та1:сЬ.ру",, Ппе 10, 1п <пос!и1е>
А второе — строка кода, в которой это значение используется:
ЕПе "дгееГ_те_рагате1:ег_т15та1:сЬ.ру", Ппе 4, 1п дгее1:_те
Как уже упоминалось ранее, для определения причины проблемы будет полезным
начать процесс ее поиска с места использования проблемной переменной или зна-
чения, а затем, перемещаясь по коду, пытаться найти строку кода, где этой пере-
менной присваивается ее значение. Но при этом следует иметь в виду, что сообщение
об ошибке не обязательно может содержать такую строку. Это часть тех обстоя-
тельств, которые делают задачу определения причин ошибок времени исполнения
такого типа более трудной, чем для обычных синтаксических ошибок.
То, что ошибки времени исполнения являются одними из самых трудных для диаг-
ностирования, и есть ключевая причина, почему я рекомендую часто и регулярно
тестировать и сохранять разрабатываемый код. Причину новой ошибки времени
исполнения намного легче определить, если после последнего тестирования было
добавлено небольшое количество строк нового кода, поскольку проблема должна
находиться в этих нескольких строках. Использование подхода типа ”пишем, ис-
полняем, повторяем” значительно сокращает область поиска причин новых оши-
бок, что, в свою очередь, позволит их обнаружить сравнительно быстро.
За исключением...
Одна из реальностей выпаса данных — нашему коду часто приходится сталки-
ваться с неподходящими или непредсказуемыми данными, поскольку они мо-
гут содержать неправильные или вообще отсутствующие значения. Например,
некоторые ячейки столбца числовых значений могут быть пустыми. Как мы
увидим, в частности, в главе 6, данные пустые ячейки не будут автоматически
преобразованы в какое-либо число, например ноль5. Это означает, что пустые
значения могут вызывать ошибки, в случае если ваш код может обрабатывать
только числовые значения.
В языке РуШоп этот тип досадных, но предсказуемых ошибок называется ис-
ключением (ехсербоп), поскольку они происходят, когда программа сталкива-
ется с исключительной или неожидаемой ситуацией, например, с неподходя-
щим типом данных. В программах по выпасу данных исключения происходят
особенно часто, их можно обеспечить возможностями обработки исключений,
т. е. дать нашему коду указание вместо выдачи сообщения об ошибке и пре-
кращения исполнения делать что-то другое.
И хотя обработка исключений может быть полезной, в рамках этой книги мы не
будем уделять этому вопросу много внимания. Это объясняется тем, что наша
цель здесь состоит в том, чтобы не просто создавать программы, которые могут
работать с определенными наборами данных, но и обеспечивать качество этих
данных. В результате большую часть времени мы будем пытаться узнать, когда и
где наши данные не соответствуют нашим ожиданиям, а не заниматься написани-
ем кода, чтобы пройти мимо этих проблем, не останавливаясь. Поэтому будет бо-
лее разумным решать вопрос с ошибками времени исполнения, когда они возни-
кают, а не пытаться предвидеть все возможные ошибки (и обрабатывать их)6.
Конечно же, можно написать обработчики исключений, которые помогут нам
конструктивно оценить качество данных. Но в большинстве случаев нам про-
сто не нужно будет делать это напрямую, поскольку библиотеки функций язы-
ка РуШоп, которые мы будем использовать (опять же, особенно в главе 6), бу-
дут выполнять большую часть этой работы для нас. Мы также минимизируем
вероятность необходимости использования обработчиков исключений, ис-
пользуя инкрементальный подход к написанию наших программ по выпасу
данных. В частности, мы будем начинать разработку и тестирование нашего
кода с подмножеством целевого набора данных, а затем будем исполнять по-
лученную программу с полным набором данных (или его версиями), чтобы
оценить ее работу. Это поможет нам сбалансировать скорость и масштаб, по-
зволяя быстро оценить качество данного набора данных, и в то же самое время
создавать код, способный работать с полным набором данных, если все пойдет
хорошо.
5 Но, как мы увидим в главе 3, это в действительности полезное поведение.
6 Информация по наилучшим практикам по покрытию кода предоставляется в статье Сос1е Сохега^е
Ве.Л РгасНсез в блоге Ооо§1е Те§1т§ В1о§ (кНр8:/Де8Йп§.§оо§1еЫо§.сот/2020/08/сос1е-соуега§е-Ье81>
ргасйсе8.Ыт1).
Логические ошибки
Самыми сложными программными ошибками являются логические ошибки, под
которыми подразумеваются ситуации, когда программа в целом работает, но толь-
ко не так, как планировалось. Эти ошибки особо коварны, поскольку с точки зрения
транслятора с ними все в порядке, т. е. ваш код не содержит никаких инструкций,
которые были бы для него непонятными или неправильными. Но не забывайте, что
компьютеры глупы, и поэтому они в блаженном неведении позволят вашей про-
грамме выполнять инструкции, создающие бессмысленные, абсурдные и даже вво-
дящие в заблуждение результаты. К примеру, можно выполнять правильные мате-
матические вычисления с некачественными данными (как рассматривается в главе 3)
или можно создать полностью функциональный код, делающий что-либо неумест-
ное или неправильное. Более того, мы уже создавали такой код!
В частности, в листинге 2.12 можно увидеть, что наши комментарии с описанием
желаемых действий кода не совсем соответствуют действиям, которые этот код
выполняет в действительности. Результаты исполнения этого кода выглядят сле-
дующим образом:
291
МипЬег о!2 сЬар^егз сл/ег 30 радез:
(Количество глав, содержащих свыше 30 страниц)
5
МипЬег о!7 сЬар^егз ипбег 30 радез:
(Количество глав, содержащих менее 30 страниц)
4
Но наши входные данные выглядят так:
раде_соип1=5 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
Видите проблему? Тогда как у нас действительно есть пять глав, содержащих свы-
ше 30 страниц, количество глав, содержащих менее 30 страниц, на самом деле рав-
но трем, поскольку одна глава имеет ровно 30 страниц.
На данном этапе это может казаться не особенно значительной ошибкой, ведь, в
конце концов, насколько важными будут последствия, если одна глава с 30 страни-
цами будет отнесена к главам, содержащим менее 30 страниц? Но предположим,
что вместо подсчета глав этот код использовался для определения права на участие
в голосовании. Если бы этот код подсчитывал только избирателей старше 18 лет, то
сотни тысяч людей были бы лишены избирательских прав.
Исправить эту логическую ошибку не представляет ни больших трудностей, ни
сложностей в аспекте кода, который нужно подкорректировать. Нам нужно лишь
заменить этот код:
# Проверяем, содержит ли текущая глава больше 30 страниц
И а_пипЬег > 30:
следующим кодом:
# Проверяем, содержит ли текущая глава 30 или больше страниц
1Г а_пипЬег >= 30:
Вызов, брошенный ошибками данного типа, состоит в том, что их недопущение
полагается полностью на ваше усердие как программиста, чтобы в процессе разра-
ботки программы не пропустить мимо своего внимания какое-либо возможное зна-
чение или несоответствие данных, которое может приводить к неправильным изме-
рениям или результатам. Поскольку транслятор не может сообщить нам о наличии
логических ошибок в разрабатываемой программе, то единственный надежный
способ не допустить их — изначально тщательно планировать свои программы, а
также тщательно выполнять проверку корректности выдаваемых ими результатов.
В разд. "Отправляемся в путь с данными СШ В1ке" в конце этой главы мы увидим,
как выглядит этот процесс, когда будем заниматься разработкой программы для
работы с набором реальных данных.
Не забываем о Сй!
Наличие ошибок в разрабатываемом коде может досадно срывать ваши планы,
но их отрицательное воздействие можно уменьшить, если добиться, чтобы но-
вые ошибки не сводили на нет достигнутый ранее прогресс в достижении ра-
ботоспособности кода. Именно поэтому я рекомендую, чтобы всякий раз, ко-
гда у вас есть новый фрагмент кода, работающий должным образом, его нужно
было не только сохранить, но также выполнить для него коммит в своем репо-
зитории Сгй.
Это не должно быть сложным процессом ни при работе с блокнотами, ни при
использовании автономных сценариев. Если вы работаете в папке
с!аШ_\угап§1т§, которую мы создали в разд. "Сводим все в одно целое" главы 7,
тогда ваш репозиторий уже создан и подключен к СгйНиЪ. Вам нужно только
добавить в локальный репозиторий все новые файлы (команда ас!с1), выполнить
для них коммит с сообщениями (команда сопптЛ), а затем сохранить их на
СгйНиЪ (команда ризЬ). Это хоть н выглядит сложно, но в действительности
требует выполнения всего лишь нескольких команд в терминале, которые по-
сле нескольких таких операций станут для вас обыденной процедурой.
Лично я всегда предпочитаю начинать любой процесс коммита проверкой ста-
туса своего репозитория, чтобы знать, какие файлы нужно добавить, а для ка-
ких нужно только выполнить коммит. В результате мой типичный процесс со-
хранения своей работы в системе Сгй выглядит следующим образом:
дг1: 51а1и5
дт.1 ас!с1 имя_файла
дИ сопит 1: -п "Описательное сообщение для коммита!" имя_файла
дл-Т ризк
Вот и все! Конечно же, операцию ризЬ не обязательно выполнять для каждого
коммита, поскольку все выполненные ранее коммиты можно сохранить на
СгйНиЬ одновременно одной командой ризЬ. В любом случае, регулярная про-
верка разрабатываемого кода таким образом означает, что если что-то пойдет
не так или если вам нужно взять перерыв, у вас всегда будет рабочий код, что-
бы возобновить работу.
Теперь, когда мы рассмотрели все основы программирования на языке РуЙюп,
можно переходить от работы с ”игрушечными” примерами с использованием
данных на основе этой книги к работе с реальными данными. Чтоб получить
первое представление о выпасе данных с использованием реального набора
данных, воспользуемся информацией, полученной из системы общего пользо-
вания велосипедами города Нью-Йорк, называющейся СШ В1ке.
Отправляемся в путь с данными СШ В1ке
Каждый месяц миллионы людей по всему миру перемещаются по городам и другим
населенным пунктам на велосипедах, получаемых при помощи систем совместного
пользования велосипедами (также называются велопрокат или велошеринг). Про-
грамма СШ В1ке города Нью-Йорк была запущена в 2013 году с шестью тысячами
велосипедов, а в 2020 году отметила свою стомиллионную поездку.
Программа СШ В1ке предоставляет в свободном доступе как архивные, так и опера-
тивные данные о работе своей системы. Чтобы получить представление о том, что
нужно для выпаса реальных данных, мы используем данные системы СШ В1ке, что-
бы получить ответ на простой вопрос, сколько поездок осуществляется каждый
день разными типами пользователей системы.
Чтобы ответить на этот вопрос, нам потребуются наши навыки работы с языком
РуЙюп, поскольку каждый день пользователи системы СШ В1ке осуществляют сот-
ни тысяч поездок. Это означает, что данные только за один день занимают слиш-
ком много строк, чтобы их можно было бы обработать посредством электронной
таблицы Ехсе1 или Ооо§1е 81зее18. Но даже на таком маломощном устройстве, как
СйгошеЪоок, этот объем данных можно обработать при помощи программы РуЙюп
без каких бы то ни было проблем.
Чтобы продумать подход к решению этой задачи, пересмотрим шаги процесса вы-
паса данных, изложенные в разд. "Что такое выпас данных?" главы 1.
1. Обнаружение или сбор данных.
2. Оценка качества данных.
3. Очистка, стандартизация и/или преобразование данных.
4. Анализ данных.
5. Визуализация данных.
6. Предоставление данных.
Для этого упражнения мы будем концентрироваться на шагах с 1 по 4, хотя, как мы
увидим, я выполнила некоторую предварительную подготовку, что позволит нам
сократить время работы на определенных шагах. Например, я уже нашла данные
системы СШ В1ке (Ьир8://сШЬ1кепус.сот/8у8€ет-(1а€а) и загрузила архивные дан-
ные поездок за сентябрь 2020 г. (кйр8://83.атагопауу8.сот/€г1рс1а€а/шс1ех.к1:т1),
проверила, что столбец Озег Туре содержит только значения СизЬэпег и ЗиЬзсгТЬег, а
также урезала полный набор данных за сентябрь 2020 г., оставив только данные для
поездок, которые начались 1 сентября 2020 г. Мы рассмотрим выполнение всех
этих процессов в последующих главах, но на данном этапе я хочу сфокусировать
внимание на том, как применять уроки этой главы к данным иным, чем те, которые
мы создали сами7.
Начинаем с создания псевдокода
Один из наилучших способов начать работу над проектом выпаса данных любого
размера — заблаговременно спланировать свой подход к нему и включить эти ос-
новные принципы в файл программы РуЙюп посредством метода, называющегося
псевдокодом. Псевдокод, по сути, означает пошаговое изложение на обычном рус-
ском (или любом другом предпочитаемом вами) языке основных аспектов своей
программы. Кроме предоставления возможности продумать, каких результатов
должна достичь ваша программа, не беспокоясь о том, как все это реализовать в
коде, псевдокод даст вам ценный ориентир, по которому можно будет возобновить
работу над проектом после перерыва. Хотя вам, несомненно, повстречается много
профессиональных программистов, которые не выполняют эту часть процесса на
регулярной основе, я могу вам гарантировать, что это поможет вам завершать рабо-
ту над своими проектами по выпасу данных более быстро и что привычка этого
типа приветствуется в любой среде профессионального программирования или ин-
теллектуальной обработки данных.
Я предпочитаю вставлять общую обрисовку программы и соответствующий псев-
докод вверху файла программы в больших блоках комментариев. Для начала я де-
лаю следующие три вещи:
1. Формулирую мой вопрос.
2. Описываю, как я буду ”отвечать” на этот вопрос.
3. Излагаю разговорным языком шаги, которые будет выполнять моя программа.
Это означает, что сначала мне придется написать в файле программы большое ко-
личество комментариев, как показано в листинге 2.17.
7 Весь код для этого упражнения находится в файлах 111Шп§_111е_гоа(1_лу1111_с111Ь1ке.ру и
111111п§_111е_гоа(1_\У1111_с111Ь1келрупЬ. Но я настоятельно рекомендую вам создать соответствующие
файлы и ввести в них предоставленный код самостоятельно.
Листинг 2.17. Программа ЫйтдЗЬе_гоасЦуИй_сШЬ|ке.ру
# Вопрос: Сколько поездок делают каждый день клиенты системы С1Л1 В1ке
# отдельно по категориям зиЬзсгтЬегз и сиз^опегз?
# Ответ: Выберите один день для исследования.
# Набор данных для этого упражнения был сгенерирован с оригинальных данных системы С1Л1
# В1ке, находящихся по этому 11Я1_-адресу Ы:1:р5://53.ата2Опам5.сот/1:г1рс1а1:а/1пс1ех.Ы:т1
# Имя файла: 202009-сШЬ1ке-1:г1рс1а1:а.С57.21р
# Обрисовка программы:
# 1. Считываем файл данных: 202009С11:1ЫкеТг1рба1:аЕхатр1е.С57
# 2. Создаем переменные для подсчета значений зиЬзсгтЬегз, сиз^опегз, и сйЬег
# 3. Для каждой строки в файле
# а. Если значение "11зег Туре" равно "8иЬзсг1Ьег,", инкрементируем на 1 значение
# "зиЬзсг1Ьег_соип1:"
# Ь. Если значение "Озег Туре" равно "Сиз1:отег,", инкрементируем на 1 значение
# "зиЬзсг1Ьег_соип1:"
# с. В противном случае инкрементируем на 1 значение переменной о1±ег
# 4. Отображаем полученные результаты
Выполнив обрисовку программы, можно приступать к реализации ее первой части:
считывания данных.
При каждом создании подобного файла помните, что даже если его со-
хранить в папке локального репозитория Ой, все равно необходимо вы-
полнить команду дП: абб, чтобы система Ой сохранила все выполненные
изменения. Не забывайте, что для файла нельзя выполнить коммит (ко-
манда д11: соттт.1), пока он не будет добавлен. Если вам требуется осве-
жить свои знания в этой области, то все эти шаги изложены в разд. "Не
забываем о ОШ" ранее в этой главе. Тогда как частота выполнения ком-
митов предоставляется полностью на ваше усмотрение, для этого упраж-
нения я рекомендую выполнять коммиты (не забывая о сопровождающем
описательном сообщении) после завершения каждого блока кода в этом
разделе. Конечно же, по мере того, как вы чувствуете себя все более уве-
ренными с процессами кодирования и коммитами Ой, вы сами определи-
те наиболее подходящие для вас частоту и ритм резервного копирования
своего кода.
Загрузка данных разных форматов может быть одним из наиболее сложных про-
цессов выпаса данных (как мы увидим более подробно в главе 4). К счастью, суще-
ствует большое количество библиотек функций РуЙюп, которые могут помочь нам
с этой задачей, и мы воспользуемся одной из них прямо сейчас. Я уже вкратце упо-
минала библиотеки в разд. "Библиотеки: занимаем пользовательские функции у
других программистов” ранее в этой главе; это, по сути, просто сборники кодовых
рецептов. Для этого проекта мы используем кодовые ”рецепты” из библиотеки С8У,
которая в основном предназначена для работы, как и следовало ожидать, с файлами
с расширением .С8У. Расширение файла .С8У означает сошша-8ерага1е<1 уа1ие8 (зна-
чения, разделенные запятыми). Если вам раньше не приходилось иметь дело с та-
кими файлами, не волнуйтесь. Мы подробно рассмотрим разные типы файлов в
главе 4. Но в данный момент наличие библиотеки С8У означает, что нам нет надоб-
ности много знать о файлах этого типа для того, чтобы работать с ними, поскольку
кодовые рецепты библиотеки будут выполнять большую часть работы для нас.
Если вы следуете этому примеру в своем файле, добавьте в него код в листинге 2.18.
Листинг 2.18. Сценарий ЫйтдДЬе_гоаОуИЬ_сШЫке.ру (продолжение)
# Импортируем библиотеку С87 О
1прог1: С87
# Открываем файл 202009СН:1ЫкеТг1рс1а1:аЕхатр1е.С87 в режиме чтения (г)
# Этот файл должен находиться в той же самой папке, что и файл автономного сценария
# или блокнота
8оигсе_ГНе = ореп( "202009СШЫкеТ гтрба^аЕхатрТе. С87"," г" )О
# Передаем наш исходный файл 8оигсе_П1е как аргумент (ингредиент) методу (рецепту)
# 01с1:Реас1ег библиотеки С87.
# Сохраняем результат в переменной с1Л1Ыке_геас1ег
с1Е1Ыке_геас1ег = с87.01с1:Реабег(5оигсе_Г11е)
# Метод 01с1:Реас1ег добавил в наши данные некоторую полезную информацию. Например,
# свойство ПеТбпапез позволяет нам обращаться ко всем значениям в первой строке
# (или заголовке)
рг1п1:(си1Ь1ке_геас1ег. Г1е1с1пате8)О
О Библиотека С8У содержит ряд полезных кодовых рецептов для работы с файлами
данных.
О Встроенная функция ореп() принимает в качестве параметров имя файла и режим
работы с ним. В данном примере целевой файл (202009СШЫкеТпрс1а1аЕхатр1е.с8у)
должен находиться в той же самой папке, что и файл автономного сценария или
блокнота. Значение параметра режима может быть г (геаб — чтение) или и (\угйе —
запись).
О Отображая значения сШЬ1ке_геас!ег.Г1е1с1пате8, мы можем видеть, что точным
именем столбца Изег Туре (тип пользователя) является изег^уре.
На данном этапе этот сценарий уже можно исполнять, и он должен выдать резуль-
тат наподобие показанного в листинге 2.19.
Листинг 2.19. Результаты исполнения сценария ЬййпдДКегоабдл/йЬсШЫке.ру
в его текущем виде
[' ±г!рс1ига±1оп1, ' 51аг1Ипе', ' 51орИпе1, ' 51а г1 51а11оп 1сГ, ' 51аг1 51а11оп
пате', 151аг1 51а11оп 1а1Пис1е', 151аг1 51а11оп ТопдПибе', ' епс! 51а11оп хсГ ,
'епс! 51а11оп пате', 'епс! 51а11оп ТаШибе', 1 епс! 51а11оп ТопдПибе1, 'Ыкетб',
'и5ег1уре', 'Ыг1Ьуеаг', 'депбег']
На данном этапе мы успешно завершили первый шаг нашей обрисовки: прочитали
данные, с которыми будем работать. Также мы использовали библиотеку сзу для
преобразования этих данных и даже для создания некоторых метаданных. Что важ-
но, теперь мы знаем, что точное имя столбца, содержащего информацию о типе
пользователя ЕГзег Туре, в действительности является изег!уре. Эта информация бу-
дет полезной, когда настанет время создавать условные операторы 1Т.. .еТзе. Чтобы
проверить, что наш сценарий работает должным образом, сохраните и исполните
его. Если все работает, как и должно (т. е. сценарий выводит на экран список загла-
вий столбцов), то теперь будет подходящее время выполнить процесс резервного
копирования в систему (Зй:
дП 51а1и5
дП соппП -п "Описательное сообщение для коммита" имя_файла_с_расширением
дП ризЬ
Помните, что при работе в Ооо§1е Со1аЪ резервное копирование можно выполнить
непосредственно на веб-сайте ОйНиЪ, выполнив последовательность команд меню
ГПе | 8ауе а сору ш СйНиЬ, не забывая при этом ввести соответствующее коммит-
сообщение в открывшемся всплывающем окне.
Успешно выполнив первый шаг нашего плана, можно переходить к выполнению
второго, как показано в листинге 2.208.
Листинг 2.20. Сценарий ЫйтдЗйе_гоасЦуй[1_сШЫке.ру (продолжение)
# Создаем переменные для хранения количества пользователей каждого типа системы
# СП1 В1ке Инициируем каждую переменную, присваивая ей значение 0.
5иЬ5Сг1Ьег_соип1 = 0
си51опег_соип! = 0
о1Ьег_и5ег_соип! = 0
Довольно просто, не так ли? Переходим к третьему шагу. Чтобы проверить каждую
строку данных в файле, нам нужно создать цикл 1ог...1п, который должен содержать
условный оператор 11...е15е для проверки наличия определенных значений в
столбце изег!уре. Чтобы отслеживать, что делает каждая строка кода, я вставила в
8 Добавьте этот код в свой файл под последней строкой предыдущего блока. Делайте это со всеми
последующими блоками кода этого сценария.
код большое количество комментариев, объясняющих действия этих строк. Соот-
ветствующий код приводится в листинге 2.21.
Листинг 2.21. Сценарий ЫйтдЗЬе_гоас1_юйЬ_сШЫке.ру (еще одно продолжение)
# Шаг 3: Обрабатываем в цикле каждую строку данных
Гог а_гом 1п с1Г1Ыке_геас1ег:О
# Шаг За: Если значение в столбце изегГуре
# текущей строки равно "ЗиЬзсгГЬег"
1Г а_гом["изегГуре"] == "ЗиЬзсгГЬег" :©
# инкрементируем на 1 значение переменной зиЬзсг1Ьег_соипГ
зиЬзсг1Ьег_соипГ = зиЬзсг1Ьег_соипГ +1
# Шаг ЗЬ: В противном случае (е!зе), если значение в столбце изегГуре
# текущей строки равно "СизГопег"
еПГ а_гом["изегГуре"] == "СизГопег":©
# инкрементируем на 1 значение переменной сизГопег_соипГ'
сизГопег_соипГ = сизГопег_соипГ + 1
# Шаг Зс: Значение столбца изегГуре текущей строки не равно ни ЗиЬзсгтЬег,
# ни СизГопег. Поэтому инкрементируем на 1 значение общей переменной
# оЬГег_изег_соипГ
е!зе:О
оГЬег_изег_соипГ = оГЬег_изег_соипГ + 1
О Нам необходимо обеспечить, чтобы данные для цикла Гог были уже преобразо-
ваны методом 01сГРеабег(), поэтому здесь нужно обращаться к переменной
с1ПЫке_геабег.
О Чтобы строки кода условного оператора 1Г были внутри цикла Гог, их нужно
сдвинуть вправо на дополнительный отступ.
О Поскольку здесь, кроме ”в противном случае” (е1зе), нам также требуется и ”ес-
ли” (й), мы используем составное ключевое слово еПГ, которое является сокраще-
нием от е1$е I/.
О Это последнее е!зе улавливает все строки данных, в которых значение столбца
изегГуре не равно одному из значений, на которые выполняется явная проверка (в
данном случае это значения ЗиЬзсгтЬег и СизГопег). Таким образом реализуется базо-
вая проверка качества данных: если этот столбец данных содержит какие-либо не-
ожидаемые значения, значение переменной оГЬег_изег_соипГ будет больше нуля (0),
и нам нужно будет исследовать исходные данные более внимательно.
С первого взгляда может показаться, что код в листинге 2.21 выполняет большой
объем работы. Но в действительности в нем выполняется только проверка значения
столбца изег1:уре каждой строки данных на равенство значению ЗиЬзсгтЬег или
СизЬэпег и при положительном результате проверки инкрементируется (увеличива-
ется) на 1 значение соответствующей переменной счетчика. Если же значение
изег1:уре не равно ни одному из этих значений, то инкрементируется значение пере-
менной счетчика оШег_изег_соипЕ
Хотя может выглядеть странным, что данный фрагмент кода содержит намного
больше строк комментариев, чем собственно кода, это, в действительности, вполне
нормально и даже хорошо. Потому что компьютер никогда не забудет, как читать
код на языке РуШоп, но вы же с течением времени полностью забудете, что и поче-
му этот код делает. Вот эти комментарии и помогут вам освежить свою память. И в
этом нет ничего плохого! В конце концов, запоминание всего кода сделало бы про-
цесс программирования довольно неэффективным. А излагая письменно подроб-
ную информацию о коде в виде комментариев, в будущем мы сможем с легкостью
понять, что он делает, без необходимости перевода операторы языка РуШоп на
обычный язык.
Прежде чем продолжать, в обязательном порядке проверьте правильность своего
кода, исполнив его. Поскольку для большинства наиболее распространенных оши-
бок отсутствие новостей означает хорошие новости, то при отсутствии сообщений
об ошибках можно выполнить резервное копирование кода в систему (Зй. В случае
же наличия каких-либо ошибок, конечно же, их нужно устранить. Теперь остался
только один очень простой шаг: вывод результатов на экран. Реализуем его самым
прямолинейным путем, используя встроенный оператор рг!п±(), как показано в
листинге 2.22.
Листинг 2.22. Сценарий ЫйтдЗЬе_гоас1_юйЬ_сШЫке.ру (последняя часть)
# Шаг 4: Отображаем результаты, не забывая включить заголовки:
рг1п1:("МитЬег о!" зиЬзсгШегз:") О # Количество подписчиков
рг1п1:(8иЬ5Сг1Ьег_соип1:)
рг1п1:("МитЬег оГ сиз^опегз:") # Количество клиентов
рг1п1:(си51:отег_соип1:)
рг1п1:("МитЬег о!" 'оШег' изегз:") # Количество прочих пользователей
рг!п1:(оШег_и5ег_соип1:)
О Обратите внимание на то, что все эти операторы ргтп!:() выровнены по левому
краю, потому что мы хотим отобразить значения только после того, как цикл 1"ог
обработает весь набор данных.
Добавьте код в листинге 2.22 в свой файл сценария, сохраните его и запустите на
исполнение. Результаты исполнения должны выглядеть наподобие показанных в
листинге 2.23.
Листинг 2.23. Результаты исполнения полного сценария
Ый1пдЗЬе_гоас1_ууйЬ_сШЫке.ру
[' ±г!рс1ига±1оп1, ' 51аг1Нте', ' 51орМте1, ' 51а Н: 51а11оп 1сГ , ' 51а г1 51а11оп
пате', 1 51аг1 51а11оп 1а1Пис1е', 151аг1 51а1!оп 1опд1±:ис1е' л ’ епс! 51а1гоп хсГ ,
'епс! 51а1гоп пате', 'епс! 51а11оп 1а111ис1е', 1 епс! 51а1гоп 1опдПис1е1, 'Ыкетск,
'и5ег1уре', ' Ыг1Ьуеаг', 'депс1ег']
МитЬег оГ зиЬзсгъЬегз:
58961
МитЬег оГ си51отег5:
17713
МитЬег оГ 'о±Нег' изегз:
0
Если ваши результаты похожи на эти, тогда примите наши поздравления. Вы ус-
пешно создали свою первую программу для выпаса реальных данных. Не забудьте
выполнить цикл резервного копирования своей работы в системе Ой.
Масштабирование
Посредством данного сценария мы решили следующие задачи:
1. Успешно подсчитали количество пользователей типа 5иЬзсг1Ьег и СизГотег, кото-
рые использовали услуги системы СШ В1ке в один день в сентябре 2020 г.
2. Удостоверились, что столбец изег!уре не содержит никаких других типов значе-
ний, кроме этих двух (поскольку значение переменной оИчеГ-изеГ-Соип! равно 0).
Если ранее вам приходилось выполнять выпас данных, используя только электрон-
ные таблицы или программы баз данных, и это первый раз использования для этой
цели средства на языке РуЙюп, есть вероятность того, что, учитывая все обстоя-
тельства, этот процесс занял больше времени, чем ваш предыдущий метод. Но, как
я уже упоминала несколько раз ранее, ключевое преимущество подхода с исполь-
зованием кодирования над многими другими методами состоит в возможности
почти незаметного для пользователя масштабирования полученного решения.
Масштабирование работает двумя способами. Прежде всего, программное решение
обрабатывает крупные наборы данных почти с такой же самой скоростью, как и
наборы данных малого размера. Например, на моем устройстве СйгошеЪоок сцена-
рий Ыйт§_111е_гоас1_\уй11_сй1Ъ1ке.ру обрабатывает данные системы СШ В1ке за один
день приблизительно за полсекунды. Обработка этим же сценарием данных за весь
сентябрь занимает приблизительно 12 секунд. Большинству прикладных программ,
которым обработка данных за весь месяц была бы хотя бы под силу, скорее всего,
потребовалось бы столько времени только для того, чтобы открыть файл данных,
не говоря уже о какой-либо его обработке. Таким образом, использование подхода
с программным решением на языке РуЙюп помогает масштабированию по той при-
чине, что мы можем обрабатывать крупные наборы данных более быстро, нежели
посредством прикладных программ. Вы можете сами удостовериться в этом, заме-
нив следующую строку сценария:
8оигсе_ГПе = ореп("202009СШЬ1кеТг1р^а1:аЕхатр'1е.С87"/,г")
этой:
8оигсе_РИе = ореп("202009-с11=1Ь1ке-1:г1рс1а1:а.С87","г")
Для автономного сценария РуЙюп можно даже измерить время обработки этого но-
вого файла данных, добавив ключевое слово 1=1пе перед командой запуска сценария:
1:1те ру1±оп _имя_файла_сцерания_.ру
Это иллюстрирует второй тип масштабирования, позволяемого программными ре-
шениями на языке РуЙюп. В частности, после того как мы создали программу для
обработки набора данных определенного размера, для той же самой обработки на-
бора данных большего размера (но с той же самой структурой) требуется лишь не-
значительное усилие. Вот в этом отношении решения на языке РуЙюп (и подход с
использованием программирования в целом) проявляют себя наилучшим образом.
В данном случае для обработки нашим сценарием данных за весь сентябрь 2020 г.
потребовалось лишь загрузить другой файл данных. Изменив имя файла, загружае-
мого оператором 2оигсе_РИе = ореп(), мы смогли обработать данные за весь сен-
тябрь, а не всего лишь за один его день. Иными словами, дополнительное усилие,
требуемое для обработки сотен тысяч дополнительных строк данных, равно в точ-
ности усилию, требуемому, чтобы скопировать и вставить новое имя файла данных.
Но основании этого примера мы могли бы обработать данные за весь год всего
лишь за несколько минут (или меньше, как мы увидим в главе 5). Это почти невоз-
можно реализовать посредством любого метода выпаса данных, не использующего
программирования.
Весь сценарий, который мы создали в этом разделе, представляет собой пример, как,
используя лишь базовые структуры языка, рассмотренные в этой главе, можно орга-
низовать по-настоящему полезный и эффективный выпас данных средствами языка
РуЙюп. Хотя в последующих главах нас ожидают новые форматы данных и задачи, я
надеюсь, что рассмотренный на данном этапе материал позволил вам получить пред-
ставление о том, сколько можно сделать посредством даже базовых средств РуЙюп
приложив лишь небольшие усилия и внимание к мелочам. Представьте, какие воз-
можности отроются перед вами, если вы продолжите изучать эту тему!
Заключение
Хотите верьте, хотите нет, но в этой главе мы рассмотрели все основные средства
языка РуЙюп, которые вам потребуются для работы по выпасу данных, а также для
решения почти любых других задач посредством этого языка. Вкратце повторим,
что мы изучили:
Типы данных
Это ”существительные” языка программирования: числа, строки, списки, слова-
ри и булевы значения.
Функции
Это ”глаголы” языка программирования: операторы, встроенные функция и
пользовательские функции.
Оператор цикла/ог..Лп...
Позволяет исполнять определенный блок кода для каждого элемента списка.
Оператор условия I/... еЬе...
Позволяет принимать решения, какие фрагменты кода исполнять, на основе, как
правило, атрибутов данных.
Ошибки
Мы рассмотрели разные типы ошибок, с которыми вам, скорее всего, придется
столкнуться при написании программ, наилучшие методы для диагностики их
причин и их предотвращения.
Мы также на практике объединили эти понятия для создания простой программы
для обработки выборочных данных системы СШ В1ке города Нью-Йорк. В после-
дующих главах мы рассмотрим этот пример более подробно, а также исследуем
новые примеры. Наша следующая задача заключается в том, чтобы расширить по-
нимание процесса оценки самих данных как части работы по выпасу данных.
ГЛАВА 3
Понимание качества данных
Данные находятся повсюду вокруг нас. Они создаются автоматически нашими мо-
бильными устройствами, покупками и физическими перемещениями. Они собира-
ются электрическими счетчиками, системами общественного транспорта и инфра-
структурой связи. Они используются для оценки нашего здоровья, зарплатного по-
тенциала и кредитоспособности1. Экономисты даже объявили, что данные — это
"новая нефть”1 2, в свете их потенциальных возможностей для преобразования мно-
гих аспектов нашей жизни.
И хотя данные могут быть в изобилии, по правде говоря, качественные данные ос-
таются в дефиците. Поборники концепции ''революции данных” утверждают, что
при наличии достаточного количества данных мы можем лучше понимать настоя-
щее и улучшить, и даже предсказать, будущее. Но чтобы все это было бы даже в
области возможного, вся эта аналитика должна быть основана на данных высокого
качества. При отсутствии данных высокого качества все наши усилия по выпасу,
анализу, визуализации и предоставлению данных дадут нам, в наилучшем случае,
не больше знаний об окружающем нас мире, чем у нас бы было с самого начала.
Тогда как это было бы пустой тратой усилий, последствия неспособности распо-
знать низкокачественные данные будут еще хуже, поскольку результатом может
быть создание рационального на вид, но искаженного в действительности пред-
ставления о реальности. Более того, поскольку управляемые данными системы ис-
пользуются для принятия широкомасштабных решений, даже небольшой объем
некачественных данных может причинить значительный вред и таить в себе боль-
шую опасность. Конечно же, для "тренировки” модели машинного обучения можно
использовать данные о сотнях или даже тысячах людей. Но если эти данные не
представляют население, к которому эта модель будет применяться, количество
людей, на которых могут распространяться последствия решений этой системы,
может быть в сотни или тысячи раз большим, чем количество в исходном обучаю-
щем наборе данных. Поскольку ставки такие высокие, обеспечение качества дан-
ных является неотъемлемой частью процесса выпаса данных. Но что означает тре-
1 См., например, книгу Тке $есге1 В1а$ Н1с1с1еп т Мог1§а§е-Арргоча1 А1§огИкт8, авторы Ешшапие1
Магйпех и Ьаигеп КпъсИпег (издательство ТЬс Магкир) (Ы1р8://1Ьетагкир.ог§/(1еп1е(1/2021/08/25/
111е-8есге1-Ыа8-Ь1(1(1еп-1п-тог1§а§е-арргоуа1-а1§ог11Ьп18).
2 См. статью ТЛе ТУогШ'з Мо81 УсйиаЫе Ке8оиг8е 18 1То копре/' ОН, Ъи1 Юа1а в журнале Тке Есопот181.
(Ы1р8://есопот181.сош/1еа(1ег8/2017/05/06/1Ье-ууог1(18-то81-уа1иаЫе-ге8оигсе-18-по-1оп§ег-оП-Ьи1-(1а1а).
бование, чтобы данные были "высокого качества”? По моему личному мнению,
данные являются высокого качества только в том случае, если они как соответст-
вуют цели их применения, так и обладают высокой внутренней целостностью.
Но что именно означают эти два термина? Это как раз то, что мы подробно иссле-
дуем в этой главе. Мы начнем с обсуждения понятия соответствия данных, кото-
рое связано с пригодностью данных для использования в конкретном контексте,
или предоставления ответа на конкретный вопрос. Затем мы разложим на состав-
ляющие аспекты целостности данных', характеристики набора данных, которые
влияют как на его соответствие данному назначению, так и на типы анализов, для
которых мы можем ответственно использовать эти данные. Наконец, мы рассмот-
рим некоторые инструменты и стратегии для поиска данных и работы с ними, ко-
торые помогут нам максимально повысить их общее качество, позволяя нам быть
уверенными в надежности результатов, полученных на их основе.
В случае если все это начинает казаться вам скучным, я хочу повторить призыв,
который я написала в разд. "Что такое выпас данных?" главы Г. попытка пропус-
тить работу по оценке качества данных может нанести ущерб вашим усилиям по
выпасу данных. В лучшем случае вы предоставите свою работу для общего исполь-
зования и столкнетесь с вопросами об использованном вами процессе, на которые у
вас не будет ответов. А в худшем случае вы в итоге будете продвигать "знания”,
которые являются ошибочными и могут нанести серьезный вред. Вы также лишите
себя хороших технических навыков, поскольку как раз решение задач качества
данных предоставляет наилучшую возможность нарастить свои знания в области
программирования. Если вы действительно хотите преуспеть в области выпаса
данных, то оценка качества данных должна быть частью установившегося порядка
вашей работы.
Не забываем о документировании
Как упоминалось ранее, подробное комментирование разрабатываемого кода
будет разумной тратой времени, если вы хотите улучшить свои навыки коди-
рования и извлечь большую пользу из своего кода в будущем. Но, в случае на-
добности, приложив достаточно усилий и времени, разобраться, что и как де-
лает программа, можно всегда по ее коду и без комментариев.
Однако то же самое нельзя сказать о работе по обеспечению качества данных:
документация в вашем дневнике данных в действительности является незаме-
нимой. Выводы касательно репрезентативности или действительности набора
данных будут в большинстве случаев основаны на информации, полученной
вами из широкого круга источников: от вашего собственного чтения и иссле-
дований до разговоров с экспертами до дополнительных наборов данных. Но
без наличия качественной документации, указывающей, кто сказал, что или
каким образом вы получили данную информацию, любая попытка повторить
или подтвердить вашу предыдущую работу будет почти однозначно обречена
на неудачу.
Почему? Потому что источники информации, особенно в Интернете3, имеют свой-
ство перемещаться, изменяться и исчезать. Например, ссылки, которые я изначаль-
но включила в эту книгу, переместились или прекратили работать в течение всего
лишь нескольких месяцев после этого. Вы можете потерять связь с экспертом, с
которым вы разговаривали несколько месяцев тому назад, или же повторный поиск
может возвратить результаты, отличающиеся от предыдущих. Тогда как вопрос
выбора конкретного подхода решать только вам, я не могу сказать достаточно уве-
ренно, насколько важным является документирование своей работы по выпасу дан-
ных, особенно что касается вопроса обеспечения качества данных (который состав-
ляет практически всю эту область). Без подробного описания выполняемого про-
цесса, когда настанет время предоставить результаты своей работы для общего
использования, вы можете обнаружить, что находитесь на зыбкой почве, что выну-
дит вас начать все повторно, причем с самого начала.
Оценка соответствия данных
Наверное, одним из самых распространенных заблуждений о выпасе данных явля-
ется представление, что это в основном количественный процесс, т. е. заключается
в основном в работе с числами, формулами и кодом. В действительности же, неза-
висимо от типа обрабатываемых данных, будь то показатели температуры или со-
общения социальных сетей, основная работа по выпасу данных заключается в при-
нятии широкого диапазона субъективных решений. Например, от таких, как точно
ли ваши данные представляют исследуемый феномен, до таких, как что делать на-
счет недостающих точек данных и имеется ли достаточно данных, чтобы получить
какое-либо значащее понимание. Первое понятие — насколько точно данный набор
данных представляет исследуемое явление — в общих чертах выражает то, что я
имею в виду под соответствием данных. И субъективная оценка соответствия на-
бора данных его предполагаемому назначению играет намного большую роль, чем
применение математических формул. Этому есть довольно простое объяснение:
наш мир довольно беспорядочное место, и даже то, что может выглядеть как про-
стейший набор данных о некотором его аспекте, всегда фильтруется через тот или
иной человеческий фильтр. Возьмем нечто такое прямолинейное, как измерение
температуры на вашем рабочем месте в течение одной недели. Теоретически, нуж-
но просто взять термометр, установить его и каждый день записывать показатели.
Что может быть проще?
Или же не все так просто? Начнем с оборудования. Какой термометр вы использо-
вали, цифровой или ртутный? Где именно вы его расположили? Возле двери, окна,
источника обогрева или охлаждения? Снимали ли показатели в одно и то же время
3 Например, см. статью КаШегз о/ 1ке ЬоМ автор Асктеппе ЬаРгапсе, в журнале Тке АйапНс
(ЬНр8:/ДЬеаНапНс.сотЛесЬпо1о«\7агсЬ1\е/2015/10/га1с1ег8-оГ-Ше-1о8ру\еЬ/409210).
каждый день? Подвергался ли термометр когда-либо воздействию прямых солнеч-
ных лучей? Какой в вашем помещении типичный уровень влажности?
Вы можете думать, что все это надуманные факторы сложности, но если вам когда-
либо приходилось жить в общем здании (например, в многоквартирном доме), то
вам, наверное, пришлось испытать чувство, что окружающая температура была
намного выше или ниже, чем показания какого-то термометра. Подобным образом,
если вам когда-либо приходилось ухаживать за больным ребенком, вы, скорее все-
го, слишком хорошо знаете, как разные типы термометров (или даже один и тот же
термометр с интервалом измерения всего лишь в несколько минут) дают разные
показания температуры тела
Иными словами, снимаемое вами двух- или трехзначное значение температуры
может подвергаться воздействию большого количества факторов, а само значение
не предоставляет никакой информации ни об одном из этих факторов. Вот поэтому,
когда мы пытаемся получить ответ на вопрос при помощи данных, знать только о
содержимом набора данных будет недостаточно; нам нужно также знать о процес-
сах и механизмах, которые использовались для сбора этих данных. Затем, имея в
своем распоряжении все, что мы знаем о том, как собирались наши данные, нам
нужно определить, действительно ли их можно использовать, чтобы дать значащий
ответ на наш конкретный вопрос.
Конечно же, данная задача не является ни новой, ни уникальной. С точно такой же
задачей сталкиваются ученые во всех научных областях в попытках обнаружить
новую информацию об окружающем нас мире. Изучение рака вряд ли могло разви-
ваться, если бы каждому отдельному ученому нужно было бы самому проводить
конкретные исследования. Без возможности основывать свои исследования на базе
работ других ученых научный и технический процесс постепенно бы остановился,
если бы вообще не сошел с рельс. Поэтому в течение времени научное сообщество
разработало три ключевые метрики для определения пригодности или соответствия
набора данных получению ответа на данный вопрос: достоверность, надежность
и репрезентативность.
Достоверность данных
В своем простейшем варианте достоверность данных описывает, в какой мере из-
мерительный прибор измеряет то, что он должен измерять. В нашем примере с из-
мерением комнатной температуры это означает обеспечение того, чтобы выбран-
ный нами термометр в действительности измерял температуру воздуха в комнате, а
не чего-либо другого. Например, тогда как традиционные спиртовые термометры,
скорее всего, будут правильно измерять температуру именно воздуха, инфракрас-
ные термометры имеют склонность измерять температуру поверхности, на которую
они направлены. Поэтому, даже в случае такого простого на вид измерения, как
измерение температуры воздуха, нам нужно учитывать инструменты и методы,
применяемые для снятия показаний данных, чтобы обеспечить их достоверность
относительно нашего вопроса.
Неудивительно, что при сборе данных о явлениях более сложных, чем простые фи-
зические явления, процесс становится еще более сложным. Конструктивная досто-
верность описывает степень эффективности фиксации нашими данными конструк-
ции (обычно абстрактной) или идеи, явления, которое мы пытаемся понять. Напри-
мер, скажем, что мы хотим узнать, какие школы в нашем районе ”наилучшие”. Какие
данные могут помочь нам получить ответ на этот вопрос? Прежде всего, нам нужно
осознать, что термин ”наилучшие” является неточным. Наилучшие в каком отноше-
нии? Нас интересуют школы с наивысшим процентом успешных выпускников? С
наилучшими результатами стандартизованных тестов? Школы с наилучшими оцен-
ками учащихся? С наилучшими оценками работы преподавателей? Уровнем удовле-
творения учеников своей школой? Уровнем участия во внеклассных активностях?
Чтобы использовать данные для того, чтобы начать отвечать на этот вопрос, снача-
ла нужно четко сформулировать две цели. Первая — выяснить, ”наилучшие” для ко-
го! Вы пытаетесь получить ответ на этот вопрос для своего ребенка? Или для ре-
бенка своего друга? Ответив на этот вопрос, вы сможете лучше выполнить вторую
цель, которая заключается в том, чтобы воплотить в жизнь ваше конкретное пред-
ставление о ”наилучшем”. Если ребенку вашего друга нравится заниматься, напри-
мер, спортом, тогда внеклассные занятия могут быть более важными, чем учебные.
В области анализа данных этот процесс выбора индикаторов называется операцио-
нализацией конструкции и неизбежно требует выбора и балансирования замените-
лей идеи или концепта, который мы пытаемся понять. Эти заменители (такие как
процент успешных выпускников, результаты тестов, внеклассные активности и т. и.)
представляют собой те элементы, о которых мы можем собрать данные, избран-
ные нами для представления абстрактного концепта (”наилучшая” школа), который
нельзя измерить непосредственно. Высококачественные данные по меньшей мере
должны обладать хорошей конструктивной достоверностью касательно нашего
вопроса, ибо в противном случае наши результаты выпаса данных будут бессмыс-
ленными.
Как? И для кого?
Начав думать о конструктивной достоверности для своих вопросов по выпасу
данных, вы, скорее всего, начнете гораздо больше любопытствовать об этом
понятии повсюду, где вам придется сталкиваться с использованием данных
для принятия решений или делания "предсказаний” об окружающем нас мире.
Более чем вероятно, что вы обнаружите множество случаев неясных утвер-
ждений (типа ”наилучший”) без предоставления достаточного или вообще ка-
кого-либо объяснения о том, каким образом это понятие ”наилучший” было
определено. Подобным образом, разработчики систем, управляемых данными,
на вопрос ”Для кого?” могут изначально ответить с энтузиазмом ”Для всех!”
Но настоящий ответ лежит в плоскости их выбора заменителей, которые они
могли выбрать эвристическим образом на основе лишь своих собственных
вкусов и предпочтений. В этих случаях настоящим ответом на вопрос ”Для ко-
го?” является ”Для таких людей, как я”.
Конечно же, иногда вам могут сказать, что просто невозможно знать, как оп-
ределяется ”наилучший”, поскольку для принятия решений или предсказания
используется так называемый ”черный ящик”, как часто описывают неконтро-
лируемые системы машинного обучения. Но, как я упоминала в разд. "Что
такое качество данных" главы 7, такой ответ несколько лукавый. В настоящее
время мы действительно не можем с уверенностью сказать, каким именно об-
разом система машинного обучения присвоила веса или приоритеты опреде-
ленным факторам при составлении предсказаний. Но при этом мы знаем, что
она всегда будет повторять и усиливать образцы, существующие в данных, ис-
пользованных для ее ”обучения”. И в таких случаях ответы на оба вопроса
”Как?” и ”Для кого?” будут находиться в составе обучающих данных, если
разработчики системы достаточно уверены в ней, чтобы предоставить их вам.
Другим типом достоверности, имеющей важность для обеспечения соответствия
данных, является достоверность содержимого. Этот тип достоверности связан с
тем, насколько полны ваши данные для данного измерения заменителя. Предполо-
жим, что в нашем примере с ”наилучшей” школой мы установили, что индикатором
”наилучшей” являются оценки учеников, но в нашем распоряжении есть оценки
только по таким предметам, как история и физкультура. Хотя для многих людей
данные по оценкам могут, в принципе, удовлетворять требованию конструктивной
достоверности, для определения наилучшей школы наличие этих данных только
по двум предметам будет недостаточным для удовлетворения требования досто-
верности содержимого, а для высококачественных данных нам нужны обе эти дос-
товерности.
Посмотрим, что скажут данные
Принятие решений на основе данных может быть мощным инструментом для
понимания происходящего в окружающем нас мире. Иногда этот подход мо-
жет быть привлекательным, потому что кажется, что он выходит за рамки
(или, по крайней мере, избегает их) необходимости продираться сквозь труд-
ные дискуссии о нормах, ценностях и этических принципах.
Но в действительности такое доверие ”данным” означает, что мы полагаемся
на ценности, предположения и предвзятости создателей этих данных, тех, кто
разработал и реализовал сбор данных. Вот поэтому данные сами по себе нико-
гда не могут быть объективными, такими могут быть только процессы выпаса
данных, используемые для их обработки.
Надежность данных
В рамках набора данных под надежностью данного индикатора имеются в виду его
точность и стабильность. Взятые вместе, они помогают нам оценить, дадут ли из-
мерения одного и того же индикатора, выполненные дважды при одинаковых усло-
виях, одинаковые — или, по крайней мере, очень похожие — результаты. Восполь-
зуемся снова нашим примером с измерением температуры: измерение температуры
ребенка при помощи орального термометра вряд ли даст очень надежные результа-
ты, поскольку для этого требуется, чтобы ребенок держал рот закрытым в течение
сравнительно длительного времени, с чем, по моему опыту, дети справляются не
очень хорошо. В противоположность этому, установка термометра под мышку мо-
жет дать более надежные показания, поскольку ребенка можно обнять, чтобы
удерживать термометр на месте. Но в этом случае показания термометра могут не
совсем точно отображать настоящую внутреннюю температуру тела ребенка, как
могут другие методы. Вот почему большинство медицинских справочников указы-
вают разные предельные температуры лихорадки для детей в зависимости от мето-
да, используемого для измерения температуры.
В случае абстрактных концептов и реальных данных определение надежности ин-
дикатора данных особенно сложно, поскольку данные никогда, по сути, нельзя со-
брать более одного раза, будь то по причине непомерно высокой стоимости или
невозможности повторить с точностью все обстоятельства. В некоторых случаях
надежность оценивается примерно, сравнивая две похожие группы и используя или
новые, или ранее собранные данные. Поэтому, хотя драматические колебания ре-
зультатов стандартизированных тестов от одного года к следующему указывают,
что эти результаты не могут быть надежным индикатором качества данной школы,
эта непостоянность сама по себе всего лишь часть проблемы. Ведь эти результаты
могут отражать качество обучения, но также могут отражать и изменения в самом
тесте или способе его оценки или какое-либо другое нарушение в среде обучения
или прохождения теста. Чтобы определить, достаточно ли надежные данные ре-
зультатов стандартизованного теста для включения их в вашу оценку ”наилучшей”
школы, нужно будет исследовать сравнительные данные за другие годы или из дру-
гих школ, а также собрать дополнительную информацию о более широких обстоя-
тельствах, которые могли бы вызвать это колебание. Конечным заключением мо-
жет быть, что большая часть данных результатов теста достаточно надежная, но
нужно удалить несколько определенных точек данных, или же что данные недоста-
точно надежные, чтобы быть составляющей высококачественного процесса обра-
ботки данных.
Репрезентативность данных
Ключевая ценность систем, управляемых данным, состоит в том, что они позволя-
ют нам создавать знания или даже делать предсказания о людях и явлениях, слиш-
ком масштабных или сложных для людей, чтобы эффективно рассуждать о них.
Как следует из этого, посредством первичной обработки и анализа данных мы мо-
жем принимать решения более быстро и объективно. С учетом мощных вычисли-
тельных средств, доступных в настоящее время частным лицам, не говоря уже об
организациях, эти управляемые данными системы, несомненно, могут генерировать
решения более быстро, чем это могут делать люди. Но правильно ли эти знания
отображают определенную совокупность лиц или ситуацию, зависит напрямую от
репрезентативности используемых данных.
Является ли набор данных достаточно репрезентативным, зависит от нескольких
обстоятельств, наиболее важное из которых восходит к вопросу ”Для кого?”, кото-
рый мы обсуждали в разд. "Достоверность данных" ранее в этой главе. При разра-
ботке расписания занятий для начальной школы возможно собрать данные для всей
совокупности учеников. Если все остальные критерии для соответствия данных
будут отвечать требованиям, тогда мы сразу же будем знать, что наши данные яв-
ляются репрезентативными, поскольку мы собрали их непосредственно со всей со-
вокупности (или о ней) лиц, к которой они будет применяться.
Но что если нам нужно решить такую же задачу для всех школ города? Крайне ма-
ловероятно, что вы сможете успешно собрать данные о каждом отдельном учащем-
ся каждой отдельной школы; это означает, что при разработке расписания вы буде-
те полагаться на входные данные только для подмножества учащихся.
Таким образом, при работе с подмножеством (или выборкой) всегда важно обеспе-
чить его репрезентативность более широкой совокупности, к которой вы плани-
руете применять свои заключения. Подробное рассмотрение методологии форми-
рования выборки из совокупности выходит за рамки этой книги4, но базовая идея
состоит в том, что для того, чтобы полученная нами информация точно распро-
странялась на определенное сообщество, используемая нами выборка данных
должна пропорционально отражать состав этого сообщества. Это означает, что нам
нужно вложить время и ресурсы в понимание нескольких аспектов этого сообщест-
ва как целого, прежде чем мы сможем понять, является ли наша выборка репрезен-
тативной.
Здесь вы, наверное, думаете: подождите, если бы мы уже смогли получить инфор-
мацию о всей совокупности, то нам вообще не нужно было бы делать из нее выбор-
ку. В некотором смысле это верно. Во многих случаях возможно получить некото-
рую информацию обо всем сообществе, вот только не совсем ту информацию, ко-
торая нам требуется. Например, в нашем сценарии с составлением расписания
занятий мы бы, в идеале, собрали информацию о том, как учащиеся каждый день
добираются до школы и возвращаются из нее домой, а также сколько времени это
занимает. Кроме этого, мы бы получили некоторую информацию о расписании ра-
боты технических работников школы. Но для всей совокупности школы мы бы
могли иметь (если мы работаем совместно со школьной системой), скорее всего,
только такую информацию, как домашние адреса, адрес школы и, возможно, тип
транспортной поддержки (Ьйр8://8сЬоо18.пус.§оу/8сЬоо1-1Не/1:гап8рогШ1оп/Ьи8-
еИ§1Ы1йу). Используя эту информацию, скорее всего, совместно с некоторой до-
полнительной административной информацией, мы могли бы начать создавать
прикидочные оценки доли от всей совокупности определенных типов учеников,
ездящих в школу, а затем попытаться воспроизвести эти доли при взятии репрезен-
4 Ашйау Валец ее и Зиргаказй СкаисШигу предоставляют легкочитаемый обзор этого предмета в своей
статье 81аН$Нс8 1УПкои1 Теагз (ЬПр8://псЬ1.п1ш.тЬ.§оу/ртс/агйс1е8/РМС3105563).
тативной выборки из результатов нашего опроса. Только на этом этапе мы будем
готовы перейти к следующему шагу процесса выпаса данных.
Как мы видим, обеспечение репрезентативности требует внимательного рассмот-
рения, какие характеристики совокупности уместны для нашего вопроса по выпа-
су данных. Также необходимо попытаться найти достаточный объем дополни-
тельной информации, чтобы обеспечить пропорциональное представление этих
характеристик нашим набором данных. Этот тест на соответствие данных многие
управляемые данными службы постоянно проваливают, что, наверное, и неудиви-
тельно. Тогда как компании и исследователи могут расхваливать количество дан-
ных, использованных в разработке своих систем, реальность такова, что боль-
шинство легкодоступных для них наборов данных обычно не являются репрезента-
тивными, скажем, для совокупности населения США или мирового населения.
Например, крайне маловероятно, что данные, например, о трендах поисковых сис-
тем, активностях в социальных сетях, использовании общественного транспорта
или владении смартфоном будут репрезентативными для более широкой сово-
купности, поскольку они неизбежно подвержены воздействию таких факторов, как
доступ к Интернету и уровень дохода. Это означает, что в этих наборах данных со-
общества имеют избыточную репрезентацию, тогда как репрезентация других
сущностей недостаточна (иногда весьма значительно). В результате имеем систе-
мы, неспособные к обобщению, например системы распознавания лиц, которые
не ”видят” лиц афроамериканцев (Ъ€ф://ргосееШп§8.т1г.рге88/у81/Ьио1атуут118а/
Ьио1а1тут118а.рс1Г).
Если вы поставлены перед проблемой нерепрезентативных данных, как ее можно
решить? Как минимум, вам нужно откорректировать (и четко изложить) свою
оценку аспекта ”Для кого?”, чтобы он отражал совокупность, которую он должен
представлять. Это сообщество — единственное, для которого будут действительны
ваши знания, полученные в результате выпаса данных. Также имейте в виду, что
репрезентативность может обеспечить только точное отображение реальности ре-
зультатами ваших усилий по выпасу данных. Она не является ценностным сужде-
нием касательно того, следует ли продолжать сохранять эту реальность. Если ре-
зультаты вашей работы по выпасу данных будут использованы для осуществлений
изменений в системе или организации, завершение вашего процесса выпаса данных
в действительности является только начальной точкой в процессе обдумывания,
каким образом применить полученные знания, особенно касательно таких сложных
вопросов, как справедливость (Ъир8://уоиШЬе.сот/ууа€сй?у=]1Х1иУс1пуук).
Это верно даже тогда, когда у нас есть данные обо всей совокупности. Например,
если нужно узнать, какие организации получили грант определенного типа, тогда
представляющей для нас интерес ”совокупностью” или сообществом будут только
те организации, которых получили эти гранты. В то же самое время проверка дан-
ных на репрезентативность по отношению ко всей совокупности в целом по-
прежнему имеет значение: в случае если одно или больше сообществ в вашей сово-
купности получателей гранта представлены чрезмерно или недостаточно, это мо-
жет быть признаком скрытых факторов, влияющих на выбор получателей.
Проблема коллапса контекста
Огромный источник "плохих” данных и анализов данных можно проследить до
явления, называющегося коллапсом контекста (Ы1р8://герйог1а.ог§/1йои§Ы8/
агсЫуе8/2013/12/08/сотт§-соп1ех1>со11ар8е.111:т1), где данные, созданные для
одной цели, используются, обычно без особого критического осмысления, для
другой. Например, онлайновые рекламные сети часто используют данные на-
шей истории покупок или просмотра веб-страниц для принятия решений, ка-
кую рекламу нам показывать или какие продукты рекомендовать. Но при этом
эти системы часто смешивают то, что мы купили, с тем, что мы будем поку-
пать, и результаты часто поразительно лишены понимания темы, что наглядно
иллюстрируется в следующем твите (Ы1р8://1лУ1йег.сош/С1г1ГготВ1иро/81а1и8/
982156453396996096):
Раупег
ШЛр @С|г1РготВ1иро
Дорогой Атагоп*
Я купила туалетное сиденье, потому что нуждалась в нем. Это была
необходимость. Я не коллекционирую их. У меня нет зависимости от
туалетных сидений. Несмотря на завлекательные рекламные сообщения
электронной почты, которые вы мне присылаете, я не подумаю однажды'
о, давай тогда куплю еще одно туалетное сиденье, побалую себя.
3:22 АМ • Арг 6, 2018 • ЕсЬоГоп
Когда данные, созданные в одном контексте (просмотр веб-страниц), исполь-
зуются для принятия решений в другом контексте (реклама), результаты могут
дойти до абсурда. Тогда как ситуации, подобные этой, принадлежат к разряду
обычных странностей онлайнового сообщества, некоторые случаи коллапса
контекста могут нанести серьезный вред, как я описывала во врезке "Распа-
ковка системы СОМРА8" главы 1.
Оценка целостности данных
Понятие соответствия данных означает, являются ли наши данные правильными
для использования их в целях получения ответа на наш вопрос по выпасу данных.
С другой стороны, понятие целостности данных главным образом означает, могут
ли имеющиеся данные поддерживать анализы, которые нужно выполнить для того,
чтобы ответить на этот вопрос. Как мы увидим в этом разделе, в процессе оценки
целостности данных необходимо рассмотреть много разных характеристик данных.
Но обязательно ли определенный набор данных должен обладать всеми этими ха-
рактеристиками, чтобы иметь высокую целостность и, следовательно, высокое ка-
чество? Нет, не обязательно. Тогда как некоторые из этих характеристик являются
необходимыми, важность других зависит от нашего специфичного вопроса и мето-
дов, необходимых для получения ответа на него. Кроме этого, за редкими исклю-
чениями, многие из этих характеристик усовершенствуются и улучшаются в про-
цессе выпаса данных.
Иными словами, тогда как обеспечение соответствия данных является обязатель-
ным, типы и степень целостности, требуемые для конкретного проекта по выпасу
данных, будут варьироваться. Конечно же, чем больше ваши данные будут соот-
ветствовать этим требованиям, тем более полезными они будут как для вас, так и
для других.
В общих чертах, набор данных высокой целостности в той или иной степени будет
обладать следующими характеристиками5:
Необходимые, но недостаточные
♦ Известного происхождения
♦ Тщательно аннотированные
Важные
♦ Уместные
♦ Полные
♦ Большого объема
♦ Многомерные
♦ Атомарные
Достижимые
♦ Непротиворечивые
♦ Ясные
♦ Размерностно структурированные
Но, как я уже упоминала, не все эти характеристики целостности данных имеют
одинаковую важность. Некоторые из них являются обязательными, тогда как дру-
гие почти всегда являются результатом определенных шагов процесса выпаса дан-
ных, а не его предшественниками. Хотя мы стремимся к тому, чтобы к началу ана-
лизов наши данные обладали как можно большим количеством этих характеристик,
нам всегда нужно достичь определенного баланса между более полным усовершен-
ствованием наших данных и завершением нашей работы вовремя, чтобы получен-
ные в результате знания были полезными и своевременными.
В этом смысле оценка целостности данных может быть полезным методом для на-
значения приоритетов нашим усилиям по выпасу данных. Например, при отсутст-
вии для набора данных любой характеристики из категории ''Необходимые, но не-
достаточные”, можно полностью забыть об его использовании. Если набору данных
не хватает одной или двух характеристик из категории "Важные”, его данные, мо-
5 Этот список взят из замечательной книги ТУспг Уои Зее И: 81тр1е УгзиаИхаНоп Тесктдиез /ог
ОиапШаНхе Апа1у818, автор 81ер1зеп Ге\у (Издательство Апа1уйс8 Ргезз), с адаптациями на основе моего
опыта по выпасу данных.
жет быть, можно будет еще спасти, объединив этот набор с другими или же огра-
ничив область анализа, и, соответственно, утверждений, которые можно выдвигать
на основе его обработки. А вот получение характеристик из категории ”Достижи-
мые” часто является целью шага очистки процесса выпаса данных, а не чем-то, что
можно ожидать от большинства данных реального мира при первом столкновении
с ними.
В конечном итоге, степень соответствия наборов данных этим характеристикам,
которые рассматриваются более подробно далее, будет зависеть от количества вре-
мени, которое вы можете позволить уделить своему проекту выпаса данных, но в
случае отсутствия хорошего солидного большинства из них вы получите ограни-
ченные знания в результате его обработки. Как подробно рассматривается в главе 6,
именно это непостоянство является причиной такого тесного переплетения облас-
тей выпаса данных и качества данных.
Необходимые, но недостаточные
Большинство времени обрабатываемые нами данные были собраны кем-то другим
или большим количеством людей и процессов, к которым у нас нет прямого досту-
па. В то же самое время мы должны быть в состоянии поддерживать как наш про-
цесс выпаса данных, так и любые полученные из него знания. Это означает, что
успешный процесс выпаса данных должен обладать парой по-настоящему важных
характеристик.
Известного происхождения
Как обсуждалось в разд. "Что такое качество данных?" главы 7, данные — это
результат человеческих решений о том, что и как измерять. Это означает, что ис-
пользование набора данных, собранных другими лицами, требует значительного
доверия к ним, особенно потому, что верифицирование каждой отдельной точки
данных возможно лишь в очень редких случаях. Если бы это было возможно, то
мы, наверное, просто собирали бы данные самостоятельно. Вот почему так важно
знать происхождение используемого набора данных: если мы не знаем, кто собрал
их, какими методами и/или с какой целью, нам будет очень трудно дать оценку,
подходят ли они для нашей цели выпаса данных или насколько правильно мы ин-
терпретируем их.
Конечно же, это не означает, что нам, к примеру, необходимо знать дни рождения и
любимые цвета всех, кто участвовал в создании используемого набора данных. Но
вы должны попытаться узнать достаточно об их профессиональном опыте, мотива-
ции для сбора этих данных (например, является ли это требованием закона), а так-
же использованные ими методы, чтобы получить некоторое понимание о том, ка-
кие индикаторы может потребоваться подтвердить, а какие можно использовать
как есть. В идеале как информация о создателях данных, так и достаточная доку-
ментация об этих процессах должна быть доступна, чтобы вы могли довольно бы-
стро получить ответы на все эти вопросы о происхождении данных. Но если эту
информацию будет трудно найти, возможно, лучше оставить этот набор данных и
найти другой. Поскольку эта информация вам нужна для того, чтобы оценить соот-
ветствие данных, может быть лучше потратить свое время на поиски другого набо-
ра данных или на сбор данных самому.
Тщательно аннотированные
Тщательно аннотированный набор данных содержит достаточно сопутствующей
информации, или метаданных, чтобы его можно было интерпретировать. Эта ин-
формация должна содержать от высокоуровневых объяснений методологии сбора
данных до ”словарей данных”, описывающих каждый индикатор данных вплоть до
его единиц. Это может казаться прямолинейной задачей, но для методов предос-
тавления такой аннотационной информации не всегда есть общепринятые стандар-
ты. Иногда она может находиться в файлах данных или в самих элементах данных,
а иногда может содержаться в отдельных файлах или документах, хранящихся в
полностью отдельном месте, чем то, из которого были получены данные.
Но независимо от их структуры или метода предоставления, надежные документы
аннотации данных являются неотъемлемым компонентом данных высокой целост-
ности, поскольку без них невозможно выполнять какие бы то ни было анализы
данных или делать какие-либо заключения на их основе. Например, представьте
себе попытку интерпретировать финансовый план, не зная разрядности его данных:
то ли это доллары, то ли тысячи долларов, то ли миллионы. Очевидно, что это бу-
дет невозможным. А чтобы понять истинно американское отношение к важности
аннотирующих документов, наподобие словарей данных, знайте, что для того, что-
бы их получить, иногда требуется подать судебный иск (кир8:/Агас.8уг.ес1и/Го1а/
ке/20210805).
Выведывание данных 1180А6
Служба сельскохозяйственного маркетинга министерства сельского хозяйства
США (178ВА) предоставляет ежедневные отчеты (Й€ф8://уууууу.ат8.и8с1а.§оу/
тагке€-пелУ8/си8€от-герог1:8) о перемещениях продуктов питания через 16 цен-
тральных сельскохозяйственных рынков по всему миру. Каждый день специали-
сты из министерства 178ВА обходят эти рынки, записывая размеры, цены, со-
стояние, происхождение и другие аспекты предлагаемых на продажу сельскохо-
зяйственных продуктов, таких как, например, яблоки (Ьйр8://оге11.1у/Вг]ВР).
И хотя эти данные обладают многими признаками высокой целостности — они
чистые, подробно сегментированы и большого объема — значения некоторых
элементов данных неясны. Например, что означают термины ”РШЕАРРЕАК”
и ’ТКАРРЕАК”? К счастью, ответ на этот вопрос значительно облегчается зна-
нием, что эти данные собраны министерством 178ВА о центральных сельскохо-
зяйственных рынках.
6 Уийез 81а1е§ БераНтеп! оГ А§пси11иге — Министерство сельского хозяйства США.
Выполняем поиск в Интернете по фразе ”1780А Тегшта! МагкеГ, прокручиваем
результаты поиска и где-то посредине страницы обнаруживаем ссылку с этими
терминами, которая ведет на отчет из центрального сельскохозяйственного рын-
ка города Лос-Анжелес (Ьйр$:/Лт\у\т.ат$.и$с1а.щ)у/тпгерог{$/йс_Гу020.Ш).
Этот отчет содержит телефонный номер, один быстрый звонок по которому
раскрывает эту тайну.
Важные
Добиться какого-либо успеха в выпасе данных, не имея достаточной информации о
происхождении набора данных и о его метаданных, — задача довольно трудная. Но
даже при их наличии мы далеко не уедем, не имея достаточного объема данных, за
правильный период или периоды времени и с правильной степенью детализации.
Вместе с этим, следующие характеристики данных часто можно оценить, а иногда
и улучшить, посредством нашей собственной работы по выпасу данных. Таким об-
разом, тогда как работать с наборами данных, которые уже обладают такими харак-
теристиками, будет, несомненно, легче, может заслуживать внимания исследование
наборов данных и без некоторых характеристик.
Своевременные
Насколько актуально состояние используемых данных? Если только вы не изучаете
какой-либо исторический период, важно обеспечить, чтобы ваши данные были дос-
таточно свежими, чтобы осмысленно отображать текущее состояние в мире. Хотя,
насколько устаревшими должны быть данные, чтобы считаться слишком устарев-
шими, будет зависеть как от исследуемого явления, так и от периодичности сбора и
предоставления данных.
Например, если вас интересует демография вашего района, но ваши самые свежие
данные были собраны несколько лет тому назад, существует высокая вероятность,
что с тех пор в этой области произошли значительные изменения. Данные по без-
работице будут устаревшими, если им свыше одного месяца, тогда как данные
биржевых котировок, полученные всего лишь несколько секунд тому назад, уже
будут слишком устаревшими, чтобы их информацию можно было использовать для
принятия решений по сделкам. Если только вы не знакомы с данной областью,
оценка своевременности данных будет, скорее всего, требовать проведения кон-
сультаций с экспертами, а также с создателем этих данных.
Полные
Содержит ли набор данных все значения данных, которые он должен содержать?
Например, в данных по яблокам во врезке "Выведывание данных 1ЛМЭА" ранее в
этой главе только несколько строк содержат описание внешнего вида, тогда как
большинство строк оставлено пустыми. Возможно ли все-таки получить полезные
знания из таких неполных данных? Чтобы ответить на этот вопрос, сначала нужно
ответить на два других. Первый: почему эти данные пропущены? Второй: необхо-
дим ли этот индикатор данных для выполнения конкретного анализа, требуемого
для нашего процесса выпаса данных?
Например, данные могут быть неполными по причине пропущенных отдельных зна-
чений или по причине нерегулярного сбора данных. Например, данные, которые
должны собираться ежемесячно, содержат разрыв между сборами в шесть месяцев.
Набор данных также может быть обрезан, что является распространенной проблемой
при попытке открыть набор данных большого размера в электронной таблице. Неза-
висимо от причины нехватки некоторых данных, ее необходимо определить, чтобы
знать, что делать дальше. В случае с данными по яблокам (см. врезку "Выведывание
данных УЗБА"ранее в этой главе) мы потенциально могли бы игнорировать катего-
рию Арреагапсе (внешний вид), если наш основной интерес состоит в определении
метода установки цен на разные сорта яблок. Альтернативно можно связаться с рын-
ком, чтобы уточнить, не представляют ли пропуски в столбце Арреагапсе некое зна-
чение по умолчанию, с явным указанием которого они просто не хотели морочиться.
Даже обрезанные данные могут не представлять проблемы, если доступная часть
данных покрывает период времени, достаточный для наших целей, но все равно бу-
дет полезным узнать настоящее количество записей и диапазон дат данных для кон-
текста. В то время как проблематичность разрывов в данных можно уменьшить при
помощи определенных статистических приемов, знание, что перерыв в сборе данных
обусловлен неким сбоем, может изменить тип выполняемых вами анализов или даже
вообще вопрос, на который вы пытаетесь получить ответ.
Иными словами, тогда как наличие полных данных всегда предпочтительнее, зная
причину отсутствия данных, вы, тем не менее, возможно, сможете продолжить ра-
ботать над своим процессом по выпасу данных. Но всегда необходимо узнать при-
чину отсутствия данных и обязательно задокументировать добытые сведения.
Большой объем
Какое количество точек данных будет ''достаточным”? Как минимум, количество
записей набора данных должно быть достаточным, чтобы поддерживать тип анали-
за, необходимый для получения ответа на ваш конкретный вопрос. Если вам требу-
ется подсчет, например, количества звонков на номер 3117, связанных с жалобами
на шум в определенный год, тогда "достаточное” количество данных означает за-
писи всех звонков на этот номер в течение данного года.
Но если ваш вопрос затрагивает общие или обобщаемые закономерности или тен-
денции, то тогда критерий достаточности будет менее четким. Например, если
нужно определить наиболее "занятую” станцию системы СШ В1ке, то какой должна
быть длительность рассматриваемого периода? Неделя? Месяц? Год? Рассматри-
вать только рабочие дни или же все дни? Правильный ответ будет частично зави-
7 Номер в США, на который можно позвонить, чтобы узнать разного рода информацию о муниципаль-
ных, государственных, общественных и прочих службах, а также для жалоб неэкстренного характера.
сеть от более подробной конкретизации вашего вопроса. Вас интересует опыт
только регулярных пользователей или гостей города? Или же вы пытаетесь выра-
ботать понимание сути вопросов для планирования транзита, размещения рознич-
ных торговых точек или качества обслуживания? Кроме этого, вы заинтересованы в
том, какая из станций является действительно наиболее ”занятой”, или же больше в
какой-либо определенной интенсивности использования велосипедов? Как часто
бывает, правильный ответ в значительной степени сводится к правильной поста-
новке вопроса, что в большинстве случаев требует предельной конкретности.
Но одна из наиболее сложных составляющих оценки ”полноты” данных состоит в
трудности принятия во внимание факторов, которые могут оказывать влияние на
исследуемую вами закономерность или тренд, не имея достаточно хороших пред-
варительных знаний о предметной области. Например, тогда как вы можете с лег-
костью ожидать, что уровень пользования услугами системы СШ В1ке может варь-
ироваться в зависимости от времени года, как насчет влияния, которое может ока-
зать сокращение услуг общественного транспорта? Или повышение стоимости
проезда? Эти изменения могут иметь последствия для нашего анализа, но как мы
можем знать это на этапе, когда мы только приступаем к работе?
Ответ заключается, как это так часто бывает, в привлечении экспертов. Возможно,
что повышение платы за проезд в общественном транспорте временно повышает
количество аренд велосипедов, но только на несколько месяцев. Возможно, что
люди, регулярно использующие велосипед для поездок по своим делам, подготов-
лены к плохой погоде и продолжают свои поездки как обычно, даже когда идет
снег. При условии наличия бесконечного времени и бесконечного объема данных
мы, возможно, сможем ответить на эти вопросы сами, но обсуждение их с другими
людьми будет намного быстрее и гораздо более информативно. И хотите верьте,
хотите нет, но эксперты есть почти в любой области. Например, быстрый поиск в
поисковой системе Сюо§1е 8с1ю1аг (Ьйр8://оге11.1у/еСЛгВ) по фразе ”8еа8опа1 пбег-
8Ыр Ыке 81загт§” (сезонные количества прокатов велосипедов) возвращает резуль-
таты от сообщений в блогах до исследований, прошедших экспертную проверку, по
этому вопросу.
Что это означает в аспекте полноты данных? Существующие исследования или, что
еще лучше, разговор в реальном времени с экспертом в данной предметной области
(см. разд. "Эксперты предметной области" далее в главе 6) помогут вам решить,
какие факторы включить в свой анализ, и в результате, какой объем данных нужен
для полноты выбранного анализа.
Многомерность
Выпас данных реального мира означает сложность реального мира, где исследуемое
явление может быть подвержено влиянию громадного количества факторов. Одним
из примеров будет пример со станцией системы СШ В1ке с "наибольшей загруженно-
стью”: кроме сезонности и общественного транспорта, роль также могли бы играть
физические особенности окружающей местности (ЫТрз ://уууууу.8С1епеесНгесйсош/
8С1епсе/агйс1е/аЬ8/рп/8136192091731057Х) или плотность станций. Если наши дан-
ные для системы СШ В1ке содержат только информацию о количестве поездок, на-
чавшихся и завершившихся на определенной станции, тогда будет очень трудно
создать анализ, который сказал бы нам что-либо большее, кроме как ”на этой стан-
ции было взято и возвращено наибольшее количество велосипедов” за данный пе-
риод времени.
Но в случае многомерных данных они будут обладать множественными атрибута-
ми или свойствами, связанными с каждой записью. Например, для архивных дан-
ных системы СШ В1ке (Ьйр8://с1йЫкепус.сот/8у8€ет-(1а€а) мы знаем, что у нас
есть все следующие данные (листинг 3.1), которые мы получили благодаря нашей
небольшой работе по выпасу данных в разд. "Отправляемся в путь с данными СШ
Вгке"главы 2.
Листинг 3.1. Свойства архивных данных системы СШ Езке в нашем распоряжении
[' ±гтрс1ига1:1оп1, 'з^агЦ-йпе', ' 51ор1гте1, ’ з^аг1: 51а1гоп 1сГ, 'з1аг1: 51а1гоп
папе', 151а г! 51а11оп 1а111ис1е', '51аг1 51а11оп 1опдНис1е1, 'епс! 51а11оп хд1,
'епс1 51а11оп папе', 'епс1 51а11оп 1а111ис1е', 1 епс1 51а11оп 1опд11ис1е1, 'ЫкетсГ,
'и5ег1уре', 'Ыг1Ьуеаг', 'деп^ег']
Как видим, данные для каждой записанной поездки содержат 15 разных свойств,
любое число из которых мы могли бы выгодно использовать для более осмыслен-
ного или подробного метода для получения понимания, какая из станций системы
СШ В1ке является наиболее "загруженной”.
Атомарность
Атомарные данные имеют высокую гранулярность (т. е. степень разбиения); они
одновременно и точно измерены, и не собраны в сводку статистик или индикато-
ров. Как правило, суммарные индикаторы типа скоростей и средних значений не
всегда особенно хорошо подходят для дальнейшего анализа в результате потери
большого количества подробностей базовых данных. Например, арифметическое
среднее для обоих следующих множеств чисел равно 30: 20, 25, 30, 45 и 15, 20, 40,
45. Тогда как сводные статистические данные часто полезны при сравнивании раз-
личных наборов данных, они не предоставляют достаточной информации о базовой
структуре данных, чтобы быть полезными для поддержки дальнейшего анализа.
Достижимость
Независимо от того, как набор данных выглядит изначально, реальность такова, что
по-настоящему чистый, хорошо организованный и не содержащий ошибок набор
данных практически невозможен. Частично это обуславливается тем обстоятельст-
вом, что со временем некоторые вещи (например, стоимость доллара) просто под-
вергаются изменениям. В результате этого те, кто занимаются выпасом данных,
должны всегда учитывать, что некоторые характеристики качества данных могут
потребовать проверки и улучшения. К счастью, именно в этом отношении гибкость
языка РуЙюп и его поддержка многократного использования кода по-настоящему
проявляют себя, поскольку выполнение этих задач будет становиться легче и быст-
рее по мере повышения уровня наших навыков программирования с течением вре-
мени.
Непротиворечивость
Данные высокого уровня целостности должны быть непротиворечивыми в не-
скольких разных отношениях. Часто наиболее очевидный тип непротиворечивости
набора данных связан с периодичностью данных, собранных в течение времени.
Постоянны ли интервалы времени между отдельными записями? Есть ли разрывы?
Нерегулярные интервалы между записями данных важно исследовать (если их
нельзя устранить), потому что они могут быть первым индикатором нарушений или
различий в самом процессе сбора данных. Другой источник широкомасштабной
противоречивости данных обычно возникает во всех случаях, связанных с исполь-
зованием текстовых полей данных, в частности, поля, содержащие имена или опи-
сания, почти неизбежно будут содержать разные написания какого-либо термина.
Даже поля, задача стандартизации значений которых с виду может казаться прямо-
линейной, могут содержать подробности разного уровня. Например, поле ”х!р собе”
(почтовый индекс) может содержать как обычные пятицифровые значения, так и
более точные ”71р+4”8 значения.
Другие типы непротиворечивости могут быть менее очевидными, но не менее важ-
ными. Например, единицы измерения должны быть одинаковыми во всем наборе
данных. Тогда как это может казаться очевидным, на самом деле обратная ситуация
может иметь место намного чаще, чем можно подумать сначала. Например, пред-
положим, что мы рассматриваем данные о стоимости яблок на протяжении периода
десяти лет. Конечно же, все цены в данных могут быть записаны в долларах, но
стоимость самого доллара будет постоянно меняться по причине инфляции. Также
большинство из нас знают, что градусы Цельсия и Фаренгейта имеют разные зна-
чения, но это не единственный такой случай, так как имперская пинта содержит
приблизительно 568 миллилитров, тогда как американская — около 473 миллилитров.
Вполне вероятно, что и рассказы о невысоком росте Наполеона Бонапарта могут
быть основаны на противоречивости размера французского дюйма XIX столетия
(около 2,71 см) и его сегодняшней версии (около 2,54 см) (Ьир8://Ьгйапшса.сот/
81огу/луа8-паро1еоп-8ЬогЦ.
Решением таких противоречивостей будет нормализация данных, прежде чем вы-
полнять какие-либо сравнения или анализы. В большинстве случаев это вопрос
простой арифметики: нам просто нужно выбрать один вариант единицы, к которо-
му мы преобразуем все другие (еще один важный момент для документации). Этот
подход применим даже с денежными единицами, работу с которыми аналитики
8 То есть 5-цифровое основное значение плюс дополнительное 4-цифровое значение. Например,
12345-6789. —Примеч. пер.
часто начинают с преобразования к "настоящим” (т. е. индексированным с учетом
инфляции) долларам, используя какой-либо год в качестве эталона. Например,
сравнить реальную стоимость (в долларах) минимальной федеральной почасовой
оплаты в 2009 году (когда она была повышена последний раз) со стоимостью в
2021 году можно, используя калькулятор инфляции, например, на веб-сайте Бюро
трудовой статистики США (Ъйр8://Ы8.§оу/с1а€а/тПайоп_са1си1а€ог.й€т9). Мы уви-
дим, что реальная стоимость часовой оплаты, в 2009 году составляющая $7,25, бу-
дет эквивалентна всего лишь $5,72 в 2021 году.
Кто на самом верху в хит-листе?
Поставщики данных часто не уточняют, какие единицы они используют или
были ли их данные нормализованы. Например, веб-сайт Вох ОГйсе Модо пре-
доставляет текущую сумму сборов мировых фильмов с самыми большими сбо-
рами (Ъйр8://ЬохоШсето]о.сот/сйаг€Лулу_€ор_11Гейте_§го88/?агеа=Х\У\У), но
при этом неясно, была ли выполнена нормализация указанных сумм к текущим
долларам. Если не была, то очевидно, что выполнение такой нормализации вы-
звало бы определенную перестановку верхних позиций списка.
Ясность
Подобно коду РуЙюп, наши данные и их метки, в идеале, должны легко читаться и
интерпретироваться. Но в реальности описатели полей и даже наборов данных ино-
гда могут быть не намного понятнее, чем таинственные коды, требующие постоян-
ной сверки со словарем данных или иными ресурсами. Частично поэтому этот тип
целостности данных почти всегда является не предшественником, а продуктом вы-
паса данных определенной степени.
Например, есть ли какая-либо логика в том факте, что для демографических и жи-
лищных оценок по результатам обследования американских общин выполненного
Бюро переписи населения США присвоен код ВР05 (Ьйр8://оге11.1у/кВ48Ь)? Воз-
можно. Но его значение очевидно для непостоянного пользователя данными Бюро
ничуть не больше, чем значение заголовка столбца ОР05_0001Е10. При этом архив с
таблицей этого обследования Бюро содержит множественные файлы, которые мо-
гут помочь вычислить значения имен файлов и заголовков столбцов. Разработка
ясного набора данных с высоким уровнем целостности может потребовать значи-
тельного объема работы по переименованию, переформатированию и переиндекса-
ции, особенно когда мы имеем дело с данными, созданными какой-либо правитель-
9 Ссылка открывает не сам калькулятор, а главную страницу сайта. Чтобы добраться до калькулятора,
на главной странице нужно выбрать ВаХа Тоок и в строке меню вверху страницы, в всплывающем
окне в столбце ВаХа КеЫеуа! Тоо18 выбрать Тор Р>ск8, Опе 8сгееп, МиЫ-8сгееп, Мар8, апй Са1си1а-
Ц)Г8, далее в открывшемся новом окне под заголовком ВаХаЬа8е8, ТаЫе8 & Са1си1аХог8 Ьу 8иЬ]ес1
щелкнуть по ссылке Са1си1аХог8, а затем выбрать ХпЛаНоп.
10 Кстати, это означает приблизительную оценку общей численности населения.
ственной организацией. И как всегда, критически важно в ходе работы документи-
ровать ваши источники информации и все переименования процессов.
Размерностно структурированные
Размерностно структурированные данные содержат поля, сгруппированные в прак-
тичные категории или дополнительно помеченные такими категориями, как, на-
пример, географический регион, год или язык. Такие свойства часто предоставляют
точки быстрого входа как для дополнения данных (эта тема рассматривается в разд.
"Аугментация данных" далее в этой главе), так и для анализа данных, поскольку
они понижают уровень усилий для операций корреляции и агрегации, которые нам
нужно выполнять самостоятельно. Эти свойства также могут служить указателями
тех аспектов, которые создатель данных посчитал важным записать.
Например, размерности данных системы СШ В1ке содержат такие аспекты, как тип
учетной записи, по которой были арендованы велосипеды, — ЗиЪзспЪег или Си§-
1ошег, а также год рождения и пол владельца учетной записи. На этом основании
можно предполагать, что разработчики данных считали, что эти свойства могли бы
предоставить полезные знания о поездках и использовании велосипедов системы СШ
В1ке. Но, как мы увидим в главе 7, они решили не включать в данные какого бы то ни
было типа указателя ”будний день/выходной”, что означает, что если нам потребует-
ся эта размерность, нам будет нужно получить ее самим.
Декодирование данных
Хотя удобочитаемые имена файлов и заголовки столбцов, несомненно, полез-
ны, наличие закодированных описаний данных не всегда плохо. Как мы уви-
дим далее, создание надежных, высококачественных наборов данных иногда
требует объединения двух или больше источников данных. В таких случаях
может быть полезным использование стандартизованных, хотя иногда и непо-
нятных кодов. Например, на веб-сайте (Ьйр8://йа€а.пу8е(1.§оу/) Департамента
образования штата Нью-Йорк предоставляются профили отдельных школ, и
для каждой из них также предоставляется несколько идентификационных но-
меров. Сами по себе эти идентификационные номера не содержат особой ин-
формации, но, выполнив в Интернете поиск по ним, можно получить ссылки
на другие документы и наборы данных, что будет способствовать упорядочи-
ванию наборов данных из множественных источников с более высокой степе-
нью достоверности.
Улучшение качества данных
Как упоминалось ранее, многие аспекты качества данных являются продуктом про-
цесса выпаса данных, связано это с согласованием и нормализацией единиц измере-
ния, уточнением значений неясных меток данных или поиском дополнительной
справочной информации о репрезентативности вашего набора данных. Одной из со-
ставляющих, иллюстрируемых этим обстоятельством, является то, что в реальном
мире обеспечение качества данных, по крайней мере, в некоторой степени является
результатом множественных, повторяющихся процессов выпаса данных. Тогда как
эти фазы процесса выпаса данных могут обозначаться разными терминами, я обычно
называю их очисткой данных и аугментацией (дополнением) данных.
Очистка данных
В действительности очистка данных, по двум причинам, — это не столько отдель-
ный шаг в процессе выпаса данных, сколько постоянная деятельность, сопровож-
дающая все остальные шаги. Во-первых, большинство данных при их получении не
являются чистыми, а во-вторых, в какой именно очистке нуждается набор данных
(или часть его), часто раскрывается постепенно в процессе работы. На высшем
уровне чистые данные можно обобщить как не содержащие ошибок или опечаток.
Например, разные единицы измерения, разные варианты написания слов или тер-
минов, а также поля, не разделенные должным образом и содержащие невозмож-
ные значения или вообще никаких значений. И хотя многие из этих недостатков
можно, по крайней мере, относительно легко распознать (хотя не всегда испра-
вить), более глубокие проблемы с данными могут остаться. Изменения измерений,
ошибки вычислений и прочие упущения, особенно в данных, генерируемых систе-
мой, часто не проявляются до тех пор, пока не будет выполнен анализ какого-либо
уровня, и реальность данных была удостоверена людьми, имеющими значительные
экспертные знания и/или непосредственный опыт с предметом.
Итеративная природа процесса очистки данных служит примером, почему выпас
данных является циклом, а не линейной последовательностью шагов. В частности,
по мере того как в процессе работы проявляется дополнительная информация о
данных и углубляется ваше понимание их взаимосвязи с окружающим миром, мо-
жет оказаться, что необходимо возвратиться к ранее выполненной работе и повто-
рить операцию с определенными аспектами данных или же откорректировать эти
аспекты. Конечно же, это всего лишь еще одна причина, по которой документиро-
вание своей работы по выпасу данных является таким важным: документацию
можно использовать, чтобы быстро определить, где и как были выполнены какие-
либо изменения или обновления, и согласовать их с вашими текущими знаниями о
данных. Без надежной документации вы можете быстро обнаружить, что необхо-
димо все начинать с начала.
Аугментация данных
Аугментация набора данных — это процесс расширения или улучшения данных,
обычно объединения их с другими наборами данных. Это настоящая природа
"больших данных” в XXI столетии11. Используя общие качества разных наборов
11 Обсуждение вопроса больших данных см. в статье СгШссй <Аие811оп8/ог В1§ Оа1а, авторы с1апаИ Ьоуб
и Ка1е Сга\уГог<1, ЬПр8:/ДапаГоп1ше.сот/ао1/Ги11/10.1080/1369118Х.2012.678878).
данных, можно собрать вместе данные из разных источников, чтобы получить бо-
лее подробную картину интересующего нас явления, заполняя пробелы, предостав-
ляя подтверждения или добавляя контекстуальные данные, помогающие нам лучше
оценить соответствие данных. В рассмотренном ранее примере с выбором ”наи-
лучшей” школы это может означать использование кодов школ, чтобы свести вме-
сте несколько типов данных, собранных разными сущностями, например, результа-
ты стандартизованных тестов уровня штатов и информацию о локальных зданиях.
Совмещая эффективное исследование и выпас данных, аугментация данных может
способствовать наращиванию качества данных и в получении ответов на вопросы
со слишком многими нюансами, чтобы на них можно было ответить на основе лю-
бого одного набора данных.
Подобно очистке данных, возможности для аугментации данных могут возникать
практически на любом этапе процесса выпаса данных. В то же самое время каждый
новый добавляемый набор данных будет порождать свой собственный процесс вы-
паса данных. Это означает, что в отличие от очистки данных, аугментация данных
не имеет окончательного "конечного состояния”, т. е. всегда будет возможность
добавить еще один набор данных. Это еще одна причина, почему для процесса вы-
паса данных так важно в его начале сформулировать конкретный и лаконичный
вопрос о его цели: при отсутствии ясно выраженной формулировки о том, что мы
пытаемся исследовать, слишком легко исчерпать время или другие ресурсы, отве-
денные на решение нашей задачи. Но есть и хорошая новость: если вы должным об-
разом вели свой дневник данных, вы никогда не упустите из виду перспективный
набор данных для возможного использования в каком-либо будущем проекте.
Заключение
Поскольку до настоящего времени у нас имеется лишь ограниченный опыт практи-
ческой работы с реальными данными, многие из рассмотренных в этой главе поня-
тий могут выглядеть несколько абстрактными. Но не стоит волноваться из-за этого.
Вскоре у нас будет приличный объем практической работы. В последующих главах
мы будем выполнять выпас данных, поступающих в разных форматах и из разных
источников, что предоставит нам возможность увидеть изнутри, как различные ха-
рактеристики качества данных влияют на принимаемые нами решения при получе-
нии, оценке, очистке, анализе и предоставлении данных. Можете быть уверенными,
что по завершению прочтения этой книги вы сможете создавать содержательные,
точные и убедительные анализы и визуализации данных, чтобы поделиться своими
знаниями об окружающем нас мире.
В следующей главе мы начнем этот процесс с рассмотрения, как преобразовывать
данные в самых разнообразных форматах в структуру, которая позволит выполнять
требуемые нам очистку, дополнение и анализ. Ну-с, приступим.
ГЛАВА 4
Работа с файловыми и канальными
данными на языке Ру№оп
В главе 3 мы рассмотрели характеристики, способствующие улучшению качества
данных: от аспектов целостности данных, таких как их полнота, непротиворечи-
вость и ясность, до качеств соответствия данных, таких как надежность, действи-
тельность и репрезентативность. Мы обсудили потребность как в очистке, так и в
стандартизации данных, а также необходимость в дополнении данных посредством
их объединения с другими наборами данных. Но как все эти теоретические понятия
можно реализовать на практике?
Очевидно, что начинать оценивать качество набора данных невозможно без пред-
варительного просмотра его содержимого, но как часто бывает — легче сказать,
чем сделать. В течение десятилетий выпас данных был высокоспециализированным
занятием, понуждая компании и организации к созданию целого ряда разных (и
иногда закрытых) форматов цифровых данных, предназначенных для удовлетворе-
ния своих конкретных требований. Часто эти форматы используют свои собствен-
ные расширения файлов, с некоторыми из которых вам, наверное, приходилось
встречаться. Например, расширения файлов х1з, сзу, с1ЬГ и зрзз обычно обозначают
файлы данных1. Хотя конкретные структуры и прочие детали этих форматов отли-
чаются друг от друга, все они относятся к типу, который я бы назвала основанными
на файлах или просто файловыми, имея в виду, что они содержат (более или менее)
архивные данные в статических файлах, которые можно загрузить из базы данных,
отправить по электронной почте или получить доступ к ним через файлообменные
сайты. Стоит отметить, что файловый набор данных будет содержать ту же самую
информацию что сегодня, что неделю, что месяц или год спустя.
В настоящее время такие файловые форматы контрастируют с форматами и интер-
фейсами данных, которые в течение последних 20 лет возникли параллельно с веб-
сервисами. В настоящее время интернет-данные используются для предоставления
обширного диапазона информации и услуг, от новостей до погодных сведений и
социальных сетей. Эти источники информации канального типа имеют свои собст-
венные уникальные форматы и структуры. Этот тип данных реального времени
1 Сравните эти расширения с другими известными вам, например, шр4 или рп§, которые обозначают
музыку и изображения, соответственно.
обозначается расширениями типа хш1, )8оп и Г88, и для доступа к ним часто требу-
ется использование специализированных АР1-интерфейсов (АррПсайоп Рго§гаш-
1п1егГасе — интерфейс прикладного программирования). В отличие от файло-
вых форматов, при обращении к адресу или ”эндпойту” канальных данных через
АР1-интерфейс они всегда предоставляют самые последние доступные данных, ко-
торые могут измениться за последние несколько дней, часов или даже секунд.
Конечно же, эти определения не идеальные. Многие организации (в особенности
правительственные учреждения) предоставляют для загрузки файловые данные,
заменяя эти файлы на новые (с теми же самыми именами) при каждом обновлении
источника данных. В то же самое время канальные данные можно загрузить и со-
хранить в обычном файле для дальнейшего использования, но их онлайновый ис-
точник обычно не предоставляет доступа к более старым версиям. Несмотря на это,
в большинстве случаев можно использовать высокоуровневые различия между
файловыми и канальными форматами данных, чтобы выбрать наиболее подходя-
щий источник данных для конкретного проекта по обработке данных.
Как узнать, какой источник данных нам требуется, файлового или канального типа?
В большинстве случаев в этом вопросе у вас не будет особого выбора. Например,
компании социальных сетей предоставляют доступ к своим каналам данных по-
средством своих АР1-интерфейсов, но обычно не предоставляют ретроспективных
(прошлых) данных. Другие типы данных (особенно данные, синтезированные из
других источников или в значительной степени пересмотренные перед выпуском)
могут быть с большей степенью вероятности предоставлены в файловых форматах.
При наличии выбора между файловым и канальным форматом выбор между ними,
по сути, будет зависеть от природы вашего вопроса по выпасу данных: если он за-
вязан на самых последних доступных данных, тогда, наверное, предпочтение сле-
дует отдать данным канального формата. Но если вас интересуют тренды, то наи-
лучшим вариантом будут файловые данные, поскольку они с большей вероятно-
стью будут содержать информацию, собранную в течение определенного периода
времени. Однако при наличии доступа к обоим форматам, нет никакой гарантии,
что они будут содержать одинаковые поля, в результате чего вам опять придется
принимать решение, какой из них выбрать для дальнейшей работы.
Один источник данных, два подхода
Мы уже работали с источником данных, доступным как в файловом, так и в
канальном формате, — это данные системы СШ В1ке, представленные в главе
2. В частности, в разд. "Отправляемся в путь с данными СШ В1ке” этой главы
мы использовали подмножество файловых данных для изучения, сколько было
предпринято поездок клиентами этой системы типа ЗиЬзспЬег и Си81ошег
1 сентября 2020 г. Для вопросов выпаса данных касательно трендов по количе-
ству поездок и деятельности станций эти ретроспективные файлы являются
неоценимым ресурсом.
В то же самое время, если бы нам требовалась информация о количестве доступ-
ных для аренды велосипедов на какой-либо конкретной станции СШ В1ке имен-
но в данный момент, эти файловые данные были бы для нас просто бесполезны-
ми. Но эту информацию можно получить посредством канала данных формата
)$оп этой системы (Ьйр8://§ЬГ8.с1йЫкепус.сот/§ЬГ8/еп/8€айоп_81:а€и8.]8Оп). Объ-
ем текста, который отображается в окне браузера при открытии этой ссылки,
не особенно удобочитаем2 для пользователя, но он содержит информацию, от-
сутствующую в файловых данных, например количество велосипедов и парко-
вочных мест, доступных в данный момент времени, а также количество неис-
правных велосипедов и т. п. Для того чтобы определить физическое местона-
хождение станции с определенным идентификационным номером 51аН.оп_тД
нам нужно дополнить эти данные информацией или с другого канала данных
системы Сй1 В1ке, или файловыми данными, которые мы уже использовали.
Иными словами, во многих случаях имеет смысл, чтобы источник данных со-
держал данные как в файловом, так и в канальном формате. Какой из этих
форматов больше подойдет, будет зависеть (как и всегда) от стоящего перед
вами конкретного вопроса по выпасу данных.
На протяжении этой главы мы будем прорабатывать практические примеры по вы-
пасу данных из нескольких источников с наиболее распространенными файловыми
и канальными форматами данных, ставя перед собой целью облегчение их рас-
смотрения, очистки, дополнения и анализа. Мы также рассмотрим некоторые фор-
маты данных, более трудные для первичной обработки, с которыми вам, возможно,
понадобится работать в силу необходимости. В ходе этих процессов мы будем в
значительной степени полагаться на подборку библиотек, разработанных сообще-
ством РуЙюп для этих целей, включая специализированные библиотеки и програм-
мы для обработки обширного диапазона документов — от электронных таблиц до
изображений. По завершении этой работы вы приобретете навыки и разработаете
примеры сценариев, необходимые для решения огромной разновидности проектов
по выпасу данных, тем самым прокладывая себе дорогу к проектам следующего
уровня.
Структурированные и неструктурированные данные
Прежде чем приступать к написанию кода и выпасу данных, я хочу вкратце обсу-
дить один важный атрибут источников данных, который может повлиять на на-
правление (и скорость) ваших проектов по выпасу данных, — наличие или отсутст-
вие структуризации.
Цель большинства проектов по выпасу данных — генерировать знания и, часто,
использовать данные для улучшения принимаемых решений. Но на принятие ре-
шений обычно отводится определенный объем времени, поэтому наша работа с
2 Хотя вскоре мы научимся, как его обрабатывать.
данными также требует сбалансированности разных аспектов. Например, вместо
того, чтобы продолжать обработку или поиск набора данных, чтобы получить ”иде-
альный” набор данных, можно объединить два или три менее идеальных набора,
чтобы создать действительную аппроксимацию исследуемого явления. При усло-
вии, что мы можем получить требуемую эффективность, не жертвуя слишком мно-
го времени в отношении качества данных, такое улучшение нашей работы с дан-
ными может также повысить ее результативность.
Один из самых простых способов повысить эффективность выпаса данных — най-
ти такие форматы данных, к которым язык РуЙюп и другие вычислительные инст-
рументы могут легко обращаться и работать с ними. Хотя достижения в области
компьютерного зрения, обработки текстов на естественных языках и машинном
обучении облегчили для компьютеров задачу анализа данных, независимо от их
базовой структуры или формата, факт остается фактом — структурированные,
машиночитаемые данные остаются наиболее простым типом данных для обработ-
ки. На самом деле, тогда как в качестве источника данных может быть использован
обширный ряд явлений или объектов — от интервью до изображений и книжных
текстов — для многих из нас ”данные” часто представляются как структурирован-
ные, числовые данные больше, чем что-либо другое.
Что мы имеем в виду под "машиночитаемые"?
Хотите верьте, хотите нет, благодаря Закону об основах доказательной политики
(ййр8://§оутй).§оу/соп€еп€/рк§/РЕА\У-115риЫ435/Ы:т1/РЕА\У-115риЫ435.Ы:т),
который вступил в силу в начале 2019 г., в США существует юридическое оп-
ределение машиночитаемых данных. Согласно этому закону, машиночитае-
мыми данными являются следующие:
данные в формате, который может легко обрабатываться компьютером
без вмешательства человека, в то же самое время обеспечивая сохран-
ность семантического значения.
Полезно, не так ли? И хотя это определение может выглядеть несколько фор-
мальным, по мере продолжения работы с данными (особенно, если вы запра-
шиваете их у правительственных учреждений) вы начнете осознавать его важ-
ность. В частности, когда речь заходит о работе с неструктурированными дан-
ными (например, текстовыми документами), существует большая разница
между данными, созданными программой текстового редактора, и данными,
полученными в результате сканирования бумажного документа. Тогда как и те
и другие являются машиночитаемыми в том смысле, что их можно просматри-
вать на компьютере, желаем вам удачи в ваших попытках получить программ-
ный доступ к тексту отсканированного документа3. Поэтому ясное (юридиче-
ское!) определение значения "машиночитаемые” на самом деле очень даже по-
лезно.
3 Как упростить эту задачу, мы рассмотрим в разд. "Выпас РРР-данных, используя Рудит” далее в
этой главе.
Структурированные данные — это любой тип данных, организованных и класси-
фицированных каким-либо образом в какой-либо вариант записей и полей. В слу-
чае данных файловых форматов это обычно строки и столбцы, а для канальных
форматов это по сути списки объектов или словари.
В противоположность им, неструктурированные данные могут состоять из смеси
данных разных типов, совмещая текст, числа и даже фотографии или иные изобра-
жения. Например, содержимое журнала или романа или звуковой сигнал песни
обычно будут считаться неструктурированными данными.
Если вы сейчас думаете: ”Погодите, но ведь романы имеют структуру! Как насчет
главы?” — тогда примите мои поздравления: вы уже думаете как специалист по
выпасу данных. Мы можем создавать данные почти обо всем, собирая информацию
об окружающем нас мире и структурируя ее4.
Ведь на самом деле все данные так и создаются: наборы данных, которые мы полу-
чаем посредством файлов и каналов, являются продуктом чьих-то решений о том,
как собирать и организовывать информацию. Иными словами, всегда есть несколь-
ко способов организации информации, но выбранная структура влияет на то, как
эту информацию анализировать. Вот почему предполагать, что данные могут ка-
ким-то образом быть ''объективными”, будет немного неверным, ведь, в конце кон-
цов, данные являются продуктом (который по своей природе субъективен) челове-
ческих решений.
Например, попробуйте провести мини-эксперимент: подумайте, как вы организо-
вываете коллекцию каких-либо предметов (например, музыкальные записи, книги,
игры или сорта чая). Потом спросите одного из своих друзей, как он организовы-
вают свою коллекцию таких же предметов. Делаете ли вы это оба одинаковым спо-
собом или нет? Если нет, то чей способ "лучше”? Теперь спросите об этом еще ко-
го-либо. И даже если системы организации, например, музыкальных записей, ис-
пользуемые вами и вашими друзьями, могут иметь некоторые похожие аспекты, я
бы очень удивилась, если бы любые две из них были в точности одинаковыми.
Иными словами, каждый из вас упорядочивает свои музыкальные записи слегка по-
другому, и при этом полностью убежден, что его система "наилучшая”. И так оно и
есть. Для каждого его система является наилучшей именно для него.
Если это напоминает вам наше обсуждение в разд. "Как? И для кого?" в главе 3, то
это не случайно, поскольку результатом ваших усилий на получение ответа на ваш
вопрос для выпаса данных и сам ответ в итоге будет, как вы могли догадаться, дру-
гой набор данных, отражающий ваши интересы и приоритеты. Этот набор также
будет организован и структурирован, в результате чего работать с ним определен-
ными способами будет легче, чем другими. Но основная идея здесь состоит не в
4 В теории вычислительных машин термины данные и информация применяются точно противопо-
ложным образом: данные — это сырые факты, собранные об окружающем нас мире, а информация
означает значащий конечный продукт процесса их организации и структурирования. Но в последние
годы, когда разговоры о "больших данных” доминируют во многих областях, моя интерпретация
данных стала более широко употребляемой, поэтому для ясности я буду продолжать использовать ее в
этой книге.
том, что любой из этих способов может быть правильным или неправильным, а в
том, что для каждого из них нужно будет идти на какие-либо компромиссы. Иден-
тификация и подтверждение этих компромиссов является ключевой частью честно-
го и ответственного использования данных.
Итак, ключевой компромисс при использовании структурированных данных со-
стоит в необходимости полагаться на решения и приоритеты кого-то другого в ор-
ганизации базисной информации. Очевидно, что это может быть хорошим, или да-
же замечательным, обстоятельством, если структуризация этих данных была вы-
полнена посредством открытого и прозрачного процесса с вовлечением хорошо
квалифицированных экспертов. Вдумчиво применяемые структуры данных могут
дать нам представление о предмете, о котором мы в противном случае могли бы
знать очень мало или вообще ничего. Но с другой стороны, существует и вероят-
ность того, что мы унаследуем чужие предвзятые и плохо продуманные решения.
Конечно же, неструктурированные данные дают нам полную свободу организовы-
вать информацию в структуры данных, которые наилучшим образом отвечают на-
шим потребностям. Неудивительно, что это вынуждает нас взять на себя ответст-
венность за использование надежного процесса обеспечения качества данных, ко-
торый может быть как сложным, так и трудоемким.
Как можно знать наперед, является ли определенный набор данных структуриро-
ванным или неструктурированным? В этом вопросе нам определенно могут помочь
расширения файлов. Канальные форматы данных имеют хоть какую-нибудь струк-
туру, даже если они содержат фрагменты произвольного текста типа сообщений
социальных сетей. Поэтому данные в файлах с расширениями .)8оп, .хш1, .Г88 или
.а!ош имеют, как минимум, структуру типа "записи и поля”. Мы это рассмотрим в
разд. "Канальные данные — интерактивные обновления через Интернет" далее в
этой главе. Данные в файлах с расширениями .С8У, .18У, .Ш, .х18(х) или .об8, как мы
увидим в следующем разделе, обычно имеют табличную структуру, со строками и
столбцами. А по-настоящему неструктурированные данные, скорее всего, будут в
документах с расширением .бос(х) или .рбГ.
Интеллектуальный поиск конкретных типов данных
Поскольку при поиске информации большинство из нас первым делом обра-
щается к поисковым системам Интернета, то разве не было бы здорово исполь-
зовать мощь этих поисковых систем для более эффективного поиска требуе-
мых нам источников (и форматов) данных?
Обычно поиск в Сети выполняется по ключевым словам, фразам или предло-
жениям, но лишь небольшая корректировка этого подхода может существенно
изменить поиск полезных форматов данных. В частности, добавляя к тексту
поиска один или несколько операторов поиска, можно дать поисковой системе
указание возвращать результаты только с конкретных веб-сайтов или доменов
(например, .§оу) или даже результаты только с конкретными расширениями
файла.
Например:
Поисковые термины или ключевые слова — Н1е1уре: .сзу
Расширение файла .С8У можно заменить любым другим требуемым вам расши-
рением, включая .18У, .Ш, .рбГ или даже .)р§, .шрЗ и т. и. В результате будут
возвращены файлы с указанным расширением, которые также содержат ука-
занные поисковые термины или ключевые слова.
Поисковые термины или ключевые слова — м1е: огеШу.сот
Этот поиск возвратит результаты, содержащие указанные поисковые термины
или ключевые слова, только с веб-сайта огеШу.сот. Это особенно удобно при
поиске данных, созданных или опубликованных определенной организацией.
Обратите внимание на то, что этот подход можно использовать для поиска в
рамках всего домена высшего уровня, например .§оу или .со.ик.
Поисковые термины или ключевые слова — тиг1:1Шр$
Возвращает результаты только с защищенных веб-сайтов.
Поисковые термины или ключевые слова — прочие термины или ключевые
слова
Хотя это не поможет вам найти конкретные файлы, использование дефиса (-)
может стать отличным способом сфокусировать поиск информации, когда вы
хотите специально исключить результаты, которые ассоциировались бы с ос-
новным поисковым термином. Чтобы увидеть этот тип поиска в действии,
сравните результаты поиска по ключевым словам доЬз с результатами
поиска по ключевым словам доЬз - арр!е.
Конечно же, использование поисковой системы — это всего лишь один из
многих возможных способов поиска данных. С дополнительными идеями по
вопросу поиска требуемых данных можно ознакомиться в приложении В.
Теперь, когда у нас есть солидные знания о разных типах источников данных, с ко-
торыми нам, скорее всего, придется работать, а также некоторое понятие того, как
их искать, можно приступать к выпасу данных.
Работа со структурированными данными
С самого начала цифровых вычислений таблица была одним из наиболее распро-
страненных способов структуризации данных. Даже в настоящее время многие из
наиболее распространенных и удобных для обработки форматов данных представ-
ляют собой не более чем таблицы или наборы таблиц. Более того, мы уже работали
с одним из очень распространенных табличных форматов данных в главе 2. а имен-
но, с форматом .С8У (сошша-8ерага1ес1 уа1ие8 — значения, разделенные запятыми).
Файловые табличные форматы данных
Все табличные форматы данных, с которыми нам приходится иметь дело, являются
образцами так называемых файлов с разделенными данными, в которых каждая за-
пись занимает отдельную строку, а поля или столбцы значений данных в строках
разделены определенным текстовым символом. Часто расширение файла набора
данных будет содержать признак текстового символа, используемого в качестве
разделителя. Например, расширение файла .С8У означает сотта-зерага^ед, уа1иез,
поскольку в этих файлах в качестве разделителя используется запятая. А расшире-
ние файла .18У означает 1аЬ-зерага1е(1 ха1иез. поскольку в них значения в строке раз-
деляются символами табуляции. Далее приводится список расширений файлов,
обычно содержащих разделенные данные, и их краткое описание:
.СЗУ
Как уже упоминалось ранее, .С8У означает сошша-8ерага1ес1 уа1ие8, т. е. значения,
разделенные запятыми. Эти файлы являются одними из наиболее распростра-
ненных среди файлов данных с табличной структурой, с которыми вам придется
работать. Почти все программные системы, обрабатывающие табличные данные
(например, правительственные или корпоративные системы данных, программы
электронных таблиц и даже специализированные программы по обработке ком-
мерческих данных), могут выдавать данные в формате .С8У, и, как мы видели в
главе 2. для работы с этим типом данных, используя язык РуЙюп, существуют
удобные библиотеки.
ЛЗУ
Опять же, как упоминалось ранее, расширение .18У означает 1аЬ-зерага1ес1 ха1иез.
т. е. значения, разделенные символами табуляции. Файлы с таким форматом дан-
ных используются уже достаточно давно, но данное описательное расширение на-
чало широко употребляться сравнительно недавно. Поставщики данных часто не
объясняют, почему они используют один разделитель, а не другой, файлы значе-
ний, разделенных символом табуляции, широко используются для наборов дан-
ных, чьи значения должны содержать запятые, например почтовые адреса.
Лх1
Структурированные файлы данных с этим расширением — часто просто замас-
кированные файлы .18У. Расширение .Ш часто использовалось в старых системах
данных для обозначения файлов, содержащих значения, разделенные символом
табуляции. Как мы увидим в проработанных примерах, следующих далее, преж-
де чем приступать к разработке кода, имеет смысл открыть и просмотреть в ба-
зовом текстовом редакторе (или редакторе кода наподобие редактора А1ош) лю-
бой файл, данные которого вы намереваетесь обрабатывать. Самостоятельный
просмотр содержимого файла — это единственный надежный способ узнать, ка-
кие в нем используются разделители.
.х1з(х)
Это расширение присваивается файлам электронных таблиц, создаваемых про-
граммой М1СГО8ОЙ Ехсе1. Поскольку кроме формул, форматирования и прочих
функциональностей эти файлы могут содержать множественные "листы”, для
хранения одного и того же объема данных им требуется больше памяти, чем для
файлов других типов. У них также есть и другие ограничения (например, они
могут содержать только определенное количество строк), что может привести к
неприятным последствиям для целостности вашего набора данных.
.0(18
Означает ореп-(1оситеп1 8ргеа(Нкее1 /Ие8, или файлы электронных таблиц фор-
мата ''открытый документ”. Это расширение установлено по умолчанию для
файлов электронных таблиц, создаваемых рядом пакетов офисных приложений
с отрытым исходным кодом, например ЫЪгеОШсе (йир8://11ЬгеоГйсе.ог§/) и
ОрепОШсе (Ъйр8://орепоШсе.ог§/(1оууп1оа(1/т(1ех.й€т1). Данные файлы имеют
возможности и ограничения наподобие файлов .х1§(х).
Прежде чем углубляться в рассмотрение работы с каждым из этих типов файлов на
языке РуЙюп, стоит уделить немного времени на обсуждение того, когда стоит от-
дать предпочтение работе с табличными данными и где их искать в таком случае.
Когда лучше использовать табличные данные
В большинстве случаев у нас нет большого выбора касательно формата наших ис-
ходных данных. Более того, необходимость вообще заниматься выпасом данных в
большой степени вызвана несоответствием имеющихся у нас данных нашим требо-
ваниям. Однако все равно неплохо бы знать, с каким форматом данных вы предпо-
читаете работать, чтобы на этом основывать первоначальный поиск данных.
В разд. "Структурированные и неструктурированные данные" ранее в этой главе
мы обсудили преимущества и ограничения структурированных данных, и теперь
мы знаем, что табличный формат данных является одним из самых старых и наибо-
лее распространенных форматов машиночитаемых данных. Это означает что на
протяжении многих лет исходные данные разных форматов были втиснуты в таб-
лицы, хотя при этом они не обязательно могут хорошо подходить для представле-
ния в табличной форме. Тем не менее этот формат может быть полезным для полу-
чения ответов на вопросы о трендах и закономерностях в течение времени. Напри-
мер, в упражнении с использованием данных системы СШ В1ке в главе 2 мы
исследовали количество клиентов типа Си81ошег8 и БиЪзспЪегз, которые арендова-
ли велосипеды в этой системе на протяжении одного месяца. Если бы мы хотели,
то мы могли бы выполнить те же самые вычисления для каждого доступного меся-
ца аренды, чтобы получить знания о каких-либо закономерностях в соотношении
этих двух величин в течение всего этого времени.
Конечно же, табличный формат данных, как правило, не лучший вариант для дан-
ных реального времени или данных, в которых не все возможные измерения со-
держат одинаковые возможные значения. Для этих типов данных лучше подходят
канальные форматы данных, которые мы рассмотрим в разд. "Канальные данные —
интерактивные обновления через Интернет" далее в этой главе.
Где искать табличные данные
Поскольку большая часть машиночитаемых данных находится в табличном форма-
те, этот формат один из самых легких для поиска. Электронные таблицы широко
используются практически во всех отраслях, и большое количество информацион-
ных систем правительственных и коммерческих организаций полагаются на про-
граммы, которые организовывают данные таким способом. Почти при каждом за-
просе данных у эксперта или организации эти данные будут, с большой долей ве-
роятности, предоставлены в табличном формате. Это также справедливо и для
большинства онлайновых порталов открытых данных и веб-сайтов обмена данны-
ми. Как рассматривалось во врезке "Интеллектуальный поиск конкретных типов
данных" ранее в главе, поиск табличных данных можно осуществлять и при помо-
щи поисковых систем, если вы знаете, как это делать.
Выпас табличных данных
посредством языка РуШоп
Чтобы проиллюстрировать, насколько просто работать с табличными данными в
РуЙюп, мы рассмотрим несколько примеров чтения данных из файлов всех типов,
упомянутых ранее в этом разделе, плюс, для полноты картины, из нескольких дру-
гих типов. В последующих главах мы рассмотрим более обширную обработку,
включающую очистку, преобразование и оценку качества данных, но на данном
этапе мы ограничимся простыми операциями доступа к данным в файлах каждого
типа и взаимодействия с ними посредством языка РуЙюп.
Чтение данных из файлов С87
В случае если вы подзабыли материал главы 2. освежим вашу память, как считы-
вать данные из файла .сзу, используя образец данных из набора данных системы
СШ В1ке. Соответствующий код приводится в листинге 4.1. Как всегда, в коммен-
тариях я даю описание работы программы, а также ссылки на файлы исходного ко-
да. Поскольку мы уже работали с файлами этого типа раньше, сейчас только рас-
смотрим, как отобразить первые несколько строк данных, чтобы посмотреть, что
они из себя представляют.
Листинг 4.1. Сценарий с§у_раг§тд.ру
# Простой пример чтения данных из файла .сзу посредством Ру1±оп,
# используя библиотеку С37.
# Исходные данные были взяты из данных системы СтМВтке:
# ЬГГрз: //бги/е. доодТе. соп/Г11е/с1/17Ь4611\1Ь$дГ_акР1л17ддМХрГддЬ91РхСи_/
# которые находятся по следующей ссылке
# Ы^рз: //зЗ. апагопамз. соп/1:г1рс1а1:а/1пс1ех. Н±гп1
# Импортируем библиотеку сз^О
1прог1: сз\/
# Открываем файл 202009С11:1ЫкеТг1р6а1:аЕхатр1е.С57 в режиме чтения (г)
# Этот файл должен находиться в той же самой папке, что и файл автономного сценария
# или блокнота
зоигсе_РИе = ореп( "202009С1Л1ЫкеТ г1рба1:аЕхатр1е. сзу" ," г" )О
# Передаем наш исходный файл зоигсе_РИе как аргумент (ингредиент) методу (рецепту)
# 01с1:Реас1ег библиотеки С57.
# Сохраняем результат в переменной с1Л1Ыке_геас1ег
с1Л1Ыке_геас1ег = сз7.01с1:Реа6ег(5оигсе_'Г11е)
# Метод 01с1:Реас1ег добавил в наши данные некоторую полезную информацию.
# Например, свойство ^1е16папез позволяет нам обращаться ко всем значениям в первой
# строке (или заголовке)
рг1п1:(с11:1Ыке_геа6ег. Г1е16патез)0
# Отображаем первые пять строк данных
1"ог I 1п гапде(0,5):О
рг1п± (пех1:(с11:1Ыке_геа6ег))
О Это наша библиотека — рабочая лошадка для работы с табличными данными.
О Встроенная функция ореп() принимает в качестве параметров имя файла и режим
работы с ним. В данном примере целевой файл (202009СИ1Ь1кеТг1рба'1:аЕхатр'1е.с87)
должен находиться в той же самой папке, что и файл автономного сценария или
блокнота. Значение параметра режима может быть г (геас! — чтение) или и (мчйе —
запись).
О Отображая значения сШЬ1ке_геас1ег.1Че16пате5, мы можем видеть, что точным
именем столбца Изег Туре (тип пользователя) является изег1:уре.
О Функция гапде() предоставляет нам возможность исполнять определенный
фрагмент кода заданное количество раз, начиная со значения первого аргумента и
заканчивая на единицу меньшим значением второго аргумента. Например, код, вы-
деленный отступом в этой строке, будет исполнен пять раз, со значением счетчика 1,
инкрементирующимся от 0 до 4, т. е. принимающим последовательные значения О,
1, 2, 3, 4. Дополнительная информация по функции гапде() приводится во врезке
"Добавляем итераторы: функция гап^е() " далее в этом разделе.
Результаты исполнения этого сценария должны выглядеть наподобие показанных в
листинге 4.2.
Листинг 4.2. Результат исполнения сценария с§у_раг§!пд.ру
[ Чгтрбига^топ1, 'з^аг^тпе', '81агЮа1е', 'з!орМте', 'з!аг1: 51аМоп 16',
'51аг! з!аМоп папе', '51аг1 з!а1гоп ТаШибе', 'з!аг1: з^аИоп ТопдтЛибе', 'епб
51а1гоп 16', 'епб з1а1!оп папе', 1 епб з1а1!оп ХаШибе1, 'епб з^аМоп
ТопдтЛибе1, 'ЫкетсГ, 'изег^уре', 'Ыг^Ь уеаг', 'депбег']
{Чгтрбига^топ1: '4225', ' зГагШпе': '2020-09-01 00:00:01.0430', '81агЮа1е':
'2020-09-01', '51ор1гте': '2020-09-01 01:10:26.6350', '51аг! з1а11оп 16':
' 3508', 'з1аг1 з!а1гоп папе': ' 81 МГсЬоТаз А7е & МапНаИап А7е', 1 з!аг! з!а1гоп
1а111ис1е': '40.809725', 'з1аг1 З1а1!оп 1опд11ис1е': '-73.953149', 'епс! 51а1!оп
1сГ : '116', ' епс1 51а1гоп папе': ' 1л1 17 81 & 8 А7е', 'епс! 51аИоп 1а111ис1е': '40.
74177603', ' епс! 51а11оп 1опд11ис1е': ' - 74.00149746', ' Ыке1с1': ' 44317',
'и5ег1уре': 'Си51отег', 'ЫгГЬуеаг': '1979', ’депс!ег': '1'}
{' 1гтрс!ига1топ': '1193', '81аг111те': '2020-09-01 00:00:12.2020', '81агЮа1е':
'2020-09-01', 'зГорИте': '2020-09-01 00:20:05.5470', 'з1аг1 51аПоп ГсГ :
'3081', '51аг1 51а11оп пате': 'СгаЬат А7е & Сгапс! 81', 'з1аг! з!а11оп
1а111ис1е': '40.711863', 'з1аг! з!а11оп 1опд11ис1е': '-73.944024', 'епс! з!а11оп
ГсГ : '3048', 'епс! з!а11оп пате': 'Ри1пат А7е & 1\1оз1гапс1 А7е', 'епс! з!а11оп
1а111ис1е': '40.68402', 'епс! з!а11оп 1опд11ис1е': '-73.94977', 'Ыкетс!': '26396',
'изег1уре': 'Сиз1отег', 'Ыг1Ьуеаг': '1969', 'депс!ег': '0'} *
Добавляем итераторы: функция гапде()
В отличие от многих других языков программирования, цикл Гог языка РуШоп
по умолчанию обрабатывает все значения в списке или наборе данных. Это
может быть удобным для быстрой обработки всего набора данных, но не го-
дится, если нужно обработать только часть его записей.
Эта проблема легко решается посредством использования переменной, назы-
вающейся итератором. Подобно любой переменной, итератору можно при-
своить любое имя, хотя традиционно используется буква I (первая буква сло-
ва Иега1ог). Как видно в листинге 4.1, одно из типичных использований итера-
торов РуЙюп — в функции гапде() (Ъйр8://(1ос8.ру€йоп.ог§/3/Шог1а1/
соп€гоШоуу.й€т1#€йе-гап§е-Гипсйоп). Это еще один пример функции управле-
ния потоком команд, которая, подобно циклам Гог и условным операторам 1Г,
обладает специальными встроенными свойствами.
Например, функция гапде() содержит переменную итератора, которая позволя-
ет немного модифицировать цикл Гог, позволяя ему обрабатывать не все стро-
ки, а только количество, указанное в итераторе. Иными словами, вместо этой
стандартной формы цикла Гог:
Гог элемент 1п полном_списке_элементов:
мы можем создать форму цикла Гог, который начинает обработку с определен-
ного элемента в списке и обрабатывает заданное количество элементов
Гог позиция_элемента 1п гапде (начальная_позиция,
>количество_элементов _для_обработки):
В листинге 4.1 вы решили отобразить первые пять строк исходного файла дан-
ных:
Гог I 1п гапде(0,5):
ргГп! (пех1(с111Ыке_геас!ег))
Следует обратить внимание на то, что диапазон итерации включает первое
значение, указанное в параметрах, но исключает второе. Это означает, что для
отображения пяти строк данных начальное значение итерации должно быть О,
поскольку индексация, т. е. нумерация элементов, списков в РуШоп начинается
с 0, а не с 1.
Чтение данных из файлов Т8У и ТХТ
Несмотря на свое название, благодаря опции указания разделителя в функции
01с1:Реабег(), библиотека сзу РуШоп является, по сути, универсальным средством
для работы с табличными данными. По умолчанию функция 01с1:Реабег() предпола-
гает, что в качестве разделителя используется символ запятой. Но этот символ
очень легко изменить, просто указав другой при вызове этой функции. Например, в
листинге 4.3 в качестве разделителя указывается символ табуляции, но с той же
легкостью можно было бы указать и любой другой символ разделителя.
Листинг 4.3. Сценарий <§у_раг§!пд.ру
# Простой пример чтения данных из файла .1:87 посредством РуШоп,
# используя библиотеку С87. Исходные данные были загружены как файл Лзу
# из электронной таблицы СоодТе Джеда Шугермана (Зеб ЗЬидегпап) по политикам США
# с профессиональным прошлым в области юриспруденции:©
# Ы:1:р8://бос8.доод1е.сот/5ргеаб8Ьее'1:8/б/1Е62-з21л1ЬгКт11:_41С36оуР658Та6МЬ25НС0Ва27У\/гА
# Импортируем библиотеку С87
1прог1: С87
# Открываем файл $ЬидегпапРго8есиШгРо111:1с1ап5-$иргетеСоиг1:Зи51:1се8.1:87
# в режиме чтения ("г").
# Этот файл должен находиться в той же самой папке, что и файл автономного сценария
# или блокнота
157_5оигсе_РИе = ореп("$ЬидегпапРго8есиШгРо111:1с1ап8-$иргетеСоиг1:Зи51:1се8Л87","г")
# Передаем наш исходный файл Г87_8оигсе_ГПе как аргумент (ингредиент) методу
# (рецепту) 01с1:Реабег библиотеки С87.
# Сохраняем результат в переменной роШ1с1ап5_геабег
роШ1с1апз_геабег = с87.01с1:Реабег(1:87_5оигсе_Н1е, бе‘1гтг1ег='V')
# Метод ОтсГРеабег добавил в наши данные некоторую полезную информацию. Например,
# свойство ПеТбпапез позволяет нам обращаться ко всем значениям в первой строке
# (или заголовке)/
рг!п1:(роШ1с1ап5_геабег. ГтеХбпапез)
# Отображаем только первую строку данных, используя функцию пех1:()
рг!п1: (пех1:(рои1:1с1ап5_геабег))
О Этот набор данных доступен на веб-сайте информационного бюллетеня Ба1а к
Р1ига1 (Данные — множественные) Джереми Сингер-Вайна (Зегешу 8т§ег-Уте'8,
@]8уте, Ййр8://(1а€ач8-р1ига1.сот).
Результаты исполнения этого сценария должны выглядеть наподобие показанным в
листинге 4.4.
Листинг 4.4. Результат исполнения сценария (8У_раг81пд.ру
[", 'ЗизГГсе', 'Тепл ЗГагГ/ЕпсГ, 1 РагГу', 'ЗГаГе', 'РгезАррГ', ' О±Нег ОГГГсез
Не1сГ , 'Ре1е7апГ РгозесиГогта! ВаскдгоипсГ ]
{": '40', 'ЗизГГсе': ' НИИап ЗГгопд', 'Тепл ЗГагГ/ЕпсГ: '1870-1880', 'РагГу':
'0/Р', 'ЗГаГе': 'РА', ' Ргез Арр±': 'СгапГ', 'О±Нег ОГГГсез Не1сГ : '08 Ноизе,
8ирг СоигГ оГ РА, е!есГ сот Гог е!ес оГ 1876', 'Ре1е7апГ РгозесиГогта!
ВаскдгоигИ': 'Тамуег'}
Что такое расширение файла?
Удобно, конечно, когда компьютеры решают ряд задач за нас автоматически и
даже ''автоматически”, но будем надеяться, что процесс изучения выпаса дан-
ных и программирования на языке РуЙюп поможет прояснить одно важное об-
стоятельство: мы обладаем намного большим влиянием на поведение наших
компьютеров, чем это может казаться с первого взгляда.
Идеальным примером этому будут расширения имен файлов. В этой главе я
постоянно выделяю моменты, как расширение файла может предоставить нам
подсказку о формате содержащегося в файле набора данных, а также, возмож-
но, более эффективно помочь нам в поиске требуемых данных. Конечно же,
компьютеры тоже используют расширения файлов, чтобы делать предположе-
ние о содержимом определенных файлов, обычно полагаясь на них, чтобы вы-
брать правильную программу для открытия файла. Вот поэтому при попытке
изменить расширение файла, намеренно или непреднамеренно, отображается
предупреждающее сообщение примерно следующего типа: "Если вы измените
расширение имени файла, файл может стать непригодным для использования.
Вы действительно хотите сделать это?" Хотя, несомненно, это сообщение ото-
бражается в интересах пользователя, может создаться впечатление, что изме-
нение расширения имени файла может действительно сделать файл непригод-
ным для использования.
В действительности же это совсем не так. Изменение расширения имени фай-
ла, например, с на .1x1 или наоборот, никоим образом не влияет на его со-
держимое. Единственное, к чему это приведет, — изменится представление
компьютера, что делать с этим файлом, то есть какую программу использовать
для его открытия.
К счастью, средства РуЙюп наподобие тех, которые мы используем в этой кни-
ге, не делают подобных предположений. При работе с табличными данными,
при условии, что указанный символ разделителя совпадает с символом разде-
лителя, используемого в файле, расширение имени файла не имеет абсолютно
никакого значения. Собственно говоря, мы создали файл .1x1, который мы ис-
пользуем далее в листинге 4.5, просто скопировав файл .18У из листинга 4.3 и
изменив его расширение.
Хотя в настоящее время расширение имени файла .18У стало сравнительно распро-
страненным, многие файлы, содержащие данные, разделенные символом табуля-
ции, но созданные более старыми системами баз данных, могут иметь расширение
файла .1x1. К счастью, как описано во врезке "Что такое расширение файла?", при
условии указания правильного символа разделителя это никоим образом не влияет
на обработку таких файлов, как демонстрируется в листинге 4.5.
Листинг 4.5. Сценарий (хСрагзтд.ру
# Простой пример чтения данных из файла .1:87 посредством Ру1Ьоп,
# используя библиотеку С87. Исходные данные были загружены как файл .1:87
# из электронной таблицы СоодТе Джеда Шугермана (Зеб ЗЬидегпап) по политикам США
# с профессиональным прошлым в области юриспруденции
# Ь11р8://бос8.доод1е.соп/8ргеаб8Ьее18/б/1Е62^2ЫЬгКт11_41С36оу()658Та6МЬ25НС0Ва27У\/гА
# Исходный файл .187 был скопирован и переименован с расширением .1x1:
# Импортируем библиотеку С87
1прог1 С87
# Открываем файл $ЬидегпапРго8еси1огРо1111с1ап8-$иргетеСоиг13и811се8.1х1
# в режиме чтения ("г").
# Этот файл должен находиться в той же самой папке, что и файл автономного сценария
# или блокнота
1х1_8ои гсе_Г11е = ореп(" $ЬидегпапРго8еси1огРо1111с1ап8- ЗиргепеСоиг! ЗизИсез. 1x1"," г")
# Передаем наш исходный файл 1х1_8оигсе_РПе как аргумент (ингредиент) методу
# (рецепту) 01с1Реас1ег библиотеки С87.
# Сохраняем результат в переменной ро1111с1ап8_геас1ег
# Добавляем параметр бе11те1ег, присваивая ему значение символа табуляции, "\1"
ро1И1с1ап8_геас1ег = с87.01с1Реабег(1х1_8оигсе_Г1Ле, с1е_Цт11ег='\±' )О
# Метод 01с1Реас1ег добавил в наши данные некоторую полезную информацию. Например,
# свойство НеТбпапез позволяет нам обращаться ко всем значениям в первой строке
# (или заголовке)/
рг1п1(роИ11с1ап8_геас1ег. НеТбпапез)
# Отображаем только первую строку данных, используя функцию пех!()
рг1п^ (пех^ (роШ1С1апз_геас1ег) )
О Как обсуждалось во врезке "Пробелы — зло!" главы 7, при использовании в коде
символов пробелов и других непечатаемых символов они должны предшествовать-
ся управляющими символами. В данном случае мы используем управляющие сим-
волы \1: для обозначения непечатаемого символа табуляции. Кроме символа табу-
ляции, нам часто придется использовать другие управляющие символы, такие как
\п для перехода на новую строку или \г для возврата каретки.
Если ваш код не содержит никаких ошибок, то результат его исполнения должен
выглядеть точно таким же, как и результат исполнения сценария в листинге 4.2,
показанный в листинге 4.3.
Здесь вы можете задаваться вопросом: как я могу знать, какой разделяющий символ
используется в обрабатываемом файле? Символ можно определить программным
способом, но самым простым будет просто посмотреть его самому. Всякий раз,
приступая (или планируя) к работе с новым набором данных, начните с открытия
его файла в наиболее простом текстовом редакторе, доступном на вашем устройст-
ве, или в любом редакторе кода. Использование наиболее простого текстового ре-
дактора объясняется тем, что в случае особенно большого файла это позволит ва-
шему устройству выделить максимальный объем памяти и вычислительных мощ-
ностей на чтение данных. Таким образом понижается вероятность зависания
программы редактора или сбоя устройства. Закрытие других исполняющихся про-
грамм и ненужных вкладок браузера также может быть полезным в этом отношении.
Далее в книге мы рассмотрим некоторые способы для просмотра небольших фраг-
ментов действительно больших файлов, но сейчас настало время попрактиковать
навыки, необходимые для оценки качества данных, которые требуют просмотра
данных и принятия суждений о них. Таким образом, хотя существуют способы ав-
томатизировать задачу определения используемого в данных символа разделителя,
будет быстрее и понятнее посмотреть его самому в текстовом редакторе. Кроме
этого, такой подход поможет вам одновременно ознакомиться и с другими важны-
ми аспектами ваших данных.
И все на свете просто текст
Одна из причин, почему так полезно просматривать файлы типа .С8У, .18У и
многих других форматов данных в текстовом редакторе, состоит в том, что
большинство форматов данных, с которыми мы будем работать, на наиболее
базовом уровне являются просто текстом. Абсолютно так же, как и обычный
текст на русском языке организован в предложения и абзацы при помощи
стандартного использования точек, пробелов, заглавных букв и новых строк,
форматы данных организовываются посредством знаков препинания, и на
практическом уровне один формат данных отличается от другого просто ис-
пользуемыми для его организации знаками препинания. Как мы уже видели,
базовые табличные данные разделяются на поля, или столбцы, при помощи
разделителей, и на записи, или строки, при помощи символа новой строки.
Канальные данные, которые мы рассмотрим в следующем разделе, отличаются
несколько большей гибкостью (и сложностью), но в итоге следуют своим соб-
ственным, сравнительно простым, правилам пунктуации и структуризации.
Конечно же, существует большое количество нетекстовых форматов данных,
которые обычно создаются специализированными, проприетарными програм-
мами, предназначенными для выполнения первичной обработки (а также ана-
лиза и визуализации) данных, не прибегая к написанию большого объема кода.
Эти программы могут способствовать более быстрому или достижимому ре-
шению высокоспецифичных задач, но их недостаток состоит в том, что они
часто дороги, сложны в освоении и негибки. Как мы увидим в разд. "Х1ХХ,
008 и все остальные" далее в главе, извлечение данных из этих проприетар-
ных форматов также может стать трудной, или даже невозможной, задачей, а
полученные результаты — оказаться ненадежными. Поэтому, тогда как сред-
ства РуШоп все еще могут помочь нам с выпасом данных некоторых наиболее
распространенных проприетарных форматов, в некоторых случаях наилучшей
опцией будет использование альтернативного программного обеспечения (или
привлечение чьей-либо помощи).
Выпас реальных данных:
понимание безработицы
Для исследования одного из более сложных табличных форматов данных мы ис-
пользуем набор данных по безработице в Соединенных Штатах. Почему? Потому
что, так или иначе, безработица затрагивает большинство из нас, а в последние де-
сятилетия США испытывали одни из особенно высоких уровней безработицы. Ста-
тистика по безработице в США издается каждый месяц Бюро трудовой статистики
(БТС), но тогда как эти данные часто публикуются новостными источниками, они
обычно рассматриваются как некий тип абстрактного показателя, как себя ведет
"экономика”. Но что эти числа представляют на самом деле, редко подвергается
глубокому обсуждению.
Когда я только начала работать в газете 1Ха11 81г ее1 Лигпа! в 2007 г., моим первым
крупным проектом было создание интерактивной панели для исследования месяч-
ных данных экономических показателей, включая показатели по безработице. Од-
ним из более интересных моментов, которые я узнала в процессе работы над проек-
том, было то, что для каждого месяца вычисляется не один показатель уровня без-
работицы, а несколько (шесть, если быть точным). В новостях обычно сообщается
показатель уровня безработицы, называющийся 173, который БТС описывает сле-
дующим образом:
Общая безработица как процент от гражданских трудовых ресурсов (офици-
альный уровень безработицы).
На первый взгляд это выглядит как прямолинейное определение безработицы: ка-
кой процент из всех людей, которые по разумным ожиданиям могли бы работать,
не работает?
Но в действительности все немного посложнее. Что означает ”быть трудоустроен-
ным” или считаться частью ”трудовых ресурсов”? Взглянув на разные показатели
безработицы, можно получить более ясное представление о том, что не учитывает
показатель С13. Показатель уровня безработицы Нб определяется следующим образом:
Общее количество безработных, плюс все лица, минимально связанные с трудо-
выми ресурсами, плюс общее количество занятых неполный рабочий день по
экономическим причинам в процентах от гражданских трудовых ресурсов, плюс
все лица, минимально связанные с трудовыми ресурсами.
Это более пространственное определение становится более понятным, когда мы
прочитаем сопроводительное примечание5:
Примечание
Лицами, минимально связанными с трудовыми ресурсами, являются лица, кото-
рые в настоящее время не трудоустроены и не ищут трудоустройства, но выра-
жают желание и доступны для трудоустройства, а также искали трудоустройст-
во в последние 12 месяцев. Отчаявшиеся работники, представляющие подмно-
жество минимально связанных с трудовыми ресурсами, предоставили причину,
связанную с рынком трудовых ресурсов, по которой они в настоящее время не
ищут работу. Лицами, работающими неполный рабочий день по экономическим
причинам, являются лица, которые хотят и доступны для трудоустройства пол-
ный рабочий день, но были вынуждены согласиться на неполное трудоустройст-
во. Обновленные контрольные группы населения вводятся ежегодно с выпуском
данных за январь.
Иными словами, если вы хотите работать и искали работу в последнем году, но ак-
тивно не искали ее в последнее время, или если вы работаете неполный рабочий
день, но хотите полное трудоустройство, тогда вы официально не считаетесь безра-
ботным по определению ИЗ. Это означает, что показатель ИЗ не обязательно отра-
жает экономическую реальность американских граждан, работающих на несколь-
ких работах (которыми с большей вероятностью будут женщины с несколькими
детьми)6, и потенциально исполнителей разовых работ (количество которых по не-
давним подсчетам составляет вплоть до 30% всех трудовых ресурсов США)7. Не-
5 Из материалов Бюро трудовой статистики США (ЬН:р8://Ы8.§оу/пеуу8.ге1еа8е/етр8ЙЛ15.Ы:т).
6 См. статью МиШр1е АоЪко1с1ег$ от 21 декабря 2018 г., авторы 81ер11апе Аигау, ИауМ Ь Би11ег и
ОиШаише УапбепЬгоиске (ЬПр8://ге8еагсЬ.8Цои18Ге(1.ог§/риЫ1сайоп8/есопоп11с-8упор8е8/2018/12/21/
тиШр1е-]’оЬЬо1(1ег8).
7 См. статью КесоттепйаНопз оп 1трго\чпр Ра1а оп Сопйпреп! ап с! АИегпаНхе \Уогк Аггапретеп^,
ЬПр8://Ыо§8.Ы8.§оу/Ыо§Аа§/соп11п§еп1>ууогкег8; и статью Тке Уа1ие о/ Е1ех1Ые УУогк: ЕхйАепсе
/гот ЕЪег Рпчегз, автор М. Кейк Сйеп и другие, серия Мюг \Уогкт§ Рарег 8епе§ Мо. 23296,
ЬПр8 ://пЬег.ог§/8у81ет/й1е8/ууогк1п§_рарег8/\у23296/ \у23296.рсИ.
удивительно тогда, что месячный показатель 176 обычно на несколько процентов
выше, чем показатель 173.
Чтобы сравнить эти показатели во времени, эти данные можно загрузить из веб-
сайта Федеральной резервной системы города Сент-Луиса. Этот сайт также содер-
жит тысячи других наборов данных экономических показателей, доступных для
загрузки в ряде форматов, включая файлы .х1§(х) табличного типа, а также файлы
канального типа (которые мы используем в сценарии в листинге 4.16).
Данные для этих упражнений можно загрузить из базы данных экономических по-
казателей Федеральной резервной системы ЕКЕБ (Ъйр8://Гге(1.8йош8Ге(1.ог§/8ег1е8/
176КАТЕ). Эта страница также отображает график показателя уровня безработицы
176, начиная с момента, когда этот показатель был создан в начале 90-х годов про-
шлого столетия.
К этому графику также можно добавить показатель 173. Для этого справа вверху
над графиком нажмите кнопку ЕДй §гарЬ и в открывшейся справа панели выбери-
те вкладку АББ ЬШЕ. Далее введите в поле поиска слово ЕПЧКАТЕ и в результа-
тах поиска выберите 17петр1оутеп€ Ка€е. Наконец, нажмите кнопку АДД Да€а
8епе8. Закройте боковую панель, нажав значок X в ее правом верхнем углу, нажми-
те кнопку Воууп1оаД и выберите опцию Ехсе18 9. Будет загружен файл .х1§, с которым
мы будем работать в последнюю очередь, поскольку, хотя этот тип файлов все еще
широко используется, это сравнительно устаревший формат, который в 2007 г. был
заменен в качестве формата по умолчанию для электронных таблиц М8 Ехсе1 фор-
матом .хкх (Ьйр8://еп.лУ1к1реД1а.ог§/лУ1к1/М1сго8ОЙ_Ехсе1#Г11е_Гогта1:8).
Чтобы получить дополнительные требуемые нам форматы, откройте загруженный
файл в программе электронных таблиц (например, Сюо§1е 811ее18), выберите в меню
опцию 8ауе А8, а затем укажите .х18х. Затем повторите эту процедуру, но в конце
укажите .оск. Теперь у вас должны быть следующие три файла, содержащие одина-
ковую информацию: Ггес1§гар11.хкх, Ггес1§гар11.оск и Ггес1§гар11.хк.9.
Если открыть оригинальный файл й*е<1§гар11.хк, то можно заметить, что
кроме данных по безработице он содержит и другие данные. Например,
в заголовках указывается информация о происхождении данных и оп-
ределении индикаторов ИЗ и И6. Для анализа показателей уровня без-
работицы, данные по которым содержат эти файлы, эти метаданные
нужно будет отделить от интересующих нас данных, находящихся да-
лее в таблице. Но на данном этапе наша цель заключается просто в пре-
образовании всех этих файлов в формат .С8У. Процесс очистки данных,
включающий удаление этих метаданных, мы рассмотрим в главе 7.
8 Эти инструкции также доступны на самом веб-сайте ГКЕП (ЬПр8://Гге(Ше1р.8иош8Геског§/Ггес1/
§гарЬ8/си81:от1ге-а-Гге(1-§гарЬ/(1а1:а4гап8Гогтайоп-а(1(1-8епе84о-ех18Йп§-11пе).
9 Эти файлы также можно загрузить из моего хранилища Ооо§1е Ппуе (ЬПр8://(1пуе.§оо§1е.сот/с1пуе/
и/0/Го1аег8/1сЕ5Та§_ГугСслууААуЬМОЬрЬЕс12Д,78Ь).
Х1_8Х, 008 и все остальные
В целом, по мере возможности, лучше избегать работы напрямую с данными в
форматах .х1зх, .оск и в большинстве других нетекстовых табличных форматах
данных. Для простого исследования наборов данных я рекомендую просматривать
такие файлы, просто открывая их в какой-либо программе электронной таблицы, а
затем сохраняя их в формате .С8У или .18У, прежде чем работать с ними средствами
РуШоп. Это не только облегчит работу с вашими файлами данных, но также даст
вам возможность увидеть их содержимое и понять, что оно собой представляет.
Сохранение файлов формата .хк(х) и других подобных форматов данных в формате
.С8У или эквивалентном текстовом формате не только уменьшит размер файла, но и
даст вам лучшее представление о том, как выглядят ''настоящие” данные при про-
смотре сохраненных таким образом файлов. Вследствие форматирования данных в
программах электронной таблицы, иногда отображаемое на экране представление
данных значительно отличается от их исходных неформатированных значений, хра-
нящихся в файле. Например, значения, которые в электронной таблице отобража-
ются как проценты (например, 10%), на самом деле в файле могут быть десятич-
ными значениями (т. е. 0,1). Это может создавать проблемы, если основывать раз-
ные аспекты обработки или анализа средствами РуШоп на увиденном вами в
электронной таблице, а не на действительном форматировании данных в таких тек-
стовых форматах данных, как, например, .С8У.
Тем не менее вам обязательно придется сталкиваться с ситуациями, в которых к
файлам формата .хк(х) и других подобных форматов нужно будет обращаться на-
прямую средствами РуШоп10 11. Например, если вам нужно регулярно (скажем, каж-
дый месяц) обрабатывать набор данных в формате .хк, то копирование и сохране-
ние файла в нужном формате каждый раз будет неизбежно времязатратным.
К счастью, то активное сообщество РуШоп, о котором мы говорили в разд. "Сооб-
щество" главы 7, создало библиотеки, которые могут с легкостью работать с впе-
чатляющим количеством форматов данных. Чтобы получить полное представление
о том, как эти библиотеки работают с более сложными исходными данными (и
форматами данных), чуть далее мы рассмотрим несколько примеров кода, который
считывает данные в указанном формате, а затем сохраняет эти же данные в новом
файле в формате .С8У.
Но для использования этих библиотек их нужно сначала установить на своем уст-
ройстве, выполнив по отдельности следующие команды в окне терминала11.
р1р гпз^аИ орепрух!
р1р т.п51аИ руехсе1-оск
р1р гпз^аИ х1гб==2.0.1
10 На момент работы над материалом данной книги программа электронной таблицы 1лЬгеОГГюе
поддерживает такое же количество строк, что и программа М8 Ехсе1 (220), но намного меньше
столбцов. А программа Ооо§1е 81зее18, наоборот, может работать с большим количеством столбцов,
нежели М8 Ехсе1, но только с 40 000 строками максимально.
11 На момент работы над материалом этой книги все эти библиотеки были доступны для использова-
ния в среде Ооо§1е Со1аЬ.
В последующих примерах кода мы используем библиотеку орепрух! для доступа
(или синтаксического разбора) данных в файлах .хкх, библиотеку руехсе!-оск для
работы с файлами .оск и библиотеку х1гс1 для чтения данных с файлов .хк. (Допол-
нительная информация о том, как искать и выбирать требуемые библиотеки РуЙюп,
приводится в разд. "Где искать библиотеки" в приложении А.)
Чтобы лучше проиллюстрировать особенности этих разных форматов данных, мы
применим подход, подобный описанному в конце разд. "Выпас данных реального
мира: понимание безработицы" ранее в этой главе. В частности, мы откроем ис-
ходный файл .хк в какой-либо программе электронной таблицы, после чего сохра-
ним его сначала как файл .хкх, а затем как файл .оск, которые будут содержать
точно такие же данные, как и исходный файл .хк. В процессе дальнейшей работы
вы получите представление о том, как эти нетекстовые форматы усложняют про-
цесс выпаса данных (по моему мнению, без какой бы то ни было для этого надоб-
ности).
Начнем с обработки файла .хкх (см. листинг 4.6), используя версию данных по без-
работице, загруженных с веб-сайта РКЕВ. Этот пример иллюстрирует одно из пер-
вых основных различий между работой с текстовыми файлами табличных данных и
нетекстовыми форматами. В частности, поскольку нетекстовые форматы поддер-
живают множественные "листы”, в начале сценария мы используем цикл Рог, в теле
которого размещается код для создания отдельных выходных файлов (по одному
для каждого листа).
Листинг 4.6. Сценарий х1§х_раг§тд.ру
# Пример чтения данных из файла .х!зх средствами Ру!Ьоп, используя библиотеку орепрух!.
# Сначала нам нужно установить эту библиотеку при помощи установщика р!р
# Ь!!рз://рур!.огд/ргодес!/орепрух!/
# Исходные данные можно создать и загрузить со следующего веб-сайта
# Ь!!рз: //Егес!. з^Тоитз^ес!. огд/зег!ез/116РАТЕ
# Указываем "главу", т.е. метод, который нужно импортировать из библиотеки орепрух!.
# В данном случае это "!оас1_могкЬоок"
1топ орепрух! !прог! !оаб_могкЬоок
# Импортируем библиотеку сз7, чтобы создать выходной файл
!прог! сз\/
# Передаем имя нашего файла в качестве аргумента методу !оас1_могкЬоок()
# библиотеки орепрух!.
# Сохраняем результат в переменной зоигсе_могкЬоок
зоигсе_могкЬоок = !оас!_\л/огкЬоок(Г!!епате = ЧтебдгарЬ.х^х')
# Рабочая книга .х!зх может содержать несколько листов.
# Отображаем их имена для справки
рг!п!(зои гсе_могкЬоок.зЬее!патез)О
# Обрабатываем в цикле рабочие листы в рабочей книге зоигсе_могкЬоок
Гог 5ЬееГ_пип, зЬееГ_пате 1п епитегаГе(8оигсе_могкЬоок.8ЬееГпате8):О
# Создаем переменную, указывающую на текущий рабочий лист,
# передавая текущее значение переменной зЬееГ_папе в зоигсе_могкЬоок
сиггепГ_зЬееГ = 5оигсе_могкЬоок[зЬееГ_папе]
# Отображаем имя листа зЬееГ_папе
рг1пГ(8ЬееГ_папе)
# Создаем выходной файл "х1зх_"+имя_листа
оиГриГ_Н1е = ореп("х1.8х_"+8ЬееГ_пате+" .с87","м")О
# Используем метод для записи библиотеки С57 для записи строк данных в выходной
# файл оиГриГ_ГПе
оиГриГ_мг1Гег = сз7.мг1Гег(оиГриГ_Г11е)
# Обрабатываем в цикле каждую строку в листе
Гог гом 1п сиггепГ_8ЬееГ.1Гег_гомз():0
# Создаем пустой список для хранения значений ячеек (столбцов) каждой строки
гом_сеШ8 = []0
# Для каждой ячейки (столбца) в каждой строке...
Гог сеШ 1п гом:
# Отображаем ее содержимое, просто чтобы посмотреть, как его видит код
рг1пГ(сеШ, сеП.уаГие)
# Добавляем значения в конец списка, используя метод аррепб()
гом_се11з.аррепб(се11.7а1ие)
# Записываем нашу новую строку данных в выходной файл
оиГриГ_мг1Гег.мг1Гегом(гом_се11з)О
# Закрываем файл .сзу, в который мы только что записали все данные
оиГриГ_ГПе. с!озе()
О Подобно методу 01сГРеабег() библиотеки С8У, метод 1оаб_могкЬоок() библиотеки
орепрух! добавляет свойства в исходные данные. В данном случае это свойство,
которое отображает имена всех листов данных в рабочей книге.
О Хотя рабочая книга в данном примере содержит только один рабочий лист, в бу-
дущем их может быть больше. Для доступа как к итератору, так и к имени листа
используется функция епипегаГе(). Таким образом мы сможем создавать по одному
файлу .С8У для каждого рабочего листа.
О Для каждого листа в рабочей книге 5оигсе_могкЬоок будет требоваться свой собст-
венный выходной файл .С8У с однозначным именем. Для создания этих файлов мы
открываем новый файл в режиме записи, передавая функции ореп() аргумент
х18х_"+имя_листа+" .сзу для имени файла и аргумент V для указания режима записи.
(До сих пор мы открывали файлы в режиме чтения, передавая функции ореп() ар-
гумент режима работы "г".)
О Функция П:ег_гом8() библиотеки орепрух! преобразовывает строки рабочей кни-
ги зоигсе_могкЬоок в список, который можно обрабатывать в цикле.
0 Библиотека орепрух! обрабатывает каждую ячейку данных как тип данных кор-
теж языка РуЙюп (Ьйр8://(1ос8.ру1:Ьоп.ог§/3/11Ьгагу/8€(1€уре8.Ы:т1#1:ир1е). Если по-
пытаться просто отображать строки текущего листа сиггеп1:_8Ьее1: напрямую, то мы
получим адреса ячеек, а не значения содержащихся в них данных. Чтобы решить
эту проблему, мы вкладываем еще один цикл внутрь текущего, который обрабаты-
вает каждую ячейку строки и добавляет значения данных в список гом_сеПз.
О Обратите внимание на то, что этот код выровнен по левому краю со строкой кода
Гог сеИ 1п гом выше. Это означает, что он находится вне этого цикла и будет ис-
полнен только после того, как все ячейки (столбцы) текущей строки были добавле-
ны в список.
Этот сценарий также демонстрирует, что как два повара могут подходить по-
разному к приготовлению одного и того же блюда, так и создатели библиотек мо-
гут принимать разные решения касательно того, как (ре)структурировать каждый
тип данных исходного файла, что имеет соответствующие последствия для нашего
кода. Например, разработчики библиотеки орепрух! решили хранить метку каждой
ячейки данных (например, Аб) и содержащееся в ней значение в кортеже (Шр!е)
РуЙюп. Это решение является причиной, по которой нам требуется другой цикл Гог
для каждой строки данных — потому что для того, чтобы создать список РуЙюп,
который потом станет одной строкой в выходном файле .сзу, нам нужно обрабаты-
вать данные по ячейке за раз. Так, если открыть файл хкхЕКБЭ (Згарй.сзу, создан-
ный сценарием в листинге 4.6, в программе электронной таблицы, мы увидим, что
значения дат, которые в столбце оЬ8егуаЕ1оп_с1а1:е исходного файла .х!§ были в фор-
мате ГГГГ-ММ-ДД, в этом файле будут в формате ГГГГ-ММ-ДД ЧЧ:ММ:СС. Это
объясняется тем, что разработчики библиотеки орепрух! решили автоматически
преобразовывать любые строковые данные, выглядящие наподобие дат, в формат
данных дат с1аГе1гте РуЙюп. Очевидно, что все эти решения не являются ни пра-
вильными, ни неправильными. Нам просто нужно принимать их к сведению при
разработке нашего кода, чтобы не исказить или неверно истолковать исходные
данные.
Разобравшись с обработкой версии .х!§х нашего файла данных, посмотрим, что
происходит при обработке его версии .оск, как показано в листинге 4.7.
Листинг 4.7. Сценарий ос1з_рагзтд.ру
# Пример чтения данных из файла .оск средствами Ру1:Ьоп,
# используя библиотеку руехсе1-оск. Сначала нужно установить эту библиотеку при помощи
# установщика р!р: Ы:1:р8://рур1.огд/ргозес17руехсе1-оск/
# Указываем "главу" или метод, который нужно импортировать из библиотеки руехсеГ-обз.
# В данном случае это метод деГ_ба!а
Ггоп руехсеГ-обз тпрогГ деГ_баГа
# Импортируем библиотеку сз7, чтобы создать выходной файл
тпрогГ С57
# Передаем имя файла в качестве аргумента методу деГ_ба!а() библиотеки руехсеГ-обз
# Сохраняем результат в переменной зоигсе_могкЬоок
зоигсе_могкЬоок = деГ_баГа("ГгебдгарЬ.обз")
# Рабочая книга .обз может содержать несколько листов
Гог зЬееГ_пате, зЬееГ_баГа 1п зоигсе_могкЬоок.1Гетз() :О
# Отображаем имя листа зЬееГ_папе
рг1пГ(зЬееГ_пате)
# Создаем выходной файл обз_"+5ЬееГ_папе+".С37 для хранения текущего листа
оиГриГ_Г1Ле = ореп("обз_"+5ЬееГ_пате+" .сз7","м")
# Используем метод для записи мг1Гег() библиотеки сз7 для записи строк данных
# в выходной файл оиГриГ_Г1Ле
оиГриГ_мг1Гег = сз7.мг1Гег(оиГриГ_П1е)
# Обрабатываем в цикле каждую строку в листе
Гог гом 1п зЬееГ_баГа:О
# Записываем каждую строку напрямую в выходной файл, используя метод мг1Гегом()
оиГриГ_мг1Гег.мг1Гегом(гом)0
# Закрываем файл .сз7, в который мы только что записали все данные
оиГриГ_Г1Ле.с!озе()
О Библиотека руехсеБобз преобразовывает наши исходные данные в тип данных
РуГЬоп ОгбегебОтсГ. Связанный метод 1Гепз() позволяет обращаться к названию и
данным каждого листа как к паре ключ/значение, которые мы можем обрабатывать
в цикле. В данном случае ключом является зЬееГ_папе, а значением — все данные
рабочего листа.
О В данном случае зЬееГ_баГа уже является списком, поэтому мы можем просто
обработать его, используя простой цикл Гог.
0 Эта библиотека преобразовывает каждую строку рабочего листа в список, вслед-
ствие чего мы можем передавать эти строки напрямую методу мг1Гегом().
В случае использования библиотеки руехсеГ-обз содержимое выходного файла .сзу
намного больше похоже на содержимое исходного файла Ггес1§гар11.х18 при его про-
смотре в программе электронной таблицы типа Ооо§1е Например, поле
оЬзег7а1:1оп_с1а1:е имеет простой формат ГГГГ-ММ-ДД. Кроме этого, разработчики
библиотеки решили рассматривать значения строки как список, что позволяет со-
хранять каждую запись напрямую в выходной файл, не требуя создания дополни-
тельных циклов или списков.
Наконец, посмотрим, что происходит при обработке исходного файла .хк, исполь-
зуя библиотеку х1гб. Соответствующий код приводится в листинге 4.8.
Листинг 4.8. Сценарий х1$_раг$тд.ру
# Простой пример чтения данных из файла .х!з средствами РуТФоп,
# используя библиотеку хг!с1. Сначала установим библиотеку х!гс1, используя установщик
# р1р: ЬН:р8://рур1.огд/ргозес17х1гс1/2.0.1/
# Импортируем библиотеку х!гс1
1прог1: х!гб
# Импортируем библиотеку С87, чтобы создать выходной файл
1прог1: С87
# Передаем имя файла в качестве аргумента методу ореп_могкЬоок() библиотеки х!гс1.
# Сохраняем результат в переменной зоигсе_могкЬоок
зоигсе_могкЬоок = х1гб.ореп_могкЬоок('Тгес1дгарЬ.х15")0
# Рабочая книга .х!з может содержать несколько листов
1"ог зЬее1:_пате 1п 5оигсе_шогкЬоок.зЬее1:_пате8():
# Создаем переменную, указывающую на текущий рабочий лист,
# передавая текущее значение зЬее1:_пате методу зЬее1:_Ьу_пзпе
сиггеп1:_8Ьее1: = 5оигсе_иогкЬоок.5ЬееОУ_папе(8Ьее1:_пате)
# Отображаем имя листа зЬее1:_пате
рг1п1:(5Ьее1:_пате)
# Создаем выходной файл х1з_"+5Ьее1:_пате+".С87 для хранения текущего листа
оиГриГ_Г11е = ореп("х15_"+5Ьее1:_пате+".С87","м")
# Используем метод для записи мг1Лег() библиотеки С87 для записи строк данных
# в выходной файл оиГриГ_ГПе
ои!ри1_нгт.1ег = С57.мг11:ег(ои1:ри1:_'Г11е)
# Обрабатываем в цикле каждую строку в листе
^ог гом_пип, гом 1п епипега1:е(сиггеп1:_8Ьее1:.де1:_гом8()):О
# Каждая строка уже является списком, но для доступа к его значениям
# нужно использовать метод гом_з/а1ие().
# Записываем каждую строку напрямую в выходной файл, используя метод мг11:егом()
оиГриГ_\л/г1Гег. мг11:егом(сиггеп1:_8Ьее1:. гом_уа1иез( гом_пип) )О
# Закрываем файл .С87, в который мы только что записали все данные
ои1ри1_^Ие. с!озе()
О Обратите внимание на то, что эта структура похожа на структуру, используемую
при работе с библиотекой с§у.
О Функция де1:_гом() библиотеки х1гб преобразовывает строки текущего рабочего
листа в список, который можно обрабатывать в цикле12.
О Значения дат, записанные в выходной файл, будут иметь несколько странное
форматирование13. Мы рассмотрим, как решить эту проблему, в разд. "Декодиро-
вание дат ЕхсеГ' главы 7.
В этом выходном файле мы увидим странный формат значений в поле
оЬзегуа1:1.оп_с1а1:е, что отражается словами создателей библиотеки х1гб14:
Даты в электронных таблицах Ехсе1: в действительности, таковых нет.
Что у нас есть, так это числа с плавающей запятой и благие надежды.
(Еа1е$ т Ехсе18ргеасЕкее18:1п геаШу, 1кеге аге по 8иск 1кт§8.
}Ека1 уои кахе аге ДоаИп§рот1 питЪегз апс!рюиз коре.)
В результате, чтобы из файлов .х1§ получить полезные даты, удобные для воспри-
ятия людьми, требуется выполнить значительную очистку. Этот вопрос обсуждает-
ся в разд. "Декодирование дат ЕхсеГ' главы 7.
Будем надеяться, что эти упражнения продемонстрировали, как, используя не-
сколько умных библиотек и немного откорректировав базовый код, средства РуЙюп
позволяют легко и быстро извлекать первичные данные из ряда табличных форма-
тов. В то же самое время я надеюсь, что эти примеры также проиллюстрировали,
почему всегда предпочтительнее15 работать с текстовыми и/или открытыми форма-
тами данных — ведь зачастую они требуют меньшего объема очистки и преобразо-
ваний, чтобы привести их в ясное, пригодное для дальнейшей обработки состояние.
12 Дополнительную информацию по функции де1:_Г0М5() см. в документации для библиотеки х1гс1
(ЬНр8://х1гс1.геасИЬес1ос8ло/еп/1аЫ8Цар1.Ыт1#х1гс1-8Ьее1).
13 Дополнительную информацию по этому вопросу см. в документации для библиотеки х1гс1
(ЬНр8://х1г(1.геасИЬес1ос8.1о/еп/1аЫ8Ц€кПе8.Мт1).
14 Из статьи Ра1е$ т Ехсе18ргеа(Екее18, авторы 81ер1зеп ЭоИп МасЫп и СИпз АУййегз
(ЬНр8://х1гс1.геасИЬес1ос8.1о/еп/1аЫ8Цс1аЫ8.Мгп1).
15 При просмотре в текстовом редакторе каждого из выходных файлов, полученных в предыдущих
примерах, можно увидеть, что формат с открытым кодом .обз является самым простым и опрятным.
Данные фиксированной ширины
Одна из самых старых версий табличных данных — это данные фиксированной
ширины. Как можно предполагать по названию, каждый столбец данных этого
формата содержит конкретное предопределенное количество символов, и всегда
только данное количество символов. Это означает, что в файлах значений фиксиро-
ванной ширины значащие данные часто дополняются дополнительными символа-
ми, например пробелами или нулями.
В современных системах данных этот формат используется редко, но с ним все еще
можно встретиться при работе с правительственными источниками данных, чья
инфраструктура может насчитывать десятки лет16. Например, управление ХОАА
(Хайопа! Осеашс апб Айтюзрйепс Абш1П181габоп — Национальное управление по
проблемам океана и атмосферы) США (Ьйр8://поаа.§оу/оиг-Ь181:огу), основанное
в начале XIX столетия, предоставляет бесплатный онлайн-доступ к широкому
диапазону подробной и актуальной информации посредством своей сети Сг1оЪа1
Н181опса1 СНша1о1о§у ХсПуогк (Глобальная сеть исторической климатологии,
Ьйр8://пс(1с.поаа.§оу/Да1:а-ассе88/1ап(1-Ьа8еД-81:айоп-Да1:аЛап(1-Ьа8еД-(1а1:а8е1:8/§1оЬа1-
Ы8€ог1са1-с11та€о1о§у-пе1ууогк-§11сп). Большинство этих данных опубликованы в
формате фиксированной ширины. Например, информация об уникальных иденти-
фикаторах станций, их местонахождении и принадлежности к конкретной сети
хранится в файле ^спсГзийопз.Ш (Ьйр8://уууууу1.пс(1с.поаа.§оу/риЬ/(1а€а/§Ьсп/
ДаПу). Для интерпретации значений метеорологических данных (многие из кото-
рых также предоставляются в файлах формата фиксированной ширины) нужно со-
поставлять данные по станциям с метеорологическими данными.
Работать с данными фиксированной ширины может быть еще более сложно, чем с
данными других табличных форматов, если у вас нет доступа к метаданным, описы-
вающим организацию файла и его полей. В случае разделенных данных часто можно
просто просмотреть файл в текстовом редакторе и достаточно уверенно идентифици-
ровать используемый разделитель. В худшем случае можно просто попробовать об-
рабатывать файл, используя разные символы разделителя, чтобы увидеть, который
даст наилучшие результаты. В случае файлов данных фиксированной ширины, осо-
бенно если они большого размера, если какое-либо поле не содержит никаких дан-
ных, можно легко непреднамеренно объединить значения нескольких полей данных.
Данные, разделенные символами табуляции, по сравнению
сданными фиксированной ширины
Как упоминалось в разд. "Файловые табличные форматы данных" ранее в
этой главе, в настоящее время все еще можно столкнуться с файлами данных,
разделенных символами табуляции, имеющих расширение файла .Ш, которое
также применяется и для файлов данных фиксированной ширины.
16 Как, например, в Пенсильвании (ЬПр8://8роШ§Ыра.ог§/пеуу8/2021/05/ра-ипетр1оутепЬс1а1т8-
оуегЬаи1-1Ьт-§81-ЬепеШ8-1аЬог-т(1и81:гу) или Колорадо (ЬН:р8://(1епуегро8Ьсот/2021/01/10/со1ога(1о-
ипетр1оутепЬЬепеЩ8-пе\\-с1а1т8-8у8Ыт).
Поскольку для разделения полей оба эти формата данных полагаются на непе-
чатаемые символы (пробел или символ табуляции), как можно точно знать, с
каким форматом данных мы имеем дело?
Это еще одна ситуация, когда вы можете избежать лишних проблем, самостоя-
тельно исследовав файл в текстовом редакторе или редакторе кода, поскольку
эти два формата данных визуально явно отличаются друг от друга. Начните с
просмотра правой части документа: разделенные символом табуляции данные
будут иметь ступенчатый правый край, т. е. записи (столбцы) данных будут
разной длины. А данные фиксированной ширины будут выровнены по право-
му краю, т. е. все записи будут заканчиваться в одной точке.
Например, ниже приводится несколько строк данных формата 18У из файла
81зи§егшапРго8есиШгРо1Шс1ап8-8иргетеСоигНи8бсе8.1:8У, с которым мы работа-
ли в листинге 4.3, как они выглядят в редакторе кода А1ош.
42 1л!агс1 НипХ 1873-1882 Р МУ СгапХ
43 МоггТзоп 1л1а11:е 1874-1888 Р ОН СгапХ
44 ЗоЬп МагзЬаП НагТап 1877-1911 Р КТ Науез
А здесь показано несколько строк данных формата фиксированной ширины из
файла еЬспс1-81айоп8.ъа:
АЕМ00041217 24.4330 54.6510 26.8 АВО ОНАВ1 1МТ1 41217
АЕМ00041218 24.2620 55.6090 264.9 А1_ А1И ТИТЬ 41218
АЕ000040930 35.3170 69.0170 3366.0 МОРТН-5А1АИС С5И 40930
Как видите, в случае данных фиксированной ширины по правому краю выров-
нены не только сами записи, но также и все числовые поля. Это еще одна под-
сказка, что перед нами данные фиксированной ширины, а не разделенные сим-
волом табуляции.
К счастью, метаданные о файле дйспб-зШюпз.Ш, который мы используем в качест-
ве нашего источника данных, включены в файл геабше.Ш, который находится в той
же самой папке веб-сайта МОАА (Ьйр8://\у\у\у1.пс(1с.поаа.§оу/риЬ/(1аТа/§Ьсп/Да11у/
геаскпе.Ш).
Просматривая файл геабше.Ш, находим заголовок IV. ЕОРМАТ ОЕ "дЬспс!- зТаМопзЛхТ"
и следующую таблицу:
УагТаЫе СоТиппз Туре
Ю 1-11 СЬагасХег
1АТ1Т1ЮЕ 13-20 РеаТ
ЮМС1Т1ЮЕ 22-30 РеаТ
ЕЬЕУАТЮМ 32-37 РеаТ
5ТАТЕ 39-40 СЬагасХег
МАМЕ 42-71 СЬагасХег
С5И ЕЬАС 73-75 СЬагасХег
НСИ/СРИ ЕЬАС 77-79 СЬагасХег
1л!МО Ю 81-85 СЬагасХег
За этой таблицей следует подробное описание содержимого или значения каждого
поля, включая информацию типа единиц измерения. Благодаря этому надежному
словарю данных, мы теперь знаем не только организацию файла зйспсЬзШюпз.Ш,
но также и как интерпретировать содержащуюся в нем информацию. Как мы уви-
дим в главе 6, поиск (или создание) словаря данных является важной частью про-
цесса оценки или улучшения качества наших данных. Но сейчас мы просто рас-
смотрим, как преобразовать эти данные фиксированной ширины в данные, разде-
ленные запятой. Соответствующий код приводится в листинге 4.9.
Листинг 4.9. Сценарий йхесЦллсйЬ_раг8тд.ру
# Пример чтения данных из файла данных фиксированной ширины средствами Ру1Ьоп.
# Исходный файл данных для этого примера предоставлен управлением МОДА и доступен здесь:
# Ы1рз: //мллл/1 . псс1с. поаа. доV/риЬ/с1а±а/дЬсп/(ЗаНу/дНспс!- 51а1!опз. 1x1
# Метаданные для файла доступны здесь:
# Ы1рз://ыьм1. псбс. поаа.до7/риЬ/ба1а/дЬсп/с1а11у/геас1те. 1x1
# Импортируем библиотеку сзу, чтобы создать выходной файл
1прОГ1 С87
ГИепате = "дЬспс1-81а11оп8"
# Для чтения обычного текстового файла не требуются никакие специальные библиотеки,
# поэтому просто открываем его в обычном режиме для чтения "г".
зоигсе_11Ле = ореп(11Лепапе+".1х1", "г")
# Встроенный метод геасПЛпез() делает то, что говорит его название:
# считывает содержимое входного текстового файла и преобразовывает его в список строк
81а11опз_Г181 = зоигсе_11Ле.геас1Г1пе8()
# Создаем выходной файл для преобразованных данных
ои1ри1_11Ле = ореп(111епате+".сз7","м")
# Используем метод для записи мг11ег() библиотеки сзу для записи строк данных
# в выходной файл ои!ри1_Г11е
ои!ри!_мг11ег = сз7.мг11ег(ои1ри1_Г1Ле)
# Создаем список заголовков
Ьеабегз = ["Ю", "1_АТ1Т1ЮЕ", "ЮМС1Т1ЮЕ", "Е1_Е7АТ1ОМ", "8ТАТЕ", "МАМЕ", "С$М_Е1_АС",
"НСЮМ_Е1_АС", "ИМО_Ю" ]О
# Записываем заголовки в выходной файл
ои!ри1_мг11ег.мг11егом(Ьеас1ег5)
# Обрабатываем в цикле все строки нашего файла (множественные "листы" отсутствуют)
1ог Ппе 1п з1а11опз_Г181:
# Создаем пустой список, в который будем добавлять каждый набор символов,
# составляющий данный "столбец" данных
пем_гом = []
# Ю: роННопз 1-11
пем_гом. аррепс!(Ипе[0:11] )О
# 1АТ1Т1ЮЕ: роИНопз 13-20
пем_гом. а ррепс! (Ипе [ 12:20 ])
# ЮМС1Т1ЮЕ: роННопз 22-30
пем_гом. аррепс!(Ипе[21:30])
# ЕЬЕУ/АТЮМ: розШопз 32-37
пем_гом. аррепс!(Ипе[31:37])
# 5ТАТЕ: роИНопз 39-40
пем_гом. а ррепс! (Ппе [ 38:40 ])
# МАМЕ; роННопз 42-71
пем_гом. а ррепс! (Ппе [ 41:71 ])
# С5М_Е1_АС: роИНопз 73-75
пем_гом. а ррепс! (Ппе [ 72:75 ])
# НСМСРМ_Е1_АС: роННопз 77-79
пем_гом. а ррепс! (Ппе [ 76:79 ])
# 1л1М0_Ю: роННопз 81-85
пем_гом. а ррепс! (Ппе [ 80:85 ])
# Теперь осталось только записать пем_гом в выходной файл, используя метод
# нпЛегон()
ои1риТ_шгПег .шгПего\л/(пе\л/_го\л/)
# Закрываем файл .С57, в который мы только что записали все данные
ои1ри1_ГПе. с!озе()
О Так как исходный файл не содержит ничего, что можно было бы использовать в
качестве заголовков столбцов, нам нужно самим создать эти заголовки на основа-
нии информации в файле геайшеЛех! (Ьир8://лулулу1.пс(1с.поаа.§оу/риЬ/(1а1:а/§Ьсп/
йаПу/геайше.Ш). Обратите внимание на то, что эти заголовки не содержат никаких
специальных символов, а вместо пробелов используются символы подчеркивания.
Это сведет к минимуму возможные проблемы при последующих очистке и анализе
этих данных.
О В РуЙюп строки текста рассматриваются просто как списки символов, поэтому
можно просто дать указание выдать нам символы между двумя значениями индек-
са. Подобно функции гапде(), здесь возвращается символ из позиции первого зна-
чения индекса, но не из второго, а из позиции, на единицу меньшей второго значе-
ния. Также вспомним, что в РуЙюп счет элементов списка начинается с нуля. Это
означает, что для каждой записи значение первого индекса будет на единицу мень-
ше значения, указываемого в метаданных, но значение второго будет таким же, как
и в метаданных.
Если открыть файл .С8У, созданный сценарием в листинге 4.9, в программе элек-
тронной таблицы, то можно будет увидеть, что форматирование значений в неко-
торых столбцах не будет единообразным. Например, в столбце Е1_Е\/АТ1ОМ числа с
десятичной частью выровнены по левому краю, а числа без десятичной части — по
правому. Что здесь происходит?
Опять же, пролить свет на эту проблему можно, открыв файл в текстовом редакто-
ре. Хотя данные в созданном нашим сценарием файле технически разделены запя-
тыми, значения, сохраненные в каждом из его разделенных столбцов, все так же
содержат дополнительные пробелы, которые они содержали в исходном файле. В
результате данные в нашем новом файле выглядят довольно похожими на данные
фиксированной ширины.
Иными словами, точно так же, как в случае с "датами” в файлах Ехсе1, преобразо-
вание содержимого нашего файла в содержимое .С8У не создает разумные типы
данных в выходном файле ''автоматически”. Задача определения типа данных, ко-
торый должно иметь каждое поле, а также их очистки, чтобы они вели себя долж-
ным образом, является частью процесса очистки данных, который мы рассмотрим в
главе 7.
Канальные данные — интерактивные обновления
через Интернет
Структура форматов табличных типов данных хорошо подходит для мира, в кото-
ром большинство "данных” уже были отфильтрованы, откорректированы и преоб-
разованы в сравнительно хорошо организованную коллекцию чисел, дат и корот-
ких строк. Но с появлением Интернета возникла необходимость в передаче боль-
ших объемов информации типа "произвольного” текста, который можно найти,
например, в новостях и лентах социальных сетей. Поскольку в состав содержимого
данных этого типа обычно входят такие символы, как запятые, точки и кавычки,
которые влияют на его семантическое значение, вписать его в традиционный фор-
мат с разделителями будет, как минимум, проблематично. Более того, предраспо-
ложение форматов с разделителями к горизонтальной организации (что влечет за
собой большой объем прокрутки влево-вправо) противоречит сетевым обычаям
вертикальной прокрутки. Канальные форматы данных были разработаны, чтобы
устранить оба эти недостатка.
На высшем уровне существует два основных типа канальных форматов данных:
ХМЬ и 18(Ж. Это текстовые форматы, которые позволяют поставщику данных оп-
ределять свою собственную структуру данных, что делает их чрезвычайно гибкими
и, следовательно, полезными для широкого спектра содержимого, которое можно
найти на веб-сайтах и интернет-платформах. Независимо от местонахождения дан-
ных этих форматов, будь то в Интернете или на локальной машине, их можно рас-
познать, помимо всего прочего, по их соответствующим расширениям файлов .хш1
И .)8ОП.
Формат .хт1
Формат ХМЬ (Ех1еп81Ые Магкир Ьап§иа§е — расширяемый язык разметки) ох-
ватывает широкий диапазон файловых форматов, включая форматы .Г88, .аШш и
даже .Ыш1. Будучи самым обобщенным типом языка разметки, формат ХМЬ об-
ладает чрезвычайной гибкостью и был, скорее всего, первоначальным форматом
для каналов данных.
Формат .роп
Формат 18ОХ (1ауа8спр1 ОЪ)ес1 ЬГоШюп — система обозначений объектов
1ауа8спр1) появился несколько позже, чем формат ХМЬ, но служит той же са-
мой цели. Формат 18ОХ менее описателен, чем формат ХМЬ, и, следовательно,
файлы данных в этом формате имеют меньший размер. Это означает, что файлы
18ОХ могут содержать почти такой же объем данных, как и файлы ХМЬ, но при
этом занимают меньше места, что особенно важно для скорости мобильного Ин-
тернета. Не менее важен и тот факт, что файлы 18ОХ являются, по сути, типом
данных больших объектов в языке программирования 1ауа8спр1, на основе ко-
торого созданы многие, если не большинство, веб-сайты и мобильные приложе-
ния. Это означает, что любой веб-сайт или программа, использующая 1ауа8спр1,
может с легкостью работать с данными в формате 18ОХ, особенно по сравнению
с данными в формате ХМЬ. К счастью, типы данных объектов 1ауа8спр1 очень
похожи на типы данных (Яс! языка РуШоп, что также делает очень прямолиней-
ной работу с данными формата 18ОХ в этом языке.
Прежде чем углубляться в рассмотрение, как работать с каждым из этих типов дан-
ных на языке РуШоп, уделим немного времени на размышления о том, когда мы
можем отдать предпочтение работе с канальными данными и где их искать в таком
случае.
Когда лучше использовать канальные данные
В некотором смысле канальный тип данных является для XXI столетия тем, чем
табличные данные были для XX: сам объем канальных данных, создаваемых, со-
храняемых и обмениваемых в Интернете каждый день, наверное, в миллионы раз
превышает объем всех вместе взятых табличных данных в мире. В значительной
мере это объясняется тем, что канальные данные обеспечивают работу веб-сайтов
социальных сетей, новостных приложений и всего прочего.
С точки зрения выпаса данных предпочтительней иметь данные канального типа в
тех случаях, когда исследуемое явление зависит от времени и обновляется часто
и/или непредсказуемо. Данные этого типа обычно создаются в ответ на различные
активные процессы, например публикации в социальных сетях, публикации ново-
стей или, скажем, запись землетрясения.
Как и файловые табличные данные, канальные данные могут содержать архивную
информацию, но, как обсуждалось в начале этой главы, первые обычно отражают
данные в том состоянии, в каком они существовали в какой-либо определенный
момент времени. Последние же, в противоположность файловым, как правило,
организовываются в обратном хронологическом порядке (сначала самые послед-
ние) — первой записью будет запись, созданная наиболее недавно к моменту об-
ращения к данным, а не в заданную дату.
Где искать канальные данные
Канальные данные используются, как правило, в Интернете, часто по специальным
1ЖЕ-адресам, которые называются конечными точками (эндпойнтами) АР1-интер-
фейса. Мы будем обсуждать в подробностях работу с АР1-интерфейсами в главе 5,
а на данном этапе будет достаточно знать, что АР1-интерфейсы — это в действи-
тельности всего лишь веб-страницы, содержащие только данные. Такие страницы
можно просматривать в обычном веб-браузере, но все, что вы там увидите, — это
сами данные. Некоторые АР1-интерфейсы возвращают разные данные в зависимо-
сти от переданной им информации. Вот эта возможность, среди прочего, и делает
процесс работы с канальными данными таким гибким: изменив всего лишь не-
сколько слов или значений в коде, можно получить доступ к совершенно другому
набору данных!
Чтобы найти АР1-интерфейсы, предоставляющие канальные данные, не требуется
применения никаких специальных поисковых стратегий, поскольку такие веб-
сайты и службы обычно хотят, чтобы их нашли. Почему? Простыми словами, код,
использующий АР1-интерфейс, обычно приносит некую выгоду предоставляющей
его компании, даже если эта выгода состоит просто в дополнительной публичности
для компании. Например, на начальном этапе существования ТмчИег многие веб-
разработчики создавали программы, используя АР1-интерфейс веб-сайта этой ком-
пании, тем самым как повышая полезность платформы, так и экономя компании
средства и усилия на выяснение требований пользователей и последующее удовле-
творение этих требований. Благодаря предоставлению такого большого объема
данных своей платформы на бесплатной основе (на начальных этапах), этот АР1-
интерфейс способствовал возникновению нескольких компаний, которые в конеч-
ном итоге были приобретены компанией ТмйНег. С другой стороны, еще большее
количество компаний были вынуждены уйти с рынка вследствие изменения или
АР1-интерфейса, или условий его использования17. Это подчеркивает одну из кон-
кретных проблем, которые могут возникнуть при работе с данными любого типа,
но особенно с данными канального типа, предоставляемыми коммерческими ком-
паниями: как данные, так и доступ к ним могут измениться в любое время и без ка-
кого бы то ни было предупреждения. Таким образом, хотя источники канальных
данных действительно представляют ценность, они могут быть эфемерными во
многих отношениях.
17 См. статью ТууШег'з 10 Уеаг &ги§§1е ууШг Вече1орег Ке1аНоп8 на веб-сайте компании ЬГогсНс АР1§
(ЬПр8://пог(Нсар18.сот/Ьт1Пег-10-уеаг-81ти§§1е-ууй11-с1еуе1орег-ге1а11оп8).
Выпас канальных данных
средствами языка РуШоп
Как и в случае с табличными данными, выпас канальных данных средствами языка
РуЛоп возможен благодаря сочетанию полезных библиотек и тому факту, что такие
форматы, как 18(Ж, уже похожи на типы данных, существующих в языке РуЙюп.
Кроме этого, в последующих разделах мы увидим, что для наших целей форматы
ХМЬ и 18(Ж часто взаимозаменяемы (хотя многие АР1-интерфейсы предоставляют
данные только в одном или другом из этих форматов).
ХМ1_: один язык разметки, чтобы править всеми остальными
Языки разметки являются самым старым видом стандартизированных форматов
документов в области вычислений. Они были разработаны с целью создания тек-
стовых документов, удобочитаемых как людьми, так и машинами. В 90-х годах
прошлого столетия язык ХМЬ начал играть все более важную роль в инфраструк-
туре Интернета, когда вследствие большой разновидности устройств, обращаю-
щихся к веб-информации и отображающих ее, отделение информации о содержи-
мом (например, текст и изображения) от информации о форматировании (например,
разметка страницы) стало более необходимым. В отличие от НТМЬ-документов, в
которых содержимое и форматирование полностью перемешаны, ХМЬ-документы
не содержат никаких инструкций касательно того, как отображать содержащуюся в
них информацию. Вместо этого они состоят из тегов и атрибутов, играющих роль
метаданных о типе содержащейся в них информации, вместе с самой информацией.
Чтобы дать вам представление о том, как выглядит ХМЬ-документ, в листинге 4.10
приводится пример подобного документа.
Листинг 4.10. Пример ХМЬ-документа
<?хп1 7ег51оп="1.0" епсоЛпд="1ЛЕ-8"?>
<па1п0ос>
<!--Это комментарий.-->
<е!епеп1:8>
<е1епеп1:1>Какой-либо текст в документе</е1епеп1:1>
<е1етеп1:2>Какие-либо другие данные в документе</е1епеп1:2>
<е!епеп1:3 зопеА1:1:г1Ьи1:е="а\/а1ие" />
</е1етеп1:8>
<8отеЕ1епеп1: апА1:1:г1Ьи1:е=''апо1±ег7а1ие''>Другое содержимое</8отеЕ1епепЬ>
</па1п0ос>
Здесь нужно обратить внимание на пару моментов. Самая первая строка называется
объявлением типа документа (босишеп! 1урс или босЧуре). В ней предоставляется
информация о том, что всю следующую часть документа нужно интерпретировать
как ХМЬ (а не как какой-либо другой язык веб-разработки разметки, некоторые из
которых мы вкратце рассмотрим далее в этой главе).
Начиная со строки:
<па1п0ос>
начинается содержимое самого документа. Одним из факторов, придающих языку
ХМЬ такую гибкость, является то, что он содержит только две настоящие грамма-
тические структуры, обе из которых включены в листинг 4.10:
Теги
Теги могут быть или парными (как еХетепИ, е1епеп1:2, 8опеЕ1епеп1: или даже
патпОос), или самозакрывающимися (как е!епеп1:3). Имя тега всегда заключается в
угловые скобки (<>). В случае закрывающего тега немедленно после открываю-
щей скобки следует косая черта (/). Пара из открывающего и закрывающего те-
га, а также самозакрывающийся тег, также называются элементами ХМЬ.
Атрибуты
Атрибуты допускаются только внутри тегов (как апАИггЬи^е в листинге 4.10).
Атрибут — это пара типа ключ/значение, в которой после имени атрибута (или
ключа) немедленно следует знак равенства (=), а за ним значение в двойных ка-
вычках (””).
Элементом ХМЬ является все содержимое, заключенное между открывающим те-
гом и соответствующим закрывающим тегом (например, <е1епеп1=5> и </е1епеп1=5>).
Таким образом, данный ХМЬ-элемент может содержать несколько тегов, каждый
из которых может также содержать другие теги. Любой элемент также может иметь
любое количество атрибутов (или ни одного). Самозакрывающийся тег также счи-
тается элементом.
Другое значимое правило для структурирования ХМЬ-документов состоит в том,
что при наличии вложенных тегов самый последний открытый тег нужно закрыть
самым первым. В листинге 4.11 показан пример правильной ХМЬ-структуры.
Листинг 4.11. Пример правильной структуры ХМЬ
<ои1:егЕ1етеп1>
<!-- Обратите внимание на то, что элемент 1ппегЕ1епеп1:1 закрыт
перед открытием элемента 1ппегЕ1етеп1:2.-->
<1ппегЕ1етеп1:1>Какое-либо содержимое</1ппегЕ1епеп1:1>
<1ппегЕ1епеп1:2>Другое содержимое</1ппегЕ1етеп1:2>
</оиГегЕ‘1егпеп'1:>
А структура в листинге 4.12 неправильная.
Листинг 4.12. Пример неправильной структуры ХМЬ
<оиГегЕ1_еп1еп1:>
<!-- НЕПРАВИЛЬНО! Элемент 1ппегЕ1епеп1:2 был открыт
прежде, чем был закрыт элемент 1ппегЕ1епеп1:1 -->
<1ппегЕ1епеп1:1>Какое-либо содержимое<1ппегЕ1етеп1:2>Другое содержимое</1ппегЕ1епеп1:1>
</1ппегЕ1епеп1:2>
с/оиГегЕТ-етеп^
Этот принцип ''последний открыт, первый закрыт” также называется вложением
(пе8Йп§), подобно вложению циклов Гог, как было показано на рис. 2.318. Вложение
имеет особенную важность в ХМЬ-документах, поскольку оно управляет одним из
основных механизмов, используемых для чтения (или парсинга) ХМЬ-документов
с кодом (или документов, составленных на других языках разметки). В ХМЬ-
документах первый элемент после объявления бос-Гуре называется корневым (гоо!)
элементом. В отформатированных ХМЬ-документах корневой элемент будет всегда
выровнен по левому краю, а следующий вложенный элемент будет сдвинут на один
уровень вправо и называется дочерним (сЫМ) элементом. Таким образом, в листин-
ге 4.10 элемент <па1п0ос> будет корневым, а элемент <е1епепГз> будет его вложен-
ным дочерним элементом. Подобным образом, элемент <па1п0ос> является
родительским (рагеп!) элементом элемента <е1етепГз> (см. листинг 4.13).
Листинг 4.13. ХМЬ-документ с комментариями
<?хп! 7ег51оп="1.0" епсосйпд="1ПТ-8"?>
<па1п0ос>
<!-- Элемент патпОос является корневым элементом, а элемент еТепепГз является его
дочерним элементом-->
<е!епепГ8>
< !-- Элемент еТепепГ является родителем (рагепГ) элементов е!епепГ1, е!епепГ2
и еТепепГЗ, которые являются сестринскими (зтЫЛпдз) элементами друг с другом
(т. е. одноуровневыми элементами). -->
< е!епепГ1>Текстовые данные в документе.</е!епепГ1>
< е1епепГ2>Какие-либо другие данные в документе.</е!епепГ2>
<е!епепГЗ 8отеАГГг1ЬиГе="а\/а1ие" />
</е1етепГз>
<!-- Элемент зопеЕТепепГ также является дочерним элементом (сЫ1б) элемента патпОос
и сестринским элементом элемента еТепепГз. -->
<8отеЕ1етепГ апАГГг1ЬиГе="апоГЬег7а1ие">Другое содержимое</8отеЕ1етепГ>
</па1п0ос>
Принимая во внимание этот тренд к генеалогическому жаргону, вы, наверное, за-
даетесь вопросом: если элемент <е!епепГ5> является родителем элемента <е!епепГЗ>,
а элемент <па1п0ос> является родителем элемента <е1етепГз>, то не делает ли это
элемент <па1п0ос> прародителем (§гапбрагеп1) элемента <е!епепГЗ>? И да и нет. То-
гда как элемент <та!п0ос> является родителем родителя элемента <е!епепГЗ>, термин
прародитель никогда не используется для описания структуры ХМЬ, поскольку с
таким подходом все может стать запутанным очень быстро. Вместо этого эти взаи-
моотношения описываются точно так, как есть: элемент стаГгЮоо является родите-
лем родителя элемента <е!епепГЗ>.
18 В отличие от кода РуШоп, вложенные элементы ХМЬ-документов не обязательно должны выде-
ляться отступами для их правильной работы, хотя такое форматирование определенно делает их более
удобочитаемыми.
К счастью, атрибуты ХМЬ-элементов избавлены от подобной сложности: они про-
сто идут как пары ключ/значения и могут существовать только внутри тегов ХМЬ:
<е1епеп1:3 8отеА1:1:г1Ьи1:е="а\/а1ие" />
Обратите внимание на отсутствие пробела по обеим сторонам знака равенства, как
и в случае между косой чертой и закрывающей угловой скобкой.
Подобно писательскому процессу на русском языке (или на языке РуЙюп), вопрос,
когда использовать теги, а когда атрибуты для определенного элемента информа-
ции, в значительной степени зависит от предпочтений и стиля автора. Например,
документы в листингах 4.14 и 4.15 содержат одинаковую информацию об этой кни-
ге, но каждый из них имеет слегка разные структуры.
Листинг 4.14. Образец данных ХМЬ о книге — предпочтение отдается атрибутам
<аВоок>
<Ьоок11Р1_ иг!="1т1:'1:р5://\ллл/\л/.огег1’1у.сот/ПЬгагу/угем/ргасГгсаТ-руГЬоп-^а-Ьа/
9781492091493"/>
<ЬоокАЬ81:гас1>
В наборах данных нас ждут фантастические открытия и ценные истории,
и эта книга поможет нам открыть их.
</ЬоокАЬз1:гасЬ>
<риЬОа1:е Фй:е="2022-02-0Г />
</аВоок>
Листинг 4.15. Образец данных ХМЬ о книге — предпочтение отдается элементам
<аВоок>
<ЬоокОР1_>
ЫД^рз://шм.огеШу .сот/11Ьгагу/71е\л//ргас1:1са1-ру1:Ьоп-с1а1:а/9781492091493
</ЬоокС1Р1_>
<ЬоокАЬ81гас1>
В наборах данных нас ждут фантастические открытия и ценные истории,
и эта книга поможет нам открыть их.
</ЬоокАЬ81:гас1>
<риЬОа1:е>2022 - 02 - 01</риЬОа1:е>
</аВоок>
Такая степень гибкости означает, что язык ХМЬ легко адаптируется к широкому
спектру источников данных и предпочтений форматирования. В то же самое время
он может легко создать ситуацию, в которой для каждого нового источника данных
требуется создание специального кода. Очевидно, что это была бы довольно неэф-
фективная система, особенно если бы большое количество людей и организаций
публиковали довольно похожие типы данных.
Поэтому неудивительно, что существует большое количество спецификаций ХМЬ,
определяющих дополнительные правила для форматирования ХМЬ-документов,
предназначенных для хранения определенных типов данных. Далее приводится
список и краткое описание нескольких заслуживающих внимания примеров таких
спецификаций, с которыми вам, возможно, придется столкнуться в процессе своей
работы по выпасу данных. Несмотря на разные названия и расширения файлов этих
форматов, с ними всеми можно работать, используя один и тот же метод, который
приводится чуть далее в листинге 4.16.
Спецификация ХМЕ К88 (Кеа11у 81шр1е 8уп(Иса1юп — действительно простая
синдикация) была впервые представлена в конце 90-х годов прошлого столетия
для работы с новостной информацией. Для этих целей также широко исполь-
зуется формат ХМЕ .а!ош.
КМЬ
Международный стандарт КМЕ (Кеу1ю1е Магкир Ьап§иа§е — язык разметки
Кеу1ю1е) применяется для работы как с двумерными, так и с трехмерными гео-
графическими данными, и совместим с такими инструментами, как Соо§1е ЕагЙг
8УС
Формат 8УС (8са1аЪ1е Уес1ог СгарЫсз — масштабируемая векторная графика)
широко применяется для веб-графики благодаря своей возможности масшта-
бировать рисунки без потери качества. Многие распространенные программы
для работы с графикой могут сохранять результаты в файлах .§у§, которые затем
можно вставлять в веб-страницы и другие документы, которые будут хорошо
выглядеть на экранах и устройствах самых разных размеров.
ЕРУВ
Открытый стандарт ЕРОВ (Е1ес1гошс риЪНсайоп Гоппа! — формат электронных
публикаций) широко применяется для цифровой публикации книг.
Как можно видеть из предшествующего списка, некоторые распространенные ва-
рианты формата ХМЕ ясно указывают на свою связь с ХМЕ, но другие — нет19.
Теперь, когда у нас есть общее представление о структуре формата ХМЕ, рассмот-
рим, как можно выполнить обработку содержимого файла в этом формате средст-
вами РуЙюп. Хотя язык РуЙюп обладает некоторыми встроенными инструментами
для парсинга ХМЕ-содержимого, мы будем использовать библиотеку 1хш1, которая
особенно хорошо справляется с парсингом ХМЕ файлов большого размера
(Ьйр8://п1ск]апе1:ак18.сот/Ыо§/йолу-1-и8е(14Ье-1хт1-11Ьгагу4о-раг8е-хт1-20х-Га81:ег-
т-ру€коп#хт1€оШс€-У8-ру€коп-8-81:апс1агс1-11Ьгагу-У8-1хт1). Хотя в последующем
примере используются файлы довольно небольшого размера, практически тот же
самый код можно было бы использовать и для файлов значительно большего раз-
мера.
19 Интересный факт: вторая буква ”х” в названии формата .х1§х означает ХМЬ.
Для начала мы используем версию ХМЬ данных показателя по безработице 116,
которые я уже загрузила из веб-сайта РКЕВ, используя его АР1-интерфейс20. Загру-
зите копию этого файла с моего хранилища Сюо§1е Эпуе (Ьйр8://(1г1уе.§оо§1е.сот/
Г11е/а/1§РСаОТТ9Хп6ВЙТ€Ур7§9Е8иосМу1аЕИ), а затем преобразуйте содержа-
щиеся в нем данные из формата ХМЬ в формат .сзу, используя для этого сценарий
в листинге 4.16. Но сначала нам нужно установить библиотеку 1хш1 при помощи
установщика р!р:
р!р ХпзХаИ 1хп1
Листинг 4.16. Сценарий хт1_раг§тд.ру
# Простой пример чтения данных из файла .хп! средствами РуХЬоп, используя библиотеку
# ХхпХ. Сначала нужно установить эту библиотеку при помощи установщика р1р:
# ЬХХрз://рур1.огд/ргозесХ/1хп1/
# Полезное руководство можно найти по этой ссылке ЫХр5://1хт1.с1е/ХиХогга1.Ыт1
# Используется экземпляр следующих данных
# ЬХХрз: //ар!. зХХоитзРеб.огд/1тес!/8ег1е8/оЬ8ег7аХ1оп5?8ег1е8_1б=ибРАТЕ& \
# ар1_кеу=ВАШ_КЛЮЧ_АР1
# Указываем "главу" или метод, который нужно импортировать из библиотеки 1хп1.
# В данном случае это еХгее, что означает ЕХепепХТгее
1топ 1хп1 тпрогХ еХгее
# Импортируем библиотеку сзу, чтобы создать выходной файл
тпрогХ сзу
# Задаем имя файла
ГХХепате = "116_ЕРЕ0_баХа"О
# Открываем файл данных для чтения, указывая режим гЬ
хт1_зоигсе_Р11е = ореп(Р11епате+".хт1","гЬ")0
# Передаем наш файл хт1_зоигсе_Р11е в качестве параметра методу еХгее.рагзе()
# библиотеки ХхпХ.
# Сохраняем результат в переменной хп1_бос
хп1_с1ос = еХгее.раг8е(хт1_5оигсе_Р11е)
# Начинаем обработку с получения корневого элемента текущего хп1-документа
с1осипепХ_гооХ = хп!_с1ос.деХгооХ()0
# Отображаем его, просто чтобы увидеть, какой он
рг!пХ(еХгее. ХозХг1пд(с1оситепХ_гооХ))
20 Мы пошагово рассмотрим использование интерфейсов АР1, подобных этому, в главе 5, но исполь-
зование этого документа на данном этапе позволит нам посмотреть, как разные форматы данных
влияют на наши взаимодействия с данными.
# Проверяем, что корневой элемент босипепГ_гоо1: является хорошо сформированным
# ХМ1_- элементом
1Г еГгее.1зе1етепГ(с1оситеп'1:_гоо1:):О
# Создаем выходной файл хт1_”+^ИепаР1е+.С5У
оиГриГ-НЛе = ореп("хт1_"+Г11епате+".сз7","м")
# Используем метод для записи иг1Лег() библиотеки С87 для записи строк данных
# в выходной файл оиГриГ_ГПе
оиГриО/гтЛег = С87.иг1Гег(оиГриГ_Г1Ле)
# Берем первый элемент нашего хп1-документа (используя босипепГ_гооГ[0])
# и записываем его ключи атрибутов как заголовки столбцов в наш выходной файл
ои1ри1_нгтЛег. игПегои(с1оситепГ_гоо1:[0]. аПггЬ. кеуз() )О
# Теперь обрабатываем в цикле каждый элемент нашего ХМ1_ файла
Гог сЫЛс1 1п с1оситеп1:_гоо1::0
# Теперь используем метод .7аТиез(), чтобы получить значения всех элементов
# в виде списка, а затем используем этот список напрямую с методом .мг1Гегом()
оиГриГ_мг1Гег. мг1Гегом(сЫ1с1. аГГгТЬ. 7аТиез())
# Закрываем файл .сз7, в который мы только что записали все данные
оиГриГ_П1е. с!озе()
О В данном случае файл данных не содержит ничего, что можно было бы исполь-
зовать в качестве имени файла (например, имя листа), поэтому мы просто сами соз-
даем имя и используем его как для загрузки исходных данных, так и в качестве
имени выходного файла.
О Значение режима использования файла (т. е. "г"), которое мы до сих пор указы-
вали во втором параметре функции ореп(), предполагало интерпретирование со-
держимого исходного файла как текст. Но поскольку библиотека 1хш1 ожидает бай-
товые, а не текстовые данные, то в данном случае мы указываем режим "гЬ" (геаб
Ъу1е§).
О Во многих случаях ХМЬ-содержимое плохо отформатировано. Чтобы удостове-
риться в том, что материал, который выглядит как качественный ХМЬ-материал, в
действительности является таковым, мы извлечем корневой элемент текущего
ХМЬ-документа и убедимся в том, что он работоспособен.
О Поскольку наш ХМЬ-материал в настоящее время сохранен как байтовые дан-
ные, нам нужно использовать метод еГгее.ГозГгГпдО, чтобы просматривать его в
этом виде.
О Благодаря библиотеке 1хш1, каждый элемент (или узел) ХМЬ нашего документа
имеет свойство аНтГЬ с типом данных РуШоп ШсГ (Шсбопагу — словарь). Все клю-
чи атрибута ХМЬ-элемента возвращаются в виде списка посредством метода
.кеуз() (Ьйр$://с1ос$.руШоп.ог|*/3/11Ьгагу/${скуре$.Мт1#{уре$тарр1п^). Поскольку
все элементы в нашем исходном файле идентичны, можно использовать ключи
первого из них, чтобы создать строку заголовков для нашего выходного файла.
О Библиотека 1хш1 (Иир8://1хт1.с1е/1и1ог1а1.Ыт1#е1етеп18-а ге-11818) преобразовыва-
ет ХМЬ-элементы в списки, в результате чего все элементы в документе можно
просматривать при помощи простого цикла 1"ог..Лп.
Как оказалось, версия ХМЬ данных по безработице имеет очень простую структу-
ру: это просто список элементов со всеми значениями, к которым мы хотим полу-
чить доступ, сохраненными в виде атрибутов.
В результате мы смогли извлечь значения атрибутов из каждого элемента в виде
списка и записать их напрямую в выходной файл .с§у всего лишь одной строкой
кода.
Конечно же, во многих случаях интересующие нас данные имеют более сложный
формат ХМЬ, например К88 или А1ош. В листинге 4.17 приводится пример обра-
ботки данных в более сложном формате ХМЬ. В частности, выполняется парсинг
канала К88 рассказов о науке и окружающей среде компании ВВС. Копию этого
исходного файла можно загрузить из моего хранилища Соо§1е Впуе
(Ьйр8://Дг1уе.§оо§1е.сот/й1е/(1/1гОак88ЬЕГтХхЕТ1роО]ТТпи06Р8У9§§2).
Листинг 4.17. Сценарий г§§_раг§!пд.ру
# Простой пример чтения данных из файла .хп! средствами РуТФоп, используя библиотеку
# 1хп1. Сначала нужно установить эту библиотеку при помощи установщика р1р:
# Ы=Хрз://рур1.огд/ргозес1:/1хт1/
# Используется экземпляр следующих данных:
# Н1Хр: //^еебз. ЬЬс1. со. ик/пемз/8с1епсе_апс1_еп71гоптеп1:/г58. хт!
# Указываем "главу" или метод, который нужно импортировать из библиотеки 1хп1.
# В данном случае это еХгее, что означает ЕХепепСГгее
1топ ХхтХ йпрогХ еХгее
# Импортируем библиотеку сзу, чтобы создать выходной файл
тпрогХ С87
# Присваиваем имя файла для простоты
В.Хелате = "ВВС Мемз - Зстепсе & ЕпуЕгоптепХ ХМ1_ Еееб"
# Открываем файл данных для чтения, указывая режим гЬ
хп1_зоигсе_Р11е = ореп(ВЛепапе+" .хт1","гЬ")
# Передаем наш файл хт1_зоигсе_Г1Ле в качестве параметра методу еХгее.рагзе()
# библиотеки 1хп1.
# Сохраняем результат в переменной хт!_с1ос'
хт1_с1ос = еХгее.рагзе(хт1_5оигсе_-Г11е)
# Начинаем обработку с получения корневого элемента текущего хп1-документа
босипепГ_гооГ = хпГ_бос.деГгооГ()
# Если корневой элемент с!оситепГ_гооГ является хорошо сформированным ХМ1_- элементом
ГГ еГгееЛ8еТепепГ(с1оситепГ_гооГ):
# Создаем выходной файл "г58_"+Шепате+" .С57"
оиГриГ_ГГ1е = ореп("г55_"+Г11епате+".с57","м")
# Используем метод для записи нгГГег() библиотеки С57 для записи строк данных
# в выходной файл оиГриГ_Г11е
оиГриГ_1л/гГГег = с87.мг1Гег(оиГриГ_Ше)
# Элемент боситепГ_гооГ[0] является "канальным" элементом
па1п_сЬаппе1 = боситепГ_гооГ[0]
# Метод ГГпд() возвращает !только! первый экземпляр имени элемента
агГ1сТе_ехапрТе = па1п_сЬаппеГ.Г1пб('ГГепГ )О
# Создаем пустой список для хранения будущих заголовков столбцов
Гад_1л2Г = []
Гог сЫЛб 1п агГ1сТе_ехапр1е.1Гегс1е5сепс1апГ8():О
# Добавляем каждый тег к нашему будущему списку заголовков
Гад_11зГ. аррепб (сЫЛб. Гад )О
# Если текущий тег имеет какие-либо атрибуты
ГГ сЫГс!.аГГг1Ь:О
# Обрабатываем в цикле ключи атрибута в теге
Гог аГГгГЬиГе_пате Гп сЫТс!.аГГг1Ь.кеу8() :0
# Добавляем имя атрибута к списку Гад_ПзГ заголовков столбцов
Гад_1л5Г.аррепс1(аГГгГЬиГе_пате)
# Записываем содержимое списка Гад_ТГзГ в выходной файл как заголовки столбцов
оиГриГ_игГГег.игГГегои(Гад_ТГ5Г)0
# Теперь нужно извлечь !все! элементы <ГГеп> из исходного файла
# Поэтому вместо метода Ппс1() используем метод Ппс1а11()
Гог ГГеп Гп таГп_сЬаппеГ.ГГпс1аЩ'ГГеп'):
# Пустой список для хранения содержимого новой строки
пем_гом = []
# Получаем содержимое каждого элемента, используя список тегов
Гог Гад Гп Гад_1Л8Г:
# Если элемент содержит что-либо с данным именем тега
II2 11ет.-Р1пскех1(1ад):
# добавляем его в конец новой строки
пем_гом. а ррепб (1Леп. Ггпд1ех1: (1=ад))
# В противном случае, проверяем, что это атрибут тзРегпаЫпк
еИ!2 ±ад == "тзРегпаЫпк":
# Берем его значение из элемента <ди!с!>
# и добавляем его в конец нашей строки
пем_гом. аррепс1(11:ет. Г1_пс1(1 ди!с11). де1:( "тзРегпаЫпк"))
# Записываем новую строку в выходной файл
ои!: ри 1_нг1Ле г. п г1Ле гош (пем_гом)
# Закрываем файл .сзу, в который мы только что записали все данные
ои^ри^Ие. с!озе()
О Как всегда, мы хотим сбалансировать материал, обрабатываемый программно и
просматриваемый визуально. При просмотре данных становится ясно, что инфор-
мация каждой статьи сохраняется в отдельном элементе 1Леп. Но, поскольку копи-
рование имен отдельных тегов и атрибутов потребовало бы слишком много време-
ни и было бы чревато ошибками, мы обработаем только один элемент Нет и созда-
дим список всех тегов (и атрибутов) в нем, которые мы затем используем в
качестве заголовков столбцов в выходном файле .с§у.
О Метод 1Легс1е8сепс1ап1:8() специфичен библиотеке 1хш1. Он возвращает только по-
томков элемента ХМЬ, тогда как более широко употребляемый метод 1Лег() воз-
вращает как сам элемент, так и его потомков (Ьйр8://1хт1.(1е/ар1.Ы:т1#йегайоп).
О Использование сЫЛбЛад позволит извлечь текст Хаднате элемента потомка. На-
пример, для элемента <риЬБа1:е> будет возвращено значение риЬБа1:е.
О В нашем элементе <1Леп> атрибут имеет только один тег, но мы все равно хотим
включить его в наш выход.
О Метод кеуз() возвращает список всех ключей в списке атрибутов, принадлежа-
щих тегу. Его имя нужно получить в виде строки, а не в виде списка с одним эле-
ментом.
О Целью всего цикла 1"ог для списка аг1:1с1е_ехатр1е было лишь создание списка
заголовков 1ад_Н.51, но это стоило приложенных усилий.
Как видно в листинге 4.17, использование в РуЙюп библиотеки 1хш1 позволяет вы-
полнять парсинг (синтаксический разбор) более сложного ХМЬ документа доволь-
но легко.
Тогда как формат данных ХМЬ продолжает пользоваться популярностью для кана-
лов новостей и другого рода файлов, некоторые его особенности делают его менее
удобным для обработки крупных объемов канальных данных современного Интер-
нета.
Прежде всего, это вопрос размера. Тогда как файлы ХМ Б могут содержать удобное
описание своего содержимого, что уменьшает надобность в отдельных словарях
данных, тот факт, что большинство элементов содержат как открывающий, так и
соответствующий закрывающий тег (например, <1Леп> и </1Леп>), делает этот фор-
мат несколько многословным, т. е. ХМЬ-документ содержит большой объем текста,
не являющегося непосредственного его содержимым. Это обстоятельство не имеет
большого значения в случае документов, содержащих несколько десятков или даже
тысяч элементов, но при необходимости обрабатывать миллионы или даже бил-
лионы сообщений социальных сетей, весь этот избыточный текст может сущест-
венно тормозить обработку.
Во-вторых, формат ХМЬ не так уж и сложно преобразовать в другие форматы дан-
ных, однако этот процесс не совсем гладкий. Библиотека 1хш1 (среди прочих) по-
зволяет довольно легко выполнять парсинг ХМЬ-содержимого средствами РуЙюп,
но решение этой же задачи посредством языка, заточенного под работу в Интерне-
те, например 1ауа8спр1, сопряжено с большими сложностями и трудностями. Учи-
тывая широкое использования языка 1ауа8спр1 в Интернете, совсем неудивительно,
что в какой-то момент был разработан канальный формат данных, работающий без
проблем, характерных для этого языка. Как мы увидим в следующем разделе, мно-
гие ограничения формата данных ХМЬ решаются объектно-подобной природой
формата 18ОХ, который в настоящее время является наиболее популярным форма-
том для канальных данных в Интернете.
Формат ^ОМ: данные следующего поколения
В принципе, формат 18ОХ похож на формат ХМ Б в том отношении, что в нем ис-
пользуется принцип вложения для группирования связанных элементов информа-
ции в записи и поля. Формат 18ОХ также достаточно удобочитаем для людей, хотя
то обстоятельство, что он не поддерживает комментарии, означает, что для каналов
18(Ж-данных могут требоваться более надежные словари данных, чем для доку-
ментов ХМ Б.
Для начала рассмотрим пример небольшого документа 18(Ж, показанного в лис-
тинге 4.18.
Листинг 4.18. Пример документа
{
"аи1±ог": "Зизап Е. МсСгедог",
"Ьоок": {
"Ьоок11К1_": //пш.огеШу.соп/ИЬгагу/71ем/ргас1:1са1-ру1:Ьоп-с1а1:а/
9781492091493/",
"ЬоокАЬз^гас!:": "В наборах данных нас ждут фантастические открытия и ценные истории,
и эта книга поможет нам открыть их.",
"риЬ0а1:е": "2022-02-01"
ъ
"рарегз": [{
"рарегОРЬ": "Ы:1:р8://1^лЛл/.и5еп1х.огд/соп1:егепсе/и8еп1х5есиг11:у15/
1:есЬп1са1-5е881оп8/рге8еп1:а1:1оп/тсдгедог",
"рарегТШе": "Тт/езМдаМпд ±Ье сотри!ег зесигПу ргасН-сез апс! пеебз о!2
доигпаИз^з",
"риЬ0а1:е": "2015-08-12"
},
{
"рарегОРЬ": "НИрз: //ш. асЪл/еЬ. огд/ап1 ЬоТоду/1л118- 5104. рей7",
"рарегТШе": "РгесНсНл/е епЬескИпдз 1"ог Ьа1:е зреесЬ с1е1ес1гоп оп
Ъл/Шег",
"риЬ0а1:е": "2018-10-31"
}
]
}
Подобно формату ХМЬ, грамматические правила формата 18(Ж довольно простые:
документы 18(Ж имеют всего лишь три различных структуры данных, которые
были представлены в документе листинга 4.18. А именно:
Пары ключ/значение
Технически, все в 18(Ж-документе является парами ключ/значение, где ключ
заключен в кавычки и расположен слева от двоеточия (:), а значение указывается
справа от двоеточия. Обратите внимание на то, что тогда как ключ всегда дол-
жен быть строкой, то его значения могут быть строками (как в аи1±ог), объекта-
ми (как в Ьоок) или списками (как в рарегз).
Объекты
Элементы объектов заключены в фигурные скобки — {}. Документ в листинге
4.18 содержит в целом четыре объекта: сам документ (обозначен фигурными
скобками, выровненными по левому краю), объект Ьоок и два безымянных объ-
екта в списке рарегз.
Списки
Списки заключаются в квадратные скобки ([]) и могут содержать только объек-
ты, разделенные запятыми.
Хотя оба формата ХМЬ и 18ОХ можно использовать для кодирования одних и тех
же данных, в их возможностях есть существенные различия. Например, формат
18(Ж не позволяет указать тип документа с1ос-1:уре, а также не поддерживает ком-
ментарии. Кроме этого, тогда как в ХМЬ списки в некоторой степени подразуме-
ваются (любой повторяющийся элемент функционирует как нечто вроде списка), то
в 18СЖ списки обозначаются заключением их элементов в квадратные скобки — [].
Наконец, хотя формат 18СЖ был разработан с учетом 1ауа8спр1, его структуры, как
вы, возможно, заметили, очень похожи на типы «Яс! и Из! языка РуЙюп. Это об-
стоятельство и делает задачу парсинга документов 18СЖ, а также посредством языка
1ауа8спр1 (и рядом других языков) средствами РуЙюп достаточно прямолинейной.
Чтобы продемонстрировать все эти возможности, в листинге 4.19 приводится код
для парсинга тех же самых данных, что и в листинге 4.16, полученных посредст-
вом АР1-интерфейса веб-сайта ЕКЕЭ, но в формате 18СЖ. Этот файл данных
можно загрузить с моего хранилища Ооо§1е Эпуе по следующей ссылке:
Ьйр8://(1г1уе.§оо§1е.еот/й1е/(1/1МрЬ2Г5дУ§НпКсШ8ТхТтЬОРНЫ(1еВ8д/у1елу?и8р=
8Йагт§.
Листинг 4.19. Сценарий ]§оп_раг§тд.ру
# Простой пример чтения данных из файла .дзоп средствами Ру1±оп,
# используя встроенную библиотеку дзоп. Используется экземпляр следующих данных:
# Ь1Д:р5://ар1.81:1ои18Геб.огд/Ггеб/8ег1е5/оЬ8ег7а1:1оп5?5ег1е5_1с1=ибРАТЕ& \
# П1е_1:уре=д зоп&ар1_кеу=ВАШ_КЛЮЧ_АР1
# Импортируем библиотеку дзоп для работы с входным файлом
1прог1: дзоп
# Импортируем библиотеку сзу, чтобы создать выходной файл
1прог1: сз\/
# Задаем имя файла
Шепапе = "116_ЕРЕ0_с1а1:а"
# Открываем файл данных для чтения, указывая режим г
35ОП_зоигсе_Г11е = ореп(Г11епате+".ззоп","г")
# Передаем наш исходный файл ззоп_зоигсе_Г11е как аргумент (ингредиент)
# методу (рецепту) 1оаб() библиотеки дзоп.
# Сохраняем результат в переменной дзоп_с1а1:а
дзоп_с1а1:а = дзоп.1оас1(з8Оп_5оигсе_Г11е)
# Создаем выходной файл "ззоп_"+Г11епате+".сз7"
ои1ри1_ГИе = ореп("ззоп_"4Ч11епате+".с87","и")
# Используем метод для записи мг11:ег() библиотеки С57 для записи строк данных
# в выходной файл ои1ри1_ГИе
ои!ри1_игг1ег = сз7.мг11:ег(ои1:ри1:_Г11е)
# Используем ключи первого элемента (в позиции 0) в качестве заголовков столбцов
ои!ри1_игг1ег. мгИ=егом(11з1:( дзоп_с1а1:а [ "оЬзеп/аЕЕопз" ] [0]. кеуз()) )О
1"ог оЬд 1п дзоп_с1а1:а["оЬ8ег7а1:1оп5"]
# Создаем пустой список для хранения значений каждого объекта
оЬд_7а1иез = []
# Для каждого ключа (который будет столбцом) в каждом объекте
1"ог кеу, уаТие 1п оЬд .1Летз() :О
# Отображаем ее содержимое, просто чтобы посмотреть, как его видит код
рг1п1:(кеу,7а1ие)
# Добавляем значения в наш список
оЬд_7а1иез.аррепб(7а1ие)
# Получив всю строку, записываем данные в выходной файл
ои1:ри1:_мг11:ег.мг11:егом(оЬз_7а1ие8)
# Закрываем файл .С37, в который мы только что записали все данные
ои^ри^-ГИе. с!озе()
О Поскольку библиотека дзоп интерпретирует каждый объект как объект представ-
ления словаря (Мф$://с1ос$.ру1йоп.ог§/3/11Ьгагу/$1(куре$.Мт1#(11с1-У1е\т$), то нам
нужно дать указание программе РуШоп преобразовать его в обычный список, ис-
пользуя для этого функцию Пз1:().
О В большинстве случаев самым простым способом найти имя (или ”ключ”) глав-
ного объекта 18(Ж в обрабатываемом документе будет простой визуальный про-
смотр документа. Но поскольку данные 18(Ж часто отображаются в одной строке,
то получить лучшее представление о ее структуре можно, вставив эту строку в поле
онлайнового форматера 18ОХЬт1 (Ьйр8://]8оп1т1:.сот/). Таким образом мы уви-
дим, что наши целевые данные являются списком с ключом оЬзегуа^топз.
О Вследствие особенностей работы библиотеки ]зоп, если попытаться просто запи-
сывать строки напрямую, мы получим значения, помеченные а не значения
самих данных. Поэтому нам нужен еще один цикл, который обрабатывает по одно-
му все значения в каждом объекте дзоп и добавляет их в конец списка оЬз_уа1иез.
Хотя формат 18(Ж менее удобочитаем для людей, чем формат ХМЬ, у него есть
другие достоинства, о которых мы уже упоминали ранее, например меньший раз-
мер файлов и более широкая совместимость кода. Подобным образом, тогда как
формат 18(Ж не так описателен, как формат ХМЬ, источники данных 18(Ж (часто
АР1-интерфейсы) обычно достаточно хорошо задокументированы, что уменьшает
необходимость предполагать, что данная пара ключ/значение может описывать.
Но, как и вся работа с данными, первый шаг к выпасу данных в формате 18ОХ со-
стоит в формировании как можно большего понимания его контекста.
Где же вы, знаки непечатаемые?
В отличие от файлов .18У и .Ш, и самого языка РуЙзоп, непечатаемые знаки не
играют никакой роли ни в ХМЬ, ни в 18(Ж. При условии, что все угловые и фи-
гурные скобки и другие знаки пунктуации расставлены должным образом, все
содержимое документов этих форматов можно втиснуть в одну строку, и они
будут работать без каких бы то ни было проблем. Примеры, которые мы рас-
сматривали в этой главе, были опрятно отформатированы, но на практике редко
можно встретить подобное оформления документов этих типов, особенно в Ин-
тернете. Хотя многие веб-браузеры отображают документы ХМЬ в представле-
нии с правильными отступами (см., например, ежедневную карту сайта газеты
Ьо8 Ап§е1е8 йше8 — Ьйр8://1айте8.сот/8йетар-202101.хш1), большая часть
данных 18ОХ отображаются как следующие одна за другой строки текста (как в
случае с канальными данными реального времени системы СШ В1ке —
Ьир8://Гее(18.с1йЬ1кепус.сот/8Ш1оп8/8Ш1оп8.]8оп).
Поскольку эффективный парсинг данных любого из этих форматов требует по-
нимания его общей структуры, первый ключевой шаг состоит в получении
должным образом отформатированного представления содержимого любого
обрабатываемого вами файла канальных данных. В случае хорошо структури-
рованного содержимого ХМЬ для этого достаточно открыть файл (или 1ЖЬ-
адрес) в веб-браузере21.
Для файлов ^8оп меньшего размера их данные можно скопировать непосредст-
венно из самого файла (выбрав все содержимое, нажав комбинацию клавиш
<С1г1>+<А>, и далее скопировать, нажав комбинацию клавиш <С1г1>+<С>), а
затем вставить в онлайновое средство форматирования типа 18ОХЫп1
(Ьйр8://]8оп1тйсош/) или 18(Ж Гогтайег (Ьйр8://]8ОпГогтайег.ог§/]8Оп-ргейу-
ргт€). Но в случае файлов особенно большого размера или отсутствия доступа
к Интернету, можно создать новый, отформатированный файл 18ОХ из неот-
форматированного исходного файла, выполнив в терминале следующую коман-
ду РуЙзоп:
са1: идТу.дзоп | ру1±оп -пдзопЛоо! > рге^Гу.дзоп
где идТу.дзоп обозначает исходный ^отформатированный файл 18ОК В резуль-
тате исполнения этой команды будет создан новый файл рге^у. дзоп, который
затем, чтобы просмотреть структуру документа, можно открыть в любом тек-
стовом редакторе или редакторе кода.
21 Если страница отображается с применением таблицы стилей, как в случае с каналом ВВС, с
которым мы работали в сценарии в листинге 4.17, для просмотра "сырого” ХМЬ-содержимого нужно
щелкнуть правой кнопкой по странице и в открывшемся контекстном меню выбрать последо-
вательность команд Веуе1орег Тоок | У1е\т Ра§е $оигсе.
Работа с неструктурированными данными
Как обсуждалось в разд. "Структурированные и неструктурированные данные"
ранее в этой главе, процесс создания данных зависит от внедрения в информацию
некой структуры, без наличия которой мы не сможем выполнять методический
анализ информации или извлекать из нее смысл. Хотя неструктурированная ин-
формация часто содержит большие фрагменты созданного человеком ”свободного”
текста, как табличные, так и канальные данные сравнительно структурированы и,
что более важно, машиночитаемы.
В противоположность, когда мы имеем дело с неструктурированными данными,
наша работа всегда связана с приближениями: мы не можем быть уверенными в
том, что наши программные усилия по выпасу возвратят точную интерпретацию
лежащей в основе информации. Это объясняется тем, что большинство неструкту-
рированных данных являются представлением содержимого, предназначенного для
восприятия и интерпретирования людьми. А как мы рассматривали в главе 2. ком-
пьютеры могут обрабатывать большие объемы данных намного быстрее и с мень-
шим количеством ошибок, чем люди, но их все равно можно сбить с толку не-
структурированными данными, которые не представляли бы никакой проблемы для
людей. Например, слегка модифицированный дорожный знак ”Стоп” компьютер
воспринимает как знак ограничения скорости (йир8://8рес€гиш.1еее.ог§/саг84йа€-
ШпкАгап8рогШ1оп/8еп8ОГ8/811§й€-8€гее€-81§п-тос11йсайоп8-сап-Гоо1-тас11ше-
1еагпт§-а1§ог1Й1Ш8). Это означает, что при работе с данными, не читаемыми ком-
пьютерами, нужно самостоятельно выполнять их дополнительную проверку и ве-
рификацию. Отметим, что посредством языка РуЙюп такие данные все же можно
преобразовать в более удобный для работы формат.
Текст на основе изображений:
доступ к данным в формате РОЕ
Формат РОГ (РогШЫе Восишеп! Рогша! — формат переносимого документа) был
создан в начале 90-х годов прошлого столетия как механизм для сохранения визу-
альной целостности электронных документов из обширного спектра источников,
будь то созданных программами текстовых редакторов или взятых из печатных ма-
териалов22. Сохранение внешнего вида документов также означало, что в отличие
от машиночитаемых форматов (например, документов, созданных программными
средствами обработки текстов), было трудно извлечь или изменить содержимое
таких документов, а это важная возможность для создания широкого спектра мате-
риалов, от цифровых версий контрактов до формальных писем.
22 Дополнительную информацию см. на странице АЬоп1 РЭГ веб-сайта компании АбоЬе
(кПр8://асгоЬаЬа(1оЬе.сот/и8/еп/асгоЬа1:/аЬо111>а(1оЬе-рсИ.111:т1).
Иными словами, трудность выпаса данных документов РИГ была на первых порах
заложена в них преднамеренно. Но поскольку возможность доступа к данным пе-
чатных документов является общей проблемой, то работа в области оптического
распознавания символов (ОРС) началась еще в конце XIX столетия23. Более того,
цифровые средства ОСК были доступны в виде программных пакетов и интернет-
услуг на протяжении нескольких десятилетий, поэтому, хотя эти средства и не иде-
альны, данные в файлах этого формата все же не полностью недоступны.
Когда следует работать с текстом б формате РОЕ
Работа с файлами РВЕ — это крайняя мера (так же как и парсинг данных веб-
ресурсов (\уеЪ-8Сгарт§), как мы рассмотрим далее в главе 5). По большому счету,
если есть возможность избежать использования информации из РВЕ документов,
следует воспользоваться этой возможностью. Поэтому процесс извлечения инфор-
мации из РВЕ-документов, как правило, требует проверки точности этих результа-
тов и является неотъемлемой частью любого рабочего процесса выпаса данных на
основе РВЕ-документов. При всем этом существует громадный объем информации,
доступной только в виде изображений или отсканированных документов РВЕ, и
платформа РуШоп предоставляет эффективный способ для извлечения достаточно
точной копии текста таких документов.
Где искать РОЕ-документы
Если вы уверены, что требуемые вам данные существуют только в формате РВЕ,
тогда можно (и нужно) воспользоваться советами во врезке "Интеллектуальный
поиск конкретных типов данных" ранее в этой главе и выполнить поиск файлов
этого типа в Интернете. Также, при запросе информации у какого-либо лица или
организации, наиболее вероятно, что они предоставят ее вам в РВЕ-формате, и вам
нужно будет самому решить задачу, как извлечь из полученных документов тре-
буемую вам информацию. Таким образом, вам не нужно будет долго искать РВЕ-
документы, к сожалению, скорее они сами найдут вас.
Выпас РОЕ-данных, используя РуШоп
Поскольку РВЕ-документы могут содержать не только отсканированные изображе-
ния, но и машиночитаемый текст (например, документы программных средств об-
работки текстов), то текстовую информацию из подобных файлов можно извлечь
программным образом со сравнительно небольшим количеством ошибок. И хотя
это звучит просто, этот метод все равно может быть ненадежным, поскольку файлы
.рбГ создаются с использованием широкого спектра кодировок, которые могут
23 См. статью ПгзгирИче Реуе1ортеп18 т РоситегП Кесо§пШоп, автор Оеог§е Ха§у, в журнале РаИегп
КесорпНюп ЬеНегх, № 79, (2016): стр. 106-112, ЬПр8://(1о1.ог§/10Л016/]\ра1тес.2015.11.024. Доступна
здесь: ЬПр8://ес8е.гр1.е(1и/~па§у/РВЕ_сЬгопо/ 2016_РКЕ_В18гирйуе_а8РиЫ18Ье(1.р(1Г.
трудно поддаваться правильному определению программой. Поэтому, хотя про-
граммный подход извлечения текста из файлов .рсД может давать результаты высо-
кой точности, возможность его применения для любого рсй-файла имеет низкую
вероятность.
Мы опишем процесс использования ОСК для распознавания и извлечения текста из
файлов .рсД. Он состоит из двух шагов:
1. Преобразование страниц документа в отдельные изображения.
2. Оптическое распознавание символов на изображениях страниц, извлечение тек-
ста и запись полученного текста в отдельные текстовые файлы.
Неудивительно, что для выполнения всех этих задач нам нужно установить еще
несколько библиотек РуЙюп. Первым делом нам нужна пара библиотек для преоб-
разования страниц РПР-документов в изображения. Первая из них, библиотека об-
щего назначения рорр1ег, устанавливается, исполняя в терминале следующую ко-
манду:
зис1о ар± гпз^аИ роррТег-иГПз
Эта библиотека нужна для обеспечения работоспособности другой, специфичной
для РуЙюп, библиотеки рйЕ21гпа§е. Как можно судить по ее названию, библиотека
рсй2ппа§е используется для преобразования файла .рсД в ряд изображений. Уста-
навливается эта библиотека, исполняя в терминале следующую команду:
р1р гпз^аИ рсК21паде
Далее нам нужно установить инструменты для реализации процесса оптического
распознавания символов. Первый из них — библиотека общего назначения
1е88егас1-осг (Ьйр8://§йЬиЬ.сош/€е88егас1:-осг/1:е88егас1:), которая использует машин-
ное обучение для распознавания текста в изображениях. Для установки выполните
в терминале следующую команду:
зис1о ар-С-де1: тпз^аП 1:е88егас1:-осг
Второй инструмент — библиотека РуЙюп ру1е88егас1, использующая библиотеку
1е88егас1-осг. Устанавливается она следующей командой:
р!р гпз^аИ ру^еззегас!:
Наконец, нам нужна вспомогательная библиотека компьютерного видения для
РуЙюп, которая заполняет промежуток между библиотекой для обработки изобра-
жений страниц и библиотекой для оптического распознавания символов. Это биб-
лиотека орепсу-руйюп, которая устанавливается, исполнив в терминале следую-
щую команду:
р1р гпз^аИ орепсу-руТФоп
Вот и все. Это может выглядеть как большое количество дополнительных библио-
тек, но следует иметь в виду, что с технической точки зрения мы используем здесь
продукт машинного обучения, одну из тех ”умных” технологий интеллектуальной
обработки данных, которая лежит в основе большей части ”искусственного интел-
лекта”. К счастью для нас, библиотека Те88егас1 сравнительно надежная и широко-
используемая: хотя она была создана в начале 80-х годов прошлого столетия ком-
панией Не\у1еИ-Раскагс1 как проприетарная система, в 2005 г. она была выложена в
открытый доступ и в настоящее время поддерживает свыше 100 языков. Поэтому
решение в листинге 4.20 можно использовать и с другими языками, кроме англий-
ского.
Листинг 4.20. Сценарий рсН_рагзтд.ру
# Простой пример чтения данных из файла . рбГ средствами Ру1±оп,
# используя библиотеку рбГ21паде для преобразования файла в изображения, а затем
# используя библиотеки орепС\/ Ьпа Ъеззегас! для извлечения текста из изображений
# Исходные данные были загружены со следующего веб-сайта:
# Ы^рз: //Шез. зПоит-зГес!. огд/Г11ез/Ы:с1ос5/риЫ1са1:1оп5/раде1-есоп/2020/12/01/ \
# ипетрТоупеп!: - 1пзи гапсе - а -1:г 1ес1 - апс1 -1:гие - заГе1:у - пе1:_5Е. рс1Г
# Импортируем встроенную библиотеку оз Ру1±оп для создания новой папки для хранения
# преобразованных изображений и выходного текста
1прог1: оз
# Импортируем метод сот/ег1:_Ггот_ра1±() из библиотеки рс1Г21таде
Ггоп рбГ21паде 1прог1: сопуег1:_Ггот_ра1±
# Встроенная библиотека д!оЬ используется для обработки в цикле всех файлов в папке,
# которые имеют определенное расширение файла, например рпд
1прог1: д!оЬ
# с\/2 - это рабочее имя библиотеки орепС\/
1прог1: су2
# Библиотека ру^еззегас!: используется для взаимодействия с процессом ОСР 1еззегас1:
1прог1: ру^еззегас!:
# Мы будем использовать название рбГ для присвоения имен нашим сгенерированным
# изображениям и текстовым файлам
рс!Г_пате = "БаГе^уМе!"
# Исходный файл рс!Г должен находиться в той же самой папке, что и этот сценарий Ру1±оп
рбГ_зоигсе_Г11е = рбГ_папе+" .рс1Г"
# Если папки с таким же именем, как у нашего исходного файла рбГ, не существует:
1Г оз.ра1±.15с11г(рс1Г_пате) == ЕаТзе:
# создаем новую папку с таким именем
1:агде1:_Го1с1ег = оз.ткс11г(рс1Г_пате)
# Сохраняем все страницы РОЕ в переменной
радез = сопуег1:_Ггот_ра1:Ь(рс1Г_5оигсе_Г11е, 300)0
# Обрабатываем в цикле все преобразованные страницы, нумеруя их, чтобы номера страниц
# можно было использовать для обозначения полученных изображений
1ог раде_пип, раде 1п епитега1е(радез):
# Создаем уникальное имя файла для каждого изображения страницы, объединяя имя
# папки и номер страницы
ГИепате = оз.ра1±.зо1п(рс11г_пате,"р"+81:г(раде_пит)+".рпд")0
# Сохраняем изображение страницы в системе
раде.зауе^Пепапе, ' РГЧС')
# Далее обрабатываем все файлы в папке с расширением файла .рпд
1ог 1пд_Г1Ле 1п д1оЬ.д1оЬ(о8.ра1±.до1п(рс11_пате, '*.рпд')):О
# Заменяем косую черту в имени файла изображения точкой
1епр_папе = 1тд_Г11е.гер1асе(
# Получаем уникальное имя страницы (например, р2) из 1епр_папе
1ех1:_Г11епате = 1етр_пате.8р11_1:(''. ")[1]О
# Далее создаем новый файл для записи (который также должен быть в целевой папке),
# присваивая ему то же самое имя, что и у соответствующего файла изображения,
# но с расширением файла .1x1
ои1ри1_Г1Ле = ореп(оз.ра1Ь.зо1п(рб1_пате,1ех1_11Лепапе+".1x1"), V)
# Интерпретируем изображение, используя библиотеку с\/2
1пд = С72.1пгеаб(1тд_11Ле)
# Создаем новую переменную для хранения результатов метода 1паде_1о_81г1пд()
# библиотеки ру1ез8егас!
сопуег1ес1_1ех1 = ру1ез8егас1.1таде_1о_81г1пд(1тд)
# Записываем извлеченный текст в выходной файл
Ои1ри1_111е.мг11е(соп7ег1ес1_1ех1)
# Закрываем выходной файл
ои!ри1_Г11е.с!озе()
О Здесь мы передаем путь к нашему исходному файлу (в данном случае это просто
имя файла, поскольку он находится в той же самой папке, что и наш сценарий) и
требуемое разрешение в точках на дюйм (ИР1 — <1018 рег шей) выходных изображе-
ний методу сот/ег1_Ггоп_ра1Ь(). Использование более низкого разрешения ускорило
бы обработку, но могло бы значительно ухудшить точность обработки по оптиче-
скому распознаванию символов. Разрешение в 300 ИР1 является стандартным "пе-
чатным” разрешением.
О Здесь мы используем метод до1п() библиотеки 08 для сохранения новых файлов в
целевой папке. Также используем функцию з1:г() для преобразования номера стра-
ницы в строку, которая используется для создания имени файла.
О Обратите внимание на то, что выражение *.рпд означает "файл с любым именем с
расширением файла .рп§”. Функция д!оЬ() создает список всех имен файлов в пап-
ке, в которой хранятся наши изображения (имя которой в данном случае равно зна-
чению переменной рс!Г_пате).
О Манипулирование строками не представляет никаких проблем! Чтобы создать
уникальные (но соответствующие именам исходных файлов изображенй) имена
файлов для наших файлов .Ш, полученных в результате обработки ОСК файлов
.рп§, нам нужно извлечь уникальное имя страницы из переменной 1пд_Г1Ле, чье зна-
чение начинается со строки 8а^е1уМе1/ и заканчивается строкой .рпд. Косая черта
заменяется точкой, в результате чего мы получаем нечто наподобие значения
Ба^уМеТ:. р1. рпд, после обработки которого функцией зр11±() по точкам получаем
список ["ЗаГе^уМеГ', "р1", "рпд"]. Наконец, мы можем получить имя страницы в
позиции 1. Выполнение всех этих манипуляций требуется по той причине, что мы
не можем быть уверенными, что функция д!оЬ() извлечет из папки с изображения-
ми, например, значение р1.рпд первым, или что она вообще будет извлекать изо-
бражения по порядку.
В принципе, этот сценарий служит нашим целям: посредством нескольких десятков
строк кода он сначала преобразовывает многостраничный рбГ-файл в изображения,
а затем записывает их содержимое (большую часть) в последовательность новых
текстовых файлов.
Но подход типа ”все в одном” также имеет свои ограничения. Преобразование фай-
ла РЭБ в изображения или изображений в текст представляет собой задачу, кото-
рую может требоваться выполнять довольно часто, но не всегда в связке с другими
операциями. Иными словами, в долгосрочной перспективе для решения этой задачи
было бы гораздо полезнее использовать два отдельных сценария, исполняя их по-
следовательно — один после другого. Более того, немного повозившись, мы могли
бы разделить этот сценарий таким образом, чтобы можно было преобразовывать в
изображения любые файлы РЭБ или любые изображения в текст, без необходимо-
сти создавать какой бы то ни было код. Неплохо, не так ли?
Этот процесс повторного продумывания и реорганизации работающего кода назы-
вается рефакторингом кода (геГас!огт§), или перепроектированием. Можно про-
вести аналогию с корректурой или редактированием обычного текста, при этом
цель в обоих случаях одинаковая: сделать конечный результат более понятным и
простым для понимания. И так же как и в случае с документированием, рефакто-
ринг кода на самом деле является всего лишь еще одним важным способом мас-
штабирования вашей работы по выпасу данных, поскольку он намного упрощает
переиспользование вашего кода. Мы рассмотрим разные стратегии для рефакто-
ринга кода и многократного использования сценариев в главе 8.
Обращение к таблицам РОЕ посредством ТаЬи1а
Посмотрев на текстовые файлы, созданные в результате исполнения сценария в
листинге 4.20, можно заметить, что они содержат много "лишнего” материала на-
подобие номеров страниц, заголовков, символов разрыва страницы и прочего ”хла-
ма” (Ъйр8://еп.лУ1к1ре(Иа.ог§/лУ1к1/СгиЙ). В то же самое время в них отсутствуют
некоторые ключевые элементы, такие как изображения и таблицы.
И хотя анализ изображений выходит за рамки данной книги (это намного более
специализированная область), стоит отметить, что нередко документы РЭЕ содер-
жат таблицы с данными, которые могли бы представлять для нас интерес. На са-
мом деле эта задача настолько распространенная, скажем, в области СМИ, что
группа журналистов спроектировала и создала инструмент, называющийся ТаЪи1а
(кйр8://€аЬи1а.€ескпо1о§у/), предназначенный специально для ее решения.
Инструмент ТаЬи1а не является библиотекой РуЙюп — это отдельная программа.
Чтобы попробовать этот инструмент, загрузите установщик для вашей системы
(Ъйр8://€аЬи1а.€ескпо1о§у/#(1оууп1оа(Мп81:а11). Для машин СйгошеЬоок и 1лпих нуж-
но загрузить файл .х!р и следовать инструкциям в файле КЕАЭМЕ.Ш. Независимо
от используемой машины, вам, вероятнее всего, потребуется сначала установить
программную библиотеку 1ауа, выполнив в терминале следующую команду:
зис1о ар± т.п51аП де^аиИ-дге
Подобно некоторым другим инструментам с открытым исходным кодом, которые
мы рассмотрим в последующих главах (например, инструмент ОрепКейпе, который
я использовала для подготовки некоторых образцов данных для главы 2 и вкратце
рассматриваю в главе 77), инструмент ТаЬи1а работает ”за кулисами” (хотя некото-
рые его операции отображаются в окне терминала), и взаимодействие с ним осуще-
ствляется через браузер. Такой подход позволяет получить доступ к традиционно-
му графическому интерфейсу, освобождая компьютерные ресурсы для интенсив-
ной работы с данными.
Заключение
Будем надеяться, что примеры кода в этой главе позволили вам начать формиро-
вать представление о широком спектре задач по выпасу данных, которые можно
решить посредством сравнительно небольшого объема кода РуЙюп, используя ком-
бинацию тщательно подобранных библиотек и нескольких фундаментальных поня-
тий программирования, с которыми мы познакомились в главе 2.
Возможно, вы заметили, что за исключением примера с РЭЕ-документом, выход
всех других примеров в этой главе был, по сути, в формате .С8У. Это никакая не
случайность. Формат .С8У не только эффективен и универсален, но, как оказывает-
ся, табличные данные требуются для выполнения практически любых базовых ста-
тистических анализов или визуализаций. Это не означает, что анализ нетабличных
данных невозможен; как раз наоборот, это то, чему посвящена большая часть со-
временных исследований в области компьютерных наук (например, машинное обу-
чение). Но поскольку эти системы часто сложные и непрозрачные, они не очень
подходят для того типа выпаса данных, на котором мы концентрируемся в этой
книге. Таким образом, мы будем направлять нашу энергию на такой тип анализов,
которые могут помочь нам понять, объяснить и выразить новые знания об окру-
жающем нас мире.
В этой главе мы концентрировались на табличных данных и заранее подготовлен-
ных версиях канальных данных, далее в главе 5 мы рассмотрим, как использовать
средства РуЙюп совместно с АР1-интерфейсами и парсингом данных для получения
данных из систем и, при необходимости, непосредственно из самих веб-страниц.
ГЛАВА 5
Доступ к интернет-данным
Интернет — это невероятный источник данных. Возможно, именно по этой причи-
не данные стали доминирующей составляющей нашей социальной, экономической,
политической и даже творческой жизни. В главе 4 наши усилия по выпасу данных
концентрировались на процессе доступа и переформатирования файловых данных,
уже сохраненных на локальных устройствах или в сетевом облаке. В то же самое
время, большая часть этих данных первоначально была получена из Интернета —
были ли они загружены с веб-сайта, как данные по безработице, или получены по
ИКЬ-адресу, как данные системы СШ В1ке. Но теперь, когда мы знаем, как исполь-
зовать средства РуЙюп для парсинга и преобразования разных файловых форматов,
настало время взглянуть на механизм первоначального получения этих файлов,
особенно если они содержат канальные данные реального времени. Для этого мы
посвятим большую часть этой главы описанию получения доступа к данным, пре-
доставляемым посредством АР1-интерфейсов, которые были вкратце упомянуты в
главе 4. Интерфейсы АР1 — это основной (а иногда и единственный) способ полу-
чения доступа к данным, генерируемым службами реального времени или систе-
мами обслуживания по требованию, такими как платформы социальных сетей, по-
токовой музыки и поисковых систем, а также другими частными и официальными
(например, создаваемыми правительством) источниками данных.
Тогда как многие достоинства АР1-интерфейсов (см. врезку "Зачем использовать
АР1-интерфейсы " далее в главе, чтобы освежить свои знания на эту тему) делают
их популярным ресурсом для предоставления компаниями, собирающими данные,
эта практика чревата значительными издержками и рисками. Для бизнесов, пола-
гающихся в своей деятельности на такую рекламу, как платформы социальных се-
тей, внешний продукт или проект, который собирает слишком большие объемы
данных, представляет собой риск относительно получения прибыли. Легкая дос-
тупность такого большого объема личных данных также значительно повышает
уровень угрозы для конфиденциальности (кйр8://(1а€арг1уасу1аЬ.ог§/рго]ес1:8/
капопутку/капопутйу.рйГ). В результате этого для получения доступа к данным
посредством многих АР1-интерфейсов требуется предварительная регистрация в
системе сборщика данных и даже вход в систему с использованием кода или про-
цесса аутентификации при каждом запросе данных. В то же самое время доступ-
ность данных, предоставляемая АР1-интерфейсами, является мощным инструмен-
том для улучшения прозрачности правительственных систем1 и ответственности
частных компаний1 2. Поэтому предварительная работа по созданию учетной записи
и защите любых ваших сценариев РуЙюп для доступа к данным на основе АРР
интерфейсов определенно стоит затраченных усилий.
В этой главе мы обсудим, как посредством АР1-интерфейсов можно получить дос-
туп к целому ряду наборов данных с веб-ресурсов. Мы рассмотрим все их разно-
видности, от базовых ресурсов, не требующих входа в систему, до высокозащи-
щенных АР1-интерфейсов с многоэтапным процессом входа в систему, используе-
мых такими социальными сетями, как, например, Тмчйег. Как мы увидим в разд.
"Доступ к веб-данным ХМЬ и ЛЮИ" далее в этой главе, более простой вариант
предполагает использование библиотеки запросов РуЙюп гедие818 для загрузки веб-
страницы, уже отформатированной как 18(Ж или ХМЬ. Все, что нам нужно, — это
ПКЪ-адрес. В разд. "Специализированные АР1-интерфейсы: добавление простой
аутентификации " далее в этой главе мы перейдем к рассмотрению процесса полу-
чения доступа к данным, предоставляемым посредством АР1-интерфейса базы дан-
ных ЕКЕЭ (Еейега! Кезегуе Есопопнс Эа1аЪа8е — база экономических данных фе-
деральной резервной системы США). Это те же самые данные, которые мы про-
сматривали, используя сценарии в листингах 4.16 и 4.19, но теперь вместо
использования предоставленных мною файлов мы будем программно загружать
данные, которые будут наиболее свежими на момент исполнения сценария.
Для этого на веб-сайте РВЕЕ) потребуется создать как учетную запись, так и свой
собственный ключ АР1-интерфейса. Наконец, в разд. "Специализированные АР1-
интерфейсы: работа с протоколом ОАи1к" далее в этой главе мы рассмотрим бо-
лее сложный процесс аутентификации для подключения к АРРинтерфейсу, тре-
буемого для платформ социальных сетей наподобие Тмчйег. Несмотря на значи-
тельный объем требуемой предварительной работы, изучение, как программно
взаимодействовать с АРРинтерфейсами, подобными этому, принесет вам большую
отдачу. В частности, в большинстве случаев вы сможете в любое время извлечь
наиболее актуальные данные, предоставляемые этими службами, просто исполнив
эти сценарии, без какой бы то ни было их модификации3. Конечно же, поскольку
АРРинтерфейсы предоставляются не всеми источниками требуемых нам данных, в
заключительном разделе этой главы "Извлечение веб-данных: источник данных по-
следней надежды" мы рассмотрим, как программно, с должной ответственностью,
извлекать (зсгаре) данные из веб-сайтов, используя библиотеку ВеаийГи! 8оир для
РуЙюп. Хотя во многих случаях эти задачи по получению доступа к данным можно
реализовать при помощи браузера и мыши, вы быстро увидите, как использование
1 Например, Министерством финансов США (ЬН:р8://Й8са1(1а1:аЛгеа8игу.§оу/ар1-(1оситеп1:айоп).
2 Два примера приводятся в статьях на веб-сайтах Тке Магкир (ЬПр8://Щетагкир.ог§/§оо§1е-Ще-
§1апЕ2021/04/09/11(ЛУ-ууе-(Н8СОУегес1-§оо§1е8-8ОС1а1-111811се-Ыоск1181>Гог-уо11Н1Ье-асРр1асетеп1:8) и КРК
(кНр8://прг.ог§/2021/08/04/1024791053/ГасеЬоок-Ьоо{8-пуи-Ш8тГогта11оп-ге8еагс11ег8-оД-Й8-р1аЦогт-
апсксг Шс8-сгу-Г).
3 Это также первый шаг к созданию своих приложений.
вспомогательных средств РуЛоп позволяет масштабировать наши усилия по извле-
чению данных, ускоряя этот процесс и делая его воспроизводимым.
Зачем использовать АРЬинтерфейсы?
В разд. "Где искать канальные данные" в главе 4 мы коснулись некоторых
причин, по которым компании и организации предоставляют доступ к данным
посредством АР1-интерфейсов. В частности, когда люди для создания какого-
либо продукта или услуги пользуются данными свободного доступа, получае-
мыми через АР1-интерфейс компании, они, по сути, предоставляют этой ком-
пании бесплатную рекламу торговой марки. Эти проекты на основе АР1-
интерфейса действуют как своего рода бесплатная служба по исследованиям и
разработкам для исходной компании: приложения, которые показывают хоро-
шие результаты, указывают на потенциально прибыльное новое направление
развития бизнеса.
Но предоставление АР1-интерфейса для своих данных может быть привлекатель-
ным даже для правительственных и некоммерческих организаций. Эти организации
также извлекают выгоду в виде публичности и получения уникальных знаний, ко-
торые могут быть получены в результате использования их данных другими. С
точки зрения выпаса данных эти АР1-интерфейсы помогают поддерживать усилия
по обеспечению прозрачности, облегчая гражданским технологам (добровольцам
или работающим на другой основе) создание более удобных и доступных интер-
фейсов к информации, представляющей общественный интерес. Отделяя данные от
интерфейса для доступа к ним, АР1-интерфейсы предоставляют способ создания
инструментов для работы с общедоступными данными, которые более устойчивы и
совместимы с возможными будущими разработками.
Доступ к веб-данным ХМЬ и
В разд. "Выпас канальных данных средствами языка Ру1коп" главы 4 мы исследо-
вали процесс получения доступа и преобразования данных в двух распространен-
ных форматах данных: ХМЬ и 18(Ж. Однако мы не рассмотрели, как перенести эти
данные из Интернета на свой компьютер. Эта задача легко решается с помощью
разносторонней библиотеки гедие818 для РуЙюп: получить доступ к данным и загру-
зить их на локальную машину можно посредством всего лишь нескольких строк
кода, без использования веб-браузера.
Для сравнения начнем с ручной загрузки двух файлов, которые мы использовали в
предыдущих примерах: файл статей из К88-канала компании ВВС, который мы
использовали в сценарии в листинге 4.17, и 18(Ж-данные системы СШ В1ке, кото-
рые мы упоминали во врезке "Один источник данных, два подхода" главы 4.
Для обоих этих источников данных процесс, по сути, одинаковый:
1. Открываем в браузере целевой 1ЖЬ-адрес, которым в данном случае является
один из следующих:
• ййр://Геес18.ЬЬс1.со.ик/пеуу8/8С1епсе_апс1_епу1гоптеп1:/г88.хт1
• ййр8://§ЬГ8.сШЬ1кепус.сот/§ЬГ8/еп/8Ш1оп_8Ши8.]8Оп
2. Щелкаем правой кнопкой по странице, в открывшемся контекстном меню выби-
раем опцию 8ауе А8 и сохраняем файл в той же самой папке, где находится наш
блокнот 1иру1ег или файл сценария РуШоп.
Вот и все! Теперь можно исполнить сценарий в листинге 4.17 с этим файлом ХМЬ или
вставить данные 18ОХ системы СШ В1ке в форматер 18ОХЫп1 (к€ф8://]8оп1т1:.сот),
чтобы увидеть, как они будут выглядеть, когда сформатированы должным образом.
Обратите внимание на то обстоятельство, что хотя страница со статьями компании
ВВС выглядит в браузере как обычная веб-страница, согласно своему расширению
.хш1 она сохраняется как хорошо отформатированный код ХМЬ.
Теперь, увидев, как эта часть процесса выполняется вручную, посмотрим, как мож-
но сделать то же самое при помощи средств РуШоп. Для краткости сценарий в лис-
тинге 5.1 загружает и сохраняет один за другим оба файла.
Листинг 5.1. Сценарий с1а1а_с1оууп1оас{.ру
# Простой пример загрузки данных веб-сайта средствами РуШоп,
# используя библиотеку гециезШ
# Исходные данные загружаются из следующих 11Р1_-адресов:
# Ы1р: //Геебз. ЬЬс1. со. ик/пемз/8с1епсе_апс1_еп71гоптеп1:/г88. хп!
# * //дЬГз. сШтЫкепус. соп/дЬШ/еп/зЬаЫоП-ЗЬаЬиз. д зоп
# Библиотека гециезШ позволяет создавать код на РуШоп, который функционирует
# подобно браузеру
тпрогЬ гедиезШ
# Имя нашего ХМЬ файла
ХМЬГИепапе = "ВВС_Р55.хп1"
# Открываем новый файл в режиме записи для сохранения в нем загруженных ХМЬ-данных
хт1_оиЬри-|:_Г11е = ореп(ХМЬШепапе, "м")
# Используем метод де1:() библиотеки гециезШ для доступа к содержимому нашего
# целевого ЫРЬ-адреса и сохраняем его переменной хп1_баШ
хп1_с1а1:а = ге^^е81:8.де1:(,Ы:1:р://Геес^8.ЬЬс^.со.^к/пеVV5/8С^епсе_апс1_епV^гоптепЬ/г58.xт^,)
# Метод де1:() библиотеки гециезШ помещает содержимое веб-страницы в свойство ЬехЬ,
# которое мы запишем непосредственно в файл хп!_ои1:ри1:_Г11е
хп1_ои1:ри1:_Г11е. мг1Ье(хт1_ба1:а. ЬехЬ)
# Закрываем выходной файл хп^ои^ри^-ГИе
хпй_ои1ри1_Н1е. с!озе()
# Выбранное имя файла 350М
ЗБОМГИепапе = "с11ЛЬ1кепус_81:а1:1оп_51:а1:и8. дзоп"
# Открываем новый файл в режиме записи для сохранения в нем загруженных 380И-данных
38ОП_оиГриГ_Г11е = ореп(350МН_1епате/'\л/'')
# Используем метод де1:() библиотеки гециез^з для доступа к содержимому нашего
# целевого 0К1_-адреса и сохраняем его переменной дзоп_с1а1:а
3зоп_с1а1:а = гециез^з. де1:(' Ы^рз: //дЫ'з. сШЫкепус. сот/дЬГз/еп/з1:а1:1оп_81:а1:и8. дзоп')
# Метод де1:() извлекает содержимое веб-страницы и записывает ее текст
# непосредственно в выходной файл озоп_ои1ри1_РИе
3 зоп_ои1ри1_ГИе. шг1Ле( д зоп_с1а1:а. 1ех1:)
# Закрываем выходной файл ззоп_оиГри1:_Г11е
3 зоП-ОЩрЩ-ГИе. с!озе()
Довольно просто, не так ли? За исключением разных имен файлов, файлы .хш1 и
^зоп, созданные сценарием в листинге 5.1, точно такие же, как и их соответствую-
щие версии, которые мы загрузили вручную. Теперь, когда у нас есть работающий
должным образом сценарий загрузки данных, все, что нам нужно сделать, чтобы
получить самые последние данные с этих 1ЖЬ-адресов, — это просто снова испол-
нить его, и старые данные в созданных ранее файлах будут заменены новыми.
Загружать вручную или создать программу загрузки?
Почти на каждом этапе нашей работы по выпасу данных будут случаться мо-
менты, когда нам нужно будет принять решения относительно способа выпол-
нения определенной задачи: делать это вручную или же создать какую-либо
программу для ее выполнения. Например, в сценарии в листинге 4.19 мы не
пытались, чтобы наша программа выясняла что-либо о структуре файла 18(Ж,
а просто сами посмотрели на данные, а затем создали соответствующий код.
Теперь, когда мы знаем, как загружать данные, используя средства РуЙюп, пе-
ред нами стоит еще один выбор: сохранять данные вручную или же создать
программы для выполнения этой задачи.
В большинстве случаев, на начальных этапах работы с данными я всегда реко-
мендую загружать пробный экземпляр данных вручную, просто потому, что
это обычно проще, чем создание сценария для этого. В особенности, если ис-
ходные данные имеют формат ХМЬ или 18(Ж, то вы, вероятно, нашли их в
Интернете, и поэтому эти данные уже открыты в браузере.
На данном этапе мы можем просто щелкнуть правой кнопкой по странице и в
контекстном меню выбрать опцию сохранить данные на наше устройство, вме-
сто того, чтобы запускать блокнот 1иру1ег или редактор кода, чтобы создать
код для загрузки этих данных. Поскольку нам нужно внимательно просмот-
реть эти данные, чтобы оценить их качество, такой подход к первой загрузки
практически всегда является разумным. Если же мы решим, что данные заслу-
живают более пристального внимания, мы, скорее всего, захотим автоматизи-
ровать процесс загрузки, создав для этого сценарий РуЙюп.
Знакомство с АРЬинтерфейсами
До этого времени большинство нашей работы по выпасу данных концентрирова-
лась на источниках данных, чье содержимое почти полностью контролируется по-
ставщиком данных. Фактически содержимое файлов электронных таблиц и доку-
ментов (и даже веб-страниц, содержащих данные ХМЬ и 18(Ж, к которым мы
только что обращались в сценарии в листинге 5.1) может меняться в зависимости
от времени доступа к ним и мы не можем, по сути, никоим образом влиять на то,
какие данные они содержат.
В то же самое время большинство из нас привыкли обращаться к Интернету, чтобы
получить информацию, которая намного больше соответствует нашим требовани-
ям. В процессе поиска информации зачастую наш первый шаг состоит в вводе клю-
чевых слов или фраз в поисковую систему, ожидая получить в ответ список тща-
тельно подобранных результатов, основанных (как минимум частично) на указан-
ной комбинации терминов. Конечно же, мы не можем контролировать, какие веб-
страницы доступны для поиска, но это настолько распространенный для большин-
ства из нас процесс, что мы редко останавливаемся, чтобы подумать о том, что
происходит за его кулисами.
Несмотря на визуальные интерфейсы, поисковые системы в действительности яв-
ляются всего лишь частным случаем АР1-интерфейсов. По сути, это просто веб-
страницы, которые позволяют взаимодействовать с базой данных, содержащей ин-
формацию о веб-сайтах в Интернете, такую как их 1ЖЕ-адреса, названия, тексты,
изображения, видео и многое другое. При вводе поискового термина с последую-
щим нажатием клавиши <Еп1ег> поисковая система осуществляет запрос к своей
базе данных на предмет содержимого, которое в некотором отношении соответст-
вует поисковому термину, а затем обновляет текущую веб-страницу, отображая на
ней список полученных результатов. Хотя специализированные АР1-интерфейсы,
предоставляемые платформами социальных сетей и другими онлайновыми служ-
бами, требуют аутентификации и структурирования поисков особым образом, по-
исковые системы и более специализированные АР1-интерфейсы обладают доста-
точно многими общими функциями, чтобы можно было узнать что-то полезное,
разобрав по частям базовый поиск системы Ооо§1е.
Базовые АРЬинтерфейсы
на примере поисковой системы
Хотя поисковая система в Интернете, вероятно, является самой простой формой
АР1, ее поведение на экране не всегда очевидно. Например, в результате поиска в
Ооо§1е по фразе "мюайзег 8еЪа81оро1” на экране, скорее всего, отобразится страница
наподобие показанной на рис. 5.1.
Г- С Л дсюд1е.спп1^егп!К?еоиг1се-Г|рЬенС!айгУМу БС1ЫпаМаН8Гта1Ш<ВС-йгпЬ6;Ьп^ен1Гг5Г1ае1мз1:оро1йочыАгее1Не«яеИвавол
&оод1е зеЬазГоро! X 4
АН ЙЗ Ые№ <? аиилид ф Мфк Щ 1п,иуев 4 Мт в ЗЫЕняд:» Тий«а
А|>рЦ* 930,(КЮ^и115 [0 50 5«1Х1С1а)
БеЬазгоро!, С А
Т<4вьс1яу 11:00 ЛМ
РагПу с1о1*йу
РгаарПаИои: 29*
| штМГу:
□ гпрИ
гетжашге РгеарйаБля Ийп<1
Рис. 5.1. Пример результатов поиска
Хотя данный формат результатов поиска будет вам довольно знакомым, давайте
рассмотрим более подробно, что происходит в строке 1ЖЕ-адреса этой страницы.
В вашем случае его содержимое будет, вероятнее всего, иным, чем показанное на
рис. 5.1, но оно должно содержать по крайней мере некоторую одинаковую инфор-
мацию. В частности, текст в строке 1ЖЕ должен содержать следующий фрагмент:
Я^еа^Ьег+зеЬа^оро!
Нашли его? Отлично. Теперь, не обновляя страницу, замените текст в строке поис-
ка на ”\уеа111ег 8ап Ггапс18со” и нажмите клавишу <Еп1ег>. Опять проверьте текст в
строке 1ЖЕ; он должен содержать следующий фрагмент:
д=иеа1±ег+5ап+1тапс18со
Наконец, вставьте в строку 1ЖЕ следующий текст и нажмите клавишу <Еп1ег>:
ЫЕрз: //шм.доод1е.сот/8еагсЬ?р=пеа1±ег+8ап+Ггапс1.8СО
Заметили что-либо? Если все сработало должным образом, то результаты поиска, по-
лученные после ввода в строку поиска Сюо§1е текста "мюайзег 8ап Ггапс18со”, будут та-
кими же (или почти такими же), как и результаты, полученные вследствие добавления
к 1ЖЕ поиска (Зоо§1е пары ключ/значение д=меа1±ег+5ап+-Ггапс18со (создав, таким
образом, полный ИВЕ Ы:1:р5://ммм.доод1е.сот/8еагсЬ?д=меа1:Ьег+5ап+-Ггапс18со). Это
объясняется тем, что фрагмент д=меа1±ег+5ап+-|тап818со является частью строки за-
проса, которая доставляет наш фактический элемент поиска в базу данных (Зоо§1е.
Весь остальной текст — это просто дополнительная информация, добавляемая сис-
темой Сюо§1е для целей специфичного оформления или отслеживания.
Хотя поисковая система Ооо§1е может добавлять (и добавляет) в наш 1ЖЬ поиска
все, что ей заблагорассудится, добавлять в него полезные пары ключ/значение мо-
жем также и мы. Например, во врезке "Интеллектуальный поиск конкретных ти-
пов данных" главы 4 мы рассмотрели, как можно выполнить поиск файлов конкрет-
ного типа. Так, для поиска файлов типа .хш1 в поле поиска нужно добавить текст
РИе1уре: .хпЪ Тот же самый результат можно получить, добавив в строку запроса в
поле 1ЖЬ соответствующую пару ключ/значение а8_+ГНе1:уре=хт1:
: //ш. доодТе. соп/веагсЬ?д=меа1:Ьег+8ап+Ггапс18СО&а8_Г11е1:уре=хт1
Модифицированный таким образом 1ЖЬ не только возвратит результаты в требуе-
мом формате, но также обновит соответствующим образом содержимое поля поиска.
В данной ситуации система поиска (Зоо§1е ведет себя почти почти так же, как и бо-
лее специализированный АР1-интерфейс, который мы будем рассматривать в ос-
тавшейся части этой главы. Большинство АР1-интерфейсов имеют общую структу-
ру, которую мы увидели в этом примере поиска. В частности, конечная точка (в
данном случае это Ьйр8://лулулу.§оо§1е.сош/8еагсЬ) объединяется с одним или не-
сколькими параметрами запроса или парами ключ/значение (например,
а8_П1е1:уре=хт1 или д=меа1:Ьег+5ап+Ггап стзсо), которые составляют строку запроса,
добавляемую после символа вопросительного знака (?). На рис. 5.2 приводится об-
щий обзор конечной точки АР1-интерфейса и структуры строки запроса.
Конечная точка и строка
запроса отделяются
друг от друга символом
вопросительного знака (?)
Пары ключ/значение отделяются
друг от друга символом
амперсанда (&)
Ьгсрз://нни.доод1е.сот/5еагсЬ?ц^игеа1Ьег+5еЬа5Горо1.®пи1п=5
первой частью ШЕ-
является конечная точка,
заканчивающаяся
вопросительным знаком (?)
Пары ключ/значение; ключ отделен
от значения знаком равенства (=).
Допустимые ключи и значения определяются
поставщиком АР1-интерфейса
Рис. 5.2. Базовая структура строки запроса
Тогда как это довольно универсальная структура, следующие две вариации строч-
ных АР1-интерфейсов запросов могут оказаться полезными:
♦ пары ключ/значение (например, а5_Г1Ле1:уре=хт1, пип=5 или даже
Я=меа1:Ьег+5ап+Ггапс18со) могут указываться в любом порядке, при условии, что
они добавляются после символа вопросительного знака (?), обозначающего на-
чало строки запроса;
♦ конкретные ключи и значения, разрешенные для данного АР1-интерфейса, опре-
деляются поставщиком этого интерфейса. Их можно узнать из документации
для интерфейса или экспериментальным путем (хотя последний подход может
вызвать проблемы). Все элементы, добавляемые в строку запроса, которые не
являются действительным ключом или параметром, будут, скорее всего, игно-
рироваться системой.
Хотя эти характеристики присущи почти всем АР1-интерфейсам, для получения
допуска к данным подавляющее большинство этих интерфейсов требует сначала
выполнить идентификацию (или аутентификацию) пользователя, выполнив вход в
систему. Кроме этого, запросы к АР1-интерфейсу должны содержать уникальные
специализированные "ключи”. Эту часть процесса АР1-интерфейсов мы и рассмот-
рим следующей.
Специализированные АРЬинтерфейсы:
добавление простой аутентификации
При работе с большинством АР1-интерфейсов первым делом необходимо создать
какую-либо учетную запись у поставщика данного интерфейса. Хотя многие АР1-
интерфейсы предоставляют свои услуги бесплатно, процесс компилирования и
хранения данных и их поиска и возврата через Интернет сопряжен с рисками и сто-
ит денег, поэтому поставщики хотят отслеживать пользователей своих АР1-
интерфейсов и в случае необходимости иметь возможность ограничивать доступ к
интерфейсу4. Первая часть процесса аутентификации обычно состоит из создания
учетной записи и получения ключа АР1 для пользователя и/или для каждого проек-
та, программы или приложения, которое планируется использовать для взаимодей-
ствия с АР1-интерфейсом. В "базовом” процессе аутентификации для работы с АР1-
интерфейсом, подобном тому, который мы осуществим здесь, после создания сво-
его ключа АР1-интерфейса на веб-сайте поставщика данной услуги для успешного
получения данных нужно лишь добавить этот ключ к запросу данных подобно лю-
бому другому параметру запроса.
В качестве примера рассмотрим, как организовать программный доступ к данным
по безработице, с которыми мы работали в сценарии в листинге 4.19. Для этого
первым делом нам нужно создать учетную запись на веб-сайте ГРЕВ и запросить у
них ключ для АР1-интерфейса. Для загрузки данных нам нужно просто добавить
полученный ключ в нашу строку запроса.
Получение ключа для АРЬинтерфейса ЕКЕО
Чтобы создать учетную запись для базы данных ЕКЕВ (Еебега! Кезегуе Есопогшс
ВаШЪазе — база экономических данных федеральной резервной системы), откройте
ее веб-страницу по адресу ййр8://Ггей.8йош8Гес1.ог§ и щелкните по ссылке МУ
АССО1ЛЧТ в правом верхнем углу (рис. 5.3).
Следуйте инструкциям в открывшемся всплывающем окне и создайте новую учет-
ную запись или выполните вход, используя свою учетную запись Соо§1е. После
4 Большинство поставщиков АР1-интерфейсов могут закрыть доступ к своей услуге в любое время и
по любому поводу. Но обычно они это делают лишь в случае слишком большого количества отправ-
ляемых запросов в течение слишком короткого времени.
создания учетной записи щелчок по ссылке МУ АССО1ЛЧТ открывает выпадаю-
щее меню, содержащее опцию АР1 Кеуз, как показано на рис. 5.4.
Рис. 5.3. Ссылка для входа в базу данных РЕЕЮ
ЕРЕО*1 ЕСОГЧОМ1С КЕ5ЕАКСН
. сн» ' „„ гейскль «V»« ** «Г.
ГНЕЙ* Ов1а
№ АГСОЦМТ* I
8₽згс1п1 РЬЁО
<□. Му Срп№4
| * муСопГеиЕ
И РгеМммгги о
Уоиг 1ги51еи с1аЪа зоигсе 5!псе 1991. Сгар|15 о
Оаы Мяз о
□ои/гПовД д гарГ1, а пр г гас к 786,000 05 ала 1гнегпайоп₽1 Гиле 5еле$ Гл э Млр5
ЗеагсК РЙЕО 0ага е.д., дйрг (пЛаПоп, инетр|оVтент
Вгсу?яе Йаьа Ьу Тад, Саьсдогу, Яе1еаве, ?оигс&, Ре1са^е Са[&пг1зг ог Е
ГКЕОсаЯ
1 5*дпви1
Рис. 5.4. Опции меню МУ АСС01ЛМТ веб-сайта РКЕО
Щелчок по этой опции открывает страницу, на которой можно запросить один или
несколько ключей АР1, нажав кнопку Кедие8€ АР1 Кеу. В результате нажатия этой
кнопки откроется страница, на которой нужно предоставить краткое описание
(длиной в одно-два предложения) приложения, в котором будет использоваться за-
прашиваемый ключ АР1. На этой же странице под полем описания приложения
предлагается ознакомиться с условиями предоставления услуг и подтвердить факт
ознакомления, установив соответствующий флажок. Чтоб отправить запрос, на-
жмите кнопку Кедие8€ АР1 Кеу.
Запрос ключа АР1 обычно удовлетворяется, и в результате открывается промежу-
точная страница, на которой отображается ваш ййр8://ге8еагсй.8Йош8Гес1.ог§/
и8егассоип€/ар1кеу8 ключ АР1. Если закрыть эту страницу, то в дальнейшем про-
смотреть свои ключи АР1 можно на странице по адресу Ьйр8://ге8еагсЬ.8Йош8Ге(1.ог§/
и8егассоип€/ар1кеу8.
Использование ключа АР1 для запроса данных
Получив ключ АР1, можно исследовать процесс запроса данных, которые мы ис-
пользовали для сценария в листинге 4.19. Для начала вставьте следующий адрес в
поле браузера:
Ы:1:р5://ар!. зНоит-зГес! .огд/Ггес1/8ег1е8/оЬ5ег7а1:1оп5?8ег1е8_1с1=ибРАТЕ&Г11е_1:уре=з8Оп
Даже если вы в настоящее время выполнили вход на веб-сайт ЕКЕЭ в используе-
мом браузере, вы получите сообщение об ошибке наподобие следующего:
{"еггог_сос!е":400,"еггог_те88аде":"Вас1 Редиез!:. \/аг1аЫе ар1_кеу 1з по!: зе!:.
Реас! Ь!:!:р5:\/\/ге5еагсЬ.5!:1ои1512ес1.огд\/с1ос5\/ар1\/ар1_кеу.Ь!:т1 -Гог тоге
тп^огта^топ."}
Это сообщение об ошибке довольно информативное: оно не только извещает о том,
что что-то пошло не так, но также содержит информацию, что могло вызвать эту
ошибку. В частности: \/аг1аЫе ар1_кеу 1з по!: зе!:. Таким образом, чтобы исправить
ошибку, нам нужно лишь добавить наш ключ АР1 в запрос как дополнительный
параметр:
Ь1:1:р8 ://арт. 81:1ои1-8Гескогд/1Тес1/8ег1.е8/оЬ8ег7а1:1-ОП8?8ег1.е8_1.с1=116РАТЕ&Г1.1е_!:уре=з8Оп&
ар1_кеу= ВАШ_КЛЮЧ_АР1
Должно быть очевидным, что ключ АР1 вставляется вместо текста ВАШ_КЛЮЧ_АР1. Те-
перь в браузере должно отобразиться содержимое наподобие следующего:
{"геаШпе_з!:аг!:": "2021-02-03", "геа!!:1те_епс!": "2021-02-03", "оЬзеп/а!:!оп_51:аг!:":
"1600-01-01", "оЬзеп/а!:1оп_епсГ: "9999-12-31", "иптЛз": "Ип", "ои!:ри!:_!:уре": 1,
"РИе_!:уре":"дзоп", "огс1ег_Ьу": "оЬзеп/а!:1оп_да!:е", "зог!:_огс1ег": "азе", "соип!:": 324,
"о!Т8е!:" :0," Нт!!:": 100000, "оЬзеп/а!:!оп5": [{"геа!!:!те_8!:аг!:": "2021-02-03",
" геа!!:1те_епсГ: "2021-02-03", "с1а!:е": "1994-01-01", 'Эа1ие": "11.7"},
{" геа!1:!те_81:аг1:": "2021-02-03", " геа1!:!те_епсГ': "2021-02-03", "с!а!:е": "2020-12-01",
"7а1ие":"11.7"}]}
Довольно ловко, не так ли? Теперь, когда мы знаем, как использовать наш ключ
АР1 для создания запросов на получение данных, рассмотрим, как индивидуализи-
ровать эти запросы, а также, как защитить свой ключ АР1 при его использовании в
сценариях РуЙюп.
Чтение документации по АРЬинтерфейсу
Как мы видели в предшествующем примере, имея ключ АР1, можно получать са-
мые последние данные из базы данных ЕКЕЭ в любое требуемое нам время, просто
создав строку запроса и добавив в нее наш ключ АР1.
Но как узнать, какие пары ключ/значение разрешены в АР1-интерфейсе базы дан-
ных ЕВЕТ) и какую информацию они возвращают? Единственный надежный спо-
соб для этого — прочитать документацию по АИ-интерфейсу, которая должна со-
держать руководство и (будем надеяться) примеры по его использованию.
К сожалению, для документации по АР1-интерфейсам не существует общепринято-
го стандарта, поэтому использование нового АР1-интерфейса всегда представляет
собой процесс проб и ошибок, особенно в случае некачественной документации
или отсутствия требуемой информации в предоставляемых примерах. Более того,
даже найти документацию для определенного АР1-интерфейса не всегда просто, и
зачастую поиск документации проще осуществить в сети.
Например, чтобы с домашней страницы базы данных ЕКЕВ (ййрз://
ГгескзйошзГеског^/) добраться до документации по ее АР1-интерфейсу, нужно
щелкнуть по вкладке Тоо18, которая находится в панели вкладок, расположенной
посредине страницы, а затем из предлагаемых опций выбрать ссылку Веуе1орег
АР1, в результате чего откроется страница с содержанием документации по этому
АР1-интерфейсу (Ьйр8://ГгеД.8Яои18ГеД.ог§/(1ос8/ар1/ГгеД). А вот поиск в сети по
фразе ’Тгеб ар1 босшпспШюп" сразу же выдаст страницу документации АР1-интер-
фейса базы данных ЕКЕВ (рис. 5.5).
Рис. 5.5. Страница содержимого документации по АР1-интерфейсу базы данных РРЕЮ
К сожалению, список ссылок на этой странице в действительности является спи-
ском конечных точек, т. е. разных базовых адресов 1ЖЕ, используемых для за-
проса более конкретной информации. (Вспомним, что конечная точка — это все,
что находится в адресе 1ЖЕ до символа вопросительного знака (?), который отде-
ляет ее от строки запроса.) В предшествующем примере для получения данных из
базы данных ЕКЕВ мы взяли конечную точку Ь1Ер5://ар1.8иои18Геб.огд/Ггес1/
8ег1е8/оЬ8ег7а1Лоп5, объединили ее с парами ключ/значение 8ег1е8_1с1=116РАТЕ,
Г11е_1:уре=з8оп, а затем добавили наш ключ АР1.
Прокрутим страницу содержимого документации до раздела 8епе8, а затем щелк-
нем по ссылке ’Тгеб/зепез/оЬзегуайопз”. В результате откроется страница
Ьйр8://Гге(1.8Яои18Ге(1.ог§/(1ос8/ар1/ Гге(1/8ег1е8_оЬ8егуайоп8.Ы:т1, содержащая спи-
сок всех действительных ключей (или параметров) запроса для данной конечной
точки, а также действительные значения для этих ключей и несколько примеров
ОКЕ запросов (рис. 5.6).
Количество возвращаемых наблюдений можно ограничить, используя параметр
Ити, а упорядочить возвращаемые результаты в обратном порядке можно, добавив
пару ключ/значение 8ог1:_огс1ег=с1е8с. Можно также указать возвращение данных в оп-
ределенном формате (например, в ХМЬ, используя пару ключ/значение РПе_1:уре=хт1)
или с использованием определенных единиц (например, отобразить вывод в виде
процентного изменения по отношению к прошлому году можно, используя пару
ключ/значение ипП8=рс1).
ССОКОМ1С М5ЕЛКСН
Ссрпрпис ПичеатсК Йр^снясез V РТог1ис1з
Му ^ссвип1 м
РНЕ
Тонг I гыеТкс! [1з1а мнгге
зцчсе 1991.
СОМОН1С ОДТА | ГЧ’З!
Рв1ЕНзе Сй1Е:Г.йяг РАЕОТпЫа РНЕЛМеиз ГНЕЙ В1од ДЬа1Л ГЧЕС
?ге<1 / вепез/о Ькеп/аН опз
АР[ Кеуз | гпз оГ Озе
йеза (роен
Ехатр1ез
- кмь
1 )&О1Ч
Рагаше'Сегь
-
+Т1(?_ИурЕ?
ИГ1СЕ 1(1
геаШ|Г|е_Йвг1
гн^1ПГпё_йПС1
« кт II
> аЛйе1
шг1_огг1ег
я сЬ«г«аг1й1>_б1ГагЕ
о(м*гуйПчп епй
^гейиепйу
» лддгрдлНгп. гцчГ11пп
» оикрик, ку(зв
Ехатр1е$
ТН15 гсдисзк сап гвкигп д^НегХМЦ 15014, гфррй ^й1|гп1-Ьес1 к«<к, рга Е1рре[1 В^ое! эргва^ьНеЕк Ьу 5е^|пд 1Нс Ц1е 1уре рагатвЕвг ко хт!, ]-5оц, ккк,. ог Х1&.
1Чй*е Шак кЬс г!е№и№ ^л1и& о? ГкЕ_курс 1э кт1. ТИрДР! кеу 'аЬсйе1гукИ|к1 гппаряге-Ьи ху; 12^4 56' 15 5зг аегПйПзкга110Г| ригрояез оп1у, и« а гтд^Ь*геч1 ДР1
Шзкеж!.
Рис. 5.6. Страница содержимого документации
для конечной точки оЬзеп/аИопз АР1-интерфейса базы данных РЕЕЮ
Путешествие в прошлое
Приблизительно в то же самое время, когда я открыла, что показатель безрабо-
тицы ПЭ не охватывает многих людей, которые, вероятно, считали (и почти
определенно ощущали) себя безработными, я также узнала еще кое-что о пуб-
ликации экономических индикаторов: их значения часто подвергаются после-
дующему изменению. После нескольких месяцев моей работы с данными эко-
номических показателей один из моих коллег мимоходом упомянул, что ско-
рость роста валового внутреннего продукта (ВВП) США за предыдущий
квартал была официально исправлена. ”Что?” — подумала я. Учитывая то об-
стоятельство, что ВВП часто используется в национальной экономической по-
литике, мысль о том, что этот индикатор можно было просто изменить не-
сколько месяцев после его публикации, была просто шокирующей.
Например, спад экономической деятельности официально определяется как
”отрицательный рост” в течение нескольких последовательных кварталов, ко-
гда ВВП следующего квартала меньше предыдущего по крайней мере два раза
подряд. Но какие последствия пересмотр значений этих показателей постфак-
тум может иметь для реальных людей, особенно для тех, которые начинают
чувствовать эффекты спада еще задолго до публикации первого варианта этих
данных?
И хотя я ничего не могу поделать с фактом существования таких корректиро-
вок, они по крайней мере отслеживаются в базе данных ЕКЕП, и все эти изме-
нения можно увидеть, используя ее АР1-интерфейс. Параметр у!п!аде_с1а!е5 для
конечной точки оЬ8еп/а!!опз (Ьйр8://ГгеД.8Йои18ГеД.ог§/(1ос8/ар1/ГгеД/8ег1е8_
оЬ8егуайоп8.Ы:т1) как раз и используется для этой цели. Например, получить
значение показателя безработицы П6 состоянием на май 2020 можно, испол-
нив запрос со следующим 1ЖЬ-адресом:
Ь!!р8: //ар!. зНоитзГес!. огд/Ггес!/8ег1е8/оЬ8ег7а!1оп8?8ег1е8_1с1=ибРАТЕ&
Г!!е_!уре=з8Оп&8Ог!_огс1ег=с1е8С&11т1!=3&71п!аде_с1а!е8=2020-05-31
&ар!_кеу=У011Р_АР1_КЕУ_НЕРЕ
Можно увидеть, что в конце мая значение показателя безработицы 116 за
март 2020 г. было 8,7%, но затем оно было откорректировано до 8,8%. Значе-
ние этого же показателя за апрель сначала было 22,8%, но затем было откор-
ректировано до 22,9%. И хотя эти корректировки могут выглядеть незначи-
тельными, следует иметь в виду, что уровень безработицы часто меняется не
больше, чем на несколько десятых процента в ту или иную сторону в течение
всего года.
Самое главное, эти изменения иллюстрируют одну из наиболее тонких про-
блем, связанных с целостностью данных: совпадает ли текущая информация с
той, которая была доступна в течение исследуемого периода времени? Если
нет, то необходимо должным образом откорректировать наши утверждения, а
может быть, и сами данные.
Защита своего ключа АР1
при использовании сценариев РуНюп
Как вы уже могли догадаться, для загрузки данных с веб-сайта ЕВЕЕ) (или с друго-
го подобного АР1-интерфейса) в примере в листинге 5.1 нужно всего лишь заме-
нить один из адресов ЕГВЕ полной строкой запроса, поскольку создаваемая им веб-
страница — это всего лишь один из многих файлов 18(Ж в Интернете.
В то же самое время этот запрос содержит особенно критичную информацию: наш
ключ АР1. Помните: что касается ЕВЕЕ) (или владельца любого другого АР1-интер-
фейса), ответственность за любую деятельность с использованием вашего ключа
АР1 несете вы. Это означает, что в то время как вы документируете, сохраняете и
управляете версиями своего кода при помощи средств наподобие Ой, нельзя допус-
тить, чтобы ваши ключи АР1 или другие учетные данные попали в файл, к которо-
му могут получить доступ сторонние лица.
Чтобы должным образом защитить свой ключ АР1, необходимо прило-
жить некоторые усилия. Если вы новичок в области работы с данными,
языком РуШоп или АР1-интерфейсами (или со всеми тремя), вы можете
поддаться искушению пропустить следующие пару разделов и просто
оставить свои учетные данные для АР1-интерфейса в файлах, которые
могут быть загруженными в Интернет5. Не следует поддаваться этому
соблазну! В данный момент вы можете думать: ”Да кому нужно утруж-
дать себя просмотром моей работы?” или: ”Я всего лишь эксперименти-
рую в любом случае, что с того, если кто-то и узнает эти учетные дан-
ные?”. Но вам следует знать две вещи.
Во-первых, как и в случае с документацией, если вопрос защиты своих
учетных данных не решить должным образом сейчас, сделать это в бу-
дущем будет намного дольше и потребует больших усилий, потому что
вы можете забыть, с чем именно вы имеете дело, или потому, что к тому
времени это может быть уже слишком поздно. Во-вторых, обычно мало
кто из нас считает, что мы делаем что-то ''важное” или достаточно замет-
ное, чтобы оно представляло интерес для кого-либо другого. Но реаль-
ность такова, что злоумышленникам все равно, на кого повесить ответст-
венность за свои деяния, и если вы представляете для них легкую цель,
они могут избрать для этого именно вас. Но последствия могут не огра-
ничиваться запретом доступа к платформе данных. В 2021 г. бывший
директор компании 8о1аг\^шс18 заявил, что масштабный несанкциони-
рованный доступ к тысячам систем с повышенным уровнем защиты по-
средством взлома программного обеспечения компании был отчасти
возможным вследствие слабого пароля, который был загружен в файл
личной учетной записи на ОйНиЬ одного стажера6. Поэтому, даже если
вы "просто практикуетесь”, будет не лишним с самого начала начать
практиковать и навыки безопасности.
Процесс защиты учетных данных для АИ-интерфейса состоит из двух частей:
1. Ключ АР1 или другую конфиденциальную информацию нужно хранить отдель-
но от остального кода. В нашем случае мы будем сохранять эти учетные данные
5 Например, при сохранении своего кода командой дП: ризЬ.
6 См. статьи Наскегз Узей 8о1аг1Ртс1$' Уоттапсе а§ат81 II т 8рга^Нп§ 8ру Сатраг§п, авторы Карйае!
8аДег, Сйпз^орйег Вш§ и Зозерй Мепп (ЬНр8://геи1ег8.сот/агПс1е/§1оЬа1-суЬег-8о1агуу1п(18/Ьаскег8-а1:-
сеп{ег-оГ-8ргаМт§-8ру-сатра1§п411гпес1-8о1апутс18-с1оттапсе-а§ат81>й-1(Ш8КВ№8Р2№) и Еогтег
8о1аг1Утй8 СЕО В1ате8 1п1егп /ог 8()1аг\\чпс18123 Рсшххюгс! Ьеак, авторы Впал Рпп§ и Оепеуа ЗапсЕ
(11Н:р8:/Лууууу.спп.сот/2021/02/26/ро1Шс8/8о1апутс18123-ра88УУОГс1-т1:егп).
в отдельном файле, который загружается основным кодом только при исполне-
нии сценария.
2. Нужно создать надежный способ, чтобы ни при каких обстоятельствах не допус-
тить копирования этого файла с учетными данными в какое бы то ни было сете-
вое хранилище при выполнении резервного копирования кода, используя, на-
пример, систему Ой.
Для этого мы вставим слово сгейепйа18 в название любого файла, который содер-
жит наши учетные данные, а затем используем файл §1й§поге, чтобы не допустить
загрузки этих файлов на ОйНиЪ.
Самый простой способ для надежного достижения обеих этих целей — определить
соглашение по именованию всех файлов, содержащих ключи АР1 или иную конфи-
денциальную информацию, связанную с выполнением входа в систему. В нашем
случае таким способом будет обязательная проверка на наличие слова сгес1еп1:1а18 в
названии любого такого файла. Затем мы создадим или обновим специальный файл
(Эй .§й1§поге, который содержит правила, указывающие системе Сгй, какие файлы в
папке репозитория никогда не должны подвергаться операции коммита и/или за-
грузке на СгйНиЪ. Добавив в этот файл правило для нашего файла учетных данных
(который содержит слово сгес1еп1ла18 в названии), мы гарантируем, что никакой
файл, содержащий конфиденциальную информацию для входа в систему, не будет
случайно загружен на СгйНиЪ.
Создание файла учетных данных
До сих пор мы размещали весь код для выполнения определенной задачи (напри-
мер, загрузки или преобразования файла с данными) в один файл РуЙюп или в
блокнот. Но чтобы обеспечить как безопасность, так и возможность многократного
использования, при работе с АР1-интерфейсами будет гораздо разумнее отделить
рабочий код от учетных данных. К счастью, это очень простой процесс.
Сначала создадим и сохраним пустой файл РуЙюп с названием РКЕВсгейепйак.ру.
Для простоты сохраним этот файл в той же самой папке, в которой будет находить-
ся код РуЙюп для загрузки данных с базы данных РКЕВ.
Затем создадим новую переменную и присвоим ей значение нашего ключа АР1 для
этой базы данных, как показано в листинге 5.2.
Листинге 5.2. Пример содержимого файла для хранения учетных данных для доступа
к базе данных ЕКЕЭ
пу_ар1_кеу = "ваш_ключ_ар!_в_двойных_кавычках"
В завершение, сохраните файл с ключом.
Использование учетных данных
в отдельном сценарии
Ключ АР1, сохраненный в виде переменной в отдельном файле, можно импортиро-
вать в любой сценарий, требующий этого ключа, используя тот же самый способ,
посредством которого мы ранее импортировали библиотеки сторонних разработчи-
ков. В листинге 5.3 приводится код сценария для загрузки данных индикатора без-
работицы Ив из базы данных ЕКЕВ, используя наш ключ АР1, хранящийся в от-
дельном от сценария файле РКЕВ сгебепбак.ру.
Листинг 5.3. Сценарий ЕКЕО_АР1_ехатр1е.ру
# Импортируем библиотеку гециез^з
1прог1: гедиез^з
# Импортируем наш ключ АР1
Тгот ЕРЕО_сгебеп1:1а15 1прог1: пу_ар1_кеу О
# Указываем требуемую конечную точку веб-сайта ЕРЕО
ЕРЕО_епбро1п1: = "Ь^^рз^/арт.з^Тоитз^еб.огд/^геб/зегтез/оЬзе^а^топз?"
# Также задаем параметры запроса и их значения
РРЕ0_рагате1:ег5 = "5ег1ез_1^=ибРАТЕ&Т11е_1:уре=з50п"
# Создаем полный 0Р1_-адрес для нашего запроса АР1, добавляя наш ключ АР1 в конец
сотрТеГе_баГа_11Р1 = РРЕО_епс1ро1п1: + РРЕ0_рагапе1:ег5 +"&ар1_кеу="+пу_ар1_кеу
# Открываем новый файл в режиме записи для сохранения в нем загруженных ЗЗОМ-данных
РРЕО_ои1:ри1:_Т11е = ореп("РРЕО_АР1_с1а1:а. дзоп" /V)
# Используем метод де1:() библиотеки гециез!: для получения содержимого нашего целевого
# 0Р1_-адреса и сохраняем его в переменной РРЕО_с1а1:а
РРЕО_с1а1:а = гециез1:5.де1:(сотр1е1:е_с1а1:а_иР1)
# Записываем содержимое веб-страницы в текстовый файл
РРЕ0_оЩри1:_Ше. шг11:е(РРЕО_с1а1:а. ЕехЕ)
# Закрываем выходной файл РРЕО_ои1:ри1:_Т11е
РРЕО_оиГриГ_Ше. с!озе()
О Доступ сценария к нашему ключу АР1 осуществляется при помощи ключевого
слова Тгоп и указания названия нашего файла, содержащего этот ключ (обратите
внимание на то, что расширение файла .ру не указывается), после чего осуществля-
ется импортирование значения переменной, используя ключевое слово ТпрогЕ
Что такое русасЬе?
Возможно, вы заметили, что при исполнении сценария в листинге 5.3 не толь-
ко загружаются данные ЕКЕП, но также создается новая папка__русасйе__,
которая содержит один файл, называющийся ЕКЕВсгебепйак.сруЙюпЗЗ.рус
(или что-то похожее). Откуда взялся этот файл и что он собой представляет?
При импортировании кода из другого файла РуЙюп он сначала преобразовыва-
ется в так называемый байткод, который, собственно, и используется устрой-
ством для исполнения сценария РуЙюп7. Файлы с байткодом РуЙюп имеют рас-
ширение .рус. Даже если вам еще не приходилось работать непосредственно с
такими файлами, ваше устройство уже содержит большое количество файлов
.рус, связанных с импортируемыми библиотеками. Но они хранятся в удален-
ной части файловой системы, посещать которую нам обычно нет надобности,
поэтому мы их и не замечаем. В обоих случаях система сохраняет ресурсы,
преобразовывая содержимое импортируемых файлов в байткод и сохраняя его
в файле .рус только один раз, а не при каждом исполнении сценария. Как при
импортировании библиотек, так и наших учетных данных, система полагается
на байткод этих ресурсов, чтобы немного ускорить исполнение сценариев.
К счастью, нам ничего не нужно делать ни с папкой_русасйе_, ни с содер-
жащимся в ней файлом .рус. При желании их можно даже удалить (они поя-
вятся снова после следующего исполнения сценария РуЙюп, который импор-
тирует ваши учетные данные). Поскольку размер файла с учетными данными
очень незначительный, создание его байткода при каждом исполнении сцена-
рия никоим образом не скажется на скорости исполнения. С другой стороны,
если его присутствие вас не беспокоит, то можете просто оставить его в покое.
Теперь, когда мы знаем, как отделить наш ключ АР1 от главной части кода, нам
нужно позаботиться о том, чтобы не допустить случайной загрузки файла с этим
ключом в Интернет при выполнении операции д1Л сопппЛ и/или д1Л ризЬ. Простым
и эффективным способом обеспечить такую безопасность будет использование
специального файла .§Ш§поге.
Основы работы с файлом .дШдпоге
Как можно видеть из его названия, файл .§Ш§поге позволяет указывать файлы, ко-
торые должны игнорироваться при работе с Сгй. Создавая (или модифицируя) для
репозитория в файле .§й1§поге правила сопоставления образцу, можно заранее оп-
ределить типы файлов, которые наш репозиторий будет отслеживать или выгру-
жать во внешнее хранилище. Хотя эту задачу можно решить вручную (никогда не
7 Вот почему программа, которая обеспечивает исполнение сценариев РуЙюп, часто называется
интерпретатором — она преобразовывает созданный людьми исходный код сценария в байтовый
код, который понимает ваше устройство.
используя команду дИ ас!с1 с файлами, которые мы хотим исключить из отслежива-
ния), использование файла .§Ш§поге гарантирует такое поведение8, а также предот-
вращает вывод предупреждений системой Ой о наличии неотслеживаемых файлов
при каждом исполнении команды д!1: 51а1и5. При отсутствии файла .§й1§поге при
каждом коммите нам нужно было бы подтверждать, какие файлы мы хотим игно-
рировать, что крайне утомительно и может привести к ошибкам. Лишь одной вы-
полненной в спешке команды дИ ас!с1 -А будет достаточно, чтобы начать отслежи-
вать наши конфиденциальные файлы с учетными данными. А удаление материла из
истории Ой сопряжено с намного большими сложностями, чем их добавление. По-
этому будет разумнее выполнить немного подготовительной работы, дабы избе-
жать возможных проблем.
Иными словами, файл .§й1§поге — это наш друг, который позволяет нам создавать
общие правила, не допускающие случайного отслеживания файлов, которые мы не
хотим отслеживать. Это также обеспечивает выдачу уведомлений системой (Эй о
статусе только тех файлов, для которых мы хотим их получать.
На данный момент мы создадим новый файл .§й1§поге в той же самой папке/репо-
зитории, в котором находится наш файл ГКЕВ сгебепбаН.ру, просто для того, что-
бы разобраться, как он работает9. Для этого создайте новый файл в редакторе кода
А1ош (или добавьте новый файл непосредственно в репозитарий СгйНиЪ) и сохра-
ните его под именем .§й1§поге в той же самой папке, в которой находится файл
ЕКЕВ сгебепбаН.ру. При этом обратите внимание на важный факт — имя этого
файла должно начинаться с точки (.).
Затем вставьте в этот файл следующий код:
# Игнорируем все файлы, содержащие учетные данные
^сгебепНа’Ез*
Как и в сценариях РуЙюп, комментарии в файлах .§й1§поге начинаются с символа
решетки (#), поэтому первая строка в этом файле просто описывает его содержи-
мое. А код во второй строке (^сгебепНаН*) — это что-то типа регулярного выра-
жения, т. е. специальной системы сопоставления образцу, позволяющей описывать
строки (включая названия файлов) таким образом, как мы бы объясняли это друго-
му человеку10. В данном случае выражение **сгебепГ1_а1з* означает "любой файл в
этом репозитории, который содержит слово сгебепйак”. Добавление этой строки в
наш файл .§Ш§поге обеспечивает, что любой файл в этом репозитории, чье назва-
ние содержит слово сгебепйа18, никогда не будет отслеживаться или загружаться на
СйНиЪ.
8 Даже при исполнении команды, скажем, дИ ас1с1 -А или дП соттИ -а.
9 Репозиторий может содержать несколько файлов .§Ш§поге в разных папках. Подробную инфор-
мацию по этому вопросу см. в документации (ЬПр8://§й-8ст.сош/(1ос8/§Ж§поге).
10 Этот подход был использован с библиотекой §1оЬ в листинге 4.20. Мы исследуем его более подроб-
но в разд. "Регулярные выражения: супермощное средство сопоставления строк" главы 7.
Чтобы увидеть наш файл .§Ш§поге в действии, сохраним его, а затем в терминале
исполним следующую команду:
д11: 51а1из
Вывод результатов исполнения этой команды должен содержать новый файл
ЕКЕВсгебепйаН.ру для кода сценария в листинге 5.3, но он не должен указываться
как ип1гаске(1 (неотслеживаемый). Чтобы убедиться в том, что файлы, которые
нужно игнорировать при резервном копировании Ой, действительно игнорируются,
можно выполнить следующую команду:
дх-Ь 51а1из --1дпогес1
Вывод этой команды будет содержать список файлов в репозитории, которые в на-
стоящее время игнорируются. Кроме файла ЕКЕВ_АР1_ехашр1е.ру, этот список,
скорее всего, будет содержать папку___русасйе__, для которой также не нужно вы-
полнять копирование в репозиторий.
Куда пропал наш файл .дШдпоге?
После сохранения и закрытия файла .§й1§поге он может не отображаться в
файловой системе. Поэтому обнаружение этого файла на своем устройстве по-
средством как визуального файлового интерфейса, так и средствами команд-
ной строки, может представлять определенную проблему.
Это объясняется тем, что по умолчанию большинство операционных систем
скрывают все файлы, название которых начинается с точки. И хотя файл
.§й1§поге всегда можно легко найти (и редактировать) на веб-сайте СгйНиЪ,
сделать это на своем устройстве можно, использовав несколько приемов, об-
легчающих его обнаружение и работу с ним.
Наш файл .§й1§поге является одним из нескольких файлов на вашем устройст-
ве, чьи названия начинаются с точки, которые вам когда-либо понадобится
просматривать и редактировать. Поэтому проще всего его найти будет, ис-
пользуя командную строку, перейдя в соответствующую папку или репозито-
рий (команда сс!) и исполнив команду для вывода списка всех объектов в дан-
ной папке:
-а
Если окажется, что папка не содержит файла .§й1§поге, его можно создать. Но
если такой файл уже есть в папке, то как можно его редактировать? Скорее
всего, вы запускали редактор кода А1ош, щелкая по его иконке, но этот редак-
тор также можно запустить из командной строки посредством ключевого сло-
ва а1:от (подобно запуску на исполнение сценариев РуЙюп, используя ключевое
слово руЫюп или ру). Таким образом, открыть файл .§й1§поге для его редакти-
рования в А1ош можно, исполнив следующую команду:
а1:от .дШдпоге
Исполнение команды может занять несколько секунд (даже при наличии дру-
гих файлов, открытых в А1ош), но использование командной строки для запус-
ка программ является легким способом нахождения и редактирования файлов,
даже если это скрытые файлы.
Специализированные АРЬинтерфейсы:
работа с протоколом ОАиЖ
На данном этапе мы располагаем всем необходимым для работы с АР1-интер-
фейсами, в которых используется так называемый базовый процесс аутентифика-
ции: мы создаем учетную запись с поставщиком АР1-интерфейса и получаем ключ
АР1, который мы добавляем в конец нашего запроса данных, как это делается в
листинге 5.3.
Хотя это очень простой процесс, он все же обладает несколькими недостатками.
Современные АРТ-интерфейсы способны на большее, чем просто возвращать дан-
ные: они также используются приложениями для, например, публикации обнов-
лений в учетных записях социальных сетей или добавления элементов в онлайно-
вое расписание. Чтобы эти приложения могли выполнять такие операции, они
требуют какого-либо доступа к соответствующей учетной записи. Но вы вряд ли
захотите предоставлять свое учетные данные приложениям и программам. Если
эту информацию предоставить приложениям просто так, то запретить приложе-
нию доступ к своей учетной записи в будущем можно будет, только изменив свое
имя пользователя и пароль. Однако в этом случае вам нужно будет предоставить
новые учетные данные тем приложениям и программам, с которыми вы хотите
продолжать работу. Таким образом ситуация может стать очень сложной и запу-
танной достаточно быстро.
Рабочий процесс аутентификации ОАщЬ предназначен для решения этой пробле-
мы: предоставляя способ доступа к АР1-интерфейсу без необходимости передачи
имени пользователя и пароля. Это достигается путем создания сценария так назы-
ваемого цикла авторизации, состоящего из трех базовых шагов:
1. Получаем и кодируем ключ АР1 и ”секрет” (каждый из которых представляет
собой простую строку, предоставляемую поставщиком АР1-интерфейса, как в
случае с ключом АР1 для базы данных ЕКЕЕ)).
2. Отправляем закодированную информацию этих двух строк как еще один ключ
специальной авторизующей конечной точки (1ЖЕ).
3. Получаем токен доступа (еще одна строка) от этой авторизующей конечной точ-
ки. Именно этот токен доступа мы, собственно, и отправляем конечной точке
данных АР1-интерфейса вместе с информацией запроса.
Хотя этот процесс может поначалу выглядеть довольно запутанным, на практике
большая часть его сводится к обмену строками с определенными ПКЕ-адресами в
определенном порядке. Да, по ходу обмена строками нам нужно будет выполнять
определенный объем кодирования их, но, как вы уже могли догадаться, эта задача
будет выполняться для нас соответствующей библиотекой РуЙюп.
Несмотря на необходимость завершить цикл авторизации и получить токен досту-
па, процесс взаимодействия с этими более специализированными АР1-интерфейса-
ми при помощи средств РуЙюп, по сути, такой же, как и тот, который мы рассмот-
рели в разд. "Специализированные АР1-интерфейсы: добавление простой аутен-
тификации" ранее в этой главе. А именно, создаем учетную запись, запрашиваем
учетные данные для АР1, а затем создаем файл, который содержит эти учетные
данные и предварительно обрабатывает их, чтобы их можно было использовать в
нашем основном сценарии. Затем наш основной сценарий извлечет эти учетные
данные, использует их для запроса данных у нашей целевой платформы, а затем
запишет полученные данные в файл результатов.
В этом примере мы будем работать с АР1-интерфейсом платформы Тмчйег, но при-
близительно тот же самый процесс можно применять и с другими платформами,
использующими подход ОАщЬ (например, РасеЪоок). Но мы не будем уделять
слишком много времени обсуждению организации конкретных запросов, посколь-
ку для подробной информации о любом АР1-интерфейсе может потребоваться от-
дельная книга. Тем не менее, освоив процесс аутентификации, вы будете обладать
необходимыми знаниями, чтобы начать экспериментировать с целым рядом других
АР1-интерфейсов, и начать практиковаться с ними, чтобы получить требуемые вам
данные. Итак, приступим к работе.
Получение учетной записи разработчика Тмйег
Нашим первым шагом (как и в случае с большинством других АР1, возможностями
которых мы хотим воспользоваться) будет запрос к Тмчйег на получение ключа
АР1. Даже если у вас уже есть учетная запись в Тмлйег, вам все равно нужно подать
заявку на получение "доступа разработчика”. Этот процесс в целом займет около 15
минут (без учета времени на проверку и/или одобрение вашей заявки компанией).
Первым делом откройте страницу подачи заявки на доступ к АР1-интерфейсу раз-
работчика Тмчйег (Ъйр8://с1еуе1орегЛуу1йег.сот/еп/арр1у-Гог-ассе88) и нажмите на
ней кнопку Арр1у Гог а Деуе1орег ассе88. После входа в систему (если он еще не
был выполнен) вам будет предложено предоставить дополнительную информацию
о том, как вы планируете использовать данный АР1-интерфейс. Для данного уп-
ражнения можно выбрать опции НоЬЬу18€ и Ехр1огш§ €Ье АР1, как показано на
рис. 5.7.
На следующем этапе нужно будет предоставить описание планируемого использо-
вания АР1-интерфейса объемом свыше 200 символов. Здесь можно ввести нечто
наподобие следующего:
Сзту 1ке ТххШег АР1/о 1еагп тоге аЬои1 Аоту с1а1а хугапуИпу \\л/к Ру 1коп.
1п1еге81ес1 т ехрегйпепИпу \\л1к ОАШк 1оорз апс1 риШпу сИфегеп! кМз
о/т/огтабоп/гот риЫлс ТхмШег/еес1з апА сопчегзаИопз.
(Использование АРРинтерфейса Т\\п1Лег необходимо, чтобы узнать больше
о выпасе данных с помощью средств РуРюп. Заинтересован(а)
в экспериментировании с циклами аутентификации ОАи1к и получении
разного рода информации из общедоступных каналов и разговоров в ТмпНег.)
С а де*еюрег.п*л11егсот/еп/рог:а1/ре(1Чол/ияесаье
* 0еуе1орег
Рог1а1
☆
Оосз СотлшИу Црда!е$ V Зиррол
#1)$еСаае5
АгвГ импда Гта(, Ш а 91Н уои 1й» пдМ
аррОсэЪоо
Р»сл (Не шю са#е (Им том с1оэе*у ге!аи»
!о (Не гуре ог «гол уои тгепв (о бо «*Ие
ис*пд и*е Т*»1«ст беУв*ррег рваКмт
ТЪеге» »оте теНав Ьеглеел (Неве «о
(Оси» ОП Л» »рОС(ЛСЖ О< уви» Гт»1Л
оО|*С!гуе
МаЮпраЬо!
ВиИЛГгд ШЫ$ (от
Тшиег икп
Ехркгод (Ье АР1
Рис. 5.7. Выбор планируемого использования АР1-интерфейса разработчиком Ъмйег
Поскольку наша цель в данном упражнении — практиковаться в загрузке данных
с платформы ТмлИег, используя сценарий РуЙюп и цикл аутентификации ОАиШ,
переключатели для всех четырех последующих вопросов можно установить на
как показано на рис. 5.8. Но если вы захотите использовать АР1-интерфейс
Тмчйег для других целей, вам нужно будет ответить на эти вопросы должным об-
разом.
Рис. 5.8. Выбор планируемого использования АР1-интерфейса разработчиком ТмИег
На следующих двух экранах проверьте выбранные вами опции и установите фла-
жок, чтобы принять Соглашение с разработчиком. Затем нажмите кнопку 8иЬш1€
аррПсаНоп, в результате чего вам будет отправлено сообщение с предложением
верифицировать адрес своей электронной почты. Если в течение нескольких минут
это сообщение не появится в папке Входящие вашего почтового ящика, проверьте
папки 8раш и корзины. Для подтверждения своей электронной почты щелкните по
соответствующей ссылке в этом сообщении.
Создание приложения и учетных данных Тмйег
После одобрения Тмчйег вашего доступа разработчика можно приступать к созда-
нию своего приложения. Для этого нужно войти в свою учетную запись Тмчйег и
открыть страницу Ъир8://(1еуе1орегЛуу1иег.сот/еп/рог€а1/рго]ес1:8-апс1-арр8. В цен-
тре этой страницы нажмите кнопку Сгеа€е Рго]ес€.
В результате последует процесс наподобие процесса подачи заявки на получение
доступа разработчика. В частности, нужно будет предоставить название для своего
проекта, указать планируемое использование АР1-интерфейса Тмчйег, дать краткое
описание планируемого использования, а также предоставить имя первого прило-
жения (арр), связанного с этим проектом. Соответствующие снимки экранов при-
водятся на рис. 5.9-5.12.
Оосз Согптипйу V ОрдаТез 8иррог1
Мате уоиг Рго|ес1
Уоиг Рго]'ес1 Ье1рз уои огдашге уоиг ууогк апд топПог уоиг изаде ууйЬ {Не
ТулПег АР1.
Рго]ес1 пате
во
ООСЗ
СоттипНу 0рс1а!е8 ✓ ЗирроП
УУЫсЬ Ьез! дезспЬез уои?
Ве1ом уои ууШ йпд орйопз оп Ноту уои 1п1еп<11о изе !НеТууй!ег Оеуе1орег
Р1аНогт.
Ехр1оппд АР1
Ыех(
Васк
Рис. 5.10. Экран указания цели проекта Тмйег
Оосз V Сотгпип’йу Орда^ез Зиррог!
ОезспЬе уоиг пе\л/ Рго|ес1
ТЫз 1гИо 1з|из1 ?ог из, Неге аТТмПег. К’П Ье1р из сгеа!е ЬеПег деуе1орег
ехрепепсез доууп 1Ье гоад.
Сгеайпд ап арр 1о де! ргасйсе изтд 1Ье Т^П1ег АР1 ил!11
РуЙ1оп.|
Мех*
Васк
Росз Соттип!(у - ОрбаГез Зиррог!
1_аз! з1ер, пате уоиг Арр
Аррз аге у/Кеге уои де! уоиг ассезз кеуз апд Гокепз апд ее! регт18зюпз.
ТКеу аге епсотраззед мЧЫл уоиг Рго]ес!з.
Ру|1юпАР1Ехрептеп1|
Сотр1е!е
Рис. 5.12. Экран названия приложения проекта Тхллйег
По завершении этого процесса отобразится экран с вашим ключом АР1, секретным
ключом АР1 и токен носителя (Ьеагег 1океп), как показано на рис. 5.1311.
Что такое, в конце концов, "приложение" (арр)?
Для большинства из нас слово "приложение” вызывает ассоциацию с играми и
услугами, доступными на телефоне, а не с использованием средств РуЙюп для
извлечения данных посредством АР1-интерфейса. Но для большинства по-
ставщиков АР1-интерфейсов приложением (арр) является все, что взаимодей-
ствует программным образом с их службами, будь то сценарий РуЙюп для за-
грузки сообщений или полномасштабное мобильное приложение, которое
пользователи могут устанавливать на свои устройства. Тем не менее, когда вы
получите свою учетную запись разработчика и начнете загружать сообщения
Тмлйег, вы можете честно говорить, что являетесь разработчиком приложений
(арр). С чем вас и поздравляем!
В целях безопасности вы сможете просматривать свой ключ АР1 (АР1 кеу) и сек-
ретный ключ АР1 (АР1 8есге1 кеу) только на этом экране, поэтому мы сразу же со-
храним их в нашем файле для учетных данных Тмлйег. (Обратите внимание на то,
что в других местах инструментальной панели разработчика эти учетные данные
называются АР1 Кеу (ключ АР1) и АР1 Кеу 8есге1 (секрет ключа АР1), — как види-
те, даже у крупных технических компаний могут быть проблемы с использованием
постоянной терминологии.) Но не стоит волноваться из-за этого. Если вы случайно
уйдете с этого экрана, неправильно скопируете ключи или допустите какую-либо
другую ошибку, то всегда можно возвратиться на инструментальную панель
(Ъйр8://йеуе1орег.Ьу1иег.сот/еп/рог€а1/йа8ЙЬоагф и щелкнуть по значку ключа
справа в поле приложения (рис. 5.14).
Затем в разделе Соп8итег Кеу8 нажмите кнопку Ке§епега€е, чтобы получить но-
вый ключ АР1 и ключ секрета ЕР1 (рис. 5.15).
Теперь нам нужно сохранить ключ АР1 и ключ секрета АР1 для нашего приложения
в новом файле учетных данных, подобном тому, который мы создали для хранения
ключа АР1 для базы данных ЕКЕТ). Для этого создайте новый файл
Т\У1йег_сгейепйа18.ру в папке, в которой будет находится ваш сценарий РуЙюп для
извлечения данных Тмчйег, и вставьте в него свой ключ АР1 и ключ секрета АР1,
как показано в листинге 5.4.
Листинг 5.4. Сценарий Тмлйег_сгес1еп<1а1§.ру
ту_Ъл/1Л1:ег_кеу = "ваш_ключ_ар!_в_двойных_кавычках"
пу_Ь\/Шег_5есге1: = "ваш_ключ_секрета_ар1_в_двойных_кавычках"
11 Эти ключи были заменены и больше не действительны.
ОазЬЬоагс!
X Рго]е<Л8
ОЯЕ11±У >
МОМТМ1У ТЛЕЕТ САР О8ЛСЕ 0
0 Т^ев15 р и11 е<1 оГ 500.000
0%
Незе!» ол Магск 5 а! 00001ЛС
РяоиктАРр
ф Ру1ЬопАР1Ехрептегй
Рис. 5.14. Инструментальная панель Тхллйег с приложениями
Ру111опАР1Ехрептеп1
ЗеНтдз Кеуз апд Токепз
СопзитегКеуе О
АР1 кеу & зесге!
АиИпепйсайоп Токепз О
Веагег Юкеп
Оепегагед РёЬгиагу 6. 2021
Ассезз Гокеп & зесгег
Рог фЗизапЕМсв
Непременно включите слово сгебепйак в название файла, в котором
хранятся ваши ключ АР1 и ключ секрета АР1 для Тмчйег. Вспомним, что
в разд. "Основы работы с файлом .§Пл§поге" ранее в этой главе мы
создали правило для игнорирования при резервном копировании любых
файлов, содержащих в своем названии слово сгебепйак, чтобы ни при
каких обстоятельствах не допустить случайной загрузки наших ключей
АР1 на сервер ОйНиЬ. Поэтому обязательно перепроверьте правиль-
ность написания названия файла с учетными данными. Для перестра-
ховки можно выполнить следующую проверку в командной строке,
чтобы подтвердить, что все файлы с учетными данными действительно
игнорируются для целей резервного копирования:
дП 51а1и5 --тдпогес!
Кодирование ключа АР I и ключа секрета АР1
До сих пор нам не нужно было делать ничего другого, что мы не делали в случае с
АР1-интерфейсом базы данных ЕКЕИ. Единственная несущественная разница —
вместо одной переменной в нашем файле учетных данных мы создали две пере-
менных: пу_ТмП1ег_кеу и пу_ТмИ1ег_5есге1.
Но теперь, прежде чем приступать к следующему шагу в процессе аутентификации,
нам нужно немного поработать со значениями этих переменных, чтобы преобразо-
вать их в правильный формат. Я не буду особо вдаваться в подробности происхо-
дящего ”под капотом” в следующих шагах; будет достаточным сказать, что эти
операции кодирования и декодирования необходимы для защиты исходных строко-
вых значений наших ключа АР1 и ключа секрета АР1, чтобы их можно было безо-
пасно пересылать по Интернету.
Для этого в наш файл Тмчйег сгебепйаН.ру мы добавим несколько строк кода, в
результате чего его содержимое будет выглядеть, как показано в листинге 5.5.
Листинг 5.5. Модифицированный файл Ту\л11ег_сгес1еп11а1з.ру
пу_Тм111ег_кеу = "ваш_ключ_ар1_в_двойных_кавычках"
пу_ТмИ1ег_5есге1 = "ваы_ключ_секрета_ар1_в_двойных_кавычках"
# Импортируем библиотеку Ьа5е64
1прог1 Ьа5е64 О
# Сначала объединяем ключ АР1 и ключ секрета АР1 в одну строку, разделяя их двоеточием
сопЫпес1_кеу_51г1пд = пу_Тм111ег_кеу+': '+пу_Тм111ег_5есге1
# Обрабатываем эту объединенную строку методом епсобе(), указывая формат А5СП
# (см.: Ы1р5://еп.м1к1реЛа.огд/м1к1/А8СИ)
епсобес1_сотЫпес1_кеу = сопЫпес1_кеу_51г1пд.епсос1е( 'азстл')
# Кодируем полученную строку в формате А5СН с использованием Ьазе64
Ьб4_епсобеб_сопЫпес1_кеу = Ьазе64. Ьб4епсобе(епсос1ес1_сотЫпес1_кеу)
# Декодируем строку обратно в представлении А5СН, чтобы подготовить
# ее для передачи по Интернету
аи1±_геас1у_кеу = Ьб4_епсос1ес1_сотЫпес1_кеу. с1есос1е(1 азол')
О Эта библиотека используется для преобразования исходного ключа АР1 и ключа
секрета АР1 в необходимый формат для передачи в конечную точку авторизации
Тмчйег.
Закодировав должным образом наш ключ АР1 и ключ секрета АР1, мы можем им-
портировать в сценарий для указания и извлечения данных только переменную
аи1±_геас1у_кеу. Теперь для извлечения сообщений из ТмйНег нам осталось только
отправить в конечную точку авторизации запрос на получение токена доступа.
Запрос токена доступа и данных из АРЬинтерфейса Тмйег
Подобно примеру, код которого приводится в листинге 5.3, мы создадим файл сце-
нария (или блокнот кода) РуЙюп, в котором будут выполняться следующие два ша-
га нашего процесса загрузки данных с платформы Тмчйег:
1. Запрос (и получение) токена доступа (который также называется токеном на
предъявителя — Ъеагег 1океп) у Тмчйег.
2. Добавление полученного токена доступа в запрос данных к Тмчйег и получение
результатов.
Запрос токена доступа, используя методы де! и рое!
Для запроса токена доступа у Тмчйег нужно всего лишь отправить правильно
оформленный запрос к конечной точке авторизации Ъйр8://ар1Луу1иег.сот/оаи1112/
€океп. Но вместо использования метода деГ (который мы использовали в листингах
5.1 и 5.3), когда ключ аи1±_геабу_кеу добавляется в конец ИКЬ конечной точки, в
данном примере мы используем метод роз!:.
В данном случае метод роз!: важен тем, что он предоставляет дополнительную
безопасность по сравнению с методом де1:12. Но основная причина его использова-
ния состоит в том, что он является стандартным способом в тех случаях, когда от
АР1-интерфейса требуется больше, чем просто возвратить данные. Таким образом,
тогда как в листинге 5.3 мы получали данные, которые были уникальными для на-
шего запроса, АР1-интерфейс возвратил бы точно такие же данные для любого дру-
гого пользователя, отправившего такой же запрос. В противоположность, при ис-
12 Например, содержимое запросов роз!: не сохраняется в истории браузера подобно тому, как это
делается для запросов де1:.
пользовании метода роз!: для отправления ключа аиШгеабукеу АР1-интерфейс
платформы Тмчйег обработает наш уникальный ключ и возвратит уникальный то-
кен доступа. Поэтому мы и используем метод роз1:.
При построении нашего запроса роз!: в РуЙюп нам нужно создать два объекта дгс1.
Один из них будет содержать заголовки запроса, включая наш ключ аи1±_геас1у_кеу
и некоторую другую информацию, а другой — данные запроса, которые в данном
случае будут указывать, что мы запрашиваем учетные данные. Затем мы просто
передадим эти два объекта в виде параметров методу роз!: библиотеки гедие818 вме-
сто добавления их в конец строки ПКЬ-адреса. Соответствующий код приводится в
листинге 5.6.
Листинг 5.6. Сценарий Тш№ег_с1а<а_с1ошп1оас1.ру
# Импортируем зашифрованный ключ из файла учетных данных
Поп Т1лЛ1:ег_сгес1епС1а18 1прог1: аи1±_геас1у_кеу
# Импортируем библиотеку герое8Г для получения данных из Интернета
1прог1: гециез^з
# Указываем конечную точку ТнШег для получения нашего маркера доступа
# (или токена носителя)
аи1:Ь_иг1 = ,Н11р2://ар^.1Vп1^ег.сот/оа^1Н2/^океп,
# Добавляем наш ключ аи1±_геас1у_кеу в шаблонный объект (Нс!, предоставляемый
# в документации по АР1-интерфейсу Т1ллЛ1ег
аи1±_Ьеас1ег8 = {
'АиГНогт-гаГт-ОП': 'Вазтс '+аи1±_геас1у_кеу,
'Соп1:еп1:-Туре': 'аррИсаНоп/х-шм-Гогтшг1епсос1еб;сЬаг8еГ=1ГГР-8'
}О
# Еще один объект (Нс! содержит описание запрашиваемых данных
аи1Н_с1а1а = {
' дгап1:_1:уре': 1 с1Леп1:_сгес1еп-Ыа'18'
}О
# Посылаем наш запрос в конечную точку авторизации и сохраняем полученные результаты
# в переменной аи1±_ге8р
аи1±_ге8р = герие81:8.ро81:(аи1±_иг1, Ьеас1ег8=аи1±_Ьеас1ег5, с1а1а=аи1Н_с1а1а)
# Извлекаем маркер доступа из данных в формате дзоп, присланных нам конечной точкой
авторизации
ассе88_Ьокеп = аи1±_ге8р. д8оп()[1 ассе88_1:океп' ]
О Данный объект (Нс! содержит информацию, которая требуется конечной точкой
авторизации, чтоб отправить нам маркер доступа. Эта информация включает наш
зашифрованный ключ и его формат данных.
О Формат объектов аи1±_Ьеас1ег5 и аи1Ь_с!а1а был определен поставщиком АР1-
интерфейса.
Довольно простой процесс, не так ли? Если все пройдет хорошо (что мы скоро про-
верим), в этот сценарий нужно будет добавить лишь несколько строк кода, чтобы
использовать полученный токен доступа для запроса настоящих данных о происхо-
дящем в ТмйНег.
Но как насчет токена доступа
в инструментальной панели разработчика?
Возможно, что при копировании своего ключа АР1 и ключа секрета АР1 из ин-
струментальной панели разработчика (см. рис. 5.13) вы обратили внимание на
наличие в ней токена на предъявителя (Веагег Токеп). Почему мы не использо-
вали этот токен вместо того, чтобы создавать сценарий для получения еще од-
ного?
Вкратце, потому что хотя АР1-интерфейс платформы Тмчйег предоставляет то-
кены доступа в инструментальной панели, эта возможность отсутствует во
многих других подобных АР1-интерфейсах. Поскольку нашей целью является
проиллюстрировать более общий процесс взаимодействия с АР1-интерфейсами,
использующими ОАиЙт, мы рассматриваем процесс прохождения цикла запро-
са токена доступа во всех подробностях, чтобы этот код можно было модифи-
цировать и использовать с другими АР1-интерфейсами.
Получив токен доступа (токен на предъявителя), можно переходить к созданию за-
проса для извлечения сообщений из платформы Тмчйег. В целях демонстрации мы
используем простой запрос, в частности, базовый запрос поиска недавних сообще-
ний Тмчйег, содержащих слово РуЙюп, с возвратом максимум четырех сообщений.
Этот запрос мы создадим на базе запроса из листинга 5.6, включив в его заголовок
полученный нами токен доступа. Полученные по этому запросу данные мы запи-
шем в файл. Но поскольку кроме текста сообщений ответ содержит большой объем
других данных 18(Ж, мы выведем на экран только текст каждого сообщения Т\уй-
1ег, просто чтобы быть уверенными в том, что мы получили правильные (если ино-
гда и неожиданные) результаты.
Листинг 5.7. Сценарий Тмйег_с1а1а_с1о№п1оас1.ру (продолжение)
# Имея в своем распоряжении токен доступа (Веагег Токеп), мы готовы
# выполнять запросы данных
# Создаем новую структуру сНс1, содержащую наш маркер доступа
зеагсЬ_Ьеас1ег5 = {
'АЩЬогТгаНоп1: 'Веагег ' + ассез8_1:океп
}
# Это конечная точка АР1-интерфейса для поиска в Тнт.11ег для версии 1.1 АР1-интерфейса
зеагсЬ_иг! = 1 Н«рз://аргЛиШег.сот/1.1/зеагсН/1иее15. дзоп'
# Создаем новый объект (Нс!, содержащий наши параметры поискового запроса
зеагсЬ_рагатз = {
' я1: ' Ру1±оп1,
' гезиТ^уре': ' гесеп!:',
'соип^': 4
}О
# Отправляем наш поисковый запрос и сохраняем полученные им результаты в переменной
# зеагсЬ_гезр
зеагсЬ_гезр = гедиез1з.де1:(8еагсЬ_иг1., Ьеас1ег5=8еагсЬ_Ьеас1ег5, рагап8=8еагсЬ_рагатз)
# Преобразовываем полученные данные в объект Э80И
Ти!11ег_с1а1а = зеагсЬ_гезр. дзоп()
# Открываем выходной файл для сохранения результатов
Т\л/111ег_ои1:ри1_Г11е = ореп("ТиШег_зеагсЬ_ге8и1Лз.дзоп", V)©
# Записываем полученные с ЪлгШ:ег данные в наш выходной файл
Ти!11ег_ои1ри1_Гг‘1е.игг1е(з1г(ТнП1ег_да1а))
# Закрываем выходной файл
Тш111ег_ои1ри1:_Г11е. с!озе()
# Обрабатываем в цикле полученные результаты и отображаем на экране текст состояния
# ТнШег
1^ог а_Тнее1: 1п ТиШег_с1а1а[ 'зГаГизез' ] :О
рп.п1(а_Тнее![ Чех!' ] + 1 \п')
О В данном случае наш запрос (ц) направлен на поиск не более четырех последних
сообщений Тмчйег о РуШоп. Напомню, что ключи и значения, которые может со-
держать этот объект, определяются поставщиком данных.
О Поскольку в ответ на запрос Тмчйег возвращает объект 18(Ж, мы использовали
функцию РуЙюп з±г(), чтобы преобразовать его в строку, прежде чем записывать
его в файл.
0 Поскольку каждый результат содержит большой объем информации, чтобы по-
лучить представление о содержимом результата, мы будем отображать только текст
каждого сообщения Тмчйег. Переменная зГаТшзез содержит список сообщений Т\уй-
1ег в объекте 18ОМ, и сам текст сообщений можно получить при помощи ключа
1ех1.
В зависимости от уровня активности пользователей ТмйНег по созданию новых со-
общений о РуЙюп, результаты могут быть разными при повторном исполнении
сценария даже в течение нескольких минут после предыдущего13. Конечно же, этот
поисковый запрос можно модифицировать для поиска любого требуемого вами
термина, просто изменив должным образом значение переменной 5еагсЬ_рагатз.
Просмотреть список всех возможных параметров и их допустимых значений можно
в документации по АР1-интерфейсу для конкретной конечной точки АР1-
интерфейса ТхуЩсг (ййрз://Деуе1орег.Ьу1йег.сош/еп/(1ос8/Ьу1йег-ар1/у1/Ьуее48/8еагсЬ/
арЬгеГегепсе/§е1>8еагс11-Ьуее1:8).
Вот и все! Платформа Тмчйег предоставляет несколько разных АР1-интерфейсов
(один из которых, например, позволяет публиковать сообщения в своей, или даже в
чьей-то другой, ленте сообщений). Но для целей извлечения и выпаса данных рас-
смотренного здесь материала должно быть достаточно, чтобы позволить вам начать
работать с другими подобными АР1-интерфейсами.
Этические нормы при работе
с АРЬинтерфейсами
Теперь, когда вы знаете, как делать запросы к АР1-интерфейсам таких служб, как
Тмчйег (и других, использующих процесс ОАиЙз), вы хорошо представляете себе
многообразие возможностей, которые можно делать с полученными данными. Но
прежде чем начинать создавать десятки сценариев для отслеживания онлайновых
разговоров на ваши любимые темы, будет уместным рассмотреть некоторые прак-
тические и этические аспекты подобной деятельности.
Во-первых, имейте в виду, что почти все АР1-интерфейсы ограничивают количест-
во запросов, которые можно выполнять в течение данного интервала времени. На-
пример, для конкретной конечной точки АР1-интерфейса, которую мы использова-
ли в листинге 5.7, можно делать максимум 450 запросов в течение 15 минут, при
этом каждый запрос может возвращать максимум 100 сообщений Тмчйег. В случае
превышения этого количества, дальнейшие запросы, скорее всего, не будут воз-
вращать никаких данных, пока платформа Тмчйег не решит, что начался следую-
щий 15-минутный интервал.
Во-вторых, даже если не читали во всех подробностях Соглашение с разработчиком
(не волнуйтесь, вы не один такой14; при необходимости, его всегда можно
найти в Интернете по адресу ййр8://(1еуе1орегЛууШег.сот/еп/(1еуе1орег4егт8/
13 Не забывайте, что при каждом повторном исполнении этого сценария старое содержимое выходно-
го файла заменяется новым.
14 См. статью Тке Сол1 о/ТеасНпу Рг А асу РоПслел в журнале 1/8: А Аоигпа1 о/Там? апА РоНсу /ог 1ке
1п/огтаПоп 8ос1е1у 4, авторы А1еес1а М. МсВопаМ апс! Ьоте Райй Сгапог (2008): 543, ЬНр8://кЬ.О8и.е(1и/
ЫШгеат/11апс11е/1811/72839/183ЕР_У4№_543.рсИ, и статью Тке ВкууелЛ1ле оп 1ке 1п1егпе1:1§погт§ 1ке
Рпхасу РоНсгез апс1 Тегтз о/8ег\лсе РоНсгез о/8ос1а1 Ие1\\'огк1пу 8ег\лссу авторы ЗопаШап А. ОЬаг и Аппе
ОеМогГ-Нньсй, ТпГогтабоп, Соттитсайоп & 8ос1е1у 23 №. 1 (2020), стр. 128-147, ЬНр8://рарег8.88гп.сот/
8о13/рарег8.сйп?аЬ81:гас1_1(1=2757465.
а§геетеп€-ап(1-роКсу), оно содержит положения, имеющие важные практические и
этические последствия. Например, Соглашение с разработчиком системы Тмчйег
прямо запрещает ''сопоставление данных вне рамок платформы ТмчИег”, т. е. объе-
динение данных Тмчйег с другой информацией о пользователе, за исключением
случаев, когда этот пользователь предоставил вам эту информацию, дав на это свое
согласие. Оно также содержит правила о том, как можно хранить и отображать со-
держимое платформы Тмчйег, получаемое посредством его АР1-интерфейса, а так-
же целый ряд других правил и ограничений.
Независимо от того, имеют ли эти условия предоставления услуг обязательную
юридическую силу15 или являются ли они действительно этичными сами по себе,
помните, что, в конечном итоге, ответственность за сбор, анализ, хранение и обмен
любыми данными, которые вы используете, с соблюдением этических норм лежит
на вас. Это означает, что необходимо учитывать конфиденциальность и безопас-
ность людей, данные о которых вы можете использовать, а также думать о послед-
ствиях их сбора и обмена.
Конечно же, если бы этические проблемы было легко идентифицировать и согласо-
вать, то мы бы жили в совершенно другом цифровом мире. Но поскольку это не
так, многие организации используют четко определенные процессы проверки и ко-
митеты по надзору, предназначенные для облегчения изучения и (если возможно)
решения этических проблем еще до начала исследований и сбора данных, оказы-
вающих влияние на людей. Лично я считаю, что хорошей начальной точкой при
рассмотрении этических вопросов является Этический кодекс Общества профес-
сиональных журналистов (8ос1е1у оГ РгоГе88Юпа1 1оигпа11818' Собе оГ ЕЙнс8,
Ьйр8://8р].ог§/е€Ыс8Собе.а8р). Хотя в этом документе подробно не рассматривают-
ся все возможные ситуации, затрагивающие этические вопросы, в нем излагаются
основные принципы, которые, по моему мнению, было бы полезно учитывать всем
пользователям данных при сборе, анализе и обмене информацией.
Учитывая все это, на мой взгляд, все же самым важным является то, что вы должны
быть готовы поддержать любой сделанный вами выбор. Одной из замечательных
возможностей извлечения и выпаса данных является возможность открытия новой
информации и генерирования новых идей. Точно так же, как навыки для этого на-
ходятся полностью в вашем распоряжении, так и ответственность за то, как вы их
используете, полностью лежит на вас.
Извлечение веб-данных:
источник данных последней надежды
Компании и организации разрабатывают АИ-интерфейсы с конкретной целью пре-
доставления доступа к наборам данным (часто обширным и разнообразным) через
15 См. статью Оа1а ЛигпаИзт апс11ке ка\\', автор У1с1опа Э. Вагапе1§ку, опубликованную То\\' СегПегрог
УоигпаЫзт в 2018 г., ЬПр8:// йоьог§/10.7916/(18-158УУ-Гу51.
Интернет. Но Интернет содержит большой объем информации, существующей в
виде веб-страниц на основе форм или с высоким уровнем форматирования, а не
предоставляемой посредством АР1-интерфейсов или в форматах С8У или РИГ. В
таких ситуациях единственное решение для получения этой информации состоит в
применении веб-извлечения данные. Это процесс получения содержимого веб-
страниц программным способом с последующим извлечением из этого содержимо-
го некоторого объема структурированных данных.
Я называю веб-извлечение данных источником данных последней надежды по той
причине, что это как технически, так и этически сложный процесс. Для создания
сценария РуЙюп для веб-извлечения данных почти всегда нужно вручную исследо-
вать запутанный НТМЬ-код, чтобы найти требуемые данные, после чего обычно
следует значительный объем обработки методом проб и ошибок, чтобы отделить
требуемую информацию от всего ненужного. Это трудоемкий, хлопотный и часто
разочаровывающий процесс. К тому же, если страница изменится даже самую ма-
лость, то вам, возможно, придется начать все сначала, чтобы ваш сценарий работал
с обновленной страницей.
Веб-извлечение данных также представляет сложность в этическом аспекте, по-
скольку многие владельцы веб-сайтов по разным причинам не хотят, чтобы их сай-
ты подвергались извлечению данных. Программы извлечения с некачественным
кодом могут перегрузить веб-сайт, делая его недоступным для других пользовате-
лей. Выполнение сценарием запросов с высокой частотой также может повысить
расходы владельца веб-сайта, так как ему нужно платить своему поставщику услуг
за возврат всех этих данных в течение короткого периода времени. Поэтому многие
веб-сайты в условиях обслуживания прямо прописывают запрет на веб-извлечение
данных.
В то же время, если важная информация — особенно о влиятельных организациях
или правительственных учреждениях — доступна только посредством веб-
страницы, тогда веб-извлечение может быть вашим единственным вариантом, хотя
это и противоречит условиям обслуживания. И хотя предоставление псевдоюриди-
ческого совета по этому вопросу выходит далеко за рамки этой книги, имейте в ви-
ду, что даже если ваши сценарии соответствуют стандартам ответственности, и для
вашей деятельности по веб-извлечению данных существует веский общественный
интерес, владелец веб-сайта может наложить на вас санкции (например, отправить
вам письмо с требованием ''прекратить и воздерживаться впредь”) или подать на
вас в суд.
Поэтому я настоятельно рекомендую, прежде чем приступать к разработке любого
сценария для веб-извлечения данных, пройтись по замечательному дереву решений
(автора Софи Чоу — 8орЫе Сйои), показанному на рис. 5.1 б16.
Определив, что веб-извлечение является вашим единственным или наилучшим ва-
риантом, можно приступать к работе.
16 Это дерево решений сопровождается таким же превосходным постом блога: ЬПр8://81огуЬепсЬ.ог§/
1о-8Сгаре-ог-поЫо-8Сгаре4Ье4есЬтса1- апс1-еШ1са1-сЬа11еп<>е8-оГ-со11есНп«-с1аЫ-оП-Ше-у\еЬ.
Стоит ли вам создавать сценарий для веб-извлечения данных?
Почему вы хотите
применить
вгб извлечение данных?
Я думаю. 410 они
садоржат какукмо
историю
ЙЖ1У
шхпедэддть даянью
Ли
Ваш проект должен
быть заверит
к определенной дате?
Да
Да
Они предоставляют
АНЬииторфййй?
П|"'й»™й1всь
с адвоы I им
Сияжигасос ними
Связались пи вы
с организацией?
Ползли ди вы запрос
НП ПрПДППТ.ТВЛйИИЙ
данных согласно Закону
I) т^ободЕ информации?
Они Могут
предоставить данные -?
Нпт
Находятся пи эти
данные в открытом
Доступе?
Падайте дэпрда
на предатзВ71Мг
данных согласно Закону
о свобода информаций
Нарушат ли 0ДШ
СцП|^р|ЛН У1.1ТЦП1Л11
ОЙ0Т|уЖИМНИЯ?
Селили проблемы
с конфиденциальностью
_______данных?______
Пересмотрите
свои разработки
Можете смело
делать это!
Рис. 5.16. Дерево решений для веб-извлечения данных
(создано Софи Чоу для ЗТогуЬепсК.огд, 2016)
Осторожно извлекаем данные с веб-сайта УГПТ
Для данного примера мы используем подход извлечения веб-данных для загрузки и
извлечения данных из веб-страницы Управления городского пассажирского транс-
порта (УГПТ) города Нью-Йорк, которая предоставляет ссылки на все данные с
турникетов городских станций метро, начиная с 2010 г. Чтобы гарантировать наи-
высший уровень ответственности в процессе веб-извлечения данных, мы обеспе-
чим, что каждый создаваемый нами сценарий РуЙюп:
1. Идентифицирует нас и предоставляет контактную информацию (в данном при-
мере мы вставим адрес электронной почты).
2. Делает паузы между запросами к веб-странице, чтобы не допустить перегрузки
сервера.
Кроме этого, мы структурируем наш сценарий, разбив его на отдельные части, что-
бы обеспечить загрузку только необходимого материала определенной веб-
страницы. Для этого мы начнем с загрузки и сохранения копии веб-страницы, со-
держащей ссылки на файлы с данными для отдельных турникетов. Затем мы созда-
дим отдельный сценарий, который обрабатывает сохраненную версию загруженной
страницы и извлекает требуемые нам данные. Таким образом, всем экспериментам
в процессе извлечения данных из веб-страницы будет подвергаться наша версия
этой страницы, что предотвратит излишнюю нагрузку на сервер. Наконец, мы соз-
дадим третий сценарий, который выполняет парсинг нашего файла с извлеченными
ссылками и загружает данные для турникетов за последние четыре недели.
Прежде чем приступать к написанию какого-либо кода, давайте посмотрим на
страницу, из которой мы планируем извлекать данные (Ьйр://луеЬ.ш1а.тГо/
Деуе1орег8/1игп8Й1е.Ыш1). Как видно, эта страница не более чем заголовок и длин-
ный список ссылок, каждая из которых ведет на текстовый файл (с расширением
.1x1) с данными в формате С8У. Чтобы загрузить файлы данных за последние не-
сколько недель, первым шагом загрузим и сохраним копию этой страницы с указа-
телями. Соответствующий код приводится в листинге 5.8.
Листинг 5.8. Сценарий МТА_1игп81Ие8_тс1ех.ру
# Импортируем библиотеку гециез^з для получения данных из Интернета
Тпрог!: гециез^з
# Указываем 0К1_-адрес загружаемой страницы
# Данная страница содержит список ссылок на все файлы данных для турникетов УГПТ
города
# Нью-Йорк вплоть до 2010 г.
п1:а_1:игп51:11е8_1пс1ех_иг1 = "ЬНр://меЬ.п1:а.1пГо/с1е7е1орег8/1:игп81:11е.Ы:т1"
# Создаем заголовочную информацию для нашего запроса веб-страницы
Неабегз = {
'Озег-АдепС : 'МогШа/5.0 (ХИ; СгО5 х86_64 13597.66.0) ' + \
'АррТеЫеЬки/537.36 (КНТМ1_, 1!ке Сеско) ' + \
'СЬгопе/88.0.4324.109 8а^аг1/537.36',
'Егоп': ’УООР МАМЕ НЕКЕ - уо^гета^^а^^ге88@ета^^ргоV^с^ег.5от,
}О
# Вместе с нашими информационными заголовками отправляем запрос де±() для данного
# 11Р1_- адреса
п1:а_меЬ_раде = гедие815.де1:(т1а_1игп8Н1е8_1пс1ех_иг1, Ьеас1ег5=Ьеас1ег5)
# Открываем локальный файл в режиме записи для сохранения содержимого загруженной
# веб-страницы
гтЬа^игпзШеЗ-Ои-Ьри^ЕИе = ореп( "МТА_1:игп81:11е8_1пс1ех. Ытп1", V)
# Записываем текст веб-страницы в наш выходной файл
т1а_-|:игп81:11е8_ои1:ри1:_Г11е. иг1Ле(п1:а_иеЬ_раде. 1ех1)
# Закрываем выходной файл
п^а^игпз^ИеЗ-Ои^ри^-ГИе. с!озе()
О Поскольку в данном случае мы не используем АР1-интерфейс, нам нужно пре-
доставить владельцу веб-страницы упреждающую информацию о том, кто мы та-
кие и как связаться с нами. В данном случае мы описываем браузер, который он
должен считать источником нашего трафика, а также предоставляем наше имя и
контактную информацию. Эти данные владелец веб-сайта сможет увидеть в журна-
лах своего сервера.
В результате исполнения этого сценария мы получим файл МТАШтзШезтбех.Йш! в
той же самой папке, что и файл сценария. Чтобы просмотреть его содержимое, вы-
полните двойной щелчок мышью по его значку, и он должен открыться в вашем
браузере, установленном по умолчанию. Поскольку мы загрузили только основной
код страницы без никаких дополнительных файлов, изображений и других мате-
риалов, к которым эта страница обычно имела бы доступ в Интернете, то страница
будет иметь несколько неаккуратный вид, примерно как показанно на рис. 5.17.
К счастью, такой внешний вид не имеет особой важности для наших целей, по-
скольку нам нужны только ссылки, которые содержатся в коде НТМЬ-страницы.
Но прежде чем начинать размышлять о том, как извлечь эти ссылки программным
способом, нам для начала нужно найти их в коде НТМЬ-страницы. Для этого нам
понадобятся средства инспектирования.
Использование средств инспектирования браузера
Откройте в браузере локальную копию страницы данных турникетов УГПТ и про-
крутите ее вниз, пока в поле зрения не появится заголовок Оа€а ГПез (см. рис. 5.17).
Чтобы наш сценарий РуЙюп мог более точно определить в коде этой страницы
только требуемую нам информацию, нам нужно определить какую-либо уникаль-
ную характеристику кода НТМЬ, заключающего эту информацию. Это облегчит
для нас задачу создания сценария, который будет быстро находить требуемое нам
содержимое. Самый легкий способ для этого — исследовать код страницы бок о
бок с ее отображением в браузере.
о РеНоппапсе
1п(31саСог5
о Рге85
Ке1еа5.е8
апсЩеуу8
о РиЬПс
Неагш%5
о Тгалзроггайоп
Кешуепбоп
Сотт158юп
ТйгпзШе ПаЫ
Кеу/К.е$оигсе8
• ПеШ ПезспрЬоп:
о Ргюг 1о 10/18/14
о Сиггеш
• Ветоге ЦпЛ/СопГго! Агеа/8га11оп Кате Кеу
Оа(а Р11е$
5ашгс1ау, РеЬгиагу 06. 2021
8атЫау, Дапиагу_30, 202.1
ЗаГигдау, 1апиагу 23, 2021
8ашгс1ау, 1апиагу 16, 2021
8аШгс1ау, 1апиагу 09. 2021
8ашЫау,Дапиагу_02,_2021
8аШгс1ау, РесетЬег 26, 2020
8аГигс1ау, РесетЬег 19, 2020
ЗаШгдау, РесетЬег 12, 2020
8аГлгс1ау, РесетЬег 05, 2020
Рис. 5.17. Просмотр в браузере локальной копии веб-страницы УГПТ
Для этого щелкните правой кнопкой мыши по тексту Оа€а ГПез. В открывшемся
контекстном меню выберите опцию 1п8рес€ (рис. 5.18).
Точное размещение и вид окна средств инспектирования будет разным для разных
браузеров, но его содержимое должно выглядеть наподобие показанного на рис.
5.19 (окно инспектирования браузера Сйгоше).
Независимо от местонахождения этого окно в вашем браузере (иногда оно будет
закреплено на боковой или нижней стороне окна браузера, как показано на рис. 5.19,
и часто содержит несколько панелей с дополнительной информацией), нам нужно
найти в его содержимом слова Ба1а ГИез. Если вы потеряли эти слова из виду (или
не видели их с самого начала) после открытия окна, просто снова щелкните правой
кнопкой мыши по этим словам в веб-странице, чтобы еще раз открыть окно ин-
спектирования.
• ПеМ ОеьспрСюп:
о Ртюг (о 10/18/14
о Сцггеп!
кетоге ЦтУСопгто! Агеа/$|агюп Кате Кеу
ОаСа
Васк
А1НЬе/( Агго*
ЗатНау,-
5 ат Нау,»
5 а ш Нау,.
8а т Нау,
8ашНау,,
ЬашНау.л
ЗашНау^
5аП1Нау,
5а Ш Нау,;
8 атНау
5ашНауу,.
Не1оас»
5ауе аз...
РГ1П1„
Са$1._
С1гкН
СЫ+Р
раде еоигсе СЫ+К
Рис. 5.18. Контекстное меню для элемента 0а1а П1е$
локальной копии веб-страницы УГПТ
Кеу/Кезоигсез
• ПеШ Ве$спр(1оп:
с Рпог со 10/18/14
о Сштет
...............~ __зГайоп №те Кеу
8|у.зрап-84.1аз1 1458.53x10497.2 л-----Л
Вага ГПе$
ЬаШгбау, ПсЬгиагу Об, 2021
Ьашгс1ду, Пшату 30, 2021
ЬаШтНау, Запиагу 2Г 2021
?заШгс1ду, 1аииагу 16, 2021
ЪаШгбау, 1апиагу 09, 2021
5аШгс1ду, 1апиагу 02, 2021
ЬаШгНау, РесстиЬег 26. 2020
13? Д~1 Е1егпеп1з Сопзо1е Зоигсез ЫеЪмогк РегТогтапсе Мегтюгу »
<!-- Еп1ег Соп1еп1 Неге
<(11>ТигпзО1е 0аСа</Н1>
▼ «Цу с1аз5-"соп1:а1пег">
<Ьг>
-Н2>Кеу/Яезоигсез^/112>
<р>
</р>
► <и1 с1аз5=’'аггои‘>...</и1>
<рх/р>
▼ с1а55-"зрап-84 1азС•
.. Ь2 0а1а Е11ез- /Ь2 - $0
<Ьг>
<а ЬгеТ (1а€а/пус1:/Уигп5Г11е/1:игп5Е11е 210286.1x1 *5а1игдау,
РеЬгиагу 06, 2021</а>
<Ьг>
<а НгеТ-'Да1а/пус1/гигп5111е/1игп5111е 210139.1x1'-5а1игс1ау,
Эапиагу 30, 2021</а>
<Ьг>
<-а Иге! - да1а/пус1/1игп5111е/1игп51Ие 210123.1x1 '5а1игДау,
Запиагу 23, 2021</а>
... платЬох Лу#соп1еп1Ьох.гоипс1Согпег8.с1еагГ|х сНу.соп(а1пег д1У.зрап-84.1аз1 Н2
Рис. 5.19. Пример окна средств инспектирования
Использование средств инспектирования
наиболее эффективным способом
Средства инспектирования браузера могут быть полезными во многих случаях,
но они особенно важны при веб-извлечении данных, когда нам требуется найти
уникальный способ для идентификации определенных фрагментов информации
на веб-странице. Например, при наведении курсора мыши на фрагмент кода под-
свечивается соответствующая часть веб-страницы. Перемещаясь таким образом
по коду в окне средств инспектирования, можно подсвечивать соответствующие
части визуального интерфейса веб-страницы, что способствует созданию сцена-
рия для более точного захвата нужных вам частей веб-страницы.
В данном случае наведем курсор мыши на следующий код в окне средств инспек-
тирования:
<сйл/ с1а88="8рап-84 1аз1:">
В результате в окне браузера должен подсветиться раздел Ва€а ГПез веб-страницы.
На основании подсвеченной области кажется, что данный фрагмент кода содержит
весь список интересующих нас ссылок, что можно подтвердить, прокручивая со-
держимое окна средств инспектирования. Здесь мы увидим, что все требующиеся
нам ссылки на файлы данных (которые заканчиваются расширением .Ш) действи-
тельно находятся внутри раздела сЙ7. (Обратите внимание на то, как они все распо-
ложены, — с отступом под этим тегом. Это еще один пример использования вло-
женности.) Теперь, если мы можем подтвердить, что класс зрап-84 1аз1: встречается
только в одном месте веб-страницы, мы получим хорошую отправную точку для
создания нашего сценария РуЙюп для извлечения списка ссылок.
Решение Ру№оп для извлечения данных из веб-страницы:
библиотека Веаи№и18оир
Прежде чем приступать к созданию следующего сценария РуЙюп, давайте удосто-
веримся, действительно ли класс зрап-84 1аз1: является уникальным на загруженной
странице. Опишем самый простой способ, как сделать это. Откройте сохраненную
веб-страницу в редакторе кода А1ош (щелкнув правой кнопкой по его значку и в
опции Открыть с помощью открывшегося контекстного меню выберите вариант
А€ош). В результате в редакторе отобразится код нашей веб-страницы. Затем вы-
полните поиск в документа по строке ”§рап-84 1а§Г. Как выясняется, подстрока
”8рап-84” встречается в нашем файле только один раз, поэтому в нашем сценарии
РуЙюп мы можем ограничиться поиском ссылочной информации, вложенной в этот
НТМЬ-тег.
Теперь мы готовы начать создание сценария РуЙюп для извлечения требуемых
ссылок из сохраненной веб-страницы. Но для этого нам нужно для начала устано-
вить специальную библиотеку ВеаибГи! 8оир, предназначенную для парсинга кода
языка разметки веб-страниц. Хотя некоторая функциональность этой библиотеки
повторяет функциональность библиотеки 1хш1, которую мы использовали в сцена-
рии в листинге 4.16, она может выполнять парсинг даже неидеально структуриро-
ванного кода НТМЬ и ХМЬ, с которым нам постоянно приходится сталкиваться в
Интернете. Кроме этого, библиотека ВеаибГи! 8оир позволяет захватывать фраг-
менты кода разметки по практически любой его характеристике — названию клас-
са, типу тега или даже значению атрибута. Поэтому это, по большому счету, иде-
альная библиотека для извлечения данных из ”супа” кода разметки, который часто
встречается в Интернете. Информацию по установке этой библиотеки можно найти
в ее документации (ййр8://сгитту.сот/8ОЙлуаге/ВеаиШи18оир/Ь84/с1ос), но легче
всего это сделать посредством того же самого процесса, который мы использовали
для установки других библиотек, в частности, при помощи команды р!р:
р1р гпзГаИ ЬеаиГТГи1.5оир4
Теперь мы можем создать сценарий РуЙюп, чтобы открыть локальную копию веб-
страницы и извлечь из нее все требуемые нам ссылки и сохранить их в простом
файле С8У. Соответствующий код приводится в листинге 5.9.
Листинг 5.9. Сценарий МТА_к1ГП8й1е8_раг8тд.ру
# Импортируем метод ВеаиНТи! 5оир из библиотеки Ьз4
1топ Ьз4 ТпрогГ ВеаиГТйЛЗоир
# Открываем локальную копию веб-страницы с данными турникетов УГПТ.
# (Сама веб-страница находится здесь: ЬГГр^/иеЬ.пГаЛпГо/с^еТорегз/ГигпзГТТе.ЬГтТ)
тГа_меЬ_раде = ореп("МТА_1:игп51:11е8_1пс1ех.Ы:т1", "г")
# Определяем базовый 11К1_-адрес для файлов данных
Ьазе_иг1 = "Ы:Гр://меЬ.тГа.1пГо/с1е7е1орег5/" О
# Метод ВеаиГПчЛЗоир принимает в качестве параметров содержимое нашей веб-страницы
# и значение типа кода, с которым нужно работать.
# В данном случае это код НТМ1_
зоир = ВеаиГ1Ги18оир(пГа_меЬ_раде, "ЫзтЕ.рагзег")
# Передаем методу Ппс1 в качестве параметров тип тега и имя класса, чтобы найти
# требуемое нам содержимое
баГа_РПе8_8есГ1оп = зоир.ПпбС'сйл/", сТа55_="5рап-84 Та8Г")О
# В данном разделе сЙ7 мы можем просто искать все теги якоря (а)
а11_с1а1:а_1Гпк5 = с1аГа_'Г11е8_8есГ1оп.’Г1пс1_аП("а")
# Открываем файл в режиме записи для хранения извлеченных ссылок
т1:а_с1а1:а_1л51: = ореп("МТА_с1а1:а_1пс1ех.с87","м")
# Методом Ппб_аП() возвращаем список всех совпадающих ссылок
Гог а_Ппк 1п аП_баГа_11пк8:
# Объединяем базовый 0Р1_-адрес с содержимым каждого свойства ЬгеГ (ссылка)
# и сохраняем результат в переменной соп!е1:е_11пк
сопр1е1:е_ипк = Ьазе_иг1+а_1Лпк["ЬгеГ"]
# Записываем эту полную ссылку в выходной файл, добавляя в конце символ новой строки
# (\п), чтобы каждая ссылка была в отдельной строке
п1:а_с1а'1:а_и21:.мг11:е(сотр1е'1:е_ипк+"\п")
# Записав все ссылки, закрываем выходной файл
т!а_с1а1а_'1л51:. с!озе()
О Если щелкнуть по какой-либо ссылке в онлайновой версии веб-страницы, мы
увидим, что первая часть ЕГКЕ-адреса, где хранятся настоящие данные, имеет вид
Ы:1:р://меЬ.т1:а.1пГо/с1е7е1орег5/, но каждая ссылка содержит только вторую полови-
ну этого ЕГКЕ-адреса (в формате баГа/пусГ/ГигпзШе/ГигпзШе^шад.ГхГ). Чтобы
обеспечить нашему сценарию загрузки рабочие ссылки, нам нужно указать ” базо-
вый” ЕГКЕ-адрес.
О Благодаря нашей работе со средствами инспектирования мы можем сразу же пе-
рейти к разделу с117 с классом зрап-84, чтобы начать поиск требующихся нам ссы-
лок. Обратите внимание, что поскольку слово сЕазз имеет специальное значение в
РуЙюп, при его использовании с ВеаибГи! 8оир к нему в конце добавляется символ
подчеркивания (т. е. с1азз_).
Ну, хорошо. Теперь у нас есть новый файл, содержащий список всех ссылок на
требующиеся нам файлы данных. Далее нам нужно создать еще один сценарий для
чтения этих ссылок и загрузки соответствующих файлов данных. Но мы не хотим
перегружать веб-сайт слишком быстрой загрузкой всех этих файлов, поэтому мы
разнесем их загрузку на секунду-две друг от друга, используя для этого встроенную
библиотеку РуЙюп бше. Кроме этого, мы загрузим только четыре файла, которые
нам действительно нужны, а не все без разбора подряд файлы. Соответствующий
исходный код сценария приводится в листинге 5.10.
Листинг 5.10. Сценарий ТАДигп8ЕИе8_с1аЕа_с1оюп1оас1.ру
# Импортируем библиотеку гециез'Ез для получения данных из Интернета
1прог1: гециез^з
# Импортируем библиотеку Ру1±оп оз для создания новой папки для хранения загруженных
# файлов данных
1прог1: оз
# Импортируем библиотеку 1:1те
1прог1: 1:1те О
# Открываем файл, содержащий список ссылок на файлы данных
п1:а_с1а1:а_ипк5 = ореп("МТА_ба1:а_1пс1ех.с87","г")
# Создаем новую папку, чтобы упорядочить сохраняемые данные
^о1с1ег_пате = "1игп51г'1е_с1а1а"
# Добавляем информационные заголовки
Ьеабегз = {
'Озег-АдепГ : 'МогШа/5.0 (XII; СгО5 х86_64 13597.66.0) ' + \
'Арр1е1л1еЬки/537.36 (КНТМ1_, Ике Сеско) ' + \
'СЬгопе/88.0.4324.109 53^^/537.36',
1Егоп': 1УО11Р МАМЕ НЕКЕ - уо^гета^^а^^ге88@ета^^ргоV^сIег.8от,
}
# Используем встроенную функцию геасПлпезО для преобразования каждой строки нашего
# файла данных в элемент списка
п1:а_11пк5_1181: = т1а_ба1:а_1Лг|к8. геа<Я1_пез()
# Удостовериваемся в отсутствии существующей папки с выбранным нами именем
# для создаваемой папки
И О5.ра1ЬЛзб1г(Го1с1ег_пате) == ЕаТзе:
# Создаем новую папку с таким именем
1агде1_Го1с1ег = оз.ткс11г(Го1с1ег_пате)
# Загружаем только требуемое количество файлов
1"ог I 1п гапде(0,4):
# Удаляем символ новой строки в конце каждой ссылки, используя для этого встроенную
# функцию з1:г1р()
ба1:а_иг1 = (т1а_1лпк5_1л51|Ч]) .з1:г1р()
# Создаем уникальный выходной файл с названием на основе нашего 11К1_-адреса
ба1а_Шепате = ба1:а_иг1.8рШ("/")[-1]О
# Запрашиваем данные
1игпзИ1е_с1а1:а_Г11е = гецие515.де1(с]а1:а_иг1, Ьеабегз=Ьеас1ег5)
# В созданной целевой папке открываем новый файл в режиме записи, используя
# соответствующее имя файла
1оса1_с1а1:а_Е1Ле = ореп(оз.ра1±.до1п(1го1с1ег_пате,с1а1:а_'Г11епате), V)
# Сохраняем содержимое загруженного файла в этом новом файле
1оса1_с1а1а_Н1е.шг11е(1игп8111е_с1а1:а_Г1’1е.'1:ех'1:)
# Закрываем локальный файл
1оса1_с1а1:а_Е1Ле. с!озе()
# Прежде чем обрабатывать следующий элемент в цикле, выдерживаем паузу в две секунды,
# используя функцию з!еер()
1=1те.з1еер(2)
О Эта библиотека позволяет сделать паузу между запросами данных в сценарии
загрузки, чтобы не перегружать сервер, отправляя ему слишком много запросов в
течение слишком короткого интервала времени (что может создать нам проблемы с
владельцем веб-сайта).
О Здесь мы разбиваем ссылку по косым чертам, а затем из получившегося списка
берем последний элемент, используя отрицательное индексирование
(ййр8://уу38сйоо18.сот/ру€йоп/§1о88_ру€йоп_8€гш§_пе§айуе_тс1ехш§.а8р). Это оз-
начает, что элемент в позиции -1 будет последним, которым в данном случае будет
имя файла .Ш. Это имя файла, который мы будем использовать для хранения ло-
кальной копии загруженных данных.
При успешном исполнении сценария у нас должна создаться новая папка ШтзШе сЫд,
содержащая четыре самых последних файла с данными турникетов. Довольно лов-
ко, не так ли?
Заключение
Изучив многие способы получения требуемых нам данных и преобразования их в
практичные форматы, возникает вопрос: а что полезного можно сделать со всем
этим? Поскольку цель выпаса данных заключается в том, чтобы иметь возможность
отвечать на интересующие нас вопросы и генерировать определенные знания об
окружающем нас мире, теперь нам нужно переходить от процесса получения дан-
ные к процессу их оценки, улучшения и анализа. С этой целью в следующей главе
мы выполним оценку качества данных общедоступного набора данных с прицелом
на понимание как его возможностей, так и ограничений, а также, как выпас данных
может помочь нам использовать его наиболее эффективным образом.
ГЛАВА 6
Оценка качества данных
В последних двух главах мы концентрировали наши усилия на выявлении данных в
разных форматах из различных источников (от электронных таблиц до веб-сайтов)
и получения доступа к этим данным. Но получение доступа к (потенциально) инте-
ресным данным — это в действительности только первый шаг. Следующий шаг —
выполнить тщательную оценку качества этих данных, чтобы понять, представляют
ли они собой для нас что-либо ценное и полезное или же являются простым мусором.
Как вы уже убедились из материала главы 3, создание качественных данных пред-
ставляет собой сложную и трудоемкую задачу. Этот процесс состоит из трех ос-
новных частей — исследования, экспериментирования и целеустремленной на-
стойчивости. Приверженность цели получения качественных данных означает, что
вы должны быть готовы вложить в этот процесс значительные усилия и объем вре-
мени и при этом быть готовыми выбросить полученные результаты и начать зано-
во. Потому как, несмотря на все вложенные усилия, некоторые данные бывает про-
сто невозможно привести к требуемому уровню качества.
По сути, последний критерий, наверное, и является причиной, делающей действи-
тельно высококачественную, содержательную работу с данными по-настоящему
трудной. Овладение техническими навыками, как, я надеюсь, вы уже начинаете
осознавать, требует некоторого усилия, но при достаточной практике вполне дос-
тижимо. Исследовательские навыки немного сложнее задокументировать и пере-
дать, но проработка примеров в этой книге поможет вам выработать те навыки, ко-
торые связаны с обнаружением и упорядочением информации, требуемой для
оценки и улучшения полученных данных.
Что касается необходимости смириться с отказом от набора данных, имеющих су-
щественные недостатки, на получение которого было потрачено десятки часов ра-
боты, то я могу посоветовать одно — попытайтесь вспомнить, что изучение знача-
щих аспектов об окружающем нас мире является ”игрой вдолгую”. Вот почему я
всегда советую, что если вы заинтересованы научиться выполнять предваритель-
ную обработку и анализ данных, вам следует начинать с определения вопроса об
окружающем мире, представляющего для вас настоящий интерес и/или важность.
Для качественного выполнения этой работы вы должны позаботиться о том, чтобы
сделать ее правильно, а не просто так, по принципу: лишь бы она была сделана. Бу-
дет также полезным ценить знания, полученные в процессе выпаса данных, потому
что вы не только овладеете новой стратегией выпаса данных или программирова-
ния на языке РуШоп, но и познакомитесь с экспертами или откроете новый инфор-
мационный ресурс. В действительности, если вам по-настоящему небезразлична
исследуемая тема, усилия, потраченные на качественную работу с данными, нико-
гда не будут потраченными напрасно. Просто в результате это может увести вас в
другое направление, нежели вы ожидали.
Часто случается так, что определенный набор данных хоть и не может предоста-
вить ответ на исходный вопрос, но все равно может пролить свет на какой-либо из
его ключевых аспектов. А в других случаях данные по исследуемому предмету мо-
гут отсутствовать, и этот факт, возможно, может быть полезным — процесс обна-
ружения, какие именно данные отсутствуют, может побудить вас (и других) полно-
стью изменить свою работу. Поскольку ”идеальный” набор данных получить не-
возможно, в определенных случаях можно, с некоторой долей осторожности,
использовать данные с недостатками, так как даже частичные знания, предостав-
ляемые такими данными, могут иметь общественную важность. В любом случае
самым важным остается ваша готовность нести личную ответственность за свои
решения — поскольку независимо от качества и происхождения исходных данных,
очищенный, дополненный, преобразованный и/или проанализированный набор
данных принадлежит уже вам.
И это не случайно, что все кажется достаточно трудным. Мы живем в такое время,
когда очень легко использовать мощные цифровые инструменты, не учитывая при
этом должным образом последствия этого использования на окружающий нас мир.
Надо всегда помнить, что разработчики ”продвинутых” технологий создают алго-
ритмические правила, которые, по большому счету, основаны на их собственных
предпочтениях, даже если они не всегда осознают это. В конце концов, данные
могут быть механизмом как для информирования, так и для манипулирования,
как для просвещения, так и для эксплуатации. В конечном итоге, обеспечение на-
правленности вашей работы на конкретную цель является уже вашей зоной ответ-
ственности.
Но что в действительности означает достижение качества данных на практике?
Чтобы получить представление об этом, мы посвятим последующий материал этой
главы оценке реальных данных с точки зрения аспектов целостности данных и со-
ответствия данных, которые были представлены в главе 3. Для этого мы будем ис-
пользовать набор данных в виде одного экземпляра данных по займам программы
РРР (Раусйеск РгоШсбоп Рго§гаш — программа защиты зарплаты или бизнес-
кредитование) правительства США. Этот набор данных содержит информацию о
миллионах кредитов, выданных малому бизнесу во время пандемии СОУГО-19.
Как мы сами увидим в последующем материале этой главы, данные программы РРР
иллюстрируют многие проблемы, присущие "найденным” данным, собранным как
государственными учреждениями, так и частными компаниями. В частности, неяс-
ные термины и необъяснимые переходы с одной версии данных на другую приво-
дят к проблемам соответствия данных, для решения которых потребуются допол-
нительные исследования. Более простыми (хотя не обязательно поддающимися бо-
лее быстрому решению) являются вопросы целостности данных, например,
подтверждение, что название данного банка пишется одинаково во всем наборе
данных или что файлы данных содержат надлежащие значения и диапазоны дан-
ных. После решения большинства из этих проблем нашим достижением в итоге
будет высокая уверенность в наших данных, но, безусловно, она не станет несо-
мненной. Как и вся работа с данными, полученные знания будут результатом при-
нятия обоснованных решений, логического мышления и большого объема выпаса
данных. Для реализации первичной обработки мы будем полагаться на библиотеку
РуЙзоп рапбаз, которая предоставляет популярный и мощный набор инструментов
для работы с табличными данными. Итак, приступим к работе.
Пандемия и программа РРР
Весной 2020 г. правительство США объявило о кредитной программе, предназна-
ченной для стабилизации американской экономики, претерпевающей последствия
потери рабочих мест в связи с пандемией СОУГО-19. Объем финансирования про-
граммы составлял почти 1 триллион долларов1, а целью программы была заявлена
помощь малому бизнесу с уплатой арендной платы и выплатой зарплат, включая
принудительные закрытия и другие ограничения. И хотя фиксировалось, что после
выплаты первого транша произошло падение уровня безработицы, некоторые орга-
ны федерального правительства сопротивлялись предоставлению прозрачных дан-
ных по потраченным средствам1 2.
Так помогли ли кредиты по программе РРР спасти малый американский бизнес?
Теперь, по прошествии определенного времени, наверное, ответ на этот вопрос дос-
таточно легко найти, но мы намерены выяснить это самостоятельно. Для этого мы
начнем с систематической оценки общего качества данных о кредитах РРР, в ходе
которой поочередно проверим все характеристики целостности данных и соответ-
ствия данных. Не менее важно и то, что мы тщательно задокументируем каждый
этап этого процесса, чтобы зафиксировать выполненные действия и логику приня-
тых решений. Этот дневник данных будет важным для нас ресурсом в будущем,
особенно, когда нам потребуется объяснить или воспроизвести какую-либо часть
нашей работы. Подобный дневник может быть оформлен в любых предпочитаемых
вами форме и формате, но лично мне нравится простое форматирование и чтобы
дневник находился вблизи кода. Поэтому я буду документировать свою работу на
языке МагЫомш в файле, который можно легко сохранить и считать с (ЭйНиЪ3. По-
скольку документирование, по сути, состоит в простой последовательной записи
всех выполняемых мною действий, я назову этот файл ррр_ргосе88_1о§.шс1.
1 Подробности о программе РРР см. здесь: ЬПр8://еп.лу1к1ре(Иа.ог§/уу1к1/РаусЬеск_Рго1:есЦоп_Рго§гап1.
2 См. статью \Уко Оо/ На1/а ТгИНоп 1п СОУЮ Ьоапз? Тке Тгишр Ас1т1пШгайоп УТопк 8ау по адресу
ЬНр8://тагке1р1асе.ог§/8ЬолУ8/таке-те-8тагЬлу1Ц1-ка1-ап(1-то11у/ууЬо-§о1:-ЬаИ-а-ЫШоп-1п-соу1(1-1оап8-
Ц1е4гитр-а(1т1Ш8Цаиоп-ууоп1:-8ау.
3 Краткую сводку команд языка МагЫомп можно найти по адресу кН:р8://§й1шЬ.сот/ас1ат-р/
тагкс!оу\п-Ьеге/\у1к1/Магкс1оу\п-Неге-СЬеа18ЬееГ
Оценка целостности данных
С чего следует начинать процесс выпаса данных — с оценки целостности данных
или их соответствия? Ответ прост: понемногу с каждого. Как обсуждается в разд.
"Что такое выпас данных?" главы 7, процесс выпаса данных никогда не следует
начинать, если у вас нет какого-либо вопроса, на который вы ищете ответ, и неко-
торого представления о том, как определенный набор данных может помочь вам
получить этот ответ. Иными словами, у вас должно четко сформироваться пред-
ставление о направлении вашего процесса выпаса данных и вы должны быть уве-
рены, что ваши данные ”подходят” для него. В то же самое время полностью оце-
нить соответствие набора данных, пока не была исследована его целостность, часто
представляет сложную задачу. Например, если данные содержат разрывы, можно
ли их каким-либо образом заполнить? Или возможно ли найти отсутствующие ме-
таданные? Если да, то тогда, возможно, мы сможем решить эти вопросы целостно-
сти и возвратиться к оценке соответствия данных, обладая более полной информа-
цией. Если затем мы обнаружим, что соответствие данных можно улучшить, до-
полнив их (мы подробно рассмотрим вопрос дополнения данных в главе 7), то это,
конечно же, запустит другой раунд оценки целостности данных. А затем этот цикл
придется начать снова.
Однако это может привести к бесконечному циклу выпаса данных, поскольку зачас-
тую одни вопросы порождают другие вопросы, и в результате мы узнаем что-то но-
вое. Вместе с тем мы не можем посвятить задаче выпаса данных безграничное время
и тратить бесконечные усилия, а также прочие ресурсы, поэтому нам нужно прини-
мать обоснованные решения и документировать их. Тщательная оценка качества на-
ших данных поможет нам правильно принимать эти решения. Систематичная про-
верка данных на целостность и соответствие не только обеспечивает получение вы-
сококачественных данных, но также вынуждает нас принимать и документировать
решения, которые можно использовать для описания (а при необходимости и для за-
щиты) любой создаваемой информации. Это не означает, что все согласятся с ваши-
ми заключениями. Но это помогает обеспечить возможность осмысленного их обсу-
ждения, что действительно продвигает знания по данному вопросу.
Поэтому пора завершить разговоры и приступить к оценке целостности данных.
Большая часть используемых в этой главе данных уже удалена из Ин-
тернета и/или заменена другими файлами; но в этом нет ничего необыч-
ного. Несмотря на это, я преднамеренно оставила материал этой главы в
его исходном виде, отражая, таким образом, реальные и типичные про-
блемы, возникающие при попытке осуществлять выпас данных в режи-
ме, сколь-либо приближенном к режиму реального времени.
Это иллюстрирует, насколько быстро, и почти бесследно, в цифровом
мире могут эволюционировать и изменяться данные. И это обстоятель-
ство следует помнить всегда. Все наборы данных, используемые в этой
главе, доступны на Ооо§1е Эпуе по адресу Ьйр8://йпуе.§оо§1е.сош/й1е/
й/1ЕШВ0пК9а9еУУУУУУС17О1ауО9Ое-аУ8КуХН/у1еуу?и8р=8Ьапп§.
Имеют ли данные известное происхождение?
На момент работы над материалом этой книги первым результатом поиска по фразе
”шо81 гесеп! РРР 1оа<1 баШ” (самые последние данные по кредитам программы РРР)
была страница на веб-сайте министерства финансов США, содержащая ссылку на
данные за август 2020 г4. Второй результат посика содержит ссылку на более све-
жие данные, предоставляемые Управлением по делам малого бизнеса — государст-
венным учреждением, отвечающим за фактическое управление этими средствами5.
Хотя оба эти веб-сайта принадлежат государственным учреждениям, чья деятель-
ность имеет отношение к программе РРР, для нас будет более целесообразным ра-
ботать с данными, предоставляемыми Управлением по делам малого бизнеса. Но
отметим, что этот веб-сайт не был первым результатом моих поисков по данному
запросу.
Я хочу обратить ваше внимание на то обстоятельство, что Министерство финансов,
хоть и является надежным источником требуемых нам данных, однако оно не явля-
ется наилучшим доступным источником этих данных. Вот поэтому важно учиты-
вать не только источник данных, но и обстоятельства, при которых вы их нашли.
Актуальны ли данные?
Если мы хотим использовать данные для того, чтобы узнать что-либо о текущем
состоянии окружающего нас мира, первым делом необходимо установить возраст
наших данных, а также удостовериться в том, что это наиболее свежие из доступ-
ных данных.
Хоть это требование и выглядит легковыполнимым, однако напомню, что многие
веб-сайты не датируют автоматически каждое сообщение, поэтому зачастую стано-
вится весьма проблематично определить, когда что-то появилось в сети. При этом
веб-сайты могут менять дату датируемого содержимого при любом, даже незначи-
тельном, его обновлении. А некоторые веб-сайты вообще регулярно обновляют да-
ту публикации, пытаясь таким образом перехитрить алгоритмы поисковых систем,
которые отдают предпочтение более свежему содержимому.
Иными словами, чтобы найти действительно самые свежие, релевантные данные
для конкретной задачи выпаса данных, придется немного потрудиться. Единствен-
ный способ — использовать несколько разных условий поиска, просмотреть не-
сколько наборов результатов и, возможно, обратиться за советом к эксперту.
4 Первоначальная ссылка на содержимое — ЬПр8://Ьоте.1геа8игу.§оу/ро11су-188ие8/саге8-асЕ
а88181:апсе-Гог-8та11-Ьи81пе88е8/8Ьа-раусЬеск-рго1:есйоп-рго§гат-1оап-1еуе1-(1а1:а, но теперь оно нахо-
дится по адресу ЬПр8 ://Ьоте Лгеа8игу.§оу/роИсу-188ие8/согопау1ги8/а88181апсе-Гог-8та11-Ьи81пе88е8/
р ау ске ск-р г о^есйо п-р г о §г ат.
5 Следующая ссылка: ЬН:р8://8Ьа.§оу/й1П(11п§-рго§гат8/1оап8/согопау1ги8-ге11еГ-орйоп8/раус11еск-
рго1есйоп-рго§гат/ррр-(1а1:а.
В процессе этой работы вы, скорее всего, найдете достаточное количество ссылок,
чтобы решить, какие доступные данные являются самыми свежими.
Полные ли данные?
На данном этапе мы знаем, что существует несколько публикаций данных по про-
грамме РРР. И даже если мы будем уверены в том, что успешно нашли самые све-
жие данные, возникает следующий вопрос: все ли это имеющиеся данные?
Поскольку нас в основном интересуют бизнесы, которые получили наиболее круп-
ные кредиты, нам потребуется исследовать только один файл: риЪНс_150к_р1и8.С8У6.
Но как мы можем знать, содержит ли этот файл данные для всех этапов программы
по текущую дату или же только данные по кредитам, выданным после первой пуб-
ликации данных в августе 2020 г.? Поскольку у нас есть доступ к обоим наборам
данных7, мы можем использовать несколько разных стратегий:
1. Найти наиболее раннюю дату (или даты) в нашем "свежем” файле данных и удо-
стовериться в том, что они предшествуют дате 8 августа 2020 г.
2. Сравнить размеры файлов и/или количество строк в двух наборах данных, чтобы
убедиться в том, что размер более позднего файла больше размера более раннего.
3. Сравнить содержащиеся в файлах данные, чтобы убедиться в том, что все запи-
си в более старом файле содержатся в более новом файле.
Здесь вы можете начать задаваться вопросами: ”Разве первой проверки, на под-
тверждение наиболее старой даты, не было бы достаточно? Зачем выполнять две
другие проверки, которые выглядят намного более трудными?” Ну, конечно, тео-
ретически, подтверждение наличия данных из более ранних этапов программы
должно быть достаточным. Но в действительности это довольно поверхностная
проверка. Очевидно, что мы не можем убедиться в полноте опубликованных феде-
ральным правительством данных, просто пытаясь собрать более охватывающие
данные самостоятельно. С другой стороны (как мы вскоре это увидим), процессы
сбора данных государственными учреждениями и крупными организациями (осо-
бенно банками) далеко не идеальны8. Поскольку, используя эти данные для того,
чтобы делать некие заключения, мы станем их владельцем, я полагаю, будет полез-
ным выполнить тщательную проверку.
6 Этот файл можно загрузить по адресу
ЬН:р8://(1пуе.§оо§1е.еот/Ше/(1/1ЕШВ0пК9а9еУУ\УУУ617О1ауО9Ое-ау8КуХН/ У1е\у?и8р=8Ьагт§.
7 Данные за август 2020 г. можно загрузить по адресу Ьйр8://(1пуе.§оо§1е.еот/й1е/й/
11луТОарЬАгеГеС(2УУВ-У1Е1р8<2Уа2х1Ат/у1еуу?118р=811агт§; а данные за февраль 2021 г. — по
адресу ЬНр8://йг1уе.§оо§1е.еот/й1е/(1/1ЕШВ0пК9а9еУУЦАУС11О1ауО9Ое-ау8КуХН/у1еуу?и8р=8Ьаг1п§.
8 Например, после финансового кризиса 2008 г. выяснилось, что многие банки не вели должную
документацию при переоформлении и продаже (или секьюритизации) ипотек, в результате чего
некоторые суды отказали им в попытках лишить домовладельцев права выкупа собственности по
причине просрочки в платежах (ЬПр8://пуйте8.сош/2009/10/25/Ьи81пе88/есопоту/25§геЕЫ:п11).
К счастью, выполнить первую проверку на полноту данных можно довольно легко:
просто открыв файл данных в каком-либо текстовом редакторе и увидев, что зна-
чение элемента Оа^еАрргоуеб первой записи равно 05/01/2020, что подразумевает на-
личие в наиболее новом наборе данных данных из первого этапа выдачи кредитов
по программе РРР весной 2020 г. Этот факт иллюстрируется на рис. 6.1.
Нги __ ОХ
(.аалныипрг 1)аГеАрргоче(1,6ВА07Г1сеСп(1е,Ргасе.4язпдМеЕКо(1, 0мггои«*гМате,5огго«егА<1дгв5е.,вчггошегСНу. ЙоггпшегЗгаГе Вогги*ег71Рг1чап51я1ич0а(в,1аа
П51аю»,Т9гт,5ВА(Изагап1уРегсег»ид|!, 1П1С14»1Аррг<л/д1Атаип1, Сиггеп1АрОГО*И1ЛпоипГгЦпЙ1У)иг5еаЛтОип1, РгьпСП18еНате 5ег«&сХг'д1е114ег1ОСаи0ПХО,ЗегУ
<П1Пд1 РпЛргНятр, 5агу1гйлд1рпг1рг АПРсрчь. 5рп/1П1Пд1 рпФ«гГ1 Гу, 5лгм1п1пд|.рпй0г!?1ЛГ.₽, 5ргу1с1 ПЦ‘ рпйрг/1 р, ЯпгаЛ!г№*МпгНг»йГ лг. И, ЬгОпеТпйЮяГ ог. 1.МТ Гп
ЛХсяСог,Ви$1пе**>М)е(>Р51сг1рНоп,РгоЗесгСЛЕу, Рг<Цес1СоилГу|«гп№,Ргс^есГ.&а1в,Рг[Щсг21р,СО, 1иЬ$йероггео, МАТСЗСобв РасяЕГЬМс1Еу,ЦТП1ТТЕ5 РДОСЕЕ
О.РАУКОИ РНОСЫ-О, МОНТОМИ 1ЛПЕК13Т РКОСПО, ВЕНТ РЯ0СЕЕО. ВЕЕТНАПСЕ ЫО1 РЯОСЕЕО, НЕА1 ТН САВЕ РйОСЕЕО.ОЕвТ 1НТЕНЕ!П РРОСЕЕР.вчЯ1пе5бТ/П&,Ог1д1Л
лЕ1лдЕспдегкр(,(1ИплТР,Ог1р1лАЕ1прсспасг,Ог1ддлдЕ1псСсп|ТегС1Еу,Йгап1пдМпп1сяаег8Еаее. Сспдег, УеЕсголгНопРгоЕ1к
0547587794 ф5/0./29?фб4Ы. РРР, 5ЦМТЕЙ СОАТТИСЬ, 1НС *',241Э НТдПкму 15 5оисА,5ивГег,. 30159 0857,12/18/7929, Рдл! Ю
₽||Н,74,ЮОеОДОДГ. /И? 76935В. /Я О,,1924Я,5упоиия Вапк,1148 ВгояЙ!М»у,С011ЖВи5г6А, 31901-2429, II, И,, Ех15Илд ог тоге тЬяп 2 уейГЗ
□1В, Змтьег 50ИТЕЙ,8С,?9158968г.5С 05,62,325519, Опап^иегеа,, 769358 73,,,,,, СогригаПоп, 19248,бупоииа банк, С0и)И81)$,<»А, ипа паше гей, Цлалзшсгсй,
0777677704.05/01/2026,0464, РРР,’РиЕАбАМТ РЬАСЕЗ. 1НС.".76В4 Боинга!! Яоав.НОГСП С|1аг1м10П. ,29420 6000,,ЕхетрС10А
4,24,199,736927 79,725927.79,9,.1924»,5уП0Уи5 ВЗпк,1148 ВгоаОнау,С019МВиЬ,СА,31991 2429,и,У,,ЕХ1«1сд ОГ тоге ЕЙап 2 УС8Г5 010,НогСП
СПаг1₽51оп С«АК1Е5Т0И,5С, 2М2Э-4098, ЗС-8Ь, 73. 561739, Опалшегеб, । 736927 79.,3о1в РгорПеСогьПхр, 19248,5упо«т14 Валк, С0ШИВ05,6А, Ма1в
Онпсб,Мол-Уе1егап,
5701497792.05/01/2920,1913,РРР.ВОГЕЛ СНИСЯЕМ'З СИНЮ, 1850 ВОУЕК АУЕ Е,5ЕАТПЕ,,90112 2922,,ЕхсжрСЮП 4,24.109,691355,691355.9, ,9561,"Вапк
оЕ Апц?г1сн НаЕюла! АззосюНол”, *100 Н. Тгуап 51, 5Ю 170",СНАЙЮТГЕ, НС. 2В2&2 4024.0, И,, чры Визгпеьэ ог 2 уеагь ог
1е55,5ЕА1Т1Е,К1Н&,ыА,98112^2922 ИА-ег, 75,,ипап&яеп-а , 691355,,,,, ,ИО1-Рго?1с СгдагпАаМоп. 9551, "Валк оГ Д/п-глса, наггопа!
Алеет я Г Юп", СНАЙ1 ОТТЕ, НС, 1/лаляшпг о8,Цпап!мег«й, V
6723567799 Й&/81/2070, 0020, РРР, К1ЙПЕГ С0Н5ТЙОСТТОН ТМС,1в61 МАЯГ1Н ЧАИСН ЯО,ВАН НЕЙЯАЙ01Ч0,,92497-1749,,ЬяетрГ1ОП
4,24.,199. 499871,499071,9, ,9551,’0впк О» АМГАСа. ИаИОПа! А55ОС1аС1О1п, "ЮТ Н ТгуоП 61, 5Т₽ 170*', СКАЯ СОТТЕ, НС, 28292 4024,0,И, .Меи Ви<Т1пе55 ОГ
? уепгч Ог ТеШл.ЗАМ ПЕКНАКОТНО, ЛАЙ О&ЯМАКРТНО, СА, 92407-1740, СА-ОЛ, 21, 230115, Упаплхегей, ,499071,,,,,, Согрш 1*С 1оп, 9551, "Влгк оГ Алт г юл,
Наг Юла! Аяюс! яг 1оп", СНАЯЮТТЕ, НСГ Ыпалзшегеб, ипвпяшегрд,
9882437702.05/03/2920,0101,РРР.ЛЕКО ВОХ Ц.С,И/Л,М/А,,,,ЕхйврМоп 4,24,190,367437,387437,9., 57338,Т1»е НипМпдГоп ЯаПапв! Вапк,17 3 Н1дП
31, СцШ*Й05,0Н, 43215 3413, 0, И,, ОПаПЛИСГСО,Й/А, ,НА, Й/А,, 25,484218, 0ла1&мего8,, 367437,,, ,, , .5/328, ГПС НипГШдСОП НАС10ПЙ1
влпк.союмвив, он,илопядег«с1, ипякюшегрл,
9774337791 85/01/2026,9191,РРР,миОЭОН ЕХТЯиЗЮНЗ ТНС.,Н/А,М/А,,.,ЕхаврТЮЛ 4,24,190,320840,329В49,0,,57328,ТЬе НипСТпдсол Наг1опв1 Вапк,!? 3
Н1дП 5С,С0И1МВ115, ОН, 43215-3413, и, Нг,тп5И!гя<1,Н/Д .НА,Н/А,,72,ЗтОа.ипап5«ег«1,.37В840,.,,,,..573УБ,П1е Нил Г VI д Гоп наглела!
Вапк,СО1имВ05,0И,УЛАп5*$геЙ, ипал$неге<1,
Рис. 6.1. Просмотр в текстовом редакторе файла данных
о новых кредитах по программе РРР
Как видим, ничего сложного. Если пойти немного дальше, то можно написать сце-
нарий для поиска в файле свежих данных наиболее старых и наиболее новых зна-
чений (т. е., дат) элемента 1_оап$1:а1:и8. Во избежание неразберихи я переименовала
более новый файл на риЫ1с_150к_р1и8_гесеп1.с8У. В настоящее время9 при попытке
перейти по ссылке на данные по кредитам по программе РРР на странице
Ьир8://Ьоте.€геа8игу.§оу/ро11су-188ие8/саге8-ас1:/а88181:апсе-Гог-8та11-
Ьи8те88е8/8Ьа-раусЬеск-рго€есйоп-рго§гат-1оап-1еуе1-Да1:а открывается другая
страница, содержащая ссылку на папку с данными 150к р1и$ 0808.x1р., для кото-
рых указана дата загрузки 14 августа 2020 г. (Ьйр8://8Ьа.арр.Ьох.сош/8/
ох4тууту114п(1Ьр14401хг411т88еГх31). Нужно загрузить всю эту папку, но мы мо-
жем извлечь данные в формате С8У и переименовать файл в
риЫ1с_ 15 0к_р1и8_080820. с 8 у.
Чтобы облегчить этот процесс, мы, как обычно, используем рапбаз — хорошо из-
вестную библиотеку РуЙюп для манипулирования табличными данными. Здесь мы
не будем подробно рассматривать эту библиотеку (желающие узнать о ней больше
могут воспользоваться другими ресурсами, например замечательной книгой Ру1коп
/ог Ба1а Апа1у$18 (Анализ данных, используя язык РуЙюп), автор Уэс Маккинни
(У^е8 МсКтпеу), издательство О’КеШу). Но мы воспользуемся ее полезными воз-
можностями для проверки качества наших данных.
9 Следующие ссылки указываются только в архивных целях, так как они указывают первоначальное
местонахождение наборов данных, использованных в этой главе.
Первым делом установим эту библиотеку, выполнив следующую команду:
р1р гпз^аП. рапбаз
Далее используем ее, чтобы извлечь из файла свежие данные по кредитам по про-
грамме РРР, и найдем самые старые и самые новые даты, которые содержит этот
файл. Соответствующий сценарий приводится в листинге 6.1.
Листинг 6.1. Сценарий ррр_ба(е_гапде.ру
# Простой сценарий для нахождения самой ранней и самой поздней дат в данных
# по кредитам по программе РРР
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб О
# Считываем свежие данные в структуру 0а1:аРгате библиотеки рапбаз, используя ее
# метод геаб_сз7()
ррр_ба1:а = рб. геаб_сз7(' риЫ1с_150к_р1из_гесеп1:. С57')
# Преобразовываем значения в столбце Оа1:еАррго7еб в "настоящие" даты
ррр_ба1:а [' Оа1:еАррго7еб' ] = рб. 1о_ба1е1гте( ррр_ба1:а [' Оа1:еАррго7еб' ],
Гогпа^^пДбДУ1 ) □
# Отображаем минимальное и максимальное значения столбца Оа1:еАррго7еб
рг1п1:(ррр_ба1:а[ 'Оа1:еАррго7еб' ] .п1п())
рг1п1:(ррр_ба1:а[' 0а1:еАррго7еб' ] .пах())
О Использование ключевого слова аз позволяет нам создать короткий псевдоним
для библиотеки, чтобы к ней можно было более легко обращаться далее в нашем
коде.
О Чтобы найти самую старую и самую новую даты, нам нужно преобразовать
строчные значения в нашем наборе данных в настоящий тип данных 0а1:е. В дан-
ном случае мы используем для этого функцию библиотеки рапбаз 1о_ба1е1гте(), пе-
редавая ей два параметра:
♦ название столбца, чьи значения нужно преобразовать
(см. Ъйр8://рап(1а8.ру(Ша.ог§/рапс1а8-с1ос8/81:аЫе/геГегепсе/ар1/
рапйазЛойа^ейте.ййп]);
♦ формат дат (см. ййр8://йос8.руЙ1оп.ог§/3/11Ьгагу/йа€ейте.ййп1#81тЙ1те-апс1-
зйрйте-ЬеЬауюг), как они в настоящее время отображаются в нашем наборе
данных.
Как видно в следующем результате исполнения этого сценария, самый первый кре-
дит в этом наборе данных был выдан 3 апреля 2020 г., а самый последний — 31 ян-
варя 2021 г.:
2020-04-03 00:00:00
2021-01-31 00:00:00
Обратите внимание на то, что хотя передача параметра формата функции
1о_с1а1:е1:гте() библиотеки рапбаз не является обязательной, это стоит сделать, по-
скольку без этой информации библиотека будет пытаться угадать, с каким форма-
том даты она работает, что может значительно увеличить время исполнения сцена-
рия. Учтите, что в данном случае задержка составит около секунды, но с набором
данных большого размера (или при обработке нескольких наборов данных) за-
держка может быть довольно значительной.
Теперь давайте сравним размер файла более новых данных с файлом данных за ав-
густ 2020 г. Посмотрев на файлы риЪНс_150к_р1и8_гесеп1.с8У и риЪНс_150к_р1и8_
080820.С8У в окне браузера, можно увидеть, что файл более свежих данных имеет
намного больший размер, чем файл более старых данных: несколько сотен мегабайт
по сравнению с около 124 МБ, соответственно. Пока что все хорошо.
Основываясь на методах, которые мы использовали в сценариях в листингах 4.9 и
5.10, создадим простой сценарий для определения количества строк в каждом из
этих файлов. Соответствующий код приводится в листинге 6.2.
Листинг 6.2. Сценарий ррр_питгош§.ру
# Простой сценарий для подсчета количества строк в каждом из файлов данных
# по кредитам по программе РРР.
# Это довольно простая задача, поэтому нам не нужно импортировать никаких
# дополнительных библиотек.
# Открываем файл данных по кредитам за август в режиме чтения
аиди81_с1а1:а = ореп("риЫ1с_150к_р1и5_080820.с87","г")
# Используя метод геасПлпезО, преобразовываем строки файла в список
рг1п1:("Аиди51: РПе Ьаз "+81:г(1еп(аиди81:_ба1:а.геаб1Лпе8()))+" гомз.") О
# Делаем то же самое и для файла со свежими данными по кредитам
гесеп1:_с1а1:а = ореп("риЫ1с_150к_р1и8_гесеп1:.с87","г")
# Опять отображаем количество строк в файле
рг1п1:("Ресеп1: НЛе Ьаз "+з1:г(1еп(гесеп1:_с1а1:а.геас1Ппе8()))+" гомз.")
О После преобразования строк файла в список количество строк можно определить
при помощи встроенного метода РуЙюп 1еп(). Для вывода этого результата на эк-
ран полученное число преобразовывается в строку, используя для этого встроен-
ный метод з1:г(). Если этого не сделать, то интерпретатор РуЙюп выдаст сообщение
об ошибке.
Исполнив этот сценарий, убеждаемся, что более старый файл данных (за август
2020 г.) содержит 662 516 строк, а более новый (датированный 1 февраля 2021 г.) —
766 500 строк.
Рассмотрим процесс сравнения содержимого этих двух файлов, чтобы убедиться в
том, что более новый файл содержит все содержимое более старого файла. Это бу-
дет многошаговый процесс, первым шагом которого будет просмотр более старого
файла данных (за август 2020 г.) в текстовом редакторе (рис. 6.2).
Рис. 6.2. Просмотр файла данных по кредитам по программе РРР за август 2020 г.
Сразу же можно увидеть некоторые отличия, это говорит нам о том, что данная
проверка будет еще более сложной, чем мы могли предполагать. Прежде всего мы
видим, что данные в более новом файле организованы в намного большее количе-
ство столбцов. Это означает, что нам нужно разработать стратегию для сопоставле-
ния записей более старого набора данных с записями более нового.
Сначала нужно разобраться, какие столбцы данных общие в обоих файлах. Для
этого мы создадим и сравним два небольших тестовых файла С8У, каждый из ко-
торых будет содержать несколько первых строк данных из каждого набора данных.
Затем создадим небольшой сценарий, который преобразовывает каждый из наших
исходных файлов в структуру 0а1:аЕгате, а затем записывает первые несколько строк
в отдельный файл С8У. (Структура ВаШГгаше — это специальный тип данных
библиотеки рапбаз для табличных данных.) Соответствующий код приводится в
листинге 6.3.
Листинг 6.3. Сценарий ррр_ба(а_5атр1е8.ру
# Небольшой сценарий для создания новых файлов С8\/, содержащих первые несколько строк
# данных исходных файлов данных
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более старого файла (за август 2020 г.) в структуру 0а1:аЕгате
# библиотеки рапбаз, используя ее метод геаб_сз7()
аидиз1:_ррр_ба1:а = рб. геаб_сзу(' риЫ1с_150к_р1из_080820. сз7')
# Метод Неас1() возвращает заголовки столбцов структуры Оа^аЕгапе вместе с первыми
# 5 строками данных
аидиз1:_8атр1е = аидиз1:_ррр_с1а1:а.Ьеас1()
# Записываем эти первые строки данных в файл С5\/ аидиз1:_5атр1е.С87, используя метод
# библиотеки рапбаз 1:о_С78()
аидиз1:_8атр1е.1:о_С57( 'аидиз^запрТе.сзу', 1пбех=Еа1зе)О
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геас1_С57()
гесеп1:_ррр_с1а1:а = рб. геаб_С57(' риЫЛс_150к_р1из_гесеп1:. С57')
# Метод Неас!() возвращает заголовки столбцов структуры 0а1:аЕгате вместе с первыми
# 5 строками данных
гесеп^затрТе = гесеп1:_ррр_с1а1:а.Ьеас1()
# Записываем эти первые строки данных в файл С5\/ гесеп1:_5атр1е.С57, используя метод
# библиотеки рапбаз 1:о_С75()
гесеп1:_5атр1е.1:о_С57(' гесеп1:_5атр^е.С5V,, 1пбех=Еа1зе)
О Метод библиотеки рапбаз 1=о_сз7() инкапсулирует несколько шагов, которые мы
ранее выполняли посредством обычных операторов РуЙюп. В частности, он откры-
вает файл в режиме записи и записывает в него данные. Поскольку библиотека
рапбаз по умолчанию добавляет столбец индексов (который, по сути, содержит но-
мера строк) к каждой создаваемой ею структуре 0а1:аЕгате, нам нужно передать ме-
тоду 1:о_с57() параметр 1пбех=Еа1зе, чтобы эти номера строк не записывались в наш
выходной файл С8У.
Получив два тестовых файла небольшого размера, просмотрим их в редакторе
электронных таблиц, чтобы сравнить заголовки столбцов и их содержимое в обоих
файлах. Содержимое более старого файла показано на рис. 6.3, а более нового — на
рис. 6.4.
А В С б Ё Р 6 Н I 3 К Г М ГМ б Р
1 1оапЯап§е Ви51пе5з№те Ас1с1ге55 С11у 81а1е 2/р ЫА1С5С/ Виз/лел ЯасеЕ1Ь СепСег Уе1егап МопРго! ^Ь^Яер Оа1еАр| Ьепс1ег СО
д $350,000-1 т!11юп ЛЕВО ВОХ Ы.С 11пал5М? Опалам? 1)пап5М/ 05/03/2 ТНе НилНп^оп
484210 №1юпа1 Вапк
~ с! $350,000-1 тШюп ВОУЕЯ СНИОКЕЫ'5 СИМС 1850 ВОУЕЯ АУЕ 8ЕАТПЕ Ыоп-Ргс Опапхм/ Опалам? Опапхм/ У 75 05/03/2 Вапк о! Атенса, \Л/А-07
Е 98112 Огеап!?; №1юла1 Аазоаайол
д $350,000-1 тЛИоп К1ЯТ1ЕУ СОЫ5ТЯОСТЮМ 1661МАЯТ1Ы 5АЫ 236115 Согрога Опалам/Опалам? Опалам/ 05/03/2 Вапк о! Атенса, СА-31
1МС ЯАГМСН ЯО ВЕЯЫАЯ01М0 92407 21 №1юпа1 АааоааНоп
с! $350,000-1 тЛЛоп Р1.ЕА5АМТ Р1АСЕ5,1МС. 7684 5ои1ЬгаЛ Мог1Ь 561730 5о1е Опалам/ Ма1е №л- 05/03/2 Зупоуиа Вапк 5С-01
Яоад СНаг1еа1оп 29420 Ргорпе* Ом/пед Уе1егап 73
с! $350,000-1 тЛ1юп 5ОМТЕЯ СОАТ1МС5,1ЫС. 2410 Н^Нм/ау 5ит1ег 325510 Согрога Опалам/Опалам/ Опалам/ 05/03/2 Зупоуиа Вапк 5С-О5
15 5ои1Ь 29150 62
Рис. 6.3. Первые пять строк данных более старого файла (за август 2020 г.)
по кредитам по программе РРР
Похоже, что первые несколько строк обоих файлов содержат некоторые одинако-
вые столбцы данных. Это обстоятельство облегчит нашу задачу по сравниванию их
содержимого и (будем надеяться) определит, как столбцы можно использовать для
сопоставления строк.
Рис. 6.4. Первые пять строк данных более нового файла по кредитам по программе РРР
Начнем с выбора одной записи (строки), в идеале такой, которая содержит как
можно больше заполненных полей. Например, строка 6 более старого файла дан-
ных (рис. 6.3) содержит в столбце ВизтпеззМапе значение 511МТЕР С0АТ1МС5, ТЫС. А бо-
лее новый файл (рис. 6.4) содержит это же значение в строке 2 в столбце
ВоггомегМапе. Более старый файл содержит значение Бупсл/из Вапк в столбце Ьепбег, и
это же значение находится в столбце 5еп/1с1пд1_епс1егМате более нового файла. Пока
что все выглядит хорошо. Или же нет?
Хотя многие данные в этих строках совпадают в обоих файлах данных, некоторые
данные, которые могут быть важными, отличаются. Например, в образце данных
более старого файла (за август 2020 г.) значение столбца 0а1:еАрргсл/ес1 равно
05/03/2020, а в образце более нового файла оно равно 05/01/2020. Если посмотреть на
другую, казалось бы, общую запись, мы увидим, что для бизнеса/заемщика РЬЕАБАМТ
РЬАСЕБ, 1МС. (строка 5 в более старых данных и строка 3 в более новых) столбец СО в
обоих файлах (столбец АЕ электронной таблицы в более новом файле и столбец Р в
более старом) содержит разные значения, а именно, 5С-01 в старых данных и 5С-06 в
новых. Что же здесь происходит?
На данном этапе нам нужно принять некоторые субъективные решения касательно
того, значения каких столбцов должны совпадать, чтобы определенную строку
можно было считать одинаковой для обоих файлов. Хорошей отправной точкой
может стать требование, чтобы совпадали название бизнеса и имя кредитора. По-
скольку на данном этапе мы знаем, что было выдано несколько траншей кредитов,
будет, наверное, удобным, чтобы значения в столбце 0а1:еАрргсл/ес1 обоих файлов
также совпадали (хотя выглядит маловероятным, что банк Бупсл/из Вапк выдал два
кредита заемщику БигтЬег Соа^тпдз, 1пс. с интервалом друг от друга всего лишь в
два дня). Но как насчет несовпадающих значений столбца СП (соп§ге88юпа1 Ш81пс1 —
избирательный округ для выборов в конгресс)? Посмотрев на карту этих избира-
тельных округов для Южной Каролины10, можно увидеть, что их границы не меня-
10 Карты избирательных округов для выборов в Конгресс США можно посмотреть в Википедии по
адресу ЬНр$://еп.уу1к1реЩа.ог<С'У1к1/$О1ПЬ_СагоПпа%27$_соп<це881Опа1_с1ШпсГ8#НШопса1_апс1_
ргезепГаШпсГЬоипсЫпез.
лись с 2013 г., хотя действительно выглядит, что первый и шестой округа вроде бы
имеют общую границу. Получаем простой вывод: данное расхождение — это про-
сто ошибка.
Как видно, мы уже нашли значительные проблемы с качеством данных, просмотрев
всего лишь пять строк данных! Очевидно, что мы не можем решить все эти расхо-
ждения одновременно, поэтому начнем с объединения строк, которые (мы надеемся)
действительно совпадают, одновременно отслеживая те, которые не совпадают.
Для этого нам нужно объединить О’от), или слить (тег§е) эти два набора данных.
Но, поскольку мы уже знаем о расхождениях в этих наборах, то нам требуется найти
какой-то способ для отслеживания этих расхождений. Иными словами, файл, соз-
данный объединением наших двух исходных файлов, должен содержать все строки
из обоих наборов данных, независимо от того, совпадают они или нет. Для этого
нам нужно выполнить с этим наборами данных операцию, называющуюся внешнее
соединение (ои!ег )от, ИПр$://рапйа$.руйа1ал)гц/рапйа$-йос$/$1аЬ1е/и$ег ц1пс1е/
тег§т§.й€т1#Ьг1еГфГ1тег-оп-тег§е-те€йос18-ге1айопа1-а1§еЬга). Соответствующий
код приводится в листинге 6.4.
Обратите внимание на то обстоятельство, что поскольку результат операции внеш-
него соединения содержит все строки обоих исходных наборов данных, количество
строк в нем равно общему количеству строк в обоих наборах, что в данном случае
составит около 1,4 миллиона строк. Но не стоит волноваться из-за такого объема,
поскольку язык РуЙюп легко справится с этим. Вопрос только в том, справится ли
ваше устройство.
Для Маспйозй или \^тс1о\У8 работа с данными объемом около 500 МБ, что требует-
ся для этих примеров, скорее всего, не представит никаких проблем. Но если вы,
подобно мне, используете устройство СйгошеЪоок или другое подобное устройст-
во, то сейчас самое время перейти на облачную платформу.
Листинг 6.4. Сценарий рррба^адот.ру
# Простой сценарий для создания новых файлов С8\/, содержащих первые несколько строк
# данных исходных файлов данных
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более старого файла (за август 2020 г.) в структуру 0а1:аЕгате
# библиотеки рапбаз, используя ее метод геаб_сзу()
аидиз1:_ррр_ба1:а = рб. геаб_сз7(' риЫ1с_150к_р!из_080820. сз71)
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геаб_сз7()
гесеп1:_ррр_ба1:а = рб. геаб_сз7(' риЫ1с_150к_р1из_гесеп1:. сз7')
# Загрузив оба файла в память, объединяем их
пегдеб_ба1:а = рб.тегде^идиз^ррр-ба^гесеп^ррр-ба^Ьо^'ои^ег',
1еН:_оп=[1ВизтпеззМапе1,1Ьепбег',1Оа^еАрргоуеб'],г1дЫ:_оп=[1ВоггомегМапе',
'5еп/1с1пд1_епбегМате',' 0а1:еАррго7еб1 ] ,1пб1са1:ог=Тгие)0
# Отображаем значения в индикаторном столбце, по умолчанию обозначенном меткой
# по _пегде
рг1п1:(тегдеб_ба1:а.7а1ие_соип1:5( '_пегде' ))О
О В данном случае передается параметр 1пс11са1:ог=Тгие, поскольку таким образом
будет создан новый столбец, содержащий информацию о том, какие строки содер-
жатся в одном или обоих файлах.
О Использование значения параметра 1пЛса1:ог=Тгие создает столбец негде, чье
значение для каждой строки показывает, с каким набором данных совпадает данная
строка. Как можно видеть в результатах исполнения этого сценария, это значение
может быть Ьо±Н (с обоими), 1еГ1:_оп1у (только с левым) или г1дЫ:_оп1у (только с
правым).
При отсутствии ошибок исполнение данного сценария должно дать следующий ре-
зультат:
_пегде
Ьо±Ь 595866
г1дЫ=_оп1у 171334
1еН_оп1у 67333
с1±уре: 1п1:64
Что это все это означает? Похоже, что было успешно сопоставлено 393 866 записей
кредитов более старого файла (за август 2020 г.) с записями в более новом файле по
названию компании, названию обслуживающего кредитора и дате одобрения кре-
дита. Метка г1дЫ:_оп1у обозначает количество записей кредитов в более новом файле,
для которых не было совпадающих записей в более старом файле. (Этот набор дан-
ных считается правым в операции соединения, поскольку значение гесеп!_ррр_с1а1:а
было передано функции рс1.тегде() во втором аргументе.) Всего имеется 171 334
таких записей. Такое количество несовпадающих записей выглядит вполне воз-
можным, поскольку за период с августа 2020 г. по февраль 2021 г. могло быть вы-
дано большое количество новых кредитов.
Но значение 67 333 метки 1еН_оп1у представляет проблему. Это количество записей
о выданных кредитах в более старом наборе данных, для которых не было найдено
совпадений в более новом наборе. Это означает, что либо наш более новый набор
данных не является полным, либо у нас есть серьезные проблемы с качеством данных.
Из нашего предшествующего и достаточно поверхностного исследования наших
выборочных данных мы уже знаем, что со столбцами ОаГеАрргсл/ес! могут быть неко-
торые проблемы. Поэтому давайте посмотрим, что будет, если устранить требова-
ния совпадения записей по этой дате. Для этого мы просто добавим фрагмент кода
из листинга 6.3 в код листинга 6.4, но опустив требование совпадения дат. Полу-
чившийся код приводится в листинге 6.5.
Листинг 6.5. Сценарий ррр_с!а1аЭот.ру (продолжение)
# Опять соединяем данных двух наборов, но без требования совпадения по Оа^еАрргоуеб
пегдеб_с!а1:а_по_с1а1:е = рб.тегде(аидиз1:_ррр_с1а1:а, гесеп1:_ррр_с1а'1:а,Ьо\л/='оиГег',
1еН_оп=[1 ВизтпеззМапе1,1 Ьепбег' ], г1дЫ:_оп=[1 ВоггошегМапе',
' 5еп/1с1пд1_епс1егМате' ] ,1пс11са1:ог=Тгие)
# Отображаем значения в индикаторном столбце, по умолчанию обозначенном меткой _пегде
рг1п1(тегдеб_ба1а_по_с1а1:е.7а’1ие_соип1:8( '.негде'))
Теперь результаты соединения этих двух наборов данных будут такими:
_пегде
Ьо1:Н 671942
г1дЫ=_оп1у 96656
1еН_оп1_у 22634
сИуре: 1п1:64
Иными словами, требуя совпадения записей только по названию компании и креди-
тора, мы "находим" еще 43 000 записей из более старого набора данных в более но-
вом. Конечно же, мы не знаем, сколько из этих новых совпадений являются резуль-
татом ошибок при вводе данных (наподобие наблюдаемой ранее ошибки ввода
05/03/2020 вместо 05/01/2020), а также, сколько из них представляют многократную
выдачу кредитов11. Все, что нам известно, — так это то, что количество записей в более
старом наборе данных, которые мы не можем найти в новом, сократилось до 22 634.
Но что если мы просто проверим, присутствует ли данная компания в обоих набо-
рах данных? Это кажется самой простой формой сравнения: теоретически, в тече-
ние многих месяцев банк или кредитор, обслуживающий кредит по программе РРР,
мог измениться или же могут (возможно) существовать дополнительные несовпа-
дения из-за ввода данных с незначительными различиями. Помните следующее: на
данном этапе наша цель — просто оценить, насколько мы можем быть уверены, что
самый последний набор данных содержит все данные из более старого набора данных.
Поэтому давайте выполним последнее соединение с ослабленными требованиями и
посмотрим, что произойдет. Для этого мы добавим в наш сценарий фрагмент кода,
показанный в листинге 6.6, который сравнивает записи в обоих наборах данных
только по названию компании.
Листнинг 6.6. Сценарий ррр_с1а1а_|01П.ру (продолжение)
# Снова соединяем оба набора данных, на этот раз только по столбцу
# ВизтпеззМапе / ВоггомегМапе
11 Управление по делам малого бизнеса начало принимать заявки на выдачу так называемых кредитов
второго раунда по программе РРР в январе 2021 г. (ЬПр8://и8сЬатЬег.сош/со/гип/Ьи81пе88-йпапс1п§/
8есопс1-с1гауу-ррр-1оап8).
пегдес1_с1а1:а_Ы2_оп1у = рб.тегде^идиз^-Ррр-ба^алесеЩ-Ррр-ба^Ьо^'оЩег',
1еН_оп=[ 'ВизтпеззМапе' ] ,г1дЫ:_оп=[ 'ВоггомегМапе' ],1пс11са'1:ог=Тгие)
# Отображаем значения в индикаторном столбце, по умолчанию обозначенном меткой _тегде
рг!пГ(тегдес1_баГа_Ь12_оп1у.7а1ие_соип1:5( '_пегде'))
Исполнение этого сценария дает следующий результат:
_пегде
Ьо±:Н 706349
г1дЫ:_оп1у 77064
1еН_оп1у 7207
б1:уре: 1п1:64
Теперь ситуация выглядит немного лучше: мы ”нашли” все 790 620 (706 349 + 77 064 +
+ 7 207) возможных кредитов, кроме 7 207, или чуть меньше 0,1% от общего коли-
чества. Это очень хорошо, и мы можем поддаться соблазну рассматривать это ко-
личество недостающих записей ”ошибкой округления” и двигаться дальше. Но
прежде чем испытывать чувство удовлетворения за то, что мы нашли 99,9% всех
кредитов по программе РРР, остановимся на минутку и рассмотрим, что собой в
действительности представляет это "небольшое” количество недостающих данных.
Даже если предположить, что каждый их этих ''недостающих” кредитов был на ми-
нимально возможную сумму (вспомним, что мы рассматриваем кредиты на сумму
от 150 000 долларов и выше), это все равно означает, что наш более новый набор
данных содержит неучтенных кредитов на сумму как минимум около $1 081 050
000 (т. е. свыше одного миллиарда долларов США). Учитывая, как многие тяжело
работают, чтобы заплатить налоги каждый год, хочется надеяться, что федеральное
правительство не собирается спокойно "потерять” один миллиард долларов налого-
плательщиков. Но что можно сделать, чтобы найти эту сумму? Вот здесь мы под-
ходим к той части работы с данными, которая может быть одновременно и слож-
ной, и стимулирующей: настало время поговорить с людьми!
Хотя обращение к экспертам в предметной области всегда будет отличной отправ-
ной точкой для исследований качества данных, но есть нечто более интересное и
полезное: информация о самих кредиторах и получателях кредитов. Используя
комбинированную информацию с названиями компаний и их местонахождений,
мы, возможно, сможем найти контактную информацию по крайней мере для неко-
торого количества из наших 7 027 "утерянных” кредитов из более старого набора
данных (за август 2020 г.) и попытаться выяснить причину этой пропажи.
Прежде чем начинать звонить людям (и да, в большинстве случаев нужно именно
звонить), нужно определиться с вопросами, которые вы хотите им задать. Первона-
чально можно поддаться соблазну и подозревать какие-либо нечестные мотивы.
Например, кредиторы утаивают средства или компании предоставляют о себе не-
верную информацию. Но старая (и очень легко модифицируемая под разные ситуа-
ции) пословица гласит: никогда не следует усматривать злой умысел в том, что
можно объяснить некомпетентностью, глупостью или небрежностью12. Иными сло-
вами, информация об этих кредитах отсутствует в более новом наборе данных, ско-
рее всего, по причине каких-либо ошибок при вводе данных или, возможно, потому
что эти кредиты просто никогда не были одобрены.
Действительно, как выяснилось после нескольких звонков разным мастерским по
ремонту автомобильных кузовов и парикмахерским по всей стране, это была наи-
более распространенная история. Многие люди, с которыми я разговаривала, опи-
сали похожую картину: они подавали заявку на получение кредита, им говорили,
что заявка была одобрена, а затем деньги просто не приходили. Пытаться подтвер-
дить, что это было причиной в каждом отдельном случае ''недостающего” кредита,
было бы непрактичным, но множественные компании, расположенные на расстоя-
ние тысяч километров друг от друга, излагали, по сути, одну и ту же историю. Это
вселяет в меня уверенность, что эти кредиты не отображаются в конечном наборе
данных по причине того, что деньги никогда не были отправлены13.
Несколько советов, как вести телефонный разговор
Если идея "холодных звонков" частным лицам или компаниям с запросом ин-
формации заставляет вас нервничать, поверьте, вы не одни такие. И хотя практи-
ка будет самым полезным подходом к преодолению этого состояния, наличие
краткого сценария для первых звонков может быть очень полезным. Далее при-
водится краткое изложение подхода к обзваниванию компаний (которое также
применимо и с частными лицами) для запроса информации. Отбросьте свою не-
уверенность, но помните, что вы просите занятых людей уделить вам свое время.
Будучи вежливым и тактичным, вы добьетесь намного больших результатов.
1. Представьтесь. Назовите свое имя и компанию. Изложите (вкратце), о чем вы
хотите говорить.
2. Уточните, может ли собеседник уделить вам время, и не начинайте излагать
причину своего звонка, не выяснив сначала, не занят ли ваш собеседник.
3. Если вы не уверены, с кем вы разговариваете, попросите позвать к телефону
владельца или менеджера компании. Если они заняты, спросите, когда можно
будет им перезвонить.
4. Вежливо спросите, с кем вы разговариваете, и подтвердите имя своего собе-
седника. Поблагодарите собеседника за уделенное вам время и завершите те-
лефонный разговор.
12 Попытки атрибутировать пословицы всегда сопряжены с проблемами. И хотя я предпочитаю
использование терминов некомпетентность и небрежность, мне нравится пояснение этой послови-
цы в "Файле жаргона” (Тйе 1аг§оп Ше, кПр8://]аг§оп-Ше.ог§/агсЫуе/]аг§оп-4.4.7.с1о8.Ш) как "Бритва
Хенлона" (Нап1оп'8 Кахог), главным образом потому, что этот документ объясняет происхождение
многих терминов компьютерного и программистского сленга.
13 Безуловно, почему они никогда не были отправлены, само по себе является интересным вопросом.
Всегда записывайте основные пункты своего телефонного разговора. И когда вы
будете перезванивать, вы сможете позвать владельца или менеджера уже по име-
ни (так как вы его записали ранее). Скажите, с кем вы разговаривали первый раз
и что он/она сказал, что вы могли перезвонить ему в это время. Если владелец
снова не сможет поговорить с вами, уточните, когда можно перезвонить, или по-
пробуйте договориться о встрече, если это возможно.
Четко и откровенно объясните цель своего звонка, а при запросе информации
изложите подробно, что вы намерены делать с полученной информацией. В пуб-
ликуемых работах никогда не указывайте личность кого бы то ни было, не полу-
чив на это их предварительное разрешение.
На данном этапе ясно, что наши данные по кредитам по программе РРР являются
достаточно полными для наших целей. Может показаться, что проверка нашего на-
бора данных на соответствие всего лишь одному из почти дюжины требований
требует больших усилий, но имейте в виду, что в процессе мы узнали ценную ин-
формацию, благодаря которой сможем выполнить следующие проверки намного
быстрее или, возможно, не выполнять их вообще. Итак, перейдем к нашему сле-
дующему критерию.
Хорошо ли данные аннотированы?
Удовлетворившись, что наши данные являются актуальными и полными, нужно
начинать подробно разбираться с тем, какую информацию в действительности со-
держат столбцы более нового набора данных по кредитам по программе РРР14. Как
и в случае с задачей оценки полноты данных, решение задачи оценки качества их
аннотирования можно начинать с самих данных. Исследуя названия столбцов и со-
держащиеся в них значения, можно получить начальное представление о том, какая
дополнительная информация нам требуется. Если название столбца выглядит опи-
сательным и значения этого столбца соответствуют нашей интерпретации его на-
звания, тогда у нас есть хорошая отправная точка.
Хотя названия столбцов и содержащиеся в них значения можно исследовать не-
сколькими способами, давайте воспользуемся созданными ранее файлами с выбо-
рочными данными. Ширина экрана обычно недостаточна для отображения большо-
го количества столбцов данных, а вот дополнительные строки можно легко про-
14 Возможно, вы заметили, что в этой главе я не рассматриваю критерии целостности данных точно в
том порядке, в котором они перечислены в главе 3. Поскольку программа РРР была смоделирована по
уже существующей кредитной программе 7(а) (11Н:р8://]’оигпа1оГассоип1:апсу.сот/пеуу8/2020/арг/
раус11еск-рго1:есйоп-рго§гат-ррр-1оап8-8Ьа-(1е1:аП8-согопау1ги8.Ы:т1), я сделала предположение (со-
мнительное), что эти данные будут хорошо аннотированными. Конечно же, это были единственные
доступные наборы данных о программе РРР, поэтому у меня был ограниченный выбор (каким он
часто бывает при работе с реальными данными).
сматривать, прокручивая экран вверх. Поэтому первым делом мы транспонируем
наши выборочные данные, т. е. преобразуем столбцы в строки, а строки в столб-
цы15. Кроме этого мы выполним дополнительную фильтрацию по типам данным,
чтобы облегчить определение, какие данные отсутствуют. В листинге 6.7 приво-
дится код первого сценария для выполнения этой задачи.
Листинг 6.7. Сценарий ррр_со1итп§_геу!еш.ру
# Простой сценарий для просмотра всех названий столбцов таблицы с данными по программе
# РРР, чтобы увидеть, что можно предположить по ним об их данных
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более нового файла в структуру 0а1:аРгате библиотеки рапбаз,
# используя ее метод геаб_сз7()
ррр_ба1:а_5атр1е = рб. геаб_С57(' гесеп1:_5атр1е. С571)
# Преобразовываем все недостающие записи в значение <ИА>, используя метод
# С0П7ег1:б1:уре5()
сот/ег1:еб_ба1:а_5атр1е = ррр_ба1:а_5атр1е.сот/ег1:_б1:уре5()0
# Транспонируем все выборочные данные
1:гап5ро5еб_ррр_ба1:а_5атр1е = сот/ег^еб-ба^а-ЗапрТеЛгапзрозеО
# Отображаем полученные результаты
рг!п1:(-|:гап5ро5еб_ррр_ба1:а_5атр1е)
О В целях ускорения процесса метод библиотеки рапбаз геаб_сзу() преобразовыва-
ет все недостающие записи в значения типа МаМ (Ио1 а НшпЬсг — не число), как
описывается в разделе Ьйрз ://рапйаз.руйа€а.ог§/рап(1а8-с1ос8/81:аЫе/и8ег_§шбе/
Ш188ш§_с1а1:а.111:т1. Эти значения преобразовываются в более общую (и интуитив-
но понятную) метку <МА> (Мо1 АуаПаЫе — нет данных), обрабатывая всю структу-
ру Эа1аРгатс методом сссл/егЬ^урезО, как описывается в разделе
Ъйр8://рап(1а8.ру(1а€а.ог§/рап(1а8-(1ос8/8€аЫе/и8ег_§ш(1е/т188т§_(Ша.й€т1#т188т§-
(1а€а-па-сопуег81оп.
В результате исполнения этого сценария становится возможным одновременный
просмотр всех исходных названий столбцов как строк, вместе с парой исходных
строк данных в виде столбцов (рис. 6.5). Просматривая эти названия столбцов и
соответствующие данные, мы можем начинать получать представление о том, что
мы знаем, а что нет.
15 Для меня лично процесс транспонирования таблицы данных легче представить как "переворачи-
вание” ее набок.
1_оапМитЬег 9547507704 9662437702
0а!еАрргоуе<1 05/01/2020 05/01/2020
$ВАОТТ1сеСос1е 464 101
РгосеззтпдМеТЬос! РРР РРР
ВоггомегЫате 8ИМТЕК С0АТ1М68, ИЧС. АЕКО вох и_с
ВоггомегАддгезз 2410 НтдЫау 15 8ои!Ь <Ж>
ВоггомегСИу ЗишТег <МА>
ВоггомегЗТаТе <ИА> ... <ИА>
ВоггомегИр 29150-9662 <ИА>
Ьоап51а1изОаТе 12/18/2020 <ИА>
1оап81а1:и5 РаЫ 1п Ри11 ... ЕхетрИоп 4
Тегт 24 24
$ВАСиагапТуРегсеп1аде 100 100
ТпИгаХАрргоуаГАтоип! 769358.78 367437.0
СиггепТАрргоуаТАлюипХ 769358.78 367437.0
ВпбХзЬигзебАтоипТ 0 0
ЕгапсМзеМате <МА> ... <ИА>
ЗегугсХпдкепдегкосаИопЮ 19248 57328
8еп/1с1пдкепс1ег11ате Зупсшиз Вапк ... ТЬе НипИпдТоп МаИопа! Вапк
$еп/1с1пд1_епс1егАс1(1ге53 1148 Вгоайыау 17 3 Н1дЬ 8!
$ег71с1пд1_епс1егСгЬу соьимвиз соымвиз
8еп/1с1пдкепс1ег8ТаТе СА ОН
$еп/1С1пд1_еп(1ег21р 31901-2429 43215-3413
Кига10гЬап1п(11са1ог и и
НиЬгопеТпсНсаТог И И
1_М11псНсаТог <НА> <ИА>
ВизхпеззАдеОезсгХрТЛоп ЕххзПпд ог тоге 1Кап 2 уеагз оЫ ... Кпапзиегес!
Рго^есТСПу 8ит!ег <ЫА>
РпЦесТСоипТуМате зимтЕк <ИА>
Рго]ес!81а1е 8С ... <ИА>
Рго]ес121р 29150-9662 <МА>
СО 5С-05 <МА>
ЗоЬзКерогТед 62 25
МАКЗСоде 325510 484210
КасеЕТЬп1с1Ту Опапзмегеб ... Опапзмегеб
1ЛТк1Т1Е8_РК0СЕЕ0 <МА> ... <МА>
РАУКОкк.РКОСЕЕО 769358.78 367437.0
М0КГ6А6Е-1НТЕКЕ8Т-РК0СЕЕ0 <ИА> <ЫА>
КЕ1ГГ_РКОСЕЕО <ИА> <ИА>
КЕЕ1ЫАМСЕ_Е1Ок_РНОСЕЕО <МА> <ЫА>
НЕАкТН_САКЕ_РКОСЕЕО <МА> <ИА>
0ЕВТ_ВМТЕКЕ8Т_РК0СЕЕ0 <МА> ... <ИА>
ВизгпеззТуре Согрога!1оп <Ж>
0г1д1па!1пдкепс1егкоса!1оп10 19248 57328
0г1д1па11пд1_епс1ег Зупоуиз Вапк ... ТЬе НипИпдТоп МаИопа! Вапк
ОггдкпаИпдкепдегСПу сошмвиз сошмвиз
0г1д1па!1пдкепс1ег8Та!:е СА ОН
Сепдег Цпапзмегед ... Опапзмегед
Уе!егап ипапзиегес! ... Кпапзмегес!
МопРгоТИ <МА> <МА>
Рис. 6.5. Таблица выборочных данных в транспонированном виде
Благодаря сравнительно описательным названиям столбцов, мы можем предпола-
гать, какие данные они, скорее всего, могут содержать. Например, значения столб-
цов с названиями Оа1:еАррго7ес1 (дата одобрения), Ргосе8в1пдМе1±ос1 (метод обработки),
Во г геме г Мате (имя заемщика), ВоггомегАс!с1ге88 (адрес заемщика), ВоггошегСНу (город
заемщика), ВоггоиегЗ^а^е (штат заемщика) и ВоггоиегМр (почтовый индекс заемщи-
ка) довольно легко понять. Но в некоторых случаях название столбца может быть
описательным, но не предоставлять всю требуемую нам информацию. Например,
название столбца 8ВАСиагап1:уРегсеп1:аде предоставляет нам информацию как о со-
держимом этого столбца, так и о его единицах измерения (предположительно это
доля кредита в процентах, гарантируемая Управлением по делам малого бизнеса).
А вот название столбца Тегп не говорит нам ничего о его значении — то ли это 24
дня, недели, месяца или года. Аналогично, значения столбца ВизтпеззАдеОезсгтртоп
описывают сами себя (например, Ех1з1:1пд ог тоге ±Ьап 2 уеагз о!с! — существует
или возрастом свыше 2 лет), а значение Ехетр1Лоп 4 столбца 1_оап$1:а1:и8 не очень
способствует пониманию, какова была судьба данного кредита. Наконец, такие на-
звания столбцов, как, например, 1_МПпсКса1ог могут легко интерпретироваться экс-
пертами, но лица, не владеющие терминологией в кредитной области, будут иметь
с ними трудности.
Таким образом, на данном этапе нам важно иметь в своем распоряжении словарь
данных, т. е. документ, который описывает содержимое данных табличного типа.
Эта необходимость объясняется тем, что хотя табличные данные очень удобны для
выполнения анализов, они по своей сути не предоставляют способа включения того
типа метаданных (т. е., данных о данных), которые нам требуются для получения
ответов на вопросы типа "Какие единицы измерения должны использоваться?” или
”Что означают закодированные категории?”. Это именно те вопросы, которые часто
возникают при работе со сложными наборами данных.
Наиболее вероятным местонахождением словаря данных должно быть то же самое
место, где мы получили исходные данные. (Вспомните, что в главе 4 мы нашли
описание данных файла дйспсЬзШюпз.Ш в файле геабше.Ш, находящегося в той же
самой папке, что и данные.) В данном случае это означает, что нам нужно снова
посетить веб-сайт Управления по делам малого бизнеса (Ьйр8://8Ьа.§оу/Гип(Пп§-
рго§гат8/1оап8/согопау1ги8-ге11еГ-орйоп8/раусЬеск-рго€есйоп-рго§гат/ррр-(1а1:а)
и посмотреть, не сможем ли мы найти на нем требуемые нам данные.
Поначалу ситуация выглядит довольно многообещающей. В разделе АП Оа€а на
этой странице находится ссылка, обещающая краткое изложение "ключевых аспек-
тов данных", как показано на рис. б.б16.
Переход по этой ссылке (Ы1р8 ://8Ьа.§оу/йоситеп€/герог€-раусЬеск-рго€есйоп-
рго§гат-ррр-1оап-йа1:а-кеу-а8рес1:8) приводит нас (на момент работы над данным
материалом) на страницу, содержащую ссылки на два документа РВР, датирован-
ные летом 2020 г. К сожалению, ни один из этих документов не содержит требуе-
мую нам информацию. Они в основном заполнены текстом наподобие отказа от
ответственности о подробностях обработки кредитов по программе РРР. Но они все
же подтверждают наше открытие, что аннулированные кредиты не будут отобра-
жаться в базе данных (рис. 6.7).
16 Со времени написания этой главы эта страница претерпела значительные изменения. В частности,
главная страница расположения данных теперь содержит и словарь данных, но он был помещен туда
только несколько месяцев спустя после первой публикации данных.
С & 5Ьа.дду/(ип111пд-ргодг4ОТ5/19эпеАогоп;лкГ11$-гс-1|еК|р11Сг15/радс:Г1еск'-|лд1ес'1|1дг1-рг'ддглгпУррр-йа(а
С Васк Го Раус Ьсск Ргст.ейюп
Ргоргапп
РаусНеск РгоГесйол
А11йа(а
Рис. 6.6. Целевая страница для данных по кредитам по программе РРР
на веб-сайте Управления по делам малого бизнеса США
й зЬа.доу/31(ез/с1еГаи11/Г|1е8/2020-08/РРР%20коап%200а(а%20-%20Кеу%20А8рес(8%2008212020-508.рдГ
реек Рго(ес(1оп Ргодгат (РРР) 1_оап Ла(а - Кеу АзресО - Орс1а(е... 2/2 — 100% 4- ГЛ <Г)
ОДА и'5'5та*1 Ви5!пе55
О О /\ Адт1П151га11ОП
РРР Ьоап 0а1а 1з Мо11псПсайие оТ Ьоап Рогдшепезз ог Ргодгат
СотрПапсе
А зта11 Ьизтезз ог поп-ргоЛ( ог$ап12айоп (Ьа(15 Изкес! т (Не риЬПс1у ге1еа$ед с!а(а Наз Ьееп арргоуед Гог а
РРР 1оап Ьу а де1е§а1ес1 (епдег. ТЬе 1оап гап^е ог ас(иа! Пз(ед атоипГ геПес(з (Ье сиггеп( 1оап арргоуа!
атоипк Ноюеуег, (Ье 1епдег’з арргоуа! доез по( геПес( а с!е(егттаЬоп Ьу $ВА (Наг (Не Ьоггои/ег !з еП§|Ые
Гог а РРР 1оап ог епиНей (о 1оап Гог^уепезз. АН РРР 1оапз аге зиЬ)ес( (о 8ВА геу1е« апд а111оапз оуег $2
гтпНюп ууН1 аиГотаЬсаНу Ье геу|е\л/ес1. Еи^Ь|'1|Чу апд сотрНапсе ууШ Ье геу|'е^ес! дипп^ (Ье 1оап Гог^уепезз
ргосезз. ЕиПНег, а зтаН Ьизтезз’з гесе»рк оГ а РРР 1оап зЬоиИ по! Ье 1п(егрге(ед аз ап епйогзетегК оГ (Не
5та11 Ьизтезз’ соттегаа1 ас(1у|(у ог Ьизтезз тоЬек
СапсеИед Ьоапз йо *Мо1 Арреаг т 1Ье РРР коап 0а1а
ТЬе риЫк РРР да(а 1пс1идез оп!у асИ’уе 1оапз. 1оапз (На( меге сапсеНед Гог апу геазоп аге по( ГлсГисЛед 1п
(Не роЬПс да(а ге1еазе. 1пс1мсПпд оп1у асЬуе 1оапз ргоук1е5 а тоге ассига(е рк(иге о( (Не Ьизтеззез Ье!п§
зиррогТед Ьу РРР, аз сапсеИед 1оап5 дИ по( гесе!уе апу РРР Гипйз.
Рис. 6.7. Фрагмент информации, которую следуем иметь в виду
при исследовании данных по программе РРР
Что же теперь делать? У нас есть несколько вариантов. Можно воспользоваться
более общей стратегией исследования, чтобы получить больше информации о про-
грамме РРР и, будем надеяться, заполнить некоторые из пробелов в наших знаниях.
Например, прочитав достаточное количество статей на веб-сайтах, направленных
на потенциальных кандидатов на участие в программе РРР, мы сможем выяснить,
что правильной единицей измерения для значений столбца Тегп, с большой долей
вероятности, являются недели. Аналогично, выполнив достаточное количество по-
исковых запросов в Интернете по терминам ЬМПпсНсаЬэг, 1_М1 1пс11са1:ог и 1_М1
1пс11са1:ог Тоапв, можно, в конце концов, натолкнуться на страницу в АОтресйа, из
которой можно будет предположить, что сокращение ЬМ1 может расшифровывать-
ся как Ьоап Мог1^а^е 1п8игапсе. Но, тем не менее, мы никогда не сможем быть до
конца уверенными в правильности нашего предположения.
Иными словами, снова настало время обратиться за помощью к людям. Но к кому
именно необходимо обратиться? Можно начать со специалистов, которые уже изу-
чали данные по программе РРР, но их будет достаточно трудно найти в связи с от-
носительно недавней публикацией данных. Поэтому, точно так же, как и когда мы
пытались выяснить, что случилось со всеми теми недостающими кредитами из бо-
лее старого набора данных, мы обратимся непосредственно к источнику данных:
Управлению по делам малого бизнеса. К счастью, веб-страница, с которой мы за-
грузили наши фактические файлы данных, содержит имя, связанное с этими за-
грузками: 81ер1зеп Могп8 (Стивен Моррис) (рис. 6.8)17.
Ц|цй||'-ц (32-01-21 ЁШ1/мка! .! 1п^ РРР > 02-01*21 РаусЬесК Рго(ес+юп Ргоргдт Эаса
6
В
В
риЬ|м:_ир_ 5
ЗПО.Й МВ
351.7 МЁ
риЫк_ир_со_1.5Ок. 3 ел
Р&Ь 1, 2021 ЬуЗкрЬеп Моггк
риЫк.мр Ц)_1501к_'1.с5У
Гей 1 2П21 Ьу ЫерН-йП Моггй
351 МН
В
В
рийИг_ир го 150к_1.С5^
ГеЬ 1 2021 МсГгб
355.6 МВ
риЫк_ир_^_1506 _ 5
ГгЬ Т, ?П21 Ьу 511,'рИсп Мпгп’*
341.5 ме
Б
риЬ1н^ир_ 15 Ок _ 2х* «
Я?Н 1., 2021 Ьу Мрггк
352.3 МН
В
риЫк_150к
1 2021 Ьу5крНен Моги*
ЗСК7.5 МВ
Рис. 6.8. Веб-страница для загрузки данных по программе РРР
Я позвонила ему и отправила сообщение по электронной почте. На момент моего
запроса в Управление по делам малого бизнеса я еще не создала словаря данных
для данных по кредитам по программе РРР. Но Моррис предоставил мне ссылки
как на исходный файл РВР, так и на словарь данных для программы кредитования 7а
(Управления по делам малого бизнеса), на которой была основана структура про-
граммы кредитования РРР. Хотя файл более новых данных (расположенный по ад-
ресу йир8://(1а€а.8Ьа.§оу/с1а1:а8е1:/7-а-504-Го1а) все равно значительно отличался от
столбцов данных по кредитам по программе РРР, тем не менее он содержал опре-
деленную информацию о некоторых ключевых элементах. Например, тот файл со-
17 Стоит обратить внимание на тот факт, что вскоре после того, как я связалась с Моррисом, эта атри-
буция была изменена на 8М.
держал описание столбца, который также назывался 1_оап$1:а1:и8 и чьи возможные
значения выглядели похожими на некоторые значения, которые мы на данном эта-
пе обнаружили в данных по кредитам по программе РРР:
1_оап$1:а1:и8 (Текущий статус кредита):
• МОТ ЕОМОЕО = Не выдан
• РТЕ = Ра1б Тп ЕиТТ (Полностью оплачен)
• СНСОЕЕ = СЬагдес! ОТТ (Списан)
• САМСЮ = СапсеТеб (Отменен)
• ЕХЕМРТ = Статус кредитов, которые были выданы, но не были отменены,
выплачены полностью или списаны, не подлежит разглашению
согласно Исключению 4 Закона о свободе информации
Так являются ли эти данные хорошо аннотированными? В некоторой степени — да.
Они не так хорошо аннотированы, как, скажем, данные Управления ЪЮАА, с кото-
рыми мы работали в разд. "Данные фиксированной ширины" главы 4. Но, приложив
некоторые усилия, мы постараемся создать наш собственный словарь данных для
данных по кредитам по программе РРР, которому мы сможем доверять.
Являются ли данные крупномасштабными?
Поскольку мы завершили проверку наших данных на полноту, то мы уже практи-
чески ответили на вопрос, являются ли используемые нами данные в целом круп-
номасштабными. Потому как наличие более 750 тысяч строк данных будет вполне
достаточным для выполнения множества полезных анализов.
В то же самое время мы еще не можем сказать, какие именно виды анализов мы
сможем выполнять, поскольку количество строк имеющихся у нас данных не будет
иметь значения, если большинство из этих строк пустые. Так как же можно решить
эту проблему? Для столбцов, в которых мы ожидаем найти только несколько воз-
можных значений, таких как 1_оап$1:а1:и8, можно использовать метод библиотеки
рапбаз 7а1ие_соип1=5(), чтобы подсчитать их значения. А вот столбцы, которые могут
содержать очень разнообразные значения (такие как ВоггоиегМапе или ВоггомегАббгез),
нам нужно специально проверять на отсутствие значений, а затем сравнить полу-
ченный результат с общим количеством строк, чтобы получить представление о
возможном объеме отсутствующих данных. Для начала мы выполним подсчет в тех
столбцах, в которых, как мы думаем, будет содержаться только небольшое количе-
ство отдельных значений. Соответствующий код приводится в листинге 6.8. Мы
также подсчитаем количество значений МА в столбце с большим количеством воз-
можных значений — ВоггсмегАсИгезз.
Листинг 6.8. Сценарий ррр_со1итп§_§иттагу.ру
# Простой сценарий для просмотра всех названий столбцов таблицы с данными по программе
# РРР, чтобы увидеть, что можно предположить о них по самим данным
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геаб_сз7()
ррр_ба1:а = рб. геаб_С57(' риЬ1Лс_150к_р1из_гесеп1:. С57')
# Отображаем количество отдельных значений, содержащихся в столбце Ьоап51:а1:и5
рг1п1:(ррр_ба1:а.7а1ие_соип1:5(' ЬоапЗ^а^из1))
# Отображаем общее количество значений, содержащихся в столбце 1_оап51:а1:и5
рг1п1:(5ит(ррр_ба1:а.7а1ие_соип1:5(1 ЬоапЗ^а^из')))
# Отображаем количество отдельных значений, содержащихся в столбце Сепбег
рг1п1:(ррр_ба1:а.7а1ие_соип1:5( 'Сепбег'))
# Отображаем общее количество значений, содержащихся в столбце Сепбег
рг1п1:(5ит(ррр_ба1:а. 7а1ие_соип1=5(1 Сепбег1)))
# Отображаем количество строк с отсутствующими значениями для столбца ВоггомегАббгезз
рг!п1:(ррр_ба1:а[ 'ВоггошегАббгезз' ] Лзпа() .зип())
В листинге 6.9 приводится результат исполнения сценария в листинге 6.8, по кото-
рому можно получить начальную картину о типе данных, фактически содержащих-
ся в приблизительно 750 тысячах строк нашего набора данных.
Листинг 6.9. Количество отдельных значений столбцов более нового набора данных
1_оап81:а1:и5
Ехепр1:1оп 4 549011
Ра1б тп Еи11 110120
Ас1тл/е Оп-ОтзЬигзеб 107368
б1:уре: 1п1:64
766499
Сепбег
Опапзмегеб 563074
Ма1е Омпеб 168969
ЕепаТе Омпеб 34456
б1:уре: 1п1:64
766499
Например, большинство кредитов имеют статус Ехешрбоп 4, который, как мы зна-
ем из нашего исследования аннотирования, означает, что кредит ”освобожден от
раскрытия согласно Исключению 4 Закона о свободе информации”18. Там можно
увидеть, что свыше двух третей претендентов на получение кредита при подаче
заявки не указали свой пол, а в 17 кредитах даже не указан адрес заемщика!
Так являются ли эти данные достаточно крупномасштабными? Да. Потому что
несмотря на то, что отсутствует довольно большое количество данных, полезная
информация содержится в достаточно большом количестве имеющихся у нас
строк и столбцов. Имейте также в виду, что в некоторых случаях можно получить
полезную информацию о чем-либо при помощи всего лишь нескольких десятков
строк данных, при условии, что они содержат действительно значащую информа-
цию. Вот почему информации о размере файла с данными или даже о количестве
строк в нем будет недостаточно для того, чтобы подтвердить, что он действи-
тельно содержит крупномасштабные данные. Для этого необходимо изучить эти
данные подробнее.
Непротиворечивы ли данные?
Из нашей проверки на полноту данных мы узнали, что формат данных по кредитам
по программе РРР был абсолютно неоднородным между первой и последующими
публикациями. В частности, данные, опубликованные, начиная с декабря 2020 г.,
были намного более подробными, чем в предыдущих публикациях
(Ъ€ф8://ууа8Ып§€опро8€.сот/Ьи8ше88/2020/06/11/€гитр-а(1т1т8€гайоп-ууоп1:-8ау-уу11о-
§о€-511-Ы111оп4ахрауег-Ьаске(1-согопау1ги8-1оап8). Кроме этого, даже названия
большинства столбцов в этих двух файлах были разными.
Поскольку теперь мы полагаемся на файл с более новыми данными, то нам нужно
думать о другом типе непротиворечивости, а именно, насколько непротиворечивы-
ми являются значения в самом наборе данных. Например, мы можем надеяться, что
в столбцах типа ТпШаТАрргоуаТАгтюип!: и Сиггеп^Арргоуа’ЕАтоип!: будут использоваться
одинаковые единицы измерения. Тем не менее лучше все-таки проверить, действи-
тельно ли это так. В листинге 6.10 приводится код сценария для быстрой проверки,
находятся ли в пределах ожидаемого значения минимального и максимального вы-
данных кредитов.
Листинг 6.10. Сценарий рр_тт_тах_1оап.ру
# Сценарий для быстрого нахождения сумм минимального и максимального одобренного
# кредита, содержащихся в наборе данных по программе РРР
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
18 Дополнительную информацию по запросам и исключениям по Закону о свободе информации см. в
разд. "Запросы РО1А/1Р приложения В, а также в полном тексте данного исключения на веб-сайте
министерства юстиции США (ЬПр8://1и8Йсе.§оу/о1р/ехетрЛоп-4-аЙег-8иргете-соиг1:8-ги11п§-Гооб-
тагкейп§-1П8Ши1е-у-аг§и8-1еабег-теб1а).
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геаб_с8у()
ррр_ба1:а = рб. геаб_сзу(1 риЫ1с_150к_р1из_гесеп1:. сзу')
# Извлекаем минимальное и максимальное значения, используя методы библиотеки рапбаз
# п1п() и пах()
рг1п1(ррр_ба1а[ТиггепГАрргоуаТАтоипГ'] .п1п())
ргтп1(ррр_ба1а[ Тиггеп^АрртоуаТАтоип!:' ] .пах())
Судя по названию нашего файла данных — 150к_р1из — можно было бы ожидать,
что минимальная сумма одобренного кредита будет $150 тысяч. Быстрый поиск в
Интернете по фразе "шахппшп 1оап ипбег ррр” (максимальная сумма кредита по
программе РРР) ведет к документу19 на веб-сайте Управления по делам малого биз-
неса. в котором указывается, что для большинства бизнесов максимальная сумма
кредита составляет $10 миллионов. Действительно, результаты исполнения нашего
сценария из листинга 6.10 подтверждают, что суммы кредитов в наших данных на-
ходятся в этих пределах:
150000.0
10000000.0
На данном этапе также следует выполнить проверку на одну из наиболее распро-
страненных (и коварных) форм противоречивости: различия в правописании. Каж-
дый раз при работе с данными, которые были введены людьми, будут встречаться
проблемы с орфографией: как минимум лишние пробелы, опечатки и разные знаки
препинания. Это представляет проблему, потому что если мы хотим быть в состоя-
нии ответить на простой с виду вопрос типа "Сколько кредитов было выдано заем-
щиком X?”, название этого банка должно писаться единообразно во всех записях
данных.
Поскольку это достаточно распространенная проблема с данными, то существует
несколько качественных подходов к ее решению. В нашем случае для проверки
единообразности написания названия банка мы воспользуемся так называемым ме-
тодом "отпечатков пальцев" (йп§егргтбп§). При поиске вариантов написания на-
званий этих компаний их можно было бы кластеризовать разными способами (на-
пример, фонетически). Но в данном случае мы воспользуемся методом "отпечатков
пальцев", поскольку он следует простому, но эффективному алгоритму, который
сводит к минимуму риск сопоставления двух названий, которые в действительно-
сти должны быть разными.
19 Документ называется Раускеск РгсЛесНоп Р г орг ат: Но\\' 1о Са1си1а1е Махипит Рост АтоитИз /ог
Р1г81 Ргстм РРР Роапз апс1 1Ука1 Ооситепгайоп 1о Рго\чс1е Ъу Ви8те88 Туре. Загрузить его можно по
следующему адресу: Ы1р8://8Ьа.§оу/811е8/беГаи11/й1е8/2021-01/РРР%20%20Но\у%201о%20Са1си1а1е
%20Мах1тит%20Еоап%20Атоип18%201ог%20Нг81%20Вгауу%20РРР%20Еоап8%20%281.17.2021
%29-508.рбГ.
Алгоритм метода "отпечатков пальцев”, который мы будем использовать, выполня-
ет следующие операции20:
1. Удаляет ведущие и замыкающие непечатные пробелы.
2. Заменяет все символы их представлением в нижнем регистре.
3. Удаляет все символы пунктуации и управления.
4. Нормализует расширенные символы латинского алфавита к их представлению
А8СП (например, §бс!е1 —> §о<1е1).
5. Разбивает строки на токены, разделенные пробелами.
6. Сортирует токены и удаляет дубликаты.
7. Соединяет токены обратно вместе.
Мы могли бы сами создать требуемый для этого код, но нам повезло, и один из чле-
нов сообщества РуЙюп уже проделал эту работу и создал библиотеку Дп§егрпп18,
которая устанавливается стандартным способом при помощи установщика ргр:
ртр гпз^аН. Ппдегрг1п1:8
На данный момент главной задачей является выяснение, присутствуют ли в наших
данных различные написания названий компаний. Вопрос преобразования данных
для устранения этих различий рассматривается в главе 7. Так что сейчас мы просто
пересчитаем все однозначные названия банков в нашем наборе данных, а затем по-
смотрим, сколько однозначных отпечатков пальцев содержит полученный список.
Если все названия банков в файле действительно различны, то тогда теоретически
оба списка должны быть одинакового размера. Если же некоторые названия банков в
нашем наборе данных "однозначны” только вследствие, например, незначительных
различий в пунктуации и непечатных символов, тогда список отпечатков пальцев
будет меньшего размера, чем список ”однозначных” названий банков. Это позволяет
предположить, что нам потребуется выполнить некоторые преобразования данных,
чтобы согласовать разные варианты названий одного и того же банка, чтобы гаран-
тировать, например, возможность извлечения в случае надобности всех кредитов,
связанных с одним банком. Соответствующий сценарий приводится в листинге 6.11.
Листинг 6.11. Сценарий ррр_1епбег_патез.ру
# Сценарий для быстрого определения наличия опечаток в названиях банков, участвующих
# в программе РРР
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Импортируем библиотеку Ппдегргтп^з для создания унифицированных меток для всех
# названий банков в нашем наборе данных
1прог1: Ппдегргтп^з
20 Чтобы получить дополнительную информацию по предмету кластеризации, посетите веб-страницу
ЬНр8://«НЬиЬ.сот/ОрепКеГте/ОрепКеГше/у\1к1/С1и8Ыг1п«-1п-ВерШ.
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геаб_с87()
ррр_ба1:а = рб. геа б_С87(' риЫ1с_150к_р1из_гесеп1:. С87')
# Создаем список однозначных названий банков из столбца Ог1д1па1:1пд1_епбеб нашего
# набора данных, используя для этого метод библиотеки рапбаз ип1цие()
ип1дие_папез = ррр_ба1:а[10г1дтпа±1пд1_епбег ’ ] .ип1цие()
# Отображаем полученное количество однозначных названий банков
рг1п1:(1еп(ип1дие_пате5))
# Создаем пустой список для хранения отпечатка пальцев каждого однозначного названия
# банка
Г1_пдегрг1_п1:_1Л81: = []
# Обрабатываем в цикле каждое название банка в полученном списке
Еог папе 1п ип1дие_патез:
# Для каждого названия банка создаем его отпечаток пальцев и добавляем его в конец
# списка
Гт.пдегргт.п1_1л51:. аррепб(Е1пдегрг1п1:8. депега1:е(пате))
# Удаляем дубликаты и сортируем список отпечатков пальцев Нпдегргт.пГ-’И.З'!:, используя
# для этого функцию зе±:()
1Чпдегрг1п1:_8е1: = 8еГ(Г1пдегрг1пГ_118Г)
# Отображаем количество элементов в списке отпечатков пальцев
рг1пГ(1еп(Г1пдегрг1п1:_8еГ))
Исполнение этого сценария дает следующий результат21:
4337
4242
Как видно, ”сырые” данные содержат 4 337 однозначных названий банков, но ко-
личество однозначных отпечатков пальцев для этих названий составляет всего
лишь 4 242. Разное количество элементов двух списков определенно предполагает
наличие в нашем наборе данных разных вариантов названий банков. Хотя в данном
случае разница составляет ”всего лишь” около 100 элементов, эти различия могут
затрагивать тысячи строк данных, поскольку каждый банк выдал в среднем сотни
кредитов (750 000 / 4 337 = 173). В результате мы не можем сказать, сколько строк
нашего исходного набора данных содержат определенное ”неправильное” название
21 При исполнении этого сценария может выводиться предупреждение, рекомендующее установить
библиотеку ру!СП. Но установка этой библиотеки сопряжена с некоторыми сложностями, и ее
наличие не изменит результатов исполнения этого сценария. Если вы планируете широко исполь-
зовать этот сценарий, то будет полезно потратить немного времени на установку этой библиотеки.
Дополнительную информацию по ее установке можно найти здесь: ЬПр8://рур1.ог§/рго1есЦРу1С17.
(также мы не можем быть уверенными в том, что это количество исчерпывающее).
В разд. "Исправлениеразнообразности написаний" главы 7 мы рассмотрим процесс
преобразования наших данных при помощи отпечатков пальцев, чтобы улучшить
идентификацию конкретных кредиторов и заемщиков.
Многомерны ли наши данные?
Во многом подобно тому, как набор данных, содержащий большое количество
строк, может с большой вероятностью быть крупномасштабным, если он содержит
большое количество столбцов, он так же может с большой вероятностью быть и
многомерным. Но, так же как и определение крупномасштабности, определение,
делают ли имеющиеся столбцы наши данные действительно многомерными, также
требует выполнения проверки качества содержащихся в них данных.
Например, хотя наш набор данных по кредитам по программе РРР содержит 50
столбцов данных, около десяти из этих столбцов содержат расширенные адресные
данные, поскольку адреса заемщика, первоначального кредитора и обслуживающе-
го кредитора разбиты на отдельные столбцы для названия улицы, города, штата и
почтового индекса. При этом остальные столбцы могут содержать однозначные
данные и нам нужно получить представление об их содержимом, чтобы понимать, с
каким количеством характеристик или особенностей данных нам в действительно-
сти нужно работать.
Например, для скольких кредитов в нашем наборе данных указано иное назначе-
ние, нежели выплата зарплаты? Хотя эта структура данных (и сама кредитная про-
грамма) позволяет заемщикам использовать полученный кредит в иных целях
(например, расходы на здравоохранение и выплату арендной платы, см.
Ьйр8://8Ьа.§оу/ГипДт§-рго§гаш8/1оап8/соу1(1-19-ге11еГ-орйоп8/раусЬеск-рго1:есйоп-
рго§гат/йг8€-(1галу-ррр-1оап), в каком объеме эти цели отражены в данных?
Точно так же, как и при оценке крупномасштабности наших данных, мы исследуем
содержимое нескольких дополнительных столбцов, чтобы определить, действи-
тельно ли предполагаемая их названиями подробность подтверждается содержа-
щимися в них данными. Соответствующий сценарий приводится в листинге 6.12.
В нем мы исследуем, сколько строк столбца РРОСЕЕО не содержат значений.
Листинг 6.12. Сценарий ррр_1оап_и8ез.ру
# Сценарий для быстрой проверки указанного заемщиками целевого назначения кредита
# по программе РРР
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более нового файла в структуру 0а1аЕгапе библиотеки рапбаз,
# используя ее метод геаб_С87()
ррр_ба1:а = рб. геаб_С87(' риЫ1с_150к_р1из_гесеп1:. сз7')
# Отображаем количество строк с отсутствующими значениями для столбца рг1п1:(ррр_с1а1:а[' 11Т1ЫТ1Е5_РРОСЕЕО' ]. 1зпа(). зип()) иТ1ЫТ1Е5_РРОСЕЕО
# Отображаем количество строк с отсутствующими значениями рг1п1:(ррр_с1а1:а[' РАУРОЫ__РРОСЕЕО' ] .1зпа() .зип()) Для столбца РАУРО1_1__РРОСЕЕО
# Отображаем количество строк с отсутствующими значениями # МОРТСАСЕ_1МТЕРЕ5Т_РРОСЕЕО рг1п1:(ррр_с1а1:а[ 'МОРТСАСЕ_1МТЕРЕ5Т_РРОСЕЕО' ] Лзпа() .зип()) Для столбца
# Отображаем количество строк с отсутствующими значениями рг1п± (ррр_с1а1:а [' РЕМТ_РРОСЕЕО' ]. 1зпа (). зип()) Для столбца РЕМТ_РРОСЕЕО
# Отображаем количество строк с отсутствующими значениями # РЕЕ1МАМСЕ_ЕЮ1__РКОСЕЕО рг!п1:(ррр_с1а1:а[' РЕЕ1МАМСЕ_ЕЮ1__РРОСЕЕО' ].!зпа().зип()) для столбца
# Отображаем количество строк с отсутствующими значениями # НЕА1_ТН_САРЕ_РРОСЕЕО рг1п1:(ррр_с1а1:а [' НЕА1_ТН_САРЕ_РРОСЕЕО1 ]. 1зпа(). зип()) Для столбца
# Отображаем количество строк с отсутствующими значениями # ОЕВТ_1МТЕРЕ5Т_РРОСЕЕО Для столбца
рггп1:(ррр_с1а1:а [' ОЕВТ_1ЫТЕРЕ5Т_РРОСЕЕО1 ]. гзпа(). зит())
# Создаем новую структуру Оа^аЕгапе, которая содержит все строки, в которых
# назначением кредита указана выплата зарплаты, а все другие расходы указываются
# как ИА (нет данных)
раугоИ_оп1у = ррр_с!а1:а[(ррр_с1а1:а[ 'иТИ1Т1Е8_РР0СЕЕ0' ] .гзпа()) & (ррр_с!а1:а
[ 'МОРТСАСЕ_1МТЕРЕ5Т_РРОСЕЕО' ] .гзпа()) & (ррр_с!а1:а
['МОРТСАСЕ-ТМТЕРЕЗТ-РРОСЕЕО^.гзпаО) & (ррр_с1а1:а[1 РЕМТ_РРОСЕЕО' ] ЛзпаО) &
(ррр_с1а1:а['РЕЕ1МАМСЕ_ЕЮ1_РРОСЕЕО'].г5па()) & (ррр_с1а1:а
[' НЕА1_ТН_САРЕ_РРОСЕЕО' ].гзпа( )) & (ррр_с1а1:а[' 0ЕВТ_1МТЕРЕ5Т_РР0СЕЕ0' ]. гзпа())
]
# Отображаем количество строк с назначением только на зарплату
рг1п1:(1еп(рауго11_оп1у. 1пбех))
Результат исполнения этого сценария показан в листинге 6.13.
Листинг 6.13. Количество каждого отдельного указанного целевого назначения кредитов
по программе РРР
570995
1828
719946
666788
743125
708892
734456
538905
Как видно по результатам исполнения этого сценария, большинство заемщиков (по
сути, все, кроме 1 828) указали целевым назначением кредита выплату зарплаты.
При этом меньше одной трети заемщиков указали, что они (возможно) также пла-
нировали использовать полученные по кредиту средства для оплаты коммунальных
услуг. Еще одна часть заемщиков сообщили, что они собирались использовать кре-
дитные средства для оплаты аренды. При этом наш последний тест показывает, что
свыше двух третей всех заемщиков указали только выплату зарплаты как целевое
назначение кредитных средств.
Что все это означает в контексте многомерности наших данных? Даже если мы ре-
шим не учитывать дополнительные столбцы, содержащие подробности адреса, или
с виду малоиспользуемые столбцы РРОСЕЕО, этот набор данных все равно содержит
довольно большой объем информации. Поэтому будем считать, что мы можем ис-
пользовать эти данные по крайней мере для того, чтобы начать делать выводы о
том, кто получил кредиты по программе РРР и как они использовали эти средства.
Но, как всегда, мы должны самостоятельно проверить и убедиться, что столбцы
или строки нашего набора данных содержат значения.
Атомарны ли данные?
Это еще один случай, когда результаты нашей предыдущей работы с данными по-
зволяют нам довольно быстро дать положительный ответ касательно этого показа-
теля целостности данных. Хотя более старая версия данных (за август 2020 г.) со-
держит только диапазоны сумм кредитов, мы знаем, что этот набор данных содер-
жит один кредит в каждой строке, включая точную сумму кредита в долларах.
Поскольку мы располагаем конкретными числами, а не сводными или агрегирован-
ными значениями, мы можем быть уверенными в том, что наши данные достаточно
детализированные (или атомарные) для поддержания широкого спектра анализов
данных, которые нам потребуется выполнить в будущем.
Понятны ли данные?
Хотя этот набор данных не оказался особенно хорошо аннотированным, мы, тем не
менее, смогли выполнить для него многие проверки на целостность данных. Это
было возможным благодаря тому, что названия его столбцов и их значения доволь-
но понятны. Например, если мы не были уверены, что означает название столбца
СО, увидев некоторые из его значений (например, С8-05), можно довольно легко
предположить, что это сокращение расшифровывается как соп^геззюпсй сНзЪггс!
(округ для голосования в Конгресс США). По мере накопления нами опыта работы
с общедоступными и государственными наборами данных (или работы в опреде-
ленной предметной области) мы сможем все быстрее понимать коды, термины и
жаргонные слова.
В случае столбцов, чьи названия были не сразу понятны, они стали для нас более
ясными после обмена несколькими сообщениями со Стивеном Моррисом и Управ-
лением по делам малого бизнеса. Например, Моррис подтвердил, что для столбца
Тегп правильными единицами измерения были месяцы (а не недели, как казалось
более вероятным сначала), и что значения столбцов РРОСЕЕО описывают целевое на-
значение кредитных средств согласно ”информации, предоставленной заемщиком
Управлению по делам малого бизнеса (как сообщено их заемщиком в форме заявки
на кредит)”.
Моя переписка с Моррисом также проиллюстрировала, почему обращение к экс-
перту по первоисточнику, если таковое вообще возможно, является важным шагом
в выполнении проверок на целостность данных. Вспомните, что вначале мне не
было понятно значение названия столбца ЬМПпсйсаТог. Поскольку мои строки выбо-
рочных данных не содержали значений в этом столбце, я выполнила ряд поисков в
Интернете и получила результат, включающий такое возможное сокращение, как
1епс1ег8 шог1§а§е тзигапсе — страхование ипотеки для кредиторов (рис. 6.9).
Какая же с этим проблема? Дело в том, что это неправильное значение этого со-
кращения. В своем сообщении (по электронной почте) Моррис объяснил мне, что
правильное значение будет Ьом/МоЬегаТе Тпсопе (низкий/средний уровень доходов), а
столбец 1_М1 ТпсИсаТог содержит значения, указывающие, находится ли заемщик гео-
графически в зоне с низким или средним уровнем доходов.
иллплЛ .пус.доу > пусЬи81пезз > агЬс1е > пус-1гп1-81огеГг... ;
МУС 1_М1 81огеТгоп11_оап - МУС Виз1пе88 - МУС.доу
□ие 1о оуегууЬе1гп1пд тТегезХ апд ПгтШес! Гипдз, ууе аге по 1опдег ассерйпд аррПсайопз (ог !Не
ЫУС ЬМ1 ЫогеГгоп! 1_оап. То Ье поИНес! о! ирйа^ез 1о 1Ыз ...
Реор1е а 1зо азк :
Сап 1М1 Ье 1пс1ис1ес11п 1оап? V
\Л/Ьа! 18 1_М11П Ьапктд? V
МЫсЬ Ьапкз ул/эме 1_М1? V
УУЬа! такез а 1оап СРА героПаЫе? V
ГеедЬаск
еп.у/1к|ресйа.огд > \м1к| > 1_епс1ег5_тог(даде_1П8игапсе •
Ьепдегз тоЛдаде тзигапсе - \Мк1рес1|а
Ьепс1ег5 тоПдаде (пзигапсе а!зо кпо\л/п аз рпуа!е тоНдаде !пзигапсе (РМ1) }п 1Ье 118,...
ЬМ1 ргептитз аге са1си1а1ес! изид а зПсЯпд зса1е Ьазес! оп 1оап атоиШ апд 1_УЙ ... 2011-
11-13. ЙеХпеуес! Тгот 'Ъирз://еп.мк|ресйа.огд/уу/тс1ех.рЬр?
1П1е=1епс1егз_тоНдаде_1П8игапсе&о1(Лд=971016555". СаТедопез;.
М1зз!пд 1гкЙса!ег | МизГ 1пс1ис1е 1псИса1ог
Рис. 6.9. Результаты поиска в Интернете значения термина 1_М1
Урок, который можно извлечь из этого, состоит в том, что если у вас нет офици-
ального словаря данных, нужно всегда быть осторожным с интерпретацией назва-
ний и значений столбцов. Даже те из них, которые могут казаться вам абсолютно
понятными, могут означать совсем не то, что вы думаете, что они означают. Если у
вас есть хоть минимальные сомнения, непременно свяжитесь с каким-либо специа-
листом или, в идеале, с теми людьми, которые собрали эти данные, чтобы подтвер-
дить свое предположение.
Размерностно структурированы ли данные?
Если понятие размерностно структурированных данных казалось немного абст-
рактным при его рассмотрении в главе 3, будем надеяться, что теперь, когда в на-
шем распоряжении есть настоящий набор данных, оно немного более понятно.
Размерностно структурированные данные включают информацию о полезных кате-
гориях или классах, которые можно использовать для группирования данных, со-
вместно с более атомарными свойствами, облегчающими выполнение более грану-
лярных анализов.
В случае данных по кредитам по программе РРР я бы сказала, что в число этих по-
лезных ”размерностных” столбцов данных входят столбцы Рига1игЬап1псНса1:ог,
НиЬгопе1пс11са1:ог, 1_МПпс11са1:ог (чье значение мы недавно выяснили), МА1С8Сос1е и в не-
которой степени столбец ЗВАОГПсе Сос1е. Столбцы ЯасеЕ1±п1с11:у, Сепс1ег и \/е1:егап
также могли бы быть размерностно полезными, но поскольку они содержат много
значений Опапзмегес!, то это ограничивает объем полезной информации, которую
можно извлечь из них. При этом другие столбцы могут дать ответы на вопросы ка-
сательно местонахождения и типа компаний-заемщиков, которые на данном этапе
воспользовались программой РРР.
Более того, такие столбцы, как, например, МА1С8Сос1е, предоставляют нам возмож-
ность дополнить наш набор данных информацией о том, к каким отраслям относят-
ся компании, воспользовавшиеся программой РРР. Эту информацию можно было
бы сравнить с набором данных Бюро трудовой статистики США или данными о
секторах занятости других государственных учреждений. Мы рассмотрим этот
процесс более подробно в разд. "Дополнение данных" главы 7.
Пока что мы смогли дать положительный ответ лишь на некоторые вопросы каса-
тельно целостности данных, при этом ответы на другие вопросы идут с определен-
ными оговорками, предполагая необходимость в дополнительных преобразованиях
и оценках, прежде чем можно будет переходить непосредственно к фазе анализа
данных. Но прежде чем заниматься этими преобразованиями, нам нужно обратить-
ся к критическому вопросу (или вопросам) по соответствию данных: демонстриру-
ют ли наши данные достоверность, надежность и репрезентативность, которая тре-
буется для того, чтобы делать осмысленные выводы о влиянии программы РРР на
малый бизнес в Соединенных Штатах.
Оценка соответствия данных
Оценив целостность нашего набора данных почти по дюжине различных показате-
лей, можно приступать к оценке уровня его соответствия нашим целям, т. е. дейст-
вительно ли эти данные могут дать нам ответы на задаваемые нами вопросы. Для
этого мы используем три основных критерия: достоверность, надежность и репре-
зентативность данных. Теперь настало время изучить наш набор данных с учетом
нашего первоначального вопроса: помогла ли программа РРР спасти малый амери-
канский бизнес?
Достоверность данных
Вспомним, что наше рабочее определение достоверности — это ”степень, в кото-
рой нечто измеряет то, что оно должно измерять”. Даже если бы наши данные по
кредитам по программе РРР были идеальными, нам нужно как-то определить, по-
могут ли они найти ответ на наш вопрос. На данном этапе мы знаем, что наш набор
данных предоставляет важную часть этого ответа, поскольку мы уже достаточно
уверены в том, что он описывает, для каких компаний был одобрен кредит по про-
грамме РРР. Посредством наших исследований его целостности (особенно каса-
тельно его полноты) и вследствие нашего поиска аннотационной информации или
метаданных мы также достаточно уверены в том, что он не содержит аннулирован-
ных кредитов. Мы убедились в этом как при помощи опубликованной информации
Управления по делам малого бизнеса о программе РРР, так и посредством прямых
контактов с компаниями-заемщиками.
Мы также можем использовать элементы нашего набора данных (особенно значе-
ния столбцов 1_оап$1:а1:и8 и ЬоапЗ^а^из 0а1:е), чтобы получить представление о том,
какие компании из свыше 750 тысяч компаний, одобренных для получения креди-
та, фактически получили средства. Для этого мы подсчитаем количество отдельных
значений столбца Ьоап51:а1:и8, используя метод 7а1ие_соип1=8(), как мы уже делали это
ранее. Соответствующий сценарий приводится в листинге 6.4.
Листинг 6.14. Сценарий ррр_1оап_8(а(и8.ру
# Сценарий для определения количества выданных кредитов
# Импортируем библиотеку рапбаз
1прог1: рапбаз аз рб
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
используя
# ее метод геаб_сз7()
ррр_ба1:а = рб. геаб_С87(' риЫ1с_150к_р1из_гесеп-|:.С87')
# Отображаем количество отдельных значений, содержащихся в столбце Ьоап51:а1:и8
рг!п1:(ррр_ба1:а[' Ьоап51:а1:и81 ] .7а1ие_соип1=5())
рг1п1:(8ит(ррр_ба1:а[11_оап5±а±:и5' ] .7а1ие_соип1=5())) О
О Обратите внимание на то, что поскольку метод 7а1ие_соип1=5() игнорирует значе-
ния МА, я также суммирую все записи, чтобы обеспечить подсчет всех строк.
Результат исполнения этого сценария приводится в листинге 6.15.
Листинг 6.15. Подсчет отдельных значений столбца Ьоап81а1и§
Ехепр1:1оп 4 549011
Ра1с1 !п ЕиП 110120
Ас1тл/е Оп-ОтзЬигзеб 107368
Мапе: ЬоапБГаГиз, а±уре: 1п1:64
766499
Как мы видим, из 766 499 кредитов свыше 100 тысячам заемщиков средства еще не
были отправлены, а другие 100 тысяч заемщиков уже выплатили свою задолжен-
ность.
Таким образом, чтобы оценить судьбу малых бизнесов, которые получили кредиты
по программе РРР, нам нужно работать только с теми из них, которые фактически
получили средства. Иными словами, наш запрос должен ограничиваться теми за-
емщиками, для которых значение столбца 1_оап$1:а1:и5 равно ЕхетрТтоп 4 или Рате! 1п
ЕиП.
Теоретически, когда бизнесы подавали заявки на кредит по программе РРР, они
запрашивали достаточную сумму, чтобы удерживаться на плаву, поэтому мы мо-
жем поддаться соблазну и предположить, что если бизнес получил кредитные
средства, то дела у него должны идти хорошо. Но точно так же, как потенциально
слишком ослабленные критерии могли позволить многим бизнесам мошенниче-
ски получить кредиты по программе РРР (Ъйр8://рарег8.88гп.сот/8о13/рарег8.сйп?
аЬ81гас! 1(1=3906395), сам факт получения бизнесом кредитных средств не является
гарантией того, что он продолжает успешно функционировать. Эта реальность ил-
люстрируется статьей в журнале \^а11 81гее11оита1 о компании, которая объявила о
банкротстве, несмотря на то что получила кредит по программе РРР
(Ьйр8://лУ8].сот/агйс1е8/Ьип(1ге(18-оГ-сотрап1е8-1Ьа1-§о1-8Йти1и8-а1Д-Ьауе-Га11еД-
11605609180). Поскольку мы уже знаем, что данный бизнес объявил о банкротстве,
если мы найдем его запись в нашем наборе данных, то мы сможем получить пред-
ставление о том, как такие записи могут потенциально выглядеть. Сценарий для
поиска этой записи приводится в листинге 6.16.
Листинг 6.16. Сценарий ррр_йпб_юа1ег(огб.ру
# Сценарий для нахождения определенного бизнеса в нашем наборе данных
# по его (частичному) названию
# Импортируем библиотеку рапбаз
1трог1: рапбаз аз рб
# Считываем данные из более нового файла в структуру 0а1:аЕгате библиотеки рапбаз,
# используя ее метод геаб_сз7()
ррр_ба1:а = рб. геаб_С57('риЬ1Лс_150к_р'1и8_гесеп1:.с87')
# Создаем и заполняем структуру 0а1:аЕгате, не содержащую строк с отсутствующими
# значениями столбца ВоггомегМапе
ррр_ба1:а_патеб_Ьоггомег5 = ррр_ба1а[ррр_ба1:а[ 'ВоггомегМапе' ] .по1па()]0
# Поскольку точное сопоставление может быть сложной задачей, воспользуемся методом
# библиотеки рапбаз 81:г.соп1:а1п5()
Ьапкгир1:су_ехатр1е = ррр_ба1=а_патеб_Ьоггомегз[ \
ррр_ба1а_папеб_Ьоггомегз['ВоггомегМапе']
.51г.соп1:агп5(’ИАТЕРЕОРО РЕСЕРП0М5' )]О
# Транспонируем результат, чтобы его было легче читать
рг1п1:(Ьапкгир1:су_ехатр1е. 1=гапзро8е())
О Библиотека рапбаз не может выполнять поиск строк в столбцах, содержащих
значение МА (нет данных), поэтому для целей проверки наша структура 0а1:аЕгате не
должна содержать строк с такими значениями в целевом столбце. (Очевидно, что в
других отношениях кредиты без названий заемщика могут представлять интерес
для более подробного исследования.)
О Хотя метод 81г.соп1:а1п5() и может выполнять успешный поиск только по части
строки, однако стоит учесть, что он чувствителен к регистру. Поэтому тот факт, что
названия компаний-заемщиков полностью написаны ЗАГЛАВНЫМИ БУКВАМИ,
имеет значение!
Результат исполнения этого сценария приводится в листинге 6.17. Он предоставля-
ет нам интересную информацию: данный кредит имеет статус Ехепр1:1оп 4, а также,
что, возможно, еще более интересно, значение его столбца ЬоапЗ^а^изОа^е равно МА.
Но во всех остальных отношениях нет никаких признаков, что этот бизнес больше
не работает.
Листинг 6.17. Результат исполнения сценария ррр Лпб \л/а1ейогб.ру
ЬоапМипЬег
Оа^еАрргоуеб
5ВА0НЧсеСобе
Ргосезз1пдМе1±об
ВоггомегМапе
ВоггомегАббгезз
ВоггоиегСИу
Воггомег51:а1:е
Воггомег21р
Ьоап51:а1:и80а1:е
7560217107
04/14/2020
353
РРР
1л1АТЕРЕОРО РЕСЕРТ1ОМ5, 1_1_С
6715 СОММЕРСЕ 5ТРЕЕТ
5РР1МСЕ1ЕЮ
7А
22150
МаМ
1_оап51а1из
Тегп
5ВАСиагап1уРегсеп1аде
1п111а1Аррго7а1Апоип1
Сиггеп1Аррго7а1Апоип1
11пс118Ьиг8ес1Атоип1
ЕгапсЫзеМапе
5ег71с1пд1епс1ег1оса11оп10
$е г 71с1пд 1_епс1е гМапе
5е г у1с1пд 1_епс1е гАс!с1 гез 8
5ег71с1пд1_епс1егС11у
5ег71с1пдЬепс1ег51а1е
$егу1с1пд1_епс1ег21р
Рига1игЬап1пс11са1ог
НиЬгопе1пс11са1ог
1_МП псНса 1о г
ВизтпеззАдеОезсгтрИоп
Ргодес1С11у
Ргодес1Соип1уМапе
Ргодес151а1е
Ргодес121р
СО
ЗоЬзРерог1ес1
МА1С5Сос1е
РасеЕ1Ьп1с11у
11Т1ЫТ1Е5_РР0СЕЕ0
РАУР01_1__РР0СЕЕ0
М0РТСАСЕ_1МТЕРЕ5Т_РР0СЕЕ0
РЕМТ_РРОСЕЕО
РЕЕ1МАМСЕ_ЕЮ1__РР0СЕЕ0
НЕА1_ТН_САРЕ_РР0СЕЕ0
0ЕВТ_1МТЕРЕ5Т_РР0СЕЕ0
ВизтпеззТуре
0г1д1па11пдЬепс1ег1оса11оп10
Ог1д1па11пд1_епс1ег
Ог1д1па11пд1_епс1егС11у
Ог1д1па11пд1_епс1ег81а1е
Сепбег
\/е!егап
МопРгоП!
ЕхепрИоп 4
24
100
413345.0
413345.0
0.0
МаМ
122873
ЕадТеВапк
7815 1л1оос1топ1 Ауе
ВЕТНЕ50А
МО
20814
и
М
МаМ
Мем Визтпезз ог 2 уеагз ог Тезз
5РР1МСЕ1ЕЮ
ЕА1РЕАХ
УА
22150-0001
7А-08
45.0
722320.0
Опапзмегес!
МаМ
413345.0
МаМ
МаМ
МаМ
МаМ
МаМ
1_1п11ес1 ЫаЫЛИу Сопрапу(ЫС)
122873
ЕадТеВапк
ВЕТНЕ50А
МО
Ма1е Омпес!
Моп-\/е1егап
МаМ
Более того, добавив следующую строку кода в конец нашего сценария, можно про-
верить, сколько кредитов имеют значение МА в столбце 1_оап51а1и80а1е. Мы увидим,
что результат будет идеально совпадать с теми кредитами, чье значение 1_оап51а1и8
равно ЕхепрИоп 4.
рг1п1(зит(ррр_с1а1а [' 1_оап51а1и80а1е' ]. 1зпа()))
Так измеряют ли эти данные кредитов по программе РРР то, что они должны изме-
рять? Я бы сказала, что да, измеряют, но не полностью. Как мы видели по результа-
там подсчета значений столбца 1_оап$1:а1:и8, не все компании в нашем наборе данных
фактически получили кредитные средства. Они были одобрены и все еще имеют
право получить эти деньги (мы знаем, что их кредиты не были аннулированы), но
107 368 этих компаний все еще не получили эти деньги, и мы не можем знать, по-
лучат ли они их когда-либо.
Также по этому набору данных нельзя получить информацию о дальнейшей судьбе
компаний, получивших кредитные средства. Некоторые из них могут продолжать
работать, а другие, возможно, обанкротились. А третьи могли быть ликвидированы,
не объявляя банкротство. Иными словами, данные программы РРР имеют высокую
достоверность при ответе на определенные части нашего вопроса, но чтобы полу-
чить ответ на весь вопрос целиком, нам потребуется намного больше, чем только
этот один набор данных.
Надежность данных
Что касается надежности данных, мы в первую очередь заинтересованы в таких
критериях, как точность и стабильность. Или, иными словами, насколько хорошо
данные программы РРР отражают, кто получил кредиты по этой программе, а так-
же, насколько вероятно, что картина того, кто получил кредиты, изменится со вре-
менем.
Благодаря нашим предшествующим исследованиям, на данный момент мы знаем,
что стабильность этого набора данных далека от идеальной. Несколько тысяч биз-
несов, которые получили одобрение на получение кредита и содержатся в более
старом наборе данных (за август 2020 г.), отсутствуют в более новом наборе дан-
ных (мы рассмотрим вызываемые этим обстоятельством последствия на репрезен-
тативность в следующем разделе). Это соответствует информации в документа-
ции Управления по делам малого бизнеса22, что аннулированные кредиты не вклю-
чаются в этот набор данных23. Также неясно, будут ли предыдущие версии данных
доступны по мере обновлений набора данных. Это затрудняет задачу определения,
какие были выполнены в нем изменения, если только не загружать и архивировать
каждый его выпуск самостоятельно.
Даже числа в самом наборе данных могут быть не особенно стабильными с течени-
ем времени. Например, мы знаем, что 538 905 компаний-заемщиков указали, что
они будут использовать кредит по программе РРР для выплаты зарплат. Но, как
пояснил представитель Управления по делам малого бизнеса Стивен Моррис в од-
22 Информацию можно загрузить по этому адресу: ЬНр8://8Ьа.§оу/(1оситепЦгерог1:-раусЬеск-рго1:есЦоп-
рго§гат-ррр-1оап-с1а1:а- кеу-авресК
23 Опять же, понимание, почему и как эти кредиты были аннулированы, может быть полезным, но мы
не найдем эту информацию в этом наборе данных, здесь есть только некоторые детали, указывающие
на то, где начинать ее искать.
ном из своих сообщений (по электронной почте): ”Эти данные в некоторой степени
обманчивы, поскольку заемщик не обязан использовать средства на указанные в
заявке цели”. Иными словами, если только в какой-либо части процесса прощения
или погашения кредита не требуется, чтобы заемщик предоставил подробную ин-
формацию о том, как были потрачены эти деньги (и эта информация впоследствии
не обновится в этом наборе данных), мы не можем быть уверены в точности или
стабильности чисел в разных столбцах РРОСЕЕО.
Репрезентативность данных
Представляют ли данные по кредитам по программе РРР всех, кто фактически по-
лучили такие кредиты? Скорее всего, да. После того как публичное негодование
вынудило многие крупные и/или публичные компании возвратить кредиты по про-
грамме РРР, выданные на ранних этапах этой программы (11йр8://пЬспеуу8.сот/
Ьи8ше88/Ьи8ше88-пелУ8/луЫсй-сотрап1е8-аге-ге€игп1п§4йе1г-ррр-1оап-Ьеге-8-118€-
П1194566), Управление по делам малого бизнеса дало понять, что они внимательно
изучат кредиты на сумму свыше $2 миллиона. В результате к началу июля 2020 г.
было возвращено или аннулировано кредитов на почти $30 миллиардов
(Ы1р8://спЬс.сот/2020/07/06/сотрап1е8-ге1игпеД-30-Ы111оп-1п-8та11-Ьи8те88-1оап8-
Ггот-ррр.Ыт]). После наших сравнительно всесторонних сравнений более старого
(август 2020 г.) и более нового (февраль 2021 г.) наборов данных мы можем быть
довольно уверенными в том, что имеющийся у нас набор данных репрезентативен
по крайней мере для тех, кто получил кредит по программе РРР на сегодняшний
день.
В то же самое время это не предоставляет нам столько полезной информации,
сколько можно показаться. Мы уже знаем, что подавляющее большинство получа-
телей кредитов по программе РРР в этом наборе данных не указали свой пол, расу,
этничность или статус прохождения военной службы (уе!егап). Это означает, что у
нас нет действенного способа узнать, насколько хорошо (если вообще) демографи-
ческие показатели получателей кредитов по программе РРР отражают численность
малых бизнесов в Соединенных Штатах. Более того, как мы увидим в главе 9, очень
маловероятно, что мы вообще сможем сделать какие-либо выводы касательно де-
мографии получателей кредитов по программе РРР, по причине очень небольшого
количества заявителей, которые предоставили эту информацию.
Но вопрос репрезентативности в действительности намного глубже, чем этот ас-
пект, — к тем 7 207 кредитам (и дальше), которые исчезли из набора данных между
его первой и последней публикацией. Эти пропавшие записи кредитов отражают
компании, которые подали заявку на кредит и получили одобрение на его получе-
ние, но, по словам одного сотрудника: ”Деньги просто так и не поступили”. Это
означает, что хотя мы знаем, сколько компаний получили кредит по программе РРР,
у нас нет никакой возможности узнать, сколько компаний подали заявку на получе-
ние кредита, поскольку эти аннулированные кредиты были удалены из набора дан-
ных. Таким образом, у нас теперь есть проблема, которую я люблю называть про-
блемой ”знаменателя”.
Проблема знаменателя
Проблема знаменателя известна почти во всех областях исследований на основе
данных, хотя она может называться и по-другому, например эталонной тестовой
(ЪепсНтагктд) или базисной (ЪазеНпе) проблемой24. Эта проблема обобщает слож-
ность попытки извлечь смысл из данных, когда у вас нет достаточной сравнитель-
ной информации, чтобы поместить ее в контекст. В большинстве случаев причиной
этому является то, что требуемые для сравнения данные никогда не были собраны.
В наших исследованиях данных программы РРР нам уже встречалась одна версия
этой проблемы: мы знаем, какие компании получили кредиты, но мы не знаем,
какие компании подали заявку, но им было отказано, или причину отказа (по
крайней мере в некоторых случаях получатели не знают причину). Это представ-
ляет проблему при оценке процесса выдачи кредитов по программе РРР, посколь-
ку мы не знаем, было ли отказано в получении кредита законным заявителям, в то
время как некоторым компаниям было выдано по несколько кредитов. Если мы
хотим знать, была ли выдача кредитов справедливой или даже эффективной, ин-
формация о том, кто не был включен в программу, представляет такую же важ-
ность, как и о том, кто был.
Некоторые из проблем знаменателя, с которыми нам пришлось столкнуться до сих
пор, можно попоробовать решить, используя какие-либо дополнительные данные.
Именно это сделали в журнале УУа11 81гее1Л>игпа1 при сравнении данных програм-
мы РРР с данными по объявлениям о банкротстве. Если вместе с обновленными
данными поставщик данных не предоставляет более старые данные, что, похоже,
происходит в случае с данными по программе РРР, то для решения таких проблем
нам придется создать свой собственный архив.
Заключение
Так помогли ли кредиты по программе РРР спасти малые американские бизнесы?
Возможно. С одной стороны, трудно представить, что можно было бы потратить
столько денег и не получить какого-либо положительного результата. Но, с другой
стороны, как реальный мир, так и имеющиеся у нас данные о нем, представляют
собой сложный и взаимозависимый беспорядок. Если получатель кредита по про-
грамме РРР изменил модель своего бизнеса и нашел новые потоки дохода, помес-
тили бы мы его в категорию бизнесов, ”спасенных” этой программой, или нет?
Аналогично, если бизнес, который не получил кредита, потерпел неудачу, так это
потому, что он не получил кредита или же он бы потерпел неудачу в любом случае?
Чем больше таких ”что если”, тем выше вероятность следующих двух развитий:
1. У нас начнется ужасная головная боль, мы решим, что по-настоящему ничего
невозможно узнать и начнем искать оправдание, чтобы отложить работу над за-
дачей на потом.
24 Термин "проблема знаменателя” (бепошта1ог ргоЫет), по-видимому, имеет специфическое значение при
использовании в области права собственности США, но я использую его здесь не совсем в этом значении.
2. Некоторое время вы играете в разные игры, бродите по Интернету или жалуе-
тесь ничего не понимающим друзьям и родственникам на то, что вы потратили
массу времени на выпас набора данных, который, возможно, абсолютно беспо-
лезен и вы с трудом можете вспомнить, почему вообще начали работать над
ним. И тут вам на ум приходит что-то, что наталкивает вас на другой подход,
возможно, более узкого вопроса для исследований, и вы возбужденно возвра-
щаетесь к своему набору данных, горя нетерпением узнать, можно ли на этот
вопрос дать лучший ответ, чем на предыдущий.
Является ли этот процесс трудоемким и извилистым? Да, является. Он также напо-
минает наше рассмотрение темы конструктивной достоверности во врезке "Как? И
для кого?" в главе 3. В конечном итоге, именно так в действительности формиру-
ются новые знания. Неопределенная, раздражающая и трудновыполнимая работа
по рассмотрению, обдумыванию и тестированию новых вариантов, с последующим
(потенциально) запуском всего процесса заново с небольшим объемом дополни-
тельной информации и понимания — вот так добываются по-настоящему ориги-
нальные знания. Это то, что каждая алгоритмическая система в мире отчаянно пы-
тается аппроксимировать или имитировать. Но если вы готовы приложить усилия,
то ваши усилия действительно могут увенчаться успехом.
На данный момент я чувствую, что узнала довольно много об этом наборе данных.
И прихожу к выводу, что его, вероятнее всего, будет недостаточно, чтобы получить
ответ на мой исходный вопрос, но я все равно могу извлечь некоторые интересные
выводы, которые можно сделать на его основе. Например, я уверена в том, что бо-
лее новый набор данных точно отражает состояние кредитов, одобренных в на-
стоящее время, поскольку я убедилась в том, что отсутствующие в более новом на-
боре данных кредиты никогда не были выданы (по той или иной причине). В то же
самое время, хотя Управление по делам малого бизнеса объявило, что по состоя-
нию на начало января 2021 г. было прощено кредитов по программе РРР на сумму
свыше 100 миллиардов долларов (Ьир8://8Ьа.§оу/агйс1е/2021/]ап/12/11-т1111оп-
раусЬеск-рго€есйоп-рго§гат-1оап8-Гог§1уеп-8О-Гаг4о1:а1т§-оуег-100-Ь1111оп), даже
спустя шесть недель столбец ЬоапЗ^а^из не содержит отдельного значения для обо-
значения таких кредитов. В начале марта Стивен Моррис из Управления по делам
малого бизнеса перестал отвечать на мои запросы по электронной почте, но по со-
стоянию на май 2021 г. для данного набора данных стал доступным словарь дан-
ных25, даже если ни он, ни обновленные данные не содержат эту информацию.
Конечно же, здесь еще много чего можно узнать: какие компании получили креди-
ты и где они находятся, на какую сумму были эти кредиты одобрены, а также, кто
их выдавал. И хотя эти данные далеко не идеальные, я могу хранить их старые ко-
пии, чтобы в случае каких-либо значительных изменений в будущем у меня под
рукой были архивы предыдущих данных. Учитывая все это, настало время перейти
от этапа оценки данных и приступить к задаче их очистки и преобразования, чем
мы и займемся в главе 7.
25 Этот словарь можно загрузить по адресу ЬПр8://(1а1:а.8Ьа.§оу/(1а1:а8еЦррр-Го1а/ге8оигсе/ааЬ8е9^9-
36с11-42е1-ЬЗЬа-е59с79ПсГ7Г0.
ГЛАВА 7
Очистка, преобразование
и дополнение данных
В большинстве случаев данные, которые мы находим, собираем или получаем, не
совсем отвечают нашим требованиям. Например, они могут иметь неудобный фор-
мат, неправильную структуру или неподходящие единицы измерения. Сами данные
могут содержать ошибки или разрывы в последовательностях, быть неоднородны-
ми. Они могут содержать непонятные ссылки или намеки на дополнительные не-
реализованные возможности. Какими бы недостатками ни обладали исходные данные,
в нашем стремлении использовать эти данные как источник знаний нам неизбежно
придется их тем или иным образом очищать, преобразовывать и/или дополнять,
чтобы извлечь из них максимальную пользу.
До сих пор мы откладывали большую часть этой работы, поскольку у нас были бо-
лее насущные задачи, требующие решения. В главе 4 мы концентрировались на из-
влечении данных из файлов сложных форматов и сохранении их в каком-либо бо-
лее удобном для нас формате. А в главе 6 наш приоритет состоял в тщательной
оценке качества наших данных, чтобы можно было принять обоснованное решение,
стоит ли вообще вкладывать усилия в их дополнение и анализ.
Но сейчас настало время закатать рукава и приступить к выполнению задачи, кото-
рую я рассматриваю как второй этап выпаса данных и обеспечения их качества:
подготовка имеющихся у нас данных для выполнения над ними требующихся нам
анализов. Наши данные находятся в требуемом нам табличноподобном формате, и
мы определили, что они имеют достаточно высокое качество, чтобы предоставить
некоторые полезные знания, — даже если они не точно такие, какими мы их изна-
чально представляли.
Очевидно, что определить и рассмотреть каждую возможную проблему или метод,
связанный с очисткой, преобразованием и/или дополнением данных, невозможно.
Поэтому мы рассмотрим подход посредством проработки примеров, с которыми
мы уже повстречались ранее, для которых требуется выполнение одной или не-
скольких из этих задач. Например, мы рассмотрим различные способы, которые
могут нам потребоваться для преобразования информации о дате, используя набо-
ры данных, с которыми мы работали в главах 2 к 4. Мы также рассмотрим способы
удаления бесполезных данных из файлов, содержащих как структурированные
данные, так и метаданные. Мы даже исследуем регулярные выражения, которые
предоставляют нам мощное средство для выбора определенных частей поля дан-
ных или для поиска определенных терминов или образцов независимо от исполь-
зуемого в них регистра и/или знаков препинания. В процессе работы мы также ох-
ватим большой круг средств и стратегий, которые могут вам потребоваться при
очистке и преобразовании большинства наборов данных. Как минимум, изложен-
ные в этой главе подходы дадут вам полезную отправную точку в случае необхо-
димости решить по-настоящему сложную или уникальную задачу.
Выбор подмножества данных системы СШ В1ке
В разд. "Отправляемся в путь с данными СШ В1ке" главы 2 мы использовали дан-
ные системы аренды велосипедов СШ В1ке для тестирования некоторых из наших
только что изученных концептов РуЙюп, например циклов Гог.Лп и операторов
условия 1Г ... е!зе. Ради удобства мы начали работу с выборочного набора дан-
ных, извлеченного из файла данных этой системы за сентябрь 2020 г.
(Ййр8://83.атагопауу8.сот/€г1рс1а€а/тс1ех.й1:т1).
Существует множество ситуаций, когда нам нужно разбить на части большие набо-
ры данных для анализа. Например, мы не располагаем достаточными временными
или вычислительными ресурсами для обработки всех данных за один раз или нас
вообще интересует только определенное подмножество из всего набора данных.
Если нам нужно выбрать только конкретное количество строк, это можно было бы
сделать при помощи цикла Гог.Лп, используя функцию гапде(), которую мы рас-
смотрели во врезке "Добавляем итераторы: функция гап^е()" в главе 4. Но нам
также может потребоваться извлечь из всего набора только некоторые данные в
зависимости от их значений. В моем случае я выбрала все аренды велосипедов, на-
чиная с 1 сентября 2020 г., но нам может потребоваться получить более специфич-
ную информацию, например, отдельно количество аренд велосипедов по субботам
и воскресеньям, а также по рабочим дням и праздникам.
Давайте начнем с первой задачи, выбора из всего набора данных аренд велосипедов
в системе СШ Вл бе, начиная с 1 сентября 2020 г.1. Концептуально это довольно про-
стая задача: нам просто нужны все строки из нашего набора данных, содержащие
аренду, начавшуюся в первый день сентября. Но, снова взглянув на наш набор
данных, станет ясно, что даже эта задача не такая и простая.
В частности, как можно увидеть на рис. 7.1, столбец з^агШте содержит не просто
дату в виде месяца, дня и года, но также часы, минуты и секунды (с точностью до
четырех знаков после запятой!).
Например, значение з^агШте в первой записи этого файла данных выглядит сле-
дующим образом:
2020-09-01 00:00:01.0430
1 Хотя формат данных файлов системы СШ В1ке изменился в начале 2021 года, более ранние файлы
имеют тот же самый формат, который используется в этих примерах.
ПпсГ.
202009СШЫкеТпрс1а1аЕхс
1 1г1рс1ига11оп,21аг111те, 51ор11те, 51аг1 51а11оп ±(1,51аг1 51а11оп пате,51аг! 51а11оп 1
пате,епс1 51а11оп 1аШис1е, епб 51а11оп ГопдПибе, Ыке±с1, 05ег1уре, Ыг1И уеаг,депс1ег
2 4225,2020-09-01 00:00:01.0430,2020-09-01 01:10:26.6350,3508,81: ГЯсЬоТаз Ауе & МапЬа
Ауе, 40.74177603, -74.00149746,44317, СизЮтег, 1979,1
3 1868,2020-09-01 00:00:04.8320,2020-09-01 00:31:13.7650,3621,27 Ауе & 9 51,40.773982
51,40.7169811, -73.94485918, 37793, СизЮтег, 1991,1
4 1097,2020-09-01 00:00:06.8990,2020-09-01 00:18:24.2260,3492,Е 118 51 & Рагк Ауе,40.
51,40.823498,-73.94386,41438,5иЬзсг±Ьег, 1984,1
5 1473,2020-09-01 00:00:07.7440,2020-09-01 00:24:41.1800,3946,51 М1сЬо1аз Ауе & V/ 137
ОпшаП Л/Л Й7/ЗЯ77 _77 0707/10 7^ЯА(7> Гнс1-лтаг 1ОО/П 7
Рис. 7.1. Первые несколько строк набора данных аренды велосипедов системы СИ! В!ке
Очевидно, что если мы хотим проанализировать только первый день аренд (или
только аренды в утренние часы пик, или только аренды по рабочим дням недели),
нам нужен способ эффективной фильтрации данных на основе только части ин-
формации, хранящейся в этом столбце. Но какие у нас имеются опции для решения
этой задачи? В следующих нескольких разделах мы рассмотрим каждую из этих
задач, последовательно выполняя поиск аренд в определенную дату, в определен-
ный период времени или в определенный ”тип” дня. В процессе мы изучим некото-
рые из инструментов РуШоп для решения подобных задач, а также когда и почему
использовать каждый из этих инструментов.
Простое разбиение
Решить первую задачу — извлечь записи аренды велосипедов за период, начинаю-
щийся с 1 сентября 2020 г., — сравнительно легко, используя совместно некоторые
инструменты, с которыми мы уже работали в предыдущих примерах. Но первым
делом нам нужно осознать, что при считывании файлов С8У средствами РуШоп
большинство данных считаются строковыми2. Это означает, что хотя для людей
вполне понятно, что значение 2020-09-01 00:00:01.0430 должно интерпретиро-
ваться как дата и время, для РуШоп же оно выглядит просто как набор цифр и про-
чих символов.
Рассматривая поле з^агШте, вопрос выделения всех аренд, заключенных начиная с
1 сентября 2020 г., немного упрощается, поскольку часть данных, содержащая ин-
формацию о дате, всегда отделяется от информации о времени одним пробелом.
Это означает, что если мы сможем найти способ выделить только часть поля перед
этим пробелом, то мы сможем легко организовать оператор условия II2 ... е1зе, что-
бы сравнить эту часть с нашей целевой строкой даты (в данном случае 2020-09-01) и
использовать это сравнение, чтобы оставить только требуемые нам записи (строки)
набора данных.
Эта задача решается довольно обыденным способом — используя встроенный ме-
тод зрИ±()- Мы уже использовали его на второстепенных ролях в предыдущих
2 И даже если бы это было не так, то мы всегда можем преобразовать их в строки.
примерах, когда нам нужно было разделить имена файлов или 1ЖЬ-адреса. В част-
ности, с помощью этого метода в разд. "Глаголы ~ функциям " главы 2 мы проиллю-
стрировали разницу между функциями и методами. Напомним, что этот метод по-
зволяет указать один символ, по которому строку следует разделить на две части. В
результате исполнения этого метода создается список, содержащий оставшиеся
части строки в исходном порядке, но без символа, по которому было выполнено
разделение. Таким образом, результатом разделения строки 2020-09-01 00:00:01.0430
по символу пробела будет список [' 2020 -09-01', '00:00:01.04301 ].
Чтобы продемонстрировать простоту и эффективность этого метода, модифициру-
ем наш сценарий из разд. "Отправляемся в путь с данными СШ В1ке" главы 2. В
листинге 7.1 я сократила комментарии к некоторым задачам, поскольку теперь эти
задачи должны быть вам знакомы, но полагаю, что будет не лишним предоставить
краткое описание назначения сценария в его начале.
Листинг 7.1. Сценарий сШЬ|ке_§ер1етЬег1_пс1е§.ру
# Цели: отфильтровать все аренды велосипедов системы С1Л1 В1ке за сентябрь
# 2020 года и сохранить в новый файл только аренды за 1 сентября 2020 года
# Краткое описание программы:
# 1. Считываем файл данных: 202009-стГт.Ьт.ке-Ггт.рбаГа.с^
# 2. Создаем новый выходной файл и записываем в него строку заголовка
# 3. Для каждой строки входного файла разделяем значение 51аг1Нпе по символу пробела:
# а. Если первый элемент получившегося списка равен "2020-09-01", записываем эту
# строку в наш выходной файл
# 4. Закрываем выходной файл
# Импортируем библиотеку С57
1прог1: С87
# Открываем файл данных в режиме чтения
8оигсе_ГИе = ореп("202009-сШЫке-1:г1рс1а1:а.С87","г")
# Открываем выходной файл в режиме записи
ои^ри^Ие = ореп("2020-09-01-сШЫке-1:г1рс1а1:а.С57","м")
# Передаем исходный файл 8оигсе_ГПе методу 01с1Реас1ег и сохраняем результат
# в переменной с1Л1Ыке_геас1ег
с11л.Ыке_геас1ег = сз7.01с1:Реас1ег(8оигсе_Г11е)
# Создаем соответствующий метод 01с1:1л1г11:ег и указываем, что заголовок должен быть
# таким же, как и названия полей в с11:1Ыке_геас1ег
ои1ри1_нг1Лег = С57.01с1Иг11ег(оЩрЩ_Г11е, Г1е1с1пате8=с11:1Ь1ке_геабег.Г1е1бпате8)
# Записываем строку заголовка в выходной файл
ои!ри1_нгтЛег. мг1ЛеЬеас1ег()
# Обрабатываем в цикле Гог...1_п список строк с1Л1Ыке_геас1ег
1"ог а_гом 1п с1Л1Ыке_геас1ег:
# Получаем значение столбца 51агШте
51аг1_1гте51атр = а_гои["51агШте"]
# Разделяем это значение по символу пробела
МтеПз!: = з^аг^-Ытез^атр.зрИ^С' ")
# Первым получившимся элементом с позицией 0 будет часть даты исходной строки
ГНе_с1а1:е = 1!те1л51:[0]
# Если значение 1Не_с1а1е совпадает с требуемой датой
И ГЬе_да1:е == "2020-09-01":
# Записываем эту строку данных в наш выходной файл
ои^ри1:_мг11:ег. мг11:егом(а_гом)
# Закрываем выходной файл
ои^ри^Пе. с!озе()
Довольно просто, не так ли? Конечно же, этот сценарий можно с легкостью моди-
фицировать для выборки другой даты или даже нескольких дат. Например, опера-
тор условия И можно модифицировать следующим образом:
И 1±е_с1а1:е == "2020-09-01" ог ГНе_с1а1:е == "2020-09-02":
Хотя для выбора двух или трех конкретных дат будет вполне достаточно части о г
этого оператора, код для большего количества дат при таком подходе будет запу-
танным. (Вспомните, например, наш неуклюжий оператор условия в листинге 6.12.)
Поэтому, чтобы отфильтровать требуемые нам даты, не создавая при этом чрез-
мерно сложного, неуклюжего и чреватого ошибками кода, нам лучше подойдет
другое средство: регулярные выражения.
Регулярные выражения:
супермощное средство сопоставления строк
Регулярные выражения (ге§и1аг ехрге88ЮП8, часто сокращается до ге§ех) позволяют
выполнять быстрый и эффективный поиск подстрок в строках большего размера
или фрагментах текста. В большинстве случаев, если обнаруживается, что для ре-
шения задачи сопоставления или фильтрации строк в операторе условия необходи-
мо использовать большое количество апс! или ог, то это будет первым признаком
необходимости использовать регулярное выражение.
Регулярные выражения используются в большинстве языков программирования.
Это лаконичные и мощные средства, но иногда чрезвычайно сложные в работе. Хо-
тя одно регулярное выражение может выразить очень сложный шаблон поиска,
процесс разработки регулярных выражений, работающих ожидаемым образом, мо-
жет быть чрезвычайно трудоемким и часто требующим большого количества проб
и ошибок, чтобы получить правильное выражение. Поскольку наша цель состоит в
том, чтобы обеспечить как эффективную, так и понятную работу по выпасу дан-
ных, здесь мы будем концентрироваться на коротких регулярных выражениях, пре-
доставляющих уникальную функциональность, которую нельзя с легкостью полу-
чить иными способами. Хотя регулярные выражения незаменимы для некоторых
задач, они не являются универсальным средством для решения любых задач и
обычно лучше всего работают при использовании их совместно с другими методами.
Для начала используем регулярное выражение для решения задачи выборки аренд
велосипедов, осуществленных в типичные часы ”утренних поездок на работу”, ко-
торые для наших целей мы определим как период с 7 до 9 часов утра. Процесс соз-
дания любого регулярного выражения начинается с получения представления о
том, что мы хотим сопоставить, а что не хотим. В данном случае мы начнем с рас-
смотрения примера записи 51агШте, находящейся вне указанного периода времени
(следовательно, это не то, что мы хотим сопоставить):
2020-09-01 00:00:01.0430
А теперь рассмотрим запись 51агН:гте, входящую в требуемый период:
2020-09-01 00:08:17.5150
Следующий этап заключается в признании, что эту задачу можно решить, исполь-
зуя рассмотренный ранее метод разбиения строки. В частности, можно разбить
строку по символу двоеточия (:), получив следующий результат:
['2020-09-01 00', '08', '17.5150']
Затем извлечем из этого списка средний элемент и, используя составной условный
оператор (т. е. оператор 1Г, содержащий два или больше условий), проверим, сов-
падает ли он со строками 07, 08 или 09.
Это определенно рабочий подход, но он несколько неудобный. В частности, его
реализация требует нескольких шагов, а оператор с тремя условиями трудно пони-
мать. С другой стороны, условное выражение позволит нам сконцентрироваться на
этих значениях часа в один прием, при этом оставаясь довольно легким для пони-
мания. Но прежде чем приступать к созданию самого регулярного выражения, вы-
полним быстрый обзор словаря регулярных выражений языка РуЙюп.
Поскольку для описания шаблонов символов и строк регулярные выражения также
используют символы и строки, для упрощения описания искомой последовательно-
сти в системе регулярных выражений в РуЙюп применяются метасимволы и специ-
альные последовательности символов. В табл. 7.1 приводится несколько наиболее
полезных символов, взятых из более обширного списка на веб-сайте \У38сйоо18
(ййр8:/Лу38СЙоо18.сот/ру€йоп/ру€Ъоп_ге§ех.а8р).
Таблица 7.1. Наиболее употребляемые компоненты регулярных выражений
Выражение Описание
[] Набор символов
пуп Указывает специальную последовательность символов (может также применяться для экранирования специальных символов)
• Любой символ (за исключением символа новой строки)
* Отсутствие или несколько вхождений символа
+ Одно или несколько вхождений символа
{} В точности указанное количество вхождений символа
1 Или, или
0 Выборка и группирование
\с! Возвращает строки, содержащие цифры 0-9
\0 Возвращает строки, НЕ содержащие цифры
\5 Возвращает строки, содержащие символ пробела
\5 Возвращает строки, НЕ содержащие символа пробела
\м Возвращает строки, содержащие любые символы, используемые в словах (буквы от а до 7, цифры от 0 до 9 и символ подчеркивания _)
\ы Возвращает строки, НЕ содержащие символов, используемых в словах
Как всегда в процессе создания письменных работ, регулярные выражения предос-
тавляют несколько способов для описания требуемого нам шаблона. В большинст-
ве случаев наша цель состоит в том, чтобы определить шаблон, который будет сов-
падать с требуемыми нам строками и при этом не будет допускать случайного сов-
падения с какими бы то ни было другими строками. Для нашей задачи ”утреннего
часа пик” мы можем воспользоваться тем обстоятельством, что в столбце 21аг1Гйле
только цифры значения часов, и никакие другие значения, заключены в двоеточия.
Это означает, что этот шаблон "заключение в двоеточия” можно использовать как
часть нашего регулярного выражения с уверенностью, что мы случайно не выберем
какую-либо другую часть строки. Чтобы проверить работу этого подхода на прак-
тике, создадим несколько образцов регулярных выражений, работу которых мы
протестируем на настоящих и искусственно созданных данных. Соответствующий
код приводится в листинге 7.2.
Листинг 7.2. Сценарий гедехДез^з.ру
# Цель этого сценария - проверить работу нескольких регулярных выражений
# с некоторыми тестовыми данными.®
# Импортируем библиотеку для работы с регулярными выражениями
1прог1: ге
# Преобразуем шаблоны регулярных выражений в объекты шаблонов, используя метод
ге.сопрПеО
Ьоокепб_гедех = ге.сопр1Ле("\з0[7-9]:")О
# Всегда пытаемся использовать описательные названия переменных
опе_з1с1ес1_гедех = ге.сопр1Ле("0[7-9]:")
# Этот пример не должен найти совпадений
запр1е1 = "2020-09-01 00:00:01.0430"
# Этот пример должен найти совпадения
запр1е2 = "2020-09-01 09:04:23.7930"
# Этот пример не должен найти совпадений
затрТеЗ = "2020-09-01 10:07:02.0510"
# Проверяем работу примеров на практике
рг1п1:("Ьоокепс1_гедех:")
р г 1 п ± (Ьоокепс1_гедех. зеа гсЬ (запр!е1))
р г 1 п ± (Ьоокепс1_гедех. зеа гсЬ (затр!е2))
р г1п±(Ьоокепб_гедех.зеа гсЬ(затрТеЗ))
рг1п1:("опе_51с1ес1_гедех:")
р г 1 п ± (опе_з1с!ес1_гедех. зеа гсЬ (запр1е1))
р г 1 п ± (опе_з1с!ес1_гедех. зеа гсЬ (запр1е2))
р г 1 п ± (опе_з1бес1_гедех. зеа гсЬ (запрХеЗ))
О Кроме данного примера, потренироваться в использовании регулярных выраже-
ний РуЙюп можно с помощью демонстрационного сценария на веб-сайте А^ЗЗсйоок
(Ьйр8://лу38сЬоо18.сош/ру€Ьоп/€гуру€Ьоп.а8р?й1епате=(1ето_ге§ех).
О Даже если регулярное выражение используется в сценарии только один раз, я
настоятельно рекомендую определять его в начале сценария и присваивать его пе-
ременной соответствующее название. Это наиболее простой и эффективный способ
для отслеживания его работы, особенно в случае использования нескольких выра-
жений.
Исполнение сценария в листинге 7.2 должно дать результат наподобие следующего:
Ьоокепс1_гедех:
Моле
<ге.Ма1:сЬ оЬдес!:; зрап=(10, 14), та1сЬ=' 09: '>
Моле
опе_з1с1ес1_гедех:
Моле
<ге.Ма1х±1 оЬдес!:; зрап=(11, 14), па1:сЬ=109:1 >
<ге.Ма1:сЬ оЬдес!:; зрап=(14, 17), та±сН='07:'>
Как можно увидеть, регулярное выражение Ьоокепс1ес1, в котором указаны оба двое-
точия, правильно находит (и не находит) совпадения во всех трех случаях; а вот с
другой стороны, выражение опе_з1с1ес1 неправильно находит совпадение в значении
зесопс15 образца затрТеЗ. Именно по этой причине важно определять искомую строку
наиболее точно. В выводе объекта Ма1:сЬ можно видеть, что он содержит информа-
цию о совпадающей подстроке (например, тай±='07:'), а также о ее положении в
исходной строке (например, начиная с позиций указателя 14-17 в строке).
Пока что все выглядит достаточно просто. Однако в случае изменения структуры
объекта дело может несколько усложниться. Например, что если мы захотим рас-
ширить требуемый временной период, чтобы он охватывал часы с 7 до 10 утра? В
его текущем состоянии регулярное выражение не справится с этой задачей, по-
скольку в нем указывается, что первым символом после двоеточия должен быть 0.
Его можно попробовать исправить, просто добавив к диапазону цифр цифры 1 и 0,
как показано ниже:
р1и8_1:еп = ге.сопр1Ле("\8[01][0789]:")
рг1п1:("р1и5_1:еп")
ргШ(р1и5_1:еп.5еагсЬ( "2020-09-01 18:09:11.0980"))
Это даст следующий результат:
р1и5_1еп
<ге.Ма1:сЬ оЬдес!:; зрап=(10, 14), та1:сЬ=, 18: ’>
Как можно увидеть в выводе результатов, проблема заключается в том, что в наших
данных используется 24-часовый формат, в результате чего будет возвращаться
целый диапазон нежелательных совпадений. Это объясняется тем, что регулярные
выражения не "видят” числа таким образом, как их видят люди, для них это просто
последовательность символов. Вот поэтому число 18 возвращается как совпадение
— наше регулярное выражение допускает выбор любой строки, начинающейся с
цифры 0 или 1, за которой следует цифра 0, 7, 8 или 9. Тогда как мы, очевидно, соз-
давали это выражение для выбора подстрок 07, 08, 09 и 10, наш код позволяет осу-
ществить выбор и других подстрок.
Решением в данном случае будет использование символа конвейеризации
”или/или” (|), чтобы объединить отдельные регулярные выражения. Полученное
регулярное выражение будет выглядеть, как показано в листинге 7.3.
Листинг 7.3. Возвращение чисел от 7 по 10
5е7еп_1о_1:еп = ге.сопр1Ле("\80[7.9]: |\з10:")
Испытайте это регулярное выражение сами с несколькими образцами данных, что-
бы убедиться в том, что оно выбирает требуемые нам подстроки (и ничего больше).
Этим я и ограничу рассмотрение предмета регулярных выражений. Подобно мето-
дам веб-извлечение данных, которые рассматриваются в разд. "Извлечение веб-
данных: источник данных последней надежды" главы 5, каждое регулярное выра-
жение (или реализуемое им решение) уникально. Но я надеюсь, что вы сможете
оценить предоставляемую ими возможность для сопоставления с образцом, реали-
зация которого посредством одних составных условных операторов и основных
строковых функций была бы очень неуклюжей.
Создание дат
Одна из причин, по которой данные, подобные датам, удобно рассматривать как
строки, состоит в том (как можно было видеть в нашей работе с разными исходны-
ми форматами данных по уровню безработицы в главе 4\ что их можно интерпре-
тировать по-разному в разных источниках данных и даже разными библиотеками
РуШоп. Тем не менее в некоторых задачах очень полезно преобразовывать подоб-
ные датам данные в фактические данные типа с1а1е1гте. Например, выделить данные
по арендам велосипедов только в рабочие дни из всех данных системы СШ В1ке
можно было бы, применив подход ’Трубой силы”. Для этого нужно определить все
даты рабочих дней (например, посмотрев их в календаре), а затем создать гигант-
ский список условий или регулярное выражение, чтобы найти требуемые совпаде-
ния. Для данных аренд за сентябрь 2020 года такое регулярное выражение может
выглядеть наподобие показанного в листинге 7.4.
Листинг 7.4. Регулярное выражение для выбора аренд в рабочие дни сентября 2020 г.
8ер1:етЬег2020_неекс1ау = ге.сотр!1е("-0[123489]-1 -1[0145678]-1 -2[1234589]-1-30-")
Это выражение, безусловно, рабочее, но его почти невозможно читать. Кроме это-
го, это все такой же, по сути, один гигантский составной условный оператор, не-
смотря на то, что будучи регулярным выражением, он реализован меньшим коли-
чеством символов. Кроме этого, это решение не очень хорошо поддается масшта-
бированию. Например, чтобы расширить наш анализ на любой другой месяц, нам
снова пришлось бы определять требуемые даты вручную по календарю.
К счастью, в состав РуШоп входит объект с1а1е1гте, обладающий несколькими
встроенными методами, который может быть полезным для решения как раз такого
рода задач. Более того, его простой метод иеекс1ау() возвращает число от 0 до 6 (0
означает понедельник, а 6 — воскресенье, см. Ьйр8://(1ос8.руШоп.ог§/3/11Ьгагу/
(1а€ейте.Ы:т1#(1а€ейте.(1а€е.луеек(1ау), в зависимости от дня недели, на который
выпадает определенная дата. Это означает, что если содержимое нашего столбца
з^агШте преобразовать в дату, то посредством этого метода можно быстро опреде-
лить день недели, соответствующий любой дате. Подобный код приводится в лис-
тинге 7.5. Этот подход позволит нам использовать наш код с другими источниками
данных (например, с данными аренд для другого месяца или года), не требуя ника-
ких изменений первоначального кода.
Листинг 7.5. Сценарий \л/еекбаупбез.ру
# Цели: отфильтровать все аренды велосипедов системы СШ В1ке за рабочие дни сентября
# 2020 года и сохранить в новый файл.
# Краткое описание программы:
# 1. Считываем файл данных: 202009-с11:1Ь1ке-1:г1рба1:а.С87
# 2. Создаем новый выходной файл и записываем в него строку заголовка
# 3. Для каждой строки в файле создаем из строки з^агШте значение даты
# а. Если дата выпадает на рабочий день, записываем строку в выходной файл
# 4. Закрываем выходной файл
# Импортируем библиотеку сШтрог!: сз7
# Импортируем ба^еИте
1топ баШ1пе 1прог1: ба!е1гте
# Открываем файл данных в режиме чтения
зоигсе_ГПе = ореп("202009-сШЫке-1:г1рба1:а.С87","г")
# Открываем выходной файл в режиме записи
ои^риШПе = ореп("202009-сШЫке-меекбау-1:г1рба1:а.С87","м")
# Преобразовываем исходный файл в структуру ОШЯеабег; сохраняем результат
# в переменной сШЫке_геабег
сШЫке_геабег = С57.01сГРеабег(зоигсе_ГПе)
# Создаем соответствующую структуру 01с1:1л1г11:еб и задаем ее название полей
ои1ри1_нг1Лег = С87.01с1Ыг11ег(ои1ри1_Г1'1е, Г1е1бпатез=сП:1Ь1ке_геабег.Г1е1бпате5)
# Записываем строку заголовка в выходной файл
ои1ри1_нгтЛег. мгПеЬеа бег ()
# Обрабатываем в цикле ^ог...1п список строк сШЫке_геабег
1"ог а_гом 1п сШЫке_геабег:
# Преобразовываем значение столбца з^агШте в объект даты
1Не_ба1е = баГеНте.зГгрНте(а_гоп[ШагШте'], '%У-%п-%б )О
# Если дата 1Не_ба1е - рабочий день
И 1:Ье_ба1:е.меекбау() <=4:0
# Записываем эту строку данных в наш выходной файл
ои^ри^мгт^ег. мг11:егом(а_гом)
# Закрываем выходной файл
ои1ри1_ГПе. с!озе()
О Как упоминалось в листинге 6.1, предоставление формата наших исходных дан-
ных (Ъир8://(1ос8.ру1:йоп.ог§/3/11Ьгагу/(1а1:ейте.Ы:т1#81:гЙ1те-81:грйте-Ьейау1ог)
будет способствовать более быстрому и надежному исполнению нашего сценария.
О Метод меекс1ау() присваивает понедельнику позицию 0 (кйр8://с1ос8.руЙ1оп.ог§/3/
11Ьгагу/йа€ейте.й€т1#йа€ейте. йа^е.ууеекйау), поэтому поиск любых значений до 4
включительно выберет все значения с понедельника по пятницу включительно.
В зависимости от используемого устройства исполнение сценария в листинге 7.5
может занять некоторое время. Например, на моем, не очень мощном устройстве,
исполнение занимает свыше 85 секунд. А вот сценарий для решения этой же задачи
с использованием регулярного выражения из листинга 7.4 исполняется всего лишь
за 45 секунд. Регулярное выражение можно также легко настроить, чтобы пропус-
кать рабочие дни недели, которые являются выходными (например, День труда,
который празднуется в США и Канаде в первый понедельник сентября).
Так какой же из этих подходов лучше? Как обычно, это зависит от разных факто-
ров. Наилучший рабочий подход для решения вашей конкретной задачи по первич-
ной обработке, очистке или преобразованию данных будет зависеть от ваших кон-
кретных требований и имеющихся ресурсов. Если для ответа на ваш вопрос потре-
буется исследовать закономерности поездок в рабочие дни в данных системы СШ
В1ке за десятилетие, лучше использовать метод меекс1ау(), чтобы не изменять код, в
случае работы с разными месяцами или годами. С другой стороны, если не требу-
ется работать с большим количеством месяцев и вашим приоритетом является ско-
рость исполнения (и абсолютная точность), то предпочтительным может стать под-
ход с использованием регулярного выражения. Вы также можете обнаружить, что
работа с регулярными выражениями вызывает у вас желание рвать на себе волосы
или что необходимость использования нескольких этапов для получения идеаль-
ных результатов сводит вас с ума. Как рассматривается подробнее в главе 8, все эти
причины могут обосновывать выбор определенного подхода, однако этот выбор
непременно должен быть вашим собственным3.
Удаление хлама из файлов данных
В главе 4 нам пришлось несколько раз "очищать” наборы данных, которые в про-
тивном случае были бы непонятными или неудобными для работы. Например, в
процессе парсинга файла оМ-8с1юо1-81у1е.х18 в листинге 4.6 мы столкнулись с парой
3 Исключением может быть работа в команде. В таком случае необходимо учитывать мнение каждого
ее члена.
отдельных проблем. Первое — электронная таблица содержала как табличные дан-
ные, так и описательную информацию в заголовке. Эту информацию, несмотря на
ее, в принципе, полезность, придется переместить в другое место, чтобы иметь
возможность анализировать всю прочую информацию. Второе — формат .х1§ не
поддерживает ''настоящие” даты, означая, что после первоначального преобразова-
ния данных из формата .х1з в формат .С8У вместо ожидаемых дат создавался набор
бессмысленных чисел. Изначально мы отложили решение этих проблем на потом,
но сейчас настало время заняться ими.
Касательно первой проблемы, я хочу подчеркнуть, что мы определенно не хотим
просто "выбросить” информацию, хранящуюся изначально вверху файла Йес1§гар11.х18.
Как мы, я надеюсь, выяснили в результате нашей работы в главе 6, метаданные яв-
ляются ценным ресурсом, который никогда не следует выбрасывать из основного
источника данных (ййрз ://1ос.§оу/рго§гат8/€еасйег8/§еШп§-8€аг€е(1-уу1Й1-рг1тагу-
8оигсе8). В таких случаях я предпочитаю разделить один файл на два. Метаданные
извлекаются из файла и сохраняются в отдельном текстовом файле, которому при-
сваивается соответствующее название. А табличные данные сохраняются в файле
.С8У, чтобы их было легко анализировать.
Соглашение прежде чем конфигурация
Даже наиболее простые примеры в этой книге требуют принятия большого
количества решений, например, при именовании переменных, файлов сцена-
риев или вывода данных и т. п. Хотя концепт "соглашение прежде чем конфи-
гурация" может не совсем соответствовать принципам языка РуШоп
(ййр8://еп.лУ1к1реШа.ог§Лу1к1/Сопуепйоп_оуег_сопй§игайоп), я считаю, что
этот концепт может многое предложить в области сбора, создания и реоргани-
зации файлов в процессе выпаса данных. В частности, применяя общее согла-
шение по именованию для нашего файла метаданных и файла табличных дан-
ных, мы можем избежать проблем как при создании этих файлов, так и при
поиске их в будущем. Вам необязательно точно следовать предлагаемому
здесь образцу, но в долгосрочной перспективе, выбрав последовательный ме-
тод именования этих файлов во всех своих проектах, вы сэкономите себе вре-
мя и усилия, а также избежите напрасного беспокойства.
При просмотре нашего исходного файла .х18 в программе электронной таблицы
можно достаточно легко увидеть, где заканчиваются метаданные и начинаются
табличные данные. Но вопрос заключается в том, как определить этот переход в
сценарии. Как часто бывает в случае очистки данных, наиболее эффективное реше-
ние этой задачи не всегда будет опрятным. Метаданные заканчиваются там, где на-
чинаются табличные данные, — в строке, содержащей заголовки таблиц. Исследуя
первое значение каждой строки при обработке файла, можно прекратить запись в
файл метаданных и начать запись в файл данных .С8У, как только мы обнаружим
первый заголовок столбца. Поскольку в данном наборе данных первым заголовком
столбца является значение оЬзеп/аГГоП-баГе, мы выполним этот переход, как только
обнаружим это значение в начале текущей строки.
Прежде чем приступать к работе, внимательно исследуйте исходный
файл, чтобы найти в нем местонахождение метаданных. Метаданные мо-
гут находиться как до, так и после табличных данных, особенно в случаях,
когда данные содержат оценочные значения или другие спецификаторы.
В листинге 7.6 приводится сценарий для создания наших двух специальных файлов
из одного исходного файла. (Если некоторые фрагменты кода этого сценария вы-
зывают у вас вопросы, вы можете освежить свою память, исследовав сценарий в
листинге 4.6.)
Листинг 7.6. Сценарий х18_те(а_раг51пд.ру
# Преобразовываем табличные данные в файле .х!з в формат С87 и сохраняем их
# в соответствующем файле. Также отделяем метаданные и сохраняем их в отдельном файле.
# Используем для всего этого библиотеку хгТб.
# Сначала установим библиотеку х!гс1 (ЬГГрз://рур1.огд/ргодесГ/х1гс1/2.0.1/), используя
# установщик р!р
# Импортируем библиотеку хТгб
ГпрогГ хТгс!
# Импортируем библиотеку С87, чтобы создать выходной файл
ГпрогГ С57
# Передаем имя файла в качестве аргумента методу ореп_могкЬоок() библиотеки хТгс!
# Сохраняем результат в переменной зоигсе_могкЬоок
зоигсе_могкЬоок = х!гс1.ореп_могкЬоок("Ггес1дгарЬ.х15")
# Открываем и именуем простой файл для хранения метаданных
зоигсе_могкЬоок_пеГас1аГа = ореп( "Ггес!дгарЬ_теГас1аГа. ГхГ", V )О
# Рабочая книга .хТз может содержать несколько листов
Гог зЬееГ_папе 1п 5оигсе_могкЬоок.5ЬееГ_папе8():
# Создаем переменную, указывающую на текущий лист, передавая текущее значение
# переменной зЬееГ_пате метода зЬееГ_Ьу_папе
сиггепГ_8ЬееГ = 5оигсе_могкЬоок.8ЬееГ_Ьу_папе(5ЬееГ_папе)
# Создаем выходной файл х18_"+8ЬееГ_папе+".С87 для хранения текущего листа
оиГриГ_П1е = ореп("х18_"+8ЬееГ_папе+" .С87","м")
# Используем метод для записи мг1Гег() библиотеки С87 для записи строк данных
# в выходной файл оиГриГ_ГПе
оиГриГ_мг1Гег = сз7.мг1Гег(оиГриГ_Г11е)
# Создаем булеву переменную для определения достижения табличных данных
18_ТаЫе_баТа = ЕаТзеО
# Обрабатываем в цикле Тог каждую строку в листе
Тог гом_пип, гом 1п епипегаТе(сиггепТ_8ЬееТ.деТ_го1\/5()):
# Извлекаем значение первого столбца текущей строки
Т1гзТ_епТгу = сиггепТ_8ЬееТ. гом_7а1иез(гом_пип)[0]
# Если строка заголовков таблицы данных
II2 Т1гзТ_епТгу == 'оЬзеп/аТТоП-баТе':
# Присваиваем переменной флага 18_ТаЫе_с!аТа значение Тгие
т.2_СаЫе_с1а1а = Тгие
# Если значение переменной Т5_ТаЫе_с1аТа равно Тгие
1Т т.5_1аЫе_с1а1а:
# Записываем эту строку в наш выходной файлп данных
оиГриГ_шг1Гег.шг1Гегош(сиггепГ_5ЬееГ. гош_7а1_ие5(го\л/_пит))
# В противном случае эта строка должна быть метаданными
еТзе:
# Для опрятного форматирования файла метаданных обрабатываем отдельные ячейки
# каждой строки метаданных
Тог 1Тет 1п сиггепТ_зЬееТ.гом(гом_пип):
# Записываем значение ячейки
8оигсе_могкЬоок_пеТабаТа.мг1Те(1Теп.7а1ие)
# Отделяем его от значения следующей ячейки символом табуляции
8оигсе_могкЬоок_теТабаТа.мг1Ле('\Т1)
# В конце каждой строки метаданных добавляем символ новой строки
зоигсе_могкЬоок_пеТас1аТа. мг1Ле(1 \п1)
# На всякий случай закроем наши выходные файлы
оиТриТ_ТИе.с!озе()
8оигсе_могкЬоок_теТабаТа.с1озе()
О Здесь мы создаем только один файл метаданных, но при необходимости мы мог-
ли бы с легкостью поместить этот процесс в цикл Тог и создавать отдельный файл
метаданных для каждого рабочего листа.
О Данный тип булевой (Тгие/ЕаХзе — истина/ложь) переменной часто называется
логической, или флаговой переменной. Идея заключается в том, чтобы установить
эту переменную вне цикла, а затем менять ее значение на обратное, при определен-
ном событии. Таким образом мы избегаем двойной обработки в цикле всех данных.
В данном случае мы используем эту переменную, чтобы определить, когда прекра-
тить запись в файл метаданных и начать запись в файл данных.
Прежде чем переходить к работе с (все еще непонятными) датами в этом файле, я
хочу обратить ваше внимание на новый метод, представленный в листинге 7.6: ис-
пользование флаговой переменной. Этот термин обычно означает любую булеву
переменную, используемую для отслеживания происхождения определенного со-
бытия или выполнения условия, особенно внутри цикла. Например, в листинге 7.6
мы используем флаговую переменную 18_ЬаЫе_с1а1:а, чтобы определить нахождение
строки данных, обозначающей начало табличных данных. Поскольку в нашем цик-
ле Гог.Лп эта строка данных, по сути, ”забывается” после считывания новой строки,
эту переменную нужно создать до входа в цикл. Таким образом переменная
18_ЬаЫе_с1а1:а оказывается вне области видимости цикла. (Предмет области види-
мости рассматривается более подробно в главе 8.)
Декодирование дат Ехсе!
Мы больше не можем избегать вопроса дат в таблицах Ехсе1. Хотя, я надеюсь, вам
не придется часто сталкиваться с этой ситуаций, я решила рассмотреть ее здесь для
полноты материала, а также по причине того, что она иллюстрирует несколько раз-
личных путей развития кода при расширении его даже, казалось бы, незначитель-
ными возможностями. Обычно в таких случаях код становится более сложным и
менее читаемым. Например, в сценарии в листинге 7.7 нам нужно проверить, со-
держит ли переменная число или нет. Хотите верьте, хотите нет, но для этого нам
потребуется отдельная библиотека, которая соответственно называется питЪегз.
Хотя это довольно просто, в листинге 7.7 мы увидим, как для преобразования этих
значений дат требуется изменять наш подход для записи табличных данных в вы-
ходной файл.
Листинг 7.7. Сценарий х18_те(а_апс1_с1а(е_раг81пд.ру
# Преобразовываем табличные данные в файле .х1з в формат сз7 и сохраняем их
# в соответствующем файле. Также отделяем метаданные и сохраняем их в отдельном файле.
# Для работы с данными дат используем библиотеку хгТс!
# Сначала установим библиотеку хТгб (Ы:1:р5://рур1.огд/ргозес1:/х1гс1/2.0.1/), используя
# установщик р1р
# Импортируем библиотеку хТгб
1прог1: хТгб
# Импортируем библиотеку С87
1прог1: С87
# Нужен для проверки, является ли данное значение каким-либо типом числа
Тгоп пипЬегз ТпрогТ МитЬег
# Требуется для парсинга и форматирования наших дат ЕхсеТ
Тгоп с1а1е1гте ТтрогТ да^е-Ыте
# Передаем имя файла в качестве аргумента методу ореп_могкЬоок() библиотеки х!гс1.
# Сохраняем результат в переменной зоигсе_могкЬоок
зоигсе_могкЬоок = х!гс1.ореп_могкЬоок("Тгес1дгарЬ.х15")
# Открываем и именуем простой файл для хранения метаданных
зоигсе_могкЬоок_пеТас1аТа = ореп("Тгес1дгарЬ_теТас1аТа.ТхТ","м")
# ап '.хТз' могкЬоок сап Ьа7е тиТТТрТе зЬееТз
Тог 8ЬееТ_пате 1п 5оигсе_иогкЬоок.8ЬееТ_пате8():
# Создаем переменную, указывающую на текущий лист, передавая текущее значение
# переменной 5ЬееТ_пате метода 5ЬееТ_Ьу_пате
сиггепТ_8ЬееТ = 5оигсе_могкЬоок.5ЬееТ_Ьу_пате(5ЬееТ_пате)
# Создаем выходной файл хТ5_"+5ЬееТ_пате+".С57 для хранения текущего листа
оиТриТ_Т1Ле = ореп("хТ5_"+5ЬееТ_пате+"_с1аТе8.С57","м")
# Используем метод для записи мг1Тег() библиотеки С57 для записи строк данных
# в выходной файл оиТриТ_Т1Ле
оиТриТ_мг1Тег = с8У.мг1Тег(оиТриТ_Т1Те)
# Создаем булеву переменную для определения достижения табличных данных
1з_ТаЫе_с1аТа = ЕаТзе
# Обрабатываем в цикле Тог каждую строку в листе
Тог гом_пип, гом 1п епитегаТе(сиггепТ_8ЬееТ.деТ_гом5()):
# Извлекаем значение первого столбца текущей строки
Т1гзТ_епТгу = сиггепТ_5ЬееТ.гом_уаТие5(гом_пит)[0]
# Если строка заголовков таблицы данных
1Т Т1гзТ_епТгу == 'оЬзеп/аТТоП-баТе':
# Присваиваем переменной флага 1з_ТаЫе_с1аТа значение Тгие
1з_ТаЫе_с1аТа = Тгие
# Если значение переменной 1з_ТаЫе_с1аТа равно Тгие
1Т 1з_ТаЫе_с1аТа:
# Извлекает значения табличных данных в отдельные переменные
ТЬе_с1аТе_пит = сиггепТ_8ЬееТ. гом_уаТие8(гом_пит)[0]
116_7аТие = сиггепТ_8ЬееТ.гом_7аТие8(гом_пит)[1]
# Создаем новый объект строки, содержащий все значения
пем_гом = [ГЬе_с1аГе_пит, 116_7а1ие]
# Если ГЬе_баГе_пип содержит число, тогда текущая строка не является строкой
# заголовка. Нужно выполнить преобразование даты
1Г 181п5Гапсе(ГЬе_с1аГе_пит, МипЬег):
# Создаем объект РуГЬоп да1:е^хте, используя метод х!с1аГе_а8_с1аГеГ1те()
# библиотеки х!гб
ГЬе_с1аГе_пит = х1гб.х!баГе.х1баГе_а8^аГеГ1те(
ГЬе_баГе_пип, зоигсе^огкЬоок. баГетобе)®
# Записываем переформатированную дату, заменяя им первое значение в новой
# строке
пем_гом[0] = ГЬе^аГе^ит. зГгГНте('%т/%с!/%У' )О
# Записываем эту новую строку в наш выходной файл данных
ои^ри'Мл/гтЛег. 1л/г1Гегом( пем_гом)
# В противном случае эта строка должна быть метаданными
е!зе:
# Для опрятного форматирования файла метаданных обрабатываем отдельные ячейки
# каждой строки метаданных
Гог 1Гет 1п сиггепГ_зЬееГ.гом(гом_пип):
# Записываем значение ячейки
5оигсе_могкЬоок_пеГабаГа.мг1Ге(1Гет.7а1ие)
# Отделяем его от значения следующей ячейки символом табуляции
8оигсе_могкЬоок_теГабаГа.мг1Ге('\Г1)
# В конце каждой строки метаданных добавляем символ новой строки
зоигсе_могкЬоок_пеГас1аГа. мг1Ге(1 \п1)
# На всякий случай закроем наши выходные файлы
оиГриГ_Г1Ле. с!озе()
зоигсе_могкЬоок_теГабаГа.с1озе()
О При преобразовании значений дат формата х1§, используя метод
х!с1аГе_а8_с1аГеГ1те() библиотеки х1гб, для правильного создания объекта РуЙтоп
баГеГГпе требуется наличие как значения числа, так и значения с1аГетос1е
(ййр8://х1гскгеасИйес1ос8ло/еп/1а€е8€/ар1.й€т1#х1гскЬоок.Воок.с1а€етос1е) рабочей
книги4.
4 В системах Мас и РС используются разные базовые даты, поскольку... на это есть важные причины.
О Здесь я решила записывать даты в файл табличных данных в формате ММ/ДД/ГГГГ,
используя соответствующий метод з1г!Г1те(), но вы можете использовать любой
другой предпочитаемый вами формат.
Как мы увидели, библиотека хЫ делает процесс преобразования дат из странного
формата Ехсе1 в нечто более понимаемое сравнительно просто. Но код в листинге
7.7 демонстрирует, как особенности обработки конкретного набора данных могут
быстро повысить сложность (особенно в виде дополнительных вложенных услов-
ных операторов 1Г) изначально очень простой программы. Это лишь одна из при-
чин, по которой мы посвятим главу 8 исследованию стратегий и методов для эф-
фективной и рациональной оптимизации нашего кода: нам нужно быть уверенными
в том, что он делает все, что нам требуется, но также достаточно удобочитаем и
пригоден для повторного использования в будущем.
Создание настоящих данных С8У
из данных фиксированной ширины
У нас частично получилось преобразование наших данных в листинге 4.7, где мы
преобразовывали исходные данные фиксированной ширины в данные, разделенные
запятыми (формата с§у). Технически нам удалось записать в выходной файл значе-
ния, разделенные запятыми, однако этот результат был довольно разочаровываю-
щим: в данных сохранились многие артефакты из исходного файла, которые с лег-
костью могли бы препятствовать нашим усилиям по анализу данных в будущем.
К счастью, данная проблема, в частности наличие ведущих или концевых непеча-
таемых символов, хорошо известна, поскольку технологии, которые обычно созда-
ют ее, существуют уже длительное время. Поэтому эта проблема решается доволь-
но легко — с помощью встроенной функции РуЙюп з±г!р(), использование которой
демонстрируется в листинге 7.8.
Листинг 7.8. Сценарий ЛхесЦллсйЬ_8(пр_раг8тд.ру
# Пример чтения данных из файла данных фиксированной ширины средствами Ру1Ьоп.
# Исходный файл данных для этого примера предоставлен управлением МОДА и доступен
# здесь: Ы1рз: //ьмы1. псбс. поаа. до7/риЬ/ба 1а/дЬсп/с1 атЪу/дЬспс!- з1а11опз. 1x1
# Метаданные для файла доступны здесь:
# Ы1рз: //долм!. псбс. поаа. до7/риЬ/ба!а/дЬсп/ба11у/геабпе. 1x1
# Импортируем библиотеку С87, чтобы создать выходной файл
1прог1 С87
Шепапе = "дЬспб-з1а11опз"
# Для чтения обычного текстового файла не требуются никакие специальные библиотеке,
# поэтому просто открываем его в обычном режиме для чтения "г".
зоигсе_ГПе = ореп(Н1епате+".1х1", "г")
# Встроенный метод геасПлпезО делает то, что говорит его название:
# считывает содержимое входного текстового файла и преобразовывает его в список строк
8ГаГ1оп8_1Л^ = 8оигсе_Г1Ле. геасПлпезО
# Создаем выходной файл для преобразованных данных
оиГриГ_Г1Ле = ореп(ГИепате+".с57","м")
# Используем метод для записи иг1Гег() библиотеки С87 для записи строк данных
# в выходной файл оиГриГ-Ше
оиГриГ_иг1Гег = С57.мг1Гег(оиГриГ_ПТе)
# Создаем список заголовков
Ьеабегз = [ "Ю", "1_АТ1Т1ЮЕ"," 1_0МС1Т1ЮЕ"," Е1_Е7АТ10М", "5ТАТЕ", "МАМЕ", "С5М_Е1_АС",
"НСМСКМ_Е1_АС", "1л1М0_Ю"]
# Записываем заголовки в выходной файл
оиГриГ_шг1Гег.иг1Гегом(Ьеас1ег5)
# Обрабатываем каждую строку в нашем файле (множественные листы невозможны)
Гог Ппе 1п 8ГаПоп5_И8Г:
# Создаем пустой список, в который будем добавлять каждый набор символов,
# составляющий "столбец" данных
пем_гом = []
# Ю: позиции 1-11
пем_гом. аррепс1( (1Лпе[0:11]). зГг1р() )О
# ШИРОТА: позиции 13-20
пем_гом. аррепс1( (Ппе [12:20]). зГг1р())
# ДОЛГОТА: позиции 22-30
пем_гом. аррепс1( (Ппе [21:30]). зГг1р())
# ВОЗВЫШЕНИЕ: позиции 32-37
пем_гом. аррепс1( (Ппе [31:37]). зГг1р())
# ШТАТ: позиции 39-40
пем_гом. аррепб ((Ппе [38:40]). зГг1р())
# НАЗВАНИЕ: позиции 42-71
пем_гом. аррепс1( (1Лпе[41:71]). зГг1р())
# С5М_Е1_АС: розГГГопз 73-75
пем_гом. аррепс1( (Ппе [72:75]). зГг1р())
# НСМСРМ_Е1_АС: розШопз 77-79
пем_гом. аррепс1( (1Лпе[76:79]). зГг1р())
# 1л1М0_Ю: розШопз 81-85
пем_гом. аррепс!( (Нпе[80:85]). г1р ())
# Теперь осталось только записать пем_гом в выходной файл, используя метод
# мг11:егом()
ои1ри1_нг!1ег. мг11:егом(пем_гом)
# На всякий случай закроем только что созданный выходной файл .С87
ои1ри1_ТгТе.сТозе()
О Если сравнить этот код с кодом в листинге 4.7, мы увидим, что они идентичны,
за исключением метода 81гТр(), применяемого в текущем сценарии к каждой стро-
ке перед тем, как она добавляется к строке данных.
Как мы видим, модифицировать исходный код для решения проблемы форматиро-
вания или ”очистки” данных не всегда сложно, но получившийся в результате сце-
нарий все же не совсем элегантный. Удалить пробелы из выходных данных при
помощи метода з±г!р() просто, но в процессе нам нужно было добавить в код
большое количество скобок, в результате чего наш код получился намного менее
удобочитаемым, чем нам бы хотелось.
Это иллюстрирует еще одно сходство создания хорошего, качественного кода Ру-
Шоп с типичным процессом создания литературных сочинений. Наши усилия в гла-
ве 4, где мы решаем выкоуровневую задачу преобразования исходного формата
данных, по крайней мере, в требуемую нам табличную структуру, можно рассмат-
ривать как своего рода наметку конечной программы. Таким образом, это предос-
тавляет нам возможность возвратиться к ней позже и откорректировать эту про-
грамму, добавив к ней возможности, позволяющие более точно обрабатывать те-
кущий набор данных.
В главе 8 мы перейдем к следующему этапу процесса редактирования. В частности,
мы займемся дальнейшим усовершенствованием этих программ (которые уже вы-
полняют все, что от них требуется), чтобы они были более краткими и понимаемы-
ми, точно так же, как мы могли бы, отредактировав, усовершенствовать свою пись-
менную работу. Такой итеративный (пошаговый) подход к программированию оз-
начает, что в конечном итоге мы получим лучший и более полезный код, а также
разобьем решение больших и сложных задач программирования в последователь-
ность менее трудных задач. Не менее важно и то, что независимо от текущего этапа
процесса, у нас есть работающая программа, к которой мы можем прибегнуть в
случае необходимости. Такой пошаговый подход особенно полезен при работе с
более сложными задачами по очистке данных, подобных той, которую мы рассмот-
рим следующей: исправление непреднамеренных разных вариантов написания од-
ного и того же слова.
Исправление разнообразности написаний
В главе 6 мы применили метод ”отпечатков пальцев” для решения проблемы воз-
можного наличия разных вариантов написаний названий компаний в данных про-
граммы РРР. Это распространенная проблема во всех наборах данных, полагаю-
щихся на ручной ввод данных. Код в листинге 6.11 дал только приблизительную
оценку действительно однозначных записей в столбце Ог1д1па11пд1_егк1ег, подсчитав,
сколько из этих записей имели однозначный "отпечаток пальца”. Мы обнаружили,
что наш набор данных содержит 4 337 однозначных названий банков, но только 4
242 однозначных ”отпечатков пальцев”. Это указывает на то, что около 95 банков в
действительности могут быть одними и теми же, но использовать разные варианты
названий, поскольку их названия создали одинаковые ”отпечатки пальцев”.
Поскольку эти 95 возможных разных написаний могли затронуть тысячи строк
данных, нам требуется какой-либо способ для преобразования нашего набора дан-
ных таким образом, чтобы мы могли с уверенностью агрегировать (объединить) его
по кредитодателю. В то же самое время мы также хотим избежать избыточного
корректирования, сгруппировав вместе записи, которые в действительности не от-
носятся друг к другу.
Это тот случай, для которого полезно преобразование данных: мы не хотим риско-
вать потерей каких бы то ни было исходных данных (сохранение их необходимо
для проверки достоверности и выборочной проверки данных), но нам также необ-
ходимо преобразовать их, чтобы иметь возможность анализировать их в будущем.
Вследствие большого размера нашего набора данных, группирование и фильтрация
его данных для удовлетворения наших требований, скорее всего, займут много
времени. Поэтому нам нужно сохранить результаты этой работы, фактически доба-
вив в набор данных новые столбцы. Это позволит нам сохранить как наши исход-
ные данные, так и полезные результаты процесса преобразования, в одном файле.
К счастью, у нас есть несколько готовых библиотек, которые значительно упро-
стят этот процесс. Поскольку мы уже знаем, как агрегировать названия, используя
процесс ”отпечатков пальцев” из листинга 6.11, более сложной задачей будет оп-
ределить, когда банки с одинаковыми ”отпечатками пальцев” в действительности
следует рассматривать как разные организации. Рассмотрим для примера вывод
из листинга 6.7, который для удобства воспроизводится в листинге 7.9. Можно
увидеть, что лишь небольшое количество полей содержит информацию о перво-
начальном кредитодателе (оп§та!т§ 1епбег). Поэтому наилучшим способом оп-
ределить, являются ли два банка со всеми одинаковыми словами в их названиях
(и поэтому имеющие одинаковые ”отпечатки пальцев”, например Г1г$1 Вапк Техаз
и Техаз РсШ Вапк) одним и тем же банком, можно будет по их значению
0г1д1паГ1пдЬепс1ег1осаГ1.оп10.
Листинг 7.9. Таблица выборочных данных в транспонированном виде
ЬоапМипЬег 9547507704
Оа1:еАррго7ес1 05/01/2020
5ВА0Н1сеСос1е 464
Ргосе881пдМе1±ос1 РРР
ВоггомегМате 811МТЕР СОАТ1ИС8, 1МС.
ВоггомегАс1с1ге88 2410 НтдЬмау 15 5ои1±
ВоггошегСтЛу 5ип1:ег
Воггомег51:а1:е <МА>
ВоггоиегПр 29150-9662
Ьоап51:а1:и80а1:е 12/18/2020
1_оап51:а1:и8 Ра1с1 1п ЕиИ
Тегп 24
5ВАСиагап1:уРегсеп1:аде 100
1п11:1а1Аррго7а1Атоип1: 769358.78
Сиггеп1Аррго7а1Атоип1: 769358.78
11пс115Ьиг5ес1Атоип1: 0
ЕгапсЫзеМапе <МА>
5е г 71с1 пд 1_епс1е г 1_оса 1Лоп 10 19248
5еп/1с1пд1_епс1егМате 8упо7из Вапк
5е г 71с1пд 1_е п с!е г Ас1с1 гез 8 1148 ВгоасЫау
5е г 71с1 пд 1_епс1е г С 1±у С0ШМВ05
5е г 71с1пд 1_епс1е г81а!е СА
5е г 71с1пд 1_е п с!е г21р 31901-2429
Рига111гЬап1пс11са1:ог и
НиЬгопеТпЛса^ог И
1_МПпс11са1:ог <МА>
Виз1пе88Аде0е8Сг1р1:1оп Ех1з1:1пд ог тоге ±Ьап 2 уеагз о!с1
Ргодес^СтЛу 5ит1:ег
Ргодес^Соип^уМапе 511МТЕР
Ргооес181а1е 5С
Ргодес1:21р 29150-9662
СО 5С-05
ЗоЬзРерог1:ес1 62
МА1С5Сос1е 325510
РасеЕ1±п1сП:у ипап5мегес1
иТ1ЫТ1Е8_РРОСЕЕО <МА>
РАУР0Ы_РР0СЕЕ0 769358.78
МОРТСАСЕ_1МТЕРЕ5Т_РРОСЕЕО <МА>
РЕИТ_РРОСЕЕО <МА>
РЕЕ1МАМСЕ_ЕЮ1__РРОСЕЕО <МА>
НЕА1_ТН_САРЕ_РРОСЕЕО <МА>
ОЕВТ_1МТЕРЕ5Т_РРОСЕЕО <МА>
ВизтпеззТуре Согрога1Лоп
0г1д1па1Лпд1еп^ег1_оса1:1оп10 19248
0г1д1па1:1пд1_епс1ег 5упо7из Вапк
0г1дтпа±тпд1_епс1егСт1:у сошмвиз
0г1д1па1Лпд1_епс1ег$1:а1:е СА
Сепбег ипапзмегес!
\/е1:егап ипап5мегес1
МопРго-ри <МА>
Прежде чем идти дальше, нам нужно разобраться, что в действительности означает
значение 0г1д1па1Лпд1епбег1оса1:1оп10. Поиск в Интернете по фразе ”оп§тайп§ 1епбег
1осайоп кГ первым результатом выдает еще один документ на веб-сайте Управ-
ления по делам малого бизнеса (Ьйр8://8Ьа.§оу/8Йе8/(1еГаи11:/й1е8/агйс1е8/
ЕТгап_Ог1§шайоп_01_2014.рДГ). Поиск в этом РВЕ-документе по слову ”1осайоп”
приводит нас на страницу, показанную на рис. 7.2, где видно, что значение
’Тосайоп ГО” обозначает главное отделение данного банка и должно быть одинако-
вым для разных отделений одного и того же банка.
8(ер 1 - Рериез! 61_8 11зег Ю/Разз\л/огс1
Арр1у оп-Ппе а11Шр8.//е\л/еЬ.8Ьа.доу/д1з
”Пр31
• Озег 10 - таке ир уоиг о\мл плиз! Ье Ье&лгеел 8 алЬ 15 сНагас1егз 1олд
» Сотаа тго - з(ал мй 21р србе апй зе!ес1 "Цюкир 2ф'_
Ьосайоп 1Р - Ш1з ууШ Ье 1ог (Не тат Ьалк 1осайоп пос Ьу ЬгапсЬ ]
• Ьодтз зЬои1д Г^дт Ье зЬагеЬ; еасЬ изег зЬоиИ зе( ир те(г омт йзег 10 &
Разного
Опсе уои Ьауе а изег Ю апс! Раззууогс!, гедиезс Ассезз ...
«мм/.5Ьа.домЛог-1епйегз
Рис. 7.2. Информация о значении 'Ъосайоп Ю" кредитодателя
Имея эту дополнительную информацию, мы можем создать свою версию данных
по кредитам по программе РРР, которая будет содержать новый столбец Ог1д1па1:1п-
д1_епс!егЕ1пдегрг1пС значением которого будут значения ”отпечатка пальцев” Ог1д1-
паС1пд1_епс1ег и 0г1д1па1:1пд1епс1ег1оса1:1оп10. Соответствующий код приводится в лис-
тинге 7.10. Позже мы сможем использовать это значение для того, чтобы быстро
агрегировать наши данные по первоначальному кредитодателю, при этом будучи
(достаточно) уверенными в том, что мы ни пропускаем совпадающих записей по
причине орфографических ошибок в названии, ни рассматриваем два отдельных
банка как один и тот же.
Листинг 7.10. Сценарий ррр_ас1сИтдегрпп(8.ру
# Простой сценарий для добавления столбца "отпечатков пальцев" к нашим данным
# по кредитам, который поможет нам подтвердить и/или исправить возможные
# орфографические ошибки или разные варианты написаний, например, названий банков
# Импортируем библиотеку сзу
1прог1: сзу
# Импортируем библиотеку ТТпдегргТпТз
ТпрогТ Т1пдегрп.п12
# Считываем последнюю выборку данных в переменную
ррр_с1аТа = ореп(' риЫ1с_150к_р1и$_гесеп1:.С57',' г')
# Выполняем предварительную обработку данных при помощи функции ОТсТРеабег
ррр_баТа_геас1ег = с87.01сТКеабег(ррр_с1аТа)
# Создаем выходной файл для записи модифицированного набора данных
аидпепТес1_ррр_с1аТа = ореп('риЬИс^БОк-рХиз^Чпдегргт.п^з.с^1, 'м')
# Создаем объект для записи данных мгТТег, чтобы можно было выводить сразу всю строку
аидтепТеб_с1аТа_мг1Тег = с5У.мг1Тег(аидтепТес1_ррр_с1аТа)
# Поскольку добавляется столбец, нужно также создать новую строку заголовка
Ьеабег_гом = []
# Для заголовка каждого столбца
Тог ТТеп 1п ррр_с1аТа_геас1ег.Т1е1с1пате8:О
# Добавляем существующий заголовок столбца
Ьеас1ег_гом.аррепс1(1Тет)
# Если столбец Ог1д1паТ1пд1_епс1ег
II2 ТТеп == ,Ог^д^паТ^пд^епс^ег,:
# Нужно добавить новый столбец
Ьеас1ег_гом.аррепс1( ,Ог1_дТпаТТпд1_епс1егР1.пдегргТпТ')
# Теперь можно записать расширенную строку заголовка в выходной файл
аидпепТеб_с1аТа_мг1Тег. мг1Тегом( Ьеабег_гом)
# Обрабатываем в цикле каждую строку данных
Тог гом 1п ррр_баТа_геабег:
# Создаем пустой лист для хранения новой строки данных
пем_гом = []О
# Обрабатываем в цикле Тог каждый столбец данных исходного набора данных
Тог со1ипп_папе 1п ррр_с1аТа_геас1ег.Т1е1с1пате8:
# Сначала добавляем значение этой строки для данного столбца
пем_гом.аррепс1(гом[со1итп_пате])
# Если название столбца Ог1д1паТ1пд1_епс1ес1, добавляем наше новое значение
# ТТпдегргТпТ
ТТ соТитп_папе == 'Ог^дТпаТТлдЬепбег':
# Наше значение ГГпдегргГпГ будет состоять из сгенерированного
# "отпечатка пальцев" ПЛЮС значения столбца Ог1дГпаГГпд1епбег1_осаГГоп18
ГЬе_ГГпдегргГпГ = ГГпдегргГпГ8.депега1е(гом[соГитп_пате]) + \
" " + гом[ 'ОгГдГпаГГпдЬепбегЬосаГГопТО' ]
# Добавляем составное значение "отпечатка пальцев" к нашей строке
пеи_гои.аррепб(1±е_Г1пдегрг1п1:)
# Завершив составление строки, записываем ее в выходной файл
аидпепГес!_с1аГа_мг1Гег.мг1Гегом(пем_гом)
# Закрываем оба файла
аидтепГеб_ррр_с1аГа. с!озе()
ррр_с1аГа.сГо5е()
О Хотя это может выглядеть излишним, первый цикл Гог используется только для
создания нашей строки заголовков. Как всегда, мы хотим избежать внедрения ор-
фографических ошибок всегда и всюду, где это возможно, поэтому в данном случае
дополнительный цикл оправдывает себя (это намного лучше, чем вводить значения
этого списка вручную).
О Поскольку мы добавляем столбец данных, нам нужно поэлементно создавать
новую строку данных в виде списка, точно так же, как мы создали заголовочную
строку.
Структура получившегося файла будет такой же, как и исходного файла, за исклю-
чением добавления в него нового столбца ОгГдГпаГГпд1_епс1егЕ1пдегргГпГ между столб-
цами ОгГдГпаГГпд1_епс1ег и ОгГдГпаГГпдЬепбегСГГу исходного файла. Соответствующий
результат показан в листинге 7.11.
Листинг 7.11. Данные программы РРР с добавленным столбцом "отпечатков пальцев"
ЬоапМипЬег 9547507704
ОаГеАррго7ес1 05/01/2020
5ВАОГГГсеСос1е 464
РгосеззГпдМеГЬос! РРР
ВоггомегМапе 50МТЕР СОАТ1МС5, 1МС.
ВоггомегАбс1ге55 2410 НГдЬмау 15 5оиГЬ
ВоггомегСГГу ЗипГег
ВоггомегЗГаГе МаМ
ВоггошегПр 29150-9662
ЬоапЗГаГизОаГе 12/18/2020
ЬоапЗГаГиз РаГс! Гп ЕиГГ
Тегп 24
ЗВАСиагапГуРегсепГаде 100
ТпГПаГАрргоуаГАтоипГ 769358.78
Сиггеп1:Аррго7а1Атоип1: 769358.78
ОпсйзЬигзе^Атоип!: 0.0
ЕгапсЫзеМапе МаМ
8е г 7тс1 пд 1_е п с! е г 1_оса Ноп 10 19248
8е г 71с1пд 1_е п с1е гМате 8упсл/из Вапк
8е г 71с1пд 1_е п с!е г Ас1с1 гез 8 1148 ВгоасЫау
8е г 7тс1 пд 1_е п с! е г С Ну соьимвиз
8е г 71с1пд 1_е п с1е г81а!е СА
8е г утстпд 1_е п с!е г21р 31901-2429
РигаИ1гЬап1пс11са1:ог и
НиЬгопеТпсйса^ог И
1_МПпс11са1:ог МаМ
Визт-пеззАдеОезсгтрНоп ЕхтзНпд ог тоге ±Кап 2 уеагз о!с!
РгодесКНу 8ит1:ег
Ргодес1:Соип1:уМате 80МТЕР
Ргооес181а1е 8С
Ргодес1:21р 29150-9662
СО 8С-05
ЗоЬзРерог1:ес1 62.0
МА1С8Сос1е 325510.0
РасеЕ1±п1сиу ипап5мегес1
иТ1ЫТ1Е8_РРОСЕЕО МаМ
РАУРО1_1__РРОСЕЕО 769358.78
МОРТСАСЕ_1МТЕРЕ8Т_РРОСЕЕО МаМ
РЕМТ_РРОСЕЕО МаИ
РЕЕ1МАМСЕ_ЕЮ1__РРОСЕЕО мМаМ
НЕА1_ТН_САРЕ_РРОСЕЕО мМаМ
ОЕВТ_1МТЕРЕ8Т_РРОСЕЕО МаМ
ВизтпеззТуре СогрогаНоп
ОгтдтпаНпдЬеп^егЬосаНопЮ 19248
Ог1д1па1:1пд1_епс1ег 8упо7из Вапк
Ог1д1паНпд1епс1егЕ1пдегрг1п1: Ьапк зупоуиз 19248
Ог1дтпа±тпд1_епс1егСт1:у сошмвиз
Ог1д1па1л.пд1_епс1ег81:а1:е СА
Сепс1ег ипап5мегес1
\/е1:егап 11пап5мегес1
МопРго’РН МаМ
Кроме того, что это преобразование поможет нам с легкостью агрегировать наши
данные по конкретному первоначальному кредитодателю (оп§таНп§ 1епс1ег), мы
также сможем продублировать это и с обслуживающим кредитодателем (зепчст^
1епс1ег). Можно даже написать сценарий, сравнивающий значения этих двух полу-
ченных ”отпечатков пальцев”, чтобы создать столбец (флаг), указывающий, кредит
выдал и обслуживает один банк или разные.
Тернистый путь к "простым" решениям
Я надеюсь, что у вас не возникло никаких трудностей с выполнением сценария в
листинге 7.10. Но я хочу уведомить вас, что это было не первое решение, которое я
попыталась применить. Более того, это было даже не второе и не третье решение.
В действительности я провела около дюжины часов, думая, кодируя, обрабатывая
данные и терпя неудачу, прежде чем я, наконец, осознала, что мой конечный под-
ход к решению этой задачи был наиболее быстрым, простым и эффективным спо-
собом найти баланс между тем, чтобы кредиты одного банка были сгруппированы
вместе и при этом случайно не объединить две разные организации.
Я предоставлю здесь описание этого процесса, поскольку (как я надеюсь, становит-
ся понятным) выпас данных (и программирование в целом) заключается не столько
в написании кода, сколько в логическом мышлении и решении проблем. Это озна-
чает, что обдумывание поставленной задачи, пробование разных решений и, воз-
можно, самое главное, готовность изменить курс, даже когда понимаешь, что это
выбрасывание ”на ветер” большого объема работы, являются намного более важ-
ными факторами в выпасе данных, чем способность написать больше чем две стро-
ки кода РуШоп по памяти5. Поэтому, с целью проиллюстрировать, что влечет за со-
бой лишь один из этих типов усилий по решению проблем, я предоставлю (сравни-
тельно) краткий обзор разных предпринятых мною подходов, прежде чем я
остановилась на решении, изложенном в разд. "Исправление разнообразности на-
писаний" этой главы.
Я начала с минимальной адаптации сценария из листинга 6.11 для создания новой
строки, содержащей только ”отпечатки пальцев”, и нового файла С8У, в который
добавлялась эта строка. Но я быстро осознала, что существует большая вероят-
ность, что некоторые банки со схожими названиями могут иметь одинаковые ”от-
печатки пальцев”. Поэтому я написала сценарий, который выполнял следующее:
1. Создавал список уникальных ”отпечатков пальцев”.
2. Для каждого уникального "отпечатка пальцев” создавал новый список (фактиче-
ски, структуру библиотеки рапбаз 0а1:аЕгате) из всех уникальных значений Ог1д1-
па1=1пд1епбег1оса1:1оп10.
3. При наличии нескольких разных значений 0г1д1па1:1пд1епс1ег1оса1:1оп10 столбец
"отпечатков пальцев" обновлялся, чтобы включать в него значение Ог1д1па1:1п-
д1_епс!ег1_оса1:1оп10, подобно тому, как мы это делали для всех записей в сценарии
в листинге 7.10.
Но даже создание этого сценария было намного более сложным, чем можно было
бы подумать по только что изложенному общему обзору. Конечно, первый шаг был
легким — мы уже практически его осуществили. Но при попытке обработки этого
нового файла средствами библиотеки рапбаз оказалось, что мой жалкий маленький
5 Поверьте мне, большинство профессиональных программистов каждые пять минут ищут в Интер-
нете информацию по разным аспектам, а не хранят информацию в памяти.
планшет СИгошеЪоок не имел достаточного объема памяти, поэтому пришлось пе-
ренести мою работу на платформу Ооо§1е Со1аЪ. Здесь у меня было больше памяти
для работы, но при каждом отвлечении от работы на более чем несколько минут
мне приходилось выполнять повторную аутентификацию и снова загружать данные
с моего файла Сгоо§1е Впуе, что каждый раз занимало дополнительное время. Кро-
ме этого, хотя я была довольно уверена в том, что я разобралась с тем, как правиль-
но обновлять значения в структуре Оа^аЕгапе, были проблемы с поиском нового
”отпечатка пальцев”, в наличии которого я была полностью уверена: иногда я нахо-
дила совпадения, а иногда получала пустую структуру 0а1:аЕгате! Вдобавок, испол-
нение шага 3 занимало три или больше минут, так что можно представить, сколько
часов (и разочарования) я потратила на то, чтобы убедиться в том, что мой код дей-
ствительно работает должным образом.
Конечно же, когда я создала (и проверила) код для этого трехшагового решения, я
осознала, что полученный результат не очень-то и отличался от начального. Более
того, он был немного менее удовлетворительным, поскольку новый формат моего
нового столбца Ог1д1па1:1пд1епбегЕ1пдегрг1п1: был непоследовательным: в одни ячей-
ки этого столбца значение 0г1д1па1=1пд1еп^ег1оса1:1оп10 добавлялось, а в другие нет.
Но поскольку фактическое значение ”отпечатка пальцев” не имело значения (толь-
ко чтобы его можно было использовать для точного агрегирования и различения
банков), зачем я усложняла себе задачу, добавляя значение ЬосаН-опЮ только к тем
из них, для которых было несколько записей? Нельзя ли было просто добавлять
значение ЬосаН-опЮ к ним всем?
Ну, конечно же, только на этом этапе я удосужилась прочитать документацию, по-
казанную на рис. 7.2, которая подтвердила, что добавление значения 1_оса1=1оп10 не
должно нарушить целостности ”отпечатков пальцев”, которые должны быть одина-
ковыми6. Вот так я и прошла весь круг: вместо того, чтобы присваивать потенци-
ально совпадающие "отпечатки пальцев”, а затем пытаться устранить проблемные
значения посредством неуклюжего и трудоемкого поиска, наилучшим решением
было сделать значение 0г1д1па1=1пд1еп^ег1оса1:1оп10 частью нового столбца ”отпечат-
ков пальцев” с самого начала.
Я потратила множество часов на то, чтобы "исправить” исходные ”отпечатки паль-
цев”, в процессе преодолевая ограниченные возможности своего устройства, капри-
зы платформы Ооо§1е Со1аЪ и утомительность внесения небольших модификаций в
сценарий с последующим многоминутным ожиданием завершения его исполнения.
Поэтому я не буду притворяться, что я не ощутила некоего разочарования, осознав,
что наилучшее решение в действительности состояло лишь в незначительной кор-
ректировке моего исходного сценария (хотя и не того, с которого я начала работу).
Но если выделить что-то одно, чему я научилась после всех этих лет выпаса дан-
ных, так это то, что умение прекратить дальнейшую работу и начать ее сначала яв-
6 Поначалу я беспокоилась, что столбец 0г1д1па1Лпд1епбег1_оса1:'1оп10 может обозначать, например,
отдельные банковские отделения.
ляется одним из важнейших навыков. Иногда нужно просто найти в себе силы и
отказаться от набора данных, даже если вы потратили на его исследование, оценку
и очистку много времени. Так же как иногда нужно отказаться от определенного
программного подхода, даже если вы потратили многие часы на чтение документа-
ции и экспериментирование с новыми методами, чтобы получить требуемые ре-
зультаты. Поскольку в итоге цель заключается не в том, чтобы использовать какой-
либо конкретный набор данных или библиотеку или программный метод. Цель со-
стоит в использовании данных, чтобы понять что-либо об окружающем нас мире.
И если вы можете сконцентрироваться именно на этом, то будет намного легче от-
казаться от какого-либо неудачного аспекта разрабатываемого решения, когда в
этом возникнет необходимость.
Вам также, вероятно, будет легче принять этот процесс — будь то отказ от набора
данных или сценария решения, на разработку которого вы потратили много време-
ни — когда вы начнете испытывать на собственном опыте, что, даже отказавшись
от чего-либо, вы научились чему-то ценному. Например, прежде чем отклониться
на "исправление” моих первоначальных, исключительно текстовых, "отпечатков
пальцев", я, по сути, не знала, как обновлять значения в структуре библиотеки рап-
баз 0а1:аЕгате. Теперь же я знаю, как делать это (я действительно знаю). Я также уз-
нала кое-что о сильных сторонах платформы (Зоо§1е Со1аЪ и ее особенностях, а
также освежила знания о некоторых ключевых "подводных камнях" при работе с
разнообразными наборами данных (этот момент рассматривается более подробно в
следующем разделе).
То же самое относится и к наборам данных, которые могут оказаться непригодны-
ми для получения ответа на определенный вопрос. Лишь то, что они не подходят
для вашего текущего проекта, не означает, что они не сгодятся для другого. Но не-
зависимо от того, увидите ли вы их когда-либо снова или нет, работая с этими на-
борами данных, вы многое узнаете: о предмете данных, о подводных камнях и воз-
можностях определенных типов данных, об экспертах в данной области и о многом
другом. Иными словами, отказ от набора данных или подхода к кодированию нико-
гда не является "напрасной тратой времени": приобретенный в процессе соответст-
вующей работы опыт только улучшит вашу следующую попытку.
Опасные подводные камни
Одна из причин, по которой важно документировать свою работу, заключается в
том, что очень часто человеком, для которого вы составляете эту документацию, в
действительности являетесь просто "будущий вы", возвращающийся к определен-
ному набору данных или сценарию (или вообще платформе РуЙюп) после перерыва
в несколько дней, недель или даже месяцев. К тому времени вещи, которые однаж-
ды были очевидными, будут казаться запутанными и неясными, если только вы
всесторонне не задокументировали их. Можно не разобраться даже с обычными
"уроками", особенно в спешке или когда вы сосредоточены на чем-либо другом.
Я сама это испытала, когда я прорабатывала упражнения в нескольких последних
главах, и это при том, что я прилагала особые усилия при проверке своей работы.
Для меня этот опыт стал еще одним напоминанием того, что если с вашим сценари-
ем что-либо не так, то это совершенно не обязательно что-то сложное, а скорее на-
оборот — что-то очень простое.
Далее приводятся два распространенных ”подводных камня”, которые следует
иметь в виду.
Проверьте используемый регистр
При каждой проверке, совпадают ли две строки, не забывайте о важности ис-
пользуемого регистра! При работе над сценарием в листинге 6.16 я поначалу не
обратила внимания на то, что названия всех предприятий (но не названия бан-
ков) были написаны заглавными буквами. Несколько минут я была в фрустра-
ции, думая, что мой набор данных не содержит записи для значения НАТЕРЕОРО
РЕСЕРТЮМ5, пока я наконец не посмотрела на данные опять и не осознала свою
ошибку.
Всегда указывайте тип данных
В процессе разработки решения для сценария в листинге 7.10, который описан в
разд. "Тернистый путь к «простым» решениям" ранее в этой главе, у меня
опять возникли проблемы с нахождением совпадений для значений, которые, я
была уверена, должны были содержаться в моем наборе данных. Но я забыла,
что библиотека рапбаз (в отличие от библиотеки сзу) пытается применять типы
данных к столбцам данных, считываемых в ее структуру 0а1:аЕгате. В данном
случае это означало, что значение столбца 0г1д1па1Лпд1еп^ег1оса1:1оп10 стало чис-
лом (а не строкой). В результате мои усилия по поиску определенных значений в
этом столбце терпели неудачу, поскольку я пыталась сравнить, например, число
71453 со строкой ”71453”, что определенно было обречено на провал.
В данном случае наиболее простым решением было добавление параметра в вы-
зове функции геас1_с57(), указывающего, что все данные должны считываться
как строки (например, Г1пдегргтп1:ес]_с1а1:а1 = рс!. геас1_с87(' риЬ11с_150к_р1и5_Г1п
дегргтп^з.сзу', сй:уре=' з!:г1.пд' ))7. Это также предотвратило преобразование не-
которых больших сумм в данных в экспоненциальное представление (например,
числа 1210681 в 1,21068е+0б).
После простых орфографических ошибок этот тип ”подводных камней” является
наиболее распространенным типом проблем, с которыми вам придется столкнуться
в процессе выпаса данных. Поэтому, если вы обнаружите, что в какой-то момент
вы допустили подобную оплошность, постарайтесь не слишком расстраиваться.
В действительности это просто признак того, что вы обладаете хорошей программ-
ной логикой, но некоторые элементы форматирования нуждаются в исправлении.
7 Я не применила этот подход в конечном коде сценария в листинге 7.10, но использовала его в
листинге 7.12.
Дополнение данных
Добавление столбца Ог1д1па1=1пд1еп^егЕ1пдегрг1п1: (см. листинг 7.10) было хорошим
способом повысить полезность и удобство данных по кредитам по программе РРР,
но другим способом добавления значения в набор данных будет использование
других наборов данных для дополнения первоначального. Это легче всего реализо-
вать для размерностно структурированного набора данных, поскольку он уже осно-
ван на каком-либо широко используемом стандарте. В нашем случае с данными
кредитов по программе РРР пример такой ситуации можно видеть в столбце МА1С-
8Сос1е. Быстрый поиск в Интернете8 по ключевым словам ”ХА1С8 собе” предостав-
ляет информацию об этом коде:
.Шог1к Атепсап 1п(1и81гу С1а881ф1саИоп 8уз1ет (Система классификации отрас-
лей Северной Америки). Система ^1С$ была разработана для удовлетворения
нужд федеральных статистических агентств при сборе, анализе и публикации
статистических данных, связанных с экономикой США.
С учетом этого мы, вероятно, сможем найти способ дополнить наши данные, доба-
вив в них информацию о коде ХА1С8 для каждой записи. Это может помочь нам
расширить наше понимание о том, бизнесы каких типов и в каких отраслях участ-
вуют в кредитной программе РРР. Мы, скорее всего, смогли извлечь полный список
кодов ХА1С8 из основного веб-сайта, но поиск в Интернете по словам ”па1С8 §Ъа”
предоставит нам более интересные возможности. В частности, Управление по де-
лам малого бизнеса предлагает РВР-документ, содержащий информацию о реко-
мендациях этого управления для бизнесов по коду ХА1С8 касательно размера биз-
неса в миллионах долларов или в количестве сотрудников (Ьйр8://8Ьа.§оу/8Йе8/
(1еГаиИ/й1е8/2019-08/8ВА%20ТаЫе%20оГ%2081ге%2081:апйагЙ8_ЕГГесйуе%20Аи§%
2019%2С%202019.р(И). Кроме предоставления более понятного описания самих
кодов ХА1С8, этой информацией можно расширить наши данные о кредитах по
программе РРР, что может помочь нам получить ответы на более общие вопросы о
том, какие бизнесы в действительности можно квалифицировать как ”малые”.
Наш процесс для этого не будет значительно отличаться от процесса слияния дан-
ных, которое мы осуществляли несколько раз ранее, как в выполняемых операциях,
так и в представляемых им вопросах. Начнем с рассмотрения веб-версии рекомен-
даций Управления по делам малого бизнеса по размеру бизнесов. Щелчок по ссыл-
ке ВАЧ 81ге 8€апс1агс18 \УеЬра§е на первой странице РВР-документа открывает
более общую страницу веб-сайта Управления по делам малого бизнеса
(Ъйр8://8Ьа.§оу/Ге(1ега1-соп€гасйп§/соп€гасйп§-§ш(1е/81ге-81:апс1агс18), в разделе
Хишепса! Кеди1гетеп€8 которого находится ссылка €аЫе оГ 8ша11 Ьи8ше88 81ге
81ап(1аг(18 — таблица стандартов размеров малых предприятий (Ьйр8://8Ьа.§оу/
Доситеп€/8иррог€-оЬ]ес€-оЬ]ес1>1:аЫе-81ге-81:апс1агс18). Внизу этой страницы можно
найти ссылку (Ъйр8:/Лулулу.8Ьа.§оу/8Йе8/йеГаиШй1е8/2022-05/ТаЫе%20оГ%2081ге%
208€апйагЙ8_ЕГГесйуе%20Мау%202%202022_Гта1.х18х) для загрузки версии ХЬ8Х
8 Один из результатов которого ведет нас к ЬНр$://па1С8.сот/ууЬаЬ1$-а-па1С8-ео(1е-у\Ьу-с1о-1-пеес1-опе.
предыдущего РЭГ-документа. Из полученного ХЬ8Х-документа можно экспорти-
ровать второй рабочий лист (который содержит фактические коды и описания) в
файл С8У, после чего эти данные можно будет импортировать и сравнивать с на-
шими данными о кредитах по программе РРР.
Как мы увидим в сценарии в листинге 7.12, при интегрировании любого нового ис-
точника данных данные этого источника нужно оценить, очистить и преобразовать
точно так же, как в случае с данными нашего "основного” набора данных. В данном
случае это означает, что в файле данных о кредитах по программе РРР нам нужно
выполнить упреждающее обновление всех значений <МА> в столбце ИА1С8Собе, заме-
нив его каким-либо флаговым значением (я выбрала для этого строку Моле). Это
нужно для того, чтобы не допустить совпадений этих записей с по сути случайны-
ми значениями <МА> в файле кодов ХА1С8, полученном на веб-сайте Управления по
делам малого бизнеса. После слияния этих двух файлов нам нужно будет прове-
рить, для каких кодов из файла данных кредитов по программе РРР не было совпа-
дений. На данном этапе мы оставим открытым вопрос о том, как выполнять эту
операцию, пока мы немного не проанализируем данные, чтобы выяснить, хотим ли
мы ”заполнить” их (например, обычными значениями или расшифровками ХА1С8),
пометить их флагом как нетипичные для Управления по делам малого бизнеса или
использовать комбинацию этих двух элементов информации.
Листинг 7.12. Сценарий ррр_аскНпд_па1С8.ру
# Сценарий для слияния данных о кредитах по программе РРР с данными МА1С8
# Управления по делам малого бизнеса о требованиях к размерам предприятий.
# (Доступны по этой ссылке: ЬГГрз://шм.зЬа.до7/боситеп1:/8иррог1:--ГаЬ’ке-зтге-зГапбагбз)
# Импортируем библиотеку рапбаз для выполнения операций слияния и сортировки
1прог1: рапбаз аз рб
# Считываем данные о кредитах по программе РРР в структуру 0а1:аРгате
ррр_ба1:а = рб.геаб_С87( ,р^Ь^^с_150к_р'^^8_Г^пдегрг^п1:5.С8V,, бГуре^зГгтпд' )О
# Считываем данные МА1С8 в отдельную структуру 0а1:аРгате
зЬа_па1сз_ба1:а = рб.геаб_С87( '8ВА-МА1С8-баГа.С87', б1:уре='з1:г1пд1)
# Если поле столбца МА1С8Собе не содержит значения, вставляем в него строку Ыопе.
ррр_ба1:а[ 'МАТСЗСобе' ] = ррр_ба1:а['МАТСБСобе'] .ГШпа("Мопе")0
# Объединяем эти два набора данных, используя для этого операцию "левое объединение"
пегдеб_ба1:а = рб.пегде(ррр_ба1:а, зЬа_па1сз_ба1:а, Ьом=' 1е^',
1_еГГ_оп=['МА1С5Собе'], г!дЫ:_оп=['ИА1С5 Собез'],
1пб1са1:ог=Тгие)
# Открываем файл для записи в него объединенных данных
пегдес1_с1а1:а_Г11е = ореп( 'ррр-Гтпдегргтп^з-апсЬпат-СЗ.с^', 'м')
# Записываем объединенные данные в наш выходной файл в формате С5\/
пегдеб_с!а1:а_Г11е. иг1Ле(пегдес1_с1а1:а. 1:о_С87())
# Отображаем значения столбца _пегде, чтобы увидеть, сколько записей данных о кредитах
# не содержат совпадений с кодом МА1С8
рг!пГ(тегдес!_баГа.7а1ие_соип1:5( '_пегде'))
# Создаем новую структуру 0а1:аЕгате только для строк без совпадений
иппа^сЬесК/аХиез = пегдеб_ба1:а[тегдес1_с1а1:а[ '_пегде' ]==,1еЛ_оп1у1 ]
# Открываем файл для записи значений без совпадений
ипта^сЬеб^аТиеЗ-ГтЛе = ореп('ррр-иппа^сЬеб-патсз-собез.с^', V)
# Записываем в новый файл С5\/ все несовпавшие коды МА1С8 из файла кредитов
# по программе РРР вместе с информацией о количестве вхождений каждого такого кода
иппа1:сЬеб_7а1ие8_Г11е. шг11:е(ипта1:сЬеб_7а1ие8.7а1ие_соип1=5(' МАТСЗСобе'). 1:о_С87())
О Параметр с!±уре=' з±г!пд' принуждает библиотеку рапбаз рассматривать все зна-
чения нашего набора данных как строки. Таким образом мы упростим выполнение
будущих операций сравнения и поиска совпадений.
О Если не выполнить эту замену, то наши данные будут непредсказуемо совпадать
со значениями МА в файле 8ВА-МА1С8-ба1а.сзу.
Дополнение набора данных таким образом, как это делается в сценарии в листин-
ге 7.12, может помочь нам расширить круг вопросов, для получения ответа на ко-
торые мы сможем его использовать, а также способствовать поддержке более бы-
строго и полного анализа и интерпретации данных. В то же самое время при любом
внедрении в набор данных новых данных нам нужно выполнить полный цикл
оценки, очистки, преобразования и (даже возможно) дополнения, которому мы
подвергли наш "основной” набор данных. Это означает, что нам всегда нужно ис-
кать компромисс между усовершенствованием наших основных данных (делая их,
возможно, более полезными) и объемом времени и усилий, прилагаемых для поис-
ка и выпаса "вторичных” данных, используемых для дополнения основных данных.
Заключение
Разнообразие возможностей очистки, преобразования и дополнения данных на-
столько же велико, как и разнообразие наборов данных и возможностей их анализа.
Но основная цель этой главы состоит в том, чтобы проиллюстрировать распростра-
ненные проблемы с очисткой, преобразованием и дополнением данных, а также
представить некоторые ключевые методы для решения этих проблем.
Но прежде чем переходить к практическому генерированию знаний на основе на-
ших данных, в главе 8 мы сделаем небольшое отступление и сконцентрируемся на
рассмотрении некоторых передовых методов программирования, которые могут
помочь нам обеспечить наивысший уровень понятности, эффективности и рацио-
нальности нашего кода. Поскольку использование языка РуЙюп для выпаса данных
уже позволяет нам выполнять задачи, которые было бы невозможно выполнить
другими инструментами, оптимизация нашего кода как для первичного, так и для
повторного использования является другим способом получения максимальной от-
дачи от каждой разрабатываемой нами программы или фрагмента кода. В боль-
шинстве случаев это означает структурирование наших файлов таким образом,
чтобы они были более универсальными, удобочитаемыми и поддающимися компо-
новке, чем мы и займемся в следующей главе.
ГЛАВА 8
Структурирование и рефакторинг кода
Прежде чем приступать к рассмотрению анализа и визуализации выпаса данных мы
сделаем небольшое отступление и обсудим некоторые стратегии для реализации
большинства задач, которые мы рассмотрели до сих пор. В предыдущих главах мы
исследовали, как получить доступ и извлечь данные из разных источников и фор-
матов, как оценить их практическое качество, а также как очистить и дополнить их
для возможного анализа. В процессе работы наши сравнительно простые програм-
мы эволюционировали и изменялись, неизбежно становясь более запутанными и
сложными. Например, циклы 1"ог приобретали один (или несколько) вложенных
условных операторов И, некоторые из которых содержали загадочные цифры, как,
например, в выражении II2 1:Ье_с1а1:е.меекс1ау() <= 4: в листинге 7.5. Является ли это
ценой за более функциональный код?
Вспомним, что комментирование кода может значительно повышать уровень по-
нимания логики разрабатываемых сценариев как для других, так и для нас самих в
будущем. Но, как оказывается, подробное документирование (как бы мне ни нра-
вился этот подход) — не единственный способ повысить ясность разрабатываемого
кода РуШоп. Подобно другим типам текстовых средств, платформа РуШоп поддер-
живает ряд полезных механизмов для структурирования и организации разрабаты-
ваемого кода. Разумное применение этих средств может упростить как первое, так
и повторные использования ваших программ.
Поэтому в этой главе мы рассмотрим инструменты и концепты, которые позволят
вам усовершенствовать свой код, сделав его одновременно удобочитаемым и при-
годным для повторного использования.
Этот процесс называется рефакторингом (геГас!опп§) и служит еще одним приме-
ром того, что использование платформы РуШоп для выпаса данных позволяет по-
высить отдачу для прилагаемых нами усилий. В частности, хотя в случае надобно-
сти мы можем полагаться на функциональность, предоставляемую чьей-либо биб-
лиотекой, мы также можем создавать новые ”заготовки”, точно настроенные под
наши требования и предпочтения.
Обзор пользовательских функций
При рассмотрении основ языка РуШоп в главе 2 мы вкратце затронули такое поня-
тие, как определяемые пользователем функции, или просто пользовательские
функции . В сценарии листинга 2.7 мы увидели, как пользовательскую функцию
можно использовать для инкапсуляции простой задачи вывода на экран приветствия
при предоставлении определенного имени. Конечно же, пользовательские функции
можно создавать для задач любого уровня сложности. Но прежде чем приступать к
рассмотрению механики создания пользовательских функций, отступим на шаг на-
зад и обдумаем, какие аспекты дизайна могут наилучшим образом помочь нам ре-
шить, когда создание пользовательской функции будет наиболее полезным. Подоб-
но любому процессу создания литературных сочинений, здесь нет жестких правил,
но далее приводится несколько эвристических методов, которые помогут решить, в
каких случаях и как имеет смысл использовать рефакторинг.
Многократное использование кода
Подобно переменным, определить фрагменты кода, упаковка которых в пользова-
тельские функции может принести пользу, можно, определив многократно выпол-
няющуюся задачу. Наличие в сценарии большого количества неудобных условных
выражений или повторяющихся шагов, от проверки входных данных до формати-
рования выходных (см. например, листинг 7.7), является индикатором того, что
следует рассмотреть возможность создания пользовательских функций. Также надо
иметь в виду, что рефакторинг может быть полезным не только в случае операций,
повторяющихся только в одном сценарии. В частности, при наличии определенных
задач, исполняющихся многократно в разных сценариях (например, проверка, яв-
ляется ли данный день рабочим, как в листинге 7.5), соответствующую пользова-
тельскую функцию всегда можно вынести в отдельный сценарий и подключать его
в любом требуемом месте, как мы это делали при обработке файлов с учетными
данными в главе 5.
Аккуратное и понятное документирование
Документирование разрабатываемого кода сродни подарку как для членов команды
разработки, так и для самого себя в будущем1 2. В то же самое время подробное до-
кументирование разрабатываемого кода может в конечном итоге понизить его удо-
бочитаемость, особенно если документировать не только используемые методы, но
также и причину их использования. Таким образом, создание действительно понят-
ного кода — это балансирование между предоставлением объема подробностей,
достаточного для понимания кода, но при этом достаточно краткого, чтобы пользо-
ватели не устали читать вашу документацию.
Выделение соответствующих фрагментов кода в пользовательские функции явля-
ется ключевым способом достичь такого баланса: подобно переменным, пользова-
тельским функциям можно (и нужно!) присваивать описательные названия. Просто
читая название функции, пользователь может получить вполне достаточную ин-
1 В данном случае "пользователем” считается сам программист.
2 Воистину, качественная документация в некоторых случаях может быть настоящим спасителем.
формацию о ее работе, без необходимости сопровождать ее код строками пояс-
няющих комментариев. Если название функции достаточно описательное и/или
данному пользователю в настоящий момент не требуются дополнительные подроб-
ности, он может просто продолжить свою работу. Но в случае надобности он все
равно сможет найти подробную документацию по этой функции, аккуратно разме-
щенную в другой части программы (или в другом файле). Это означает, что строч-
ные комментарии для вашего основного сценария могут быть сравнительно крат-
кими, но при этом без ущерба для полноты документации.
Недостаточная функциональность по умолчанию
Это, может быть, и не самая лучшая причина для создания пользовательских функ-
ций, но зато абсолютно реальная. Со временем в ваших работах по выпасу данных
могут обнаружиться некоторые повторяющиеся задачи, но существующие функции
и библиотеки, используемые для их решения, могут быть не совсем оптимальными.
Возможно, что какая-либо функция имеет труднозапоминаемое название и вам по-
стоянно приходится вспоминать, как именно она называется. Или вы постоянно
забываете добавить какой-либо параметр, что усложняет всю вашу работу (да-да, я
говорю о параметре с1±уре=' з±г!пд' функции рс1.геас1_с87()). Для разработчиков, ра-
ботающих самостоятельно или в небольшой команде, будет абсолютно приемле-
мым создавать пользовательские функции, упрощающие их работу просто потому,
что они упрощают их работу. Вам не нужно искать какое-либо серьезное обоснова-
ние для этого. Если это упростит вашу работу, то полный вперед! В этом и заклю-
чается мощь программиста.
Конечно же, здесь есть определенные ограничения. Если только не использовать
гораздо более формальный и сложный подход к разработке кода РуЙюп, вы не
сможете, например, определить новую функцию, имеющую то же самое название,
что и уже существующая функция, или изменить поведение операторов + или -3.
Но если вы просто хотите немного откорректировать работу какой-либо сущест-
вующей функции, то с этим не будет никаких проблем. Только обязательно под-
робно задокументируйте свою версию.
Область видимости
Рассмотрев некоторые из причин для возможного рефакторинга разрабатываемого
кода посредством пользовательских функций, можно вкратце рассмотреть и меха-
низм для этого. Вероятно, самое важное понятие, которое нужно понимать для соз-
дания пользовательских функций, — это область видимости, или действия (зсоре),
функции. Хотя мы еще не использовали этот термин, мы работали с обозначаемой
им возможностью с момента объявления наших первых переменных в сценариях в
листингах 2.2-2.4.
3 Эти вещи вполне реализуемы, но выходят далеко за рамки большинства операций по выпасу данных
и, поэтому, за рамки данной книги.
В них мы увидели, что мы можем:
1. Создавать именованные переменные и присваивать значения их содержимому
(например, аиШог="$и5ап Е. МсСгедог").
2. Обращаться к содержимому этих переменных по их названиям и передавать их
значения функциям (например, рг1п1:(аи1:Ьог)).
При этом мы знаем, что программа, состоящая только из строки вызова функции,
которой передается наша объявленная переменная, вызовет ошибку:
рг1п1:(аиШог)
Это объясняется тем, что во ”вселенной” нашего однострочного сценария не суще-
ствует ячейки памяти с названием аиШог. В результате интерпретатор РуШоп выда-
ет нам сообщение об ошибке и отказывается продолжать исполнение программы.
В программировании под областью видимости имеется в виду текущая вселен-
ная” с точки зрения определенного фрагмента кода. Область видимости отдельного
сценария эволюционирует по мере считывания компьютером каждой строки кода
сверху вниз, вызывая вполне ожидаемое поведение сценариев в листингах 8.1 и 8.2.
Листинг 8.1. Переменная аиШог отсутствует в области видимости
# "Вселенная" этой строки кода не содержит переменной аиШог.
# Интерпретатор выдает сообщение об ошибке
рг1п1:(аи1:Ьог)
Листинг 8.2. Область видимости содержит переменную аиШог
# Создаем переменную аиШог
аиШог = "Зизап Е. МсСгедог"
# "Вселенная" этой строки кода содержит переменную аиШог.
# Продолжаем исполнение
рг!п1:(аиШог)
Точно так же, как и при создании каждой переменной в памяти компьютера создает-
ся новый ”контейнер” для хранения ее содержимого, при объявлении каждой функ-
ции для нее создается новая "вселенная” или область видимости. Это означает, что
при использовании пользовательских функций мы разделяем наш код не только ви-
зуально, но также логически и функционально. В результате мы можем обращаться с
созданными нами пользовательскими функциями так же, как и со встроенными ме-
тодами РуШоп и библиотечными функциями, которые мы использовали в этой книге:
как с ”рецептами”, для которых мы предоставляем ”ингредиенты”, и получать в ре-
зультате их исполнения некоторое значение или новый объект РуШоп. Единственное
отличие состоит в том, что пользовательские функции мы ”готовим” сами.
Чтобы получить представление, что все это означает на практике, возвратимся к
сценарию в листинге 2.8, но слегка откорректировав его, как показано в листинге 8.3.
Листинг 8.3. Сценарий дгее<_те_геу!§Иес1.ру
# Создаем функцию, отображающую на экране приветствие по любому переданному ей имени
с1еГ дгее1:_те(а_пате):
рг1п1:("\/аг1аЫе 'а_папе' 1п 'дгее1:_те': "+а_папе)
рггп1("НеП.о "+а_пате)
# Создаем переменную с именем аи1±ог
аи1±ог = "Зизап Е. МсСгедог"
# Создаем другую переменную с именем есИЛог
есИ±ог = "ЗеН В1е1е1"
а_папе = "Ру1±оп"
рг1п1:("\/аг1аЫе 'а_папе' 1п па1п зсг1р1:: "+а_папе)
# Используем нашу пользовательскую функцию дгее1:_те() для вывода персонализированного
# сообщения "НеИо..." для каждого пользователя
дгее1=_те(аи-1:Ьог)
дгее!:_те(ес111:ог)
рг1п1:("\/аг1аЫе 'а_папе' 1п па1п зсгтр!: адатп: "+а_папе)
Исполнение этого сценария дает следующий результат:
\/аг1аЫе 'а_папе' 1п па1п зсг1р1=: Ру1±оп
\/аг1аЫе 'а_папе' 1п 'дгее1:_те': Зизап Е. МсСгедог
НеНо Зизап Е. МсСгедог
\/аг1аЫе 'а_папе' 1п 'дгее1:_те': ЗеГГ В1е1е1
НеНо ЗеГГ В1е1е1
\/аг1аЫе 'а_папе' 1п патп зсгтр!: адатп: Ру1±оп
Поскольку любая пользовательская функция автоматически получает свою собст-
венную область видимости, она может видеть только те переменные и значения,
которые явно передаются ей. В свою очередь, переменные и значения внутри этой
функции фактически скрыты от кода основного сценария. Одно из последствий
этого состоит в том, что при создании пользовательских функций нам не нужно
беспокоиться о том, что используемые в них названия переменных уже могли быть
использованы в основном сценарии, или наоборот. В результате нам нужно приду-
мывать меньшее количество уникальных названий переменных, что помогает при
создании сценариев большего размера и сложности. Это также означает, что, до-
бившись ожидаемой работы пользовательской функции, мы можем использовать ее
и даже модифицировать ее работу без необходимости вносить корректировки в пе-
ременные и функции в сценарии, в котором она используется.
Определение параметров функции
У нас уже есть довольно значительный опыт предоставления ”ингредиентов” (ко-
торые официально называются аргументами) методам и функциям, встроенным в
РуЙюп и предоставляемым используемыми библиотеками. Но приступая к разра-
ботке пользовательских функций, нам нужно исследовать более подробно процесс
для определения параметров, передаваемых этим функциям4.
Прежде всего, в отличие от некоторых других языков программирования, язык Ру-
Йюп не требует (более того, даже по-настоящему не позволяет) от программистов
настаивать на том, чтобы параметры функции были определенного типа данных5.
Поэтому при желании (или по неведению) функции вполне возможно передать
данные неправильного типа. Таким образом, решение о том, каким образом прове-
рять (и проверять ли вообще) достоверность передаваемых пользовательской
функции аргументов, принимается вами как разработчиком этой функции. В прин-
ципе, к решению этой задачи можно подойти тремя способами:
♦ проверяется тип данных всех передаваемых функции аргументов и в случае ка-
кого-либо несоответствия выдается предупреждение или сообщение об ошибке;
♦ код заключается в блок 1гу...ехсер1: (ййр8://уу38сйоо18.сош/руй1оп/
ру€Ьоп_€гу_ехсер€.а8р), чтобы улавливать определенные типы ошибок, не пре-
рывая исполнение всей программы. При этом можно также выдавать сообщение
о причине ошибки;
♦ не беспокоиться об этом и позволить пользователю функции (т. е. программи-
сту) самому решить все проблемы при помощи сообщений об ошибках языка
РуЙюп по умолчанию.
Это может казаться несколько либеральным, но на данном этапе моя основная ре-
комендация — выбрать третий вариант: не беспокоиться об этом. Но не потому,
что не будет ошибок, они будут, и во врезках "Быстрая прокрутка" главы 2 были
описаны некоторые из них. А потому что наш основной интерес состоит в выпасе
данных, а не в разработке программ РуЙюп уровня предприятия. Как и в случае со
сценариями в главе 4. мы хотим сбалансировать задачи, решаемые нами, и задачи,
выясняемые и решаемые самим программистом (кем бы он ни был). Поскольку в
случае их неправильной работы наши программы не выведут из строя какой-либо
важный веб-сайт или не исказят единственную копию важных данных, кажется бо-
лее разумным не пытаться предвидеть и реагировать на каждую возможную ошиб-
ку, которая может возникнуть при их работе. Конечно же, если четко и ясно доку-
ментировать разрабатываемые функции (мы рассмотрим этот процесс более под-
4 Строго говоря, параметры означают названия переменных, указываемых в определении функции, а
аргументы — это сами значения этих переменных, передаваемые функции при ее вызове. Но на
практике эти термины используются взаимозаменяемо.
5 Так называемые языки со статическим контролем типов данных (81айса11у Турес!1ап§иа§е§) не разре-
шат исполнение кода, если функции или методу переданы данные неправильного типа.
робно в разд. "Использование рус1ос для документирования сценариев и пользова-
тельских функций" далее в этой главе), тогда другие программисты будут иметь
всю требуемую информацию, чтобы не допускать ошибок с самого начала.
Доступные опции
Даже если разрабатываемые вами пользовательские функции не предназначены для
использования тысячами других пользователей, их все равно можно сделать гиб-
кими и полнофункциональными. Один из самых простых способов для этого —
разрабатывать функции для решения наиболее распространенной версии задачи, но
при этом делать их в некоторой степени адаптируемыми, снабдив их возможностью
принимать дополнительные аргументы, подобно тому как это делается в библиоте-
ке рапбаз6 и других библиотеках. Например, нашу функцию дгее1:_те() можно мо-
дифицировать таким образом, чтобы при желании вместо приветствия по умолча-
нию ”Не11о” она выводила любое приветствие, передаваемое в нее программистом.
Таким образом мы можете создавать функции, которые можно эффективно исполь-
зовать в разных контекстах. Для примера рассмотрим модифицированную версию
функции дгее1:_те(), код которой приводится в листинге 8.4.
Листинг 8.4. Сценарий дгее<_те_ор<юп§.ру
# Создаем функцию, отображающую на экране приветствие по любому переданному ей имени
с!еГ дгее1:_те(а_пате, дгееН.пд="Неио"):
рг1п1:(дгее1:1пд+" "+а_папе)
# Создаем переменную с именем аи1±ог
аи1±ог = "Зизап Е. МсСгедог"
# Создаем другую переменную с именем есН±ог
ес11±ог = "ЗеГГ В1е1е1"
# Используем нашу пользовательскую функцию дгее1:_те() для вывода
# индивидуализированного приветствия для каждого пользователя.
# По умолчанию выводим приветствие "Не11о"
дгее^пеСаи^Ьог)
# Программист задает приветствие "Н1"
д гее1:_те (есйЛо г, д гее1:1пд=" Н1")
Как видно, добавить необязательные аргументы можно так же просто, как и задать
значение по умолчанию в определении функции. Если при вызове функции про-
граммист передаст ей другое значение, то оно просто заменит значение по умолчанию.
6 Опять этот параметр с11уре=1З'Ьгт.пд'!
Предоставление аргументов
Кроме предоставления значения по умолчанию в объявлении функции, пользова-
тельской функции можно передавать необязательные дополнительные аргументы.
В частности, язык РуЙюп поддерживает два общих типа необязательных аргумен-
тов, *агдз и **киагд5:
*агдз
Этот параметр полезен для передачи функции нескольких значений, когда при-
своение им всех названий и/или значений по умолчанию было бы довольно тру-
доемким. Значения, передаваемые через *агдз, сохраняются в списке, и в функ-
ции доступ к ним можно получить, обрабатывая их поочередно посредством
цикла . Лп (т.е., -Гог агд 1п агдз).
**кмагдз
Параметр **киагд5 похож на *агдз, с той разницей, что он позволяет передавать в
функцию произвольное количество именованных аргументов, не присваивая им
значения по умолчанию, как это делалось в сценарии в листинге 8.4. Доступ к
передаваемым таким образом значениям можно получить посредством метода
киагдз.де^О (например, ту_7аг=кмагдз.де1:(дгее1:1пд)).
Наличие параметров *агдз и **киагд8 может показаться удобным способом оста-
вить открытыми разные варианты при разработке пользовательских функций. Но я
должна сказать вам, что пользовательские функции в частности и сценарии в целом
всегда лучше разрабатывать для решения существующих задач, а не для задач, с
которыми, вы полагаете, вам, возможно, придется иметь дело когда-либо в буду-
щем. Хотя идея чрезвычайной гибкости может изначально казаться привлекатель-
ной, это обычно ведет к трате большого количества времени, думая о возможных
будущих задачах, вместо того чтобы решать те, которые стоят перед вами в на-
стоящее время. А у кого есть для этого время, когда нам нужно заниматься выпа-
сом данных?
Возвращаемые значения
До сих пор наши модификации функции дгее1:_те() не отличались большими амби-
циями: мы, по сути, просто использовали их для вывода на экран индивидуально
настроенных сообщений. При этом функции внешних библиотек, которые мы ис-
пользовали, обладали невероятно мощными возможностями. Например, взять дан-
ные в простом формате разделения запятыми (.сзу) и преобразовать их в структуру
0а1:аЕгате библиотеки рапбаз или преобразовать целый файл .х1з в коллекцию под-
робных списков и атрибутов, улавливающих почти все аспекты этого многоуровне-
вого типа файла. Такой уровень программирования на РуЙюп выходит за рамки
этой книги, но мы все же можем создавать опрятные, чрезвычайно полезные поль-
зовательские функции, используя мощь возвращаемых значений.
Если параметры/аргументы представляют собой ”ингредиенты” наших функций-
”рецептов”, то возвращаемые значения — это конечное блюдо, которое употребля-
ется остальной программой. По сути, возвращаемые значения — это обыкновенные
данные; они могут быть литералами (например, строка «Не11о») или переменными
любого типа данных. Возвращаемые значение полезны тем, что они позволяют нам
передать функции требуемые для нее значения и получить обратно значения, тре-
буемые нам, не беспокоясь (по крайней мере, с точки зрения, т. е. области видимо-
сти, основной программы) о внутренностях этого процесса. Для примера модифи-
цируем функцию дгее1:_те() (см. листинг 8.3) для возвращения значения. Соответ-
ствующий код приводится в листинге 8.5.
Листинг 8.5. Сценарий шаке_дгееНпд.ру
# Создаем функцию, возвращающую приветствие по любому переданному ей имени
с!еГ паке_дгее1:1пд(а_пате):
геГигп("Не11о "+а_папе)
# Создаем переменную с именем аи1±ог
аи1±ог = "5изап Е. МсСгедог"
# Создаем другую переменную с именем есИ±ог
есИ±ог = "ЗеН В1е1е1"
# Используем нашу пользовательскую функцию дгее1:_те() для создания и сохранения
# индивидуализированных сообщений "Не11о..." для каждого пользователя
аи1±ог_дгее1:1пд = паке_дгее1:1пд(аи1±ог)
есНГог_дгееГ1пд = паке_дгее1:1пд(ес111:ог)
# Теперь выводим на экран приветствия, созданные и возвращенные каждым вызовом функции
рг!п1:(аи1±ог_дгее1:1пд)
р г 1п± (есНЛог_д гееС1пд)
Поначалу вы можете задаваться вопросом, и в чем же польза этого подхода. В том,
что хотя наша основная программа увеличилась в размере, она, возможно, стала
более гибкой и понимаемой. Поскольку функция паке_дгее1:1пд() возвращает значе-
ние, а не просто печатает приветствие на экране, круг возможностей его использо-
вания значительно расширяется. Конечно же, его можно просто отобразить на эк-
ране, как это делается в листинге 8.5, но возвращаемое значение можно сохранить в
переменной для какой-либо последующей обработки. Например, можно добавить
следующую строку кода:
рг1п1:(ес111:ог_дгее1:1пд+", Ком аге уои?")
Эта новая модификация может не выглядеть чем-то особенным, но она позволяет
как выделить некоторую работу (в данном случае добавление приветствия ”Не11о” к
любому имени) в функцию, так и предоставить большую гибкость в аспекте ис-
пользования результата (например, добавить дополнительный текст к одному, но не
к другому).
Работа со стеком
Конечно же, создание новой переменной лишь для хранения простого приветствия,
как это делается в сценарии в листинге 8.5, создает больше проблем, нежели поль-
зы. Но нигде не сказано, что результат одной функции обязательно сохранять в пе-
ременной, прежде чем передавать его другой. В действительности функции можно
вкладывать друг в друга, так что вывод одной функции просто становится вводом
другой. Мы уже применяли этот подход в нашей предыдущей работе — при пере-
даче строк функции рг!п±(), например, добавляя вызов функции з±г1р() в процесс
создания данных, разделенных запятыми (формат С8У) в сценарии в листинге 7.8.
Но его можно применять с любым набором функций, предполагая, что одна функ-
ция возвращает значение, требуемое следующей. Соответствующий пример приво-
дится в листинге 8.6. Это просто сценарий из листинга 8.5, модифицированный
вставкой в него новой функции, которая добавляет текст ”, 1ю\у аге у он” к тексту
приветствия.
Листинг 8.6. Сценарий таке_дгеейпд_по_уаг8.ру
# Функция, возвращающая приветствие по любому переданному ей имени
бе!2 паке_дгее!:1пд(а_пате):
геГигпС'НеНо "+а_папе)
# Функция, добавляющая вопрос к любому приветствию
бе!2 абб_диез1:1оп(а_дгее1:1пд):
ге!шгп(а_дгееС1пд+", Ком аге уои?")
# Создаем переменную с именем аи!±ог
аи!=Ьог = "5изап Е. МсСгедог"
# Создаем другую переменную с именем есйЛог
есПЛог = "Зе!2!2 В1е1е1"
# Отображаем сообщение приветствия
рг1п!:(таке_дгее1:1пд(аи1:Ьог))
# Передаем сообщение приветствия функции добавления вопроса и отображаем конечный
# результат
рг1п!:(абб_дие81:1оп(таке_дгее1:1пд(еб11:ог)))
Хотя оператор рг1п!:(таке_дгее1:1пд(аи1:Ьог)) довольно легко поддается пониманию,
оператор рг1п!:(абб_дие81:1оп(таке_дгее1:1пд(еб11:ог))) уже более сложный. Это как раз
иллюстрирует, что на практике количество вложений вызовов функций должно
быть в разумных пределах. Чем больше количество вложенных вызовов функций,
тем сложнее понимать этот код, несмотря на то что логика порядка операций оста-
ется прежней: самая внутренняя функция исполняется первой, а ее возвращаемое
значение "всплывает” и подается на вход следующей функции. Возвращаемое зна-
чение этой функции так же передается на вход следующей функции и так далее.
В традиционной терминологии программирования это называется стеком функций.
в котором самая внутренняя функция находится внизу стека, а самая внешняя —
вверху7. Стек функций для последней строки кода сценария в листинге 8.6 иллюст-
рируется на рис. 8.1.
Этот тип вложения вызовов функций лежит в основе всей философии программи-
рования (Ъйр8://еп.уу1к1реШа.ог§/уу1к1/Гипсйопа1_рго§гаттш§), но ради удобочи-
таемости соответствующего кода в большинстве случаев его следует использовать
с осторожностью.
Рис. 8.1. Стек вложенных вызовов функций
Рефакторинг для получения удовольствия
и прибыли
Ознакомившись с некоторыми ключевыми принципами и механизмами для рефак-
торинга разрабатываемого кода, рассмотрим, как это можно использовать для по-
вышения ясности некоторых наших сценариев из предыдущих глав. В процессе ис-
следования следующих примеров помните, что (подобно любому процессу редак-
тирования) решение относительно того, каким образом и какой код подвергать
рефакторингу, зависит от личных предпочтений и стиля конкретного программи-
ста. Поэтому после каждого примера я буду описывать логику моих решений. Эти
рассуждения могут оказаться полезными для вас при разработке своего собствен-
ного подхода к рефакторингу.
Функция для определения рабочих дней
Сценарий в листинге 7.5 считывает данные по аренде велосипедов системы СШ
В1ке (Иир$://$3.ата7опауу$.сот/1т1рйа1а/тс1ех.1йт1), выбирает из них данные только
для аренд по рабочим дням и сохраняет их в новом файле. Нельзя сказать, чтобы
7 Этот термин, возможно, был причиной, почему так назван форум 81аск Ехсйап^е.
используемый в этом сценарии подход был чем-то фундаментально плох, но все же
я полагаю, что он является хорошим кандидатом для рефакторинга по ряду причин.
Во-первых, сценарий полагается на пару непривлекательных вызовов функций с
не очень описательными названиями. Первый вызов требуется для преобразова-
ния строки данных даты в собственно формат да-Се-Ыгпе, с которым РуЙюп может
работать:
1Не_с1а1е = сIа1:е1:^те.51:гр^:^те(а_гоVV[,51:агН:^те,], 1 %У-%т-%с1 %Н:%М:%8.%Г')
И хотя встроенный метод меекбау() довольно прямолинеен (хотя его лучше было бы
назвать с1ауобл/еек()), нам нужно сравнивать его результат с ”магическим числом” 4,
чтобы определить, действительно ли значение 1Не_с1а1е является рабочим днем:
И 1:Ье_с1а1:е.меекс1ау() <= 4:
В целом, я полагаю, что эти фрагменты кода были бы более удобочитаемыми, если
бы они не содержали этих достаточно неясных форматов и сравнений.
Во-вторых, задача проверки, является ли определенная строка, похожая на дату,
рабочим днем (с понедельника по вторник), выглядит типом задачи, которую в об-
ласти выпаса данных может потребоваться решать сравнительно часто. Если выде-
лить этот код в пользовательскую функцию, то ее можно будет использовать в дру-
гих сценариях.
Мой подход к рефакторингу сценария из листинга 7.5 приводится в листинге 8.7.
Листинг 8.7. Сценарий ууеекс1ау_пс1ез_геГасИогес1.ру
# Цель: отфильтровать все аренды велосипедов системы СПл В1ке за рабочие дни
# сентября 2020 года и сохранить в новый файл
# Краткое описание программы:
# 1. Считываем файл данных: 202009-с11лЬ1ке-1:г1рс1а1:а.С87
# 2. Создаем новый выходной файл и записываем в него строку заголовка
# 3. Для каждой строки в файле создаем из строки 51агШпе значение даты
# а. Если дата выпадает на рабочий день, записываем строку в выходной файл
# 4. Закрываем выходной файл
# Импортируем библиотеку С87
1трог1: С87
# Импортируем да-Ье-Ыте
Нот с!а1е1гте 1трог1: с!а1е1гте
бе!2 та!п():О
# Открываем файл данных в режиме чтения
зоигсе_ЕПе = ореп("202009-с11лЬ1ке-1:г1рс1а1:а.С87","г")
# Открываем выходной файл в режиме записи
ои1:ри1:_Н1е = ореп (" 202009 - с!Н Ь1_ке - пеекбау -1: г!рс1а1:а. сз V", "и")
# Передаем исходный файл зоигсе_НЛе методу ОтсГРеабег и сохраняем результат
# в переменной с1Л1Ыке_геас1ег
с1ПЫке_геабег = сз7.01сГРеабег(зоигсе_Г11е)
# Создаем соответствующую структуру 0хс1:1л1гх1:ед; указываем, что названия ее полей
# нужно взять из с1Л1Ыке_геас1ег
ои!риГ_игг1ег = С57.01сГ1л1г1Гег(оиГриГ_Г11е,
Г1е1бпапез=с1Г1Ь1ке_геас1ег. ПеТбпатез)
# Записываем строку заголовка в выходной файл
ои1ри1_нгтЛег. иг1ГеЬеас1ег()
# Обрабатываем в цикле Гог...1п список строк с1Г1Ыке_геабег
Гог а_гом 1п с1Г1Ыке_геабег:
# Если текущее значение зГагГГГпе является рабочим днем
1Г 1з_меекбау(а_гом[' зГагГПте' ]) :О
# Записываем эту строку данных в наш выходной файл
оиГриГ_мг1Гег.мг1Гегом(а_гом)
# Закрываем выходной файл
оиГриГ_Г1Ле.с!озе()
беГ 1з_меекбау(баГе_зГг1пд, баГе_ГогтаГ='%У-%п-%б %Н:%М:%8.%Г'):О
# Преобразовываем значение в баГе_зГг1пд в формат баГеПте
ГЬе_баГе = баГеПте.5ГгрГ1те(баГе_зГг1пд, баГе_ГогпаГ)
# Если значение ГЬе_с1аГе является рабочим днем (т. е. целым числом от 0 до 4)
1Г ГЬе_баГе.меекбау() <= 4:
геГигп(Тгие)
е!зе:
геГигп(Еа!зе)
1Г___папе__== "___па1п__":©
па1п()
О Основной сценарий оформляется в виде функции па1п() согласно одному из со-
глашений РуШоп и служит важной практической цели. Поскольку интерпретатор
РуШоп обрабатывает код сценариев сверху вниз, то если не заключить этот код в
функцию, то при достижении оператора 1Г 1з_меекбау(а_гом[ 'зГагШпе' ]) у интер-
претатора еще не будет определения функции 1з_меекбау() и он выдаст сообщение
об ошибке.
О Функция 1з_меекбау() используется для преобразования даты в виде строки в
дату, а затем в соответствующее числовое значение рабочего дня (от 0 до 4). Опи-
сательное название функции передает смысл, не заставляя пользователя разбирать-
ся во всех подробностях процесса.
О Интерпретатор считывает код сверху вниз, и хотя он сначала доходит до обеих
функций — па1п() и 15_меекс1ау() — он не должен исполнять никакого кода, пока не
дойдет до конца сценария.
Как видно, хотя основная часть кода (и даже многие комментарии) в листинге 8.7
та же самая, как и в листинге 7.5, этот код переупорядочен таким образом, что
часть па1п() сценария становится намного более краткой и удобочитаемой. Если
программисту захочется узнать подробности определения рабочих дней, то он мо-
жет найти эти подробности здесь же, в определении функции 1з_меекс1ау(). Если же
его эти подробности не интересуют, он может просто прочитать код основной час-
ти сценарии и с легкостью убедиться в том, что он реализует именно то, о чем го-
ворится в кратком описании в начале сценария.
Что такое раздел _тат_?
Оператор II2 в примечании О листинга 8.7 — это одно из соглашений РуЙюп и
сокращение, указывающее компьютеру, выполняется ли этот сценарий напря-
мую. Основная цель защитного оператора II2 — предотвратить непредвиденное
поведение при импорте одного сценария РуЙюп в другой8. Другой способ ми-
нимизации шансов перекрытия или "сбоя” кода — агрегировать полезные
функции в файлах с описательным названием (например, ба1е_й1псйоп8.ру), а
затем при необходимости импортировать их.
Хотя конструкция II2 ___папе_ == "__патп__": обеспечит бесперебойное испол-
нение программы при (почти) любых обстоятельствах, по своему опыту могу
сказать, что будет легче отслеживать часто используемые функции, собрав их
в одном (или нескольких) файлах с понятными описательными названиями.
Опрятные метаданные
В главе 7 мы собрали на основе кода сценариев 4.8 и 7.6 сценарий (см. листинг 7.7),
который как обрабатывает даты в формате М1сго8ой Ехсе1, так и разделяет исход-
ный файл данных на текстовый файл с метаданными и структурированный файл
значений, разделенных запятыми (С8У). Получившийся сценарий делал все, что
нам требовалось, но это достигалось за счет увеличения объема кода, ухудшения
его удобочитаемости и наполнения труднопонимаемыми условиями и вызовами
непонятных функций форматирования дат.
8 Полезное описание и демонстрация обоснования этого соглашения приводятся по адресу
ЬПр8://Ггеесо(1есатр.ог§/пелУ8/й-пате-та1п-руЩоп-ехатр1е.
Мы можем немного улучшить этот код, вынеся форматирование разных типов ис-
ходного содержимого (в файлах .с§у или .Ш) в отдельные функции. Но для этого
необходимо немного переупорядочить и уточнить логику нашего сценария. Этот
процесс также иллюстрирует некоторые сложности, которые могут возникнуть при
вводе всей требуемой информации в пользовательские функции. В листинге 8.8
иллюстрируется подход, предпочитаемый мною в настоящее время для решения
этих задач.
Листинг 8.8. Сценарий Х18_те1а_апс1_с1а1е_раг8тд_ге1ас1огес1.ру
# Преобразовываем табличные данные в файле .хТз в формат С87 и сохраняем их
# в соответствующем файле. Также отделяем метаданные и сохраняем их в отдельном файле.
# Для работы с данными дат используем библиотеку хгТб
# Сначала установим библиотеку хТгб, используя установщик р1р:
# ЬГГрз: //рур!.огд/ргодесГ/хТгб/2.0.1/
# Импортируем библиотеку хТгб
Тпрог!: х1гс1
# Импортируем библиотеку сзу
ТпрогГ С87
# Требуется для проверки, является ли данное значение каким-либо типом числа
Тгоп пипЬегз Тпрог!: МитЬег
# Требуется для парсинга и форматирования наших дат ЕхсеТ
Тгоп баГеПпе ТтрогГ баГеПте
беГ та1п():
# Загружаем данные в переменную зоигсе_могкЬоок посредством метода ореп_могкЬоок()
зоигсе_могкЬоок = хТгб.ореп_могкЬоок("ГгебдгарЬ.хТ8")
дТоЬаТ 1±е_ба1:етобеО
ГЬе_баГетобе = 5оигсе_могкЬоок. баГетобеО
# Открываем и именуем текстовый файл для хранения метаданных
8оигсе_могкЬоок_те1:абаГа = ореп( "ГгебдгарЬ_те-1:абаГа. 1x1", V)
# Рабочая книга .хТз может содержать несколько листов
Гог 8Ьее1_папе 1п 5оигсе_могкЬоок.8Ьее1_пате8():
# Создаем переменную, указывающую на текущий рабочий лист
сиггеп1_8Ьее1 = 8оигсе_могкЬоок.5Ьее1_Ьу_пате(8Ьее1_пате)
# Создаем выходной файл х18_"+8Ьее1:_пате+" .С87 для текущего листа
ои^ри^-ГИе = ореп("х1з_"+5Ьее1:_пате+"_ба1:е8.С57","м")
# Записываем строки в формате .С87 посредством метода мг1Лег()
ои^ри-Мл/гтЛег = сз7.мг11:ег(ои1:ри1:_Т11е)
# Создаем булеву переменную для определения достижения табличных данных
т.5_1аЫе_с1а1а = ЕаТзе
# Обрабатываем в цикле Тог каждую строку в листе
Тог гом_пип, гом 1п епитегаТе(сиггепТ_5ЬееТ.деТ_гомз()):
# Извлекаем значение первого столбца текущей строки
Тт.г21_еп1гу = сиггепТ_8ЬееТ.гом_7а1ие8(гом_пип)[0]
# Если строка заголовков таблицы данных
II7 Т1гзТ_епТгу == 'оЬзе^аТТоп^аТе':
# Присваиваем переменной флага 1з_ТаЫе_с1аТа значение Тгие
1з_ТаЫе_с1аТа = Тгие
# Если значение переменной т.5_1аЫе_с1а1а равно Тгие
1Т 1з_ТаЫе_баТа:
# Передаем требуемые данные функции сгеаТе_ТаЫе_гом()
пем_гом = сгеаТе_ТаЫе_гом(сиггепТ_зЬееТ, гом_пип)
# Записываем эту новую строку в наш выходной файл данных
оиТриТ^г1Тег.мг1Тегом(пем_гом)
# В противном случае эта строка должна быть метаданными
еТзе:
# Передаем требуемые данные функции сгеа1е_те1а_1ех1()
пе1:ас1а1:а_11пе = сгеаТе_пеТа_ТехТ(сиггепТ_8ЬееТ, гом_пип)
# Записываем эту новую строку в наш выходной файл метаданных
5оигсе_могкЬоок_пеТабаТа.мг1Те(пеТабаТа_ипе)
# На всякий случай закроем наши файлы
оиТриТ_Т11е. с!озе()
зоигсе_могкЬоок_пеТабаТа. с!озе()
беТ сгеаТе_ТаЬ1е_гом(1±е_5ЬееТ, ТЬе_гом_пип):
# Извлекает значения табличных данных в отдельные переменные
ТЬе_баТе_пип = ТЬе_зЬееТ. гом_7а1ие5(ТЬе_го\л/_пит)[0]
116_7аТие = 1Не_5Нее1. гом_7аТиез(1±е_гом_пит)[1]
# Создаем новый объект строки, содержащий все значения
пем_гом = [ГЬе_с1аГе_пит, 116_7а1ие]
# Если ГЬе_с1аГе_пит содержит число, тогда текущая строка не является строкой
# заголовка. Нужно выполнить преобразование даты
1Г 1з1п8Гапсе(ГЬе_с1аГе_пит, МипЬег):
# Создаем объект РуГЬоп йаГеГгте, использую метод х!с1аГе_а5_с1аГеНте()
# библиотеки х!гс1
ГЬе_с1аГе_пит = х!гс1.х1с1аГе.х1с1аГе_а5_с1аГеГ1те(ГЬе_с1аГе_пит, ГЬе_баГетос1е)
# Создаем новый список, содержащий значение ГЬе_баГе_пип (в формате ММ/ДД/ГГГГ
# посредством функции 51гГ1гте()) и значение во втором столбце
пем_гом = [1=Ье_ба1:е_пит.51:гГ1:1те('%т/%б/%У') ,116_7а1ие]
# Возвращаем полностью отформатированную строку
геГигп(пеи_гсил/)
с!еГ сгеаГе_пеГа_ГехГ(ГЬе_8ЬееГ, ГЬе_гсм_пит):
пе1:а_11пе = ""
# Для форматирования файла метаданных обрабатываем отдельные ячейки каждой строки
# метаданных
Гог 1Гет 1п ГЬе_5ЬееГ.гом(ГЬе_гом_пип):
# Записываем в ячейку значение с символом табуляции в конце
теГа_1Лпе = теГа_1Лпе + 1Гет.7а1ие + ' \±'
# В конце каждой строки метаданных добавляем символ новой строки
пеГа_1Лпе = теГа_1Лпе+'\п'
# Возвращаем полностью отформатированную строку
геГигп(теГа_1Лпе)
Г Г папе == " паГп
па1п()
О Объявление переменной глобальной (§1оЪа1) дает доступ к ее значению любой
функции. В данном случае имеется в виду глобальная область видимости. Но ис-
пользовать глобальные переменные следует с умеренностью. Обратите внимание
на то, что в РуЙюп глобальным переменным нельзя присваивать значения в той же
самой строке кода, в которой они объявляются. Это объясняет, почему здесь ис-
пользуется два отдельных оператора — один для объявления глобальной перемен-
ной, а другой для присвоения ей значения.
О Например, если не создавать глобальную переменную для формата даты
(ГЬе_с1аГетос1е), то нам нужно было бы передавать это значение функции
сгеаГе_ГаЫе_гсм() в виде дополнительного аргумента, что выглядит несколько не-
уместным.
Сравнивая код листинга 8.8 с кодом в листинге 8.7, можно увидеть в них много
общего: вместо одного линейно исполняемого фрагмента кода отдельные функцио-
нальности выделены в отдельные методы, а я па1п() защищен условным операто-
ром II2 __папе_ == "__па!п_Этот пример почти идеально совпадает с кодом в
листинге 7.7: импортируются одни и те же библиотеки, и хотя код в листинге 8.8
реорганизован в три отдельные функции, по большому счету он почти идентичен
коду в листинге 7.7.
Один из моментов, которые я хочу здесь проиллюстрировать, заключается в том,
что рефакторинг кода не обязательно требует громадных усилий и может дать
важные и полезные результаты. Как программа, основной сценарий в листинге
8.8, по большому счету, содержит только логику решения задачи, но никаких
подробностей этого решения, которые реализуются нашими пользовательскими
функциями. Если бы мне потребовалось загрузить с базы данных ЕКЕЭ и обрабо-
тать новый набор данных, я вполне спокойно могла бы применить этот сценарий,
что посмотреть, что из этого выйдет, поскольку в случае каких-либо проблем я
знаю точно, куда идти, чтобы их исправить. Например, в случае возникновения
каких-либо проблем форматирования с новым источником данных мне не потре-
буется "бродить” по всей программе, потому как я почти наверняка знаю, что
причина будет крыться в коде либо функции сгеа1:е_1:аЫе_гом(), либо функции сге-
а!е_те1а_1ех1(). Это означает, что для того, чтобы приспособить этот сценарий для
работы с новыми (подобными) источниками данных, нужно будет проверить (воз-
можно) всего лишь десяток строк кода. А это намного лучше, чем иметь дело с
почти 100 строками!
Иными словами, рефакторинг сценариев для выпаса данных обычно не требует
создания слишком большого объема дополнительного кода, но при необходимости
облегчит использование или адаптирование кода в будущем. Это поможет избавить
вас от необходимости исследования большего объема кода, чем это необходимо. То
есть рефакторинг — это еще один способ, который поможет вам масштабировать
свою работу по выпасу данных при использовании языкаРуЙюп.
Глобальные переменные
Глобальные переменные РуЙюп (см., например, листинг 8.8) отличаются от
обычных переменных двумя признаками:
1. Ключевое слово ^1оЪа1 указывает, что к данной переменной можно обращать-
ся (а также изменять ее) из любой функции сценария. Иными словами, она
имеет глобальную область видимости.
2. Глобальной переменной (как, например, 1:Ье_с1а1:етос1е) нельзя присвоить значе-
ние в строке ее объявления, как это можно делать с большинством других пе-
ременных (например, т.2_1аЫе_с1а1а = ЕаТзе). Вместо этого значение таким пе-
ременным присваивается в отдельной строке кода, следующей сразу же за
строкой ее объявления.
Сейчас вы, наверное, думаете: эй, а это ключевое слово §1оЪа1 — удобная штука.
Почему бы не сделать все мои переменные глобальными? Потому что, хотя гло-
бальные переменные могут быть полезными в некоторых случаях, их необходи-
мо использовать осмотрительно, как последнее средство.
Правда, использование глобальных переменных в коде может содействовать
уменьшению объема модификаций кода при рефакторинге сценария. Но недос-
татки использования большого количества глобальных переменных могут пре-
взойти преимущества рефакторинга, особенно когда идет речь об использовании
одинаковых имен переменных в разных функциях и сценариях. Со временем в
процессе разработки кода вы начнете вырабатывать свои собственные соглаше-
ния для именования переменных, а также создавать набор наиболее часто ис-
пользуемых функций. Но если какой-либо из ваших сценариев будет содержать
глобальную переменную с тем же самым названием, как и название переменной
в подключаемой функции, ваш код начнет вести себя странным и непредсказуе-
мым образом. Подобное наложение может привести вас к длительному поиску
причины проблемы, поскольку для ее обнаружения вам придется исследовать
каждую строку своего кода. Иными словами, глобальные переменные следует
использовать в высшей степени осторожно и крайне редко или вообще не ис-
пользовать, если это возможно.
Учитывая все причины не использовать глобальные переменные, почему я в дан-
ном случае все же решила использовать одну из них? Прежде всего, метод
зоигсе_могкЬоок.с1а1:етос1е может возвратить только одно возможное значение, по-
скольку каждая электронная таблица Ехсе1 имеет только один атрибут с1а1:етос1е. По-
этому, даже если определенная рабочая книга электронной таблицы имела бы 20
разных рабочих листов, содержащих 100 столбцов данных, для них всех будет
только одно, единственное, значение с1а1:етос1е. Получается, что значение с1а±етос1е в
действительности является "глобальным”, и будет логичным, чтобы глобальной
была и переменная, используемая для его хранения. А поскольку значение с1а±:етос1е
никогда не нужно будет обновлять в сценарии, то получение из него неожиданного
значения несет минимальный риск.
Но, как и в случае с любой писательским творчеством, эти решения являются де-
лом личных предпочтений, хотя собственные предпочтения также могут со време-
нем измениться. Мне сначала нравилась симметричность создания одной функции
для построения каждой строки табличных данных и другой функции для построе-
ния каждой строки текста метаданных, однако нарушение этой симметрии и не ис-
пользование глобальной переменной с!а1:етос1е также имеет свои достоинства. Соот-
ветствующий пример приводится в сценарии в листинге 8.9.
Листинг 8.9. Сценарий х18_те(а_апс1_с1а(е_раг8тд_геТас1огес1_адат.ру
# Преобразовываем табличные данные в файле .хТз в формат сз7 и сохраняем их
# в соответствующем файле. Также отделяем метаданные и сохраняем их в отдельном файле.
# Для работы с данными дат используем библиотеку хг!с1.
# Сначала установим библиотеку х1гб, используя установщик р1р:
# ЬТТрз: //рур!.огд/ргодесТ/хТгб/2.0.1/
# Импортируем библиотеку хТгс!
ТпрогТ хТгс!
# Импортируем библиотеку С87
ТпрогТ С87
# Требуется для проверки, является ли данное значение каким-либо типом числа
Тгоп пипЬегз ТпрогТ МитЬег
# Требуется для парсинга и форматирования наших дат ЕхсеТ
Тгоп с1а1е1гте ТтрогТ баТеТТте
беТ та!п():
# Загружаем данные в переменную зоигсе_могкЬоок посредством метода ореп_могкЬоок()
зоигсе_могкЬоок = хТгб.ореп_могкЬоок("ТгебдгарЬ.х1з")
# Открываем и именуем текстовый файл для хранения метаданных
зоигсе_могкЬоок_пеТабаТа = ореп("ТгебдгарЬ_теТабаТа. ТхТ", "м")
# Рабочая книга .хТз может содержать несколько листов
Тог зЬееТ-Пате 1п 8оигсе^огкЬоок.8ЬееТ_патез():
# Создаем переменную, указывающую на текущий рабочий лист
сиггепТ_зЬееТ = 8оигсе_могкЬоок.5ЬееТ_Ьу_пате(зЬееТ_пате)
# Создаем выходной файл х1з_"+8ЬееТ_пате+".С87 для текущего листа
оиТриТ_Т11е = ореп("хТ5_"+8ЬееТ_пате+"_с1аТе8.С87","1\/")
# Записываем строки в формате .сзу посредством метода мг1Тег()
оиТриТ_мг1Тег = сз7.мг1Тег(оиТриТ_Т1Те)
# Создаем булеву переменную для определения достижения табличных данных
1з_ТаЫе_с1аТа = РаТзе
# Обрабатываем в цикле Тог каждую строку в листе
Тог гом_пип, гом 1п епитегаТе(сиггепТ_5ЬееТ.деТ_гомз()):
# Извлекаем значение первого столбца текущей строки
Т1гзТ_епТгу = сиггепТ_зЬееТ.гом_з/а1иез(гом_пип)[0]
# Если строка заголовков таблицы данных
1Т Т1гзТ_епТгу == 'оЬзеп/аТ1оп_с1аТе':
# Присваиваем переменной флага 1з_ТаЫе_баТа значение Тгие
1з_ТаЫе_баТа = Тгие
# Если значение переменной т.5_1аЫе_с1аГа равно Тгие
1Г 1з_ГаЫе_с1аГа:
# Извлекает значения табличных данных в отдельные переменные
ГЬе_с1аГе_пит = сиггепГ_зЬееГ.гом_7а1ие5(гом_пип)[0]
116_7а1ие = сиггепГ_8ЬееГ.гом_7а1ие8(гом_пип)[1]
# Если ГЬе_с1аГе_пит содержит число, тогда текущая строка не является
# строкой заголовка, поэтому преобразовываем дату.
1Г 151пзГапсе(ГЬе_с1аГе_пит, 1\1ипЬег):О
ГЬе_с1аГе_пит = ГогтаГ_ехсе1^аГе(ГЬе_с1аГе_пит, зоигсе^огкЬоок. баГепобе)
# Записываем эту новую строку в наш выходной файл данных
оиГриГ_мг1Гег.мг1Гегом([ГЬе_с1аГе_пит, 116_7аТие])
# В противном случае эта строка должна быть метаданными
еТзе:
# Передаем требуемые данные функции сгеаГе_пеГа_ГехГ()
пеГас1аГа_'1Лпе = сгеаГе_пеГа_ГехГ(сиггепГ_8ЬееГ, гом_пип)
# Записываем эту новую строку в наш выходной файл метаданных
8оигсе_могкЬоок_пеГас1аГа.мг1Ге(теГас1аГа_ипе)
# На всякий случай закроем наши файлы
оиГриГ_П1е. с!озе()
5оигсе_погкЬоок_теГас1аГа. с!озе()
беГ ГогтаГ_ехсе1_баГе(а_ба1:е_пит, ГЬе_с1аГетос1е):
# Создаем объект РуГЬоп даГеГгте, используя метод х1_с1аГе_а5_баГеГГте()
# библиотеки х!гс1
а_с!аГе_пит = х!гб.х1баГе.х1с1аГе_а8_баГеГ1те(а_баГе_пит, ГЬе_с1аГетос1е)
# Создаем новый список, содержащий значение ГЬе_с!аГе_пит (в формате ММ/ДД/ГГГГ
# посредством функции 51гГМпе()) и значение во втором столбце
ГогпаГГес1_с1аГе = а_с1а1:е_пит.51:гГ1:гте()
геГи г п (Го гпаГГеб_с1аГе)
беГ сгеаГе_теГа_ГехГ(ГЬе_зЬееГ, ГЬе_гом_пип):
пеГа_1Лпе = ""
# Для форматирования файла метаданных обрабатываем отдельные ячейки каждой строки
# метаданных
Гог 1Геп 1п ГЬе_зЬееГ.гом(ГЬе_гом_пит):
# Записываем в ячейку значение с последующим символом табуляции
пе1:а_11пе = пе1:а_11пе + 1Лет.7а1ие + ' \±1
# В конце каждой строки метаданных добавляем символ новой строки
пе1:а_11пе = пе1:а_Ипе+'\п'
ге^игпСте^аЗт-пе)
1_-Г_папе_ == "_патп_
па1п()
О Хотя я больше не использую подход с передачей параллельной информации
пользовательской функции для создания выходных файлов метаданных и таблич-
ных данных, отказ от этой симметрии избавляет меня от необходимости создавать
глобальную переменную.
Какой из этих подходов лучше? Как и во всем, что касается творчества, это зависит
от личных предпочтений, вариантов использования и аудитории. Некоторые груп-
пы или организации будут иметь свое особое мнение касательно использования или
не использования глобальных переменных, некоторые отдадут предпочтение более
коротким решениям, а другие будут видеть ценность в структурной симметрии или
возможности повторного использования. Хотя в сценарии в листинге 8.9 я жертвую
некоторой структурной симметрией, в нем создается функция с более обширными
возможностями повторного использования. Решение же всегда остается за вами.
Использование рус1ос для документирования сценариев
и пользовательских функций
До сих пор мы применяли подход всестороннего, но в произвольной форме, доку-
ментирования разрабатываемого кода. В принципе, в этом нет ничего плохого, осо-
бенно если такой подход помогает обеспечить вообще ведение документирования.
Но по мере расширения вашего набора сценариев РуЙюп (и их целевой аудитории)
будет полезно иметь возможность проверить свою коллекцию пользовательских
функций без необходимости открывать и исследовать каждый соответствующий
файл по отдельности. Наличие большого количества открытых файлов значительно
повышает вероятность внесения ошибки в какую-либо широко используемую
функцию в результате, например, нечаянного нажатия клавиши. А это может очень
быстро испортить весь ваш день.
К счастью, добавив лишь немного форматирования, мы можем адаптировать наши
существующие комментарии и описания программ для работы с функцией команд-
ной строки рус!ос. Это позволит нам выводить описание сценариев и функций в окно
командной строки, просто предоставив соответствующее название файла.
Чтобы продемонстрировать эту возможность в действии, начнем с рефакторинга
еще одного сценария. На этот раз мы модифицируем исходный код сценария в лис-
тинге, несколько сократив его размер. В процессе я также обновлю комментарии в
начале сценария (а также добавлю некоторые комментарии к новой функции), что-
бы сделать их совместимыми с командой рудое. Конечный результат приводится в
листинге 8.10.
Листинг 8.10. Сценарий Нхед_^1с1й1_8(пр_раг81пд_ге{ас(огес1.ру
Форматер данных управления МОАА О
Считывает данные из файла данных управления МОАА фиксированной ширины средствами РуГЬоп
и сохраняет их в новый файл в формате С87 (значения, разделенные запятыми)
Исходный файл данных для этого примера предоставлен управлением МОАА и доступен здесь:
ЬГГрз: //ши1. псдс. поаа. доу/риЬ/да1а/дЬсп/даг1у/дЬспс1- зГаПопз. 1x1:
Метаданные для файла доступны здесь:
ЬГГрз: //ши1. псдс. поаа. доу/риЬ/да-Ьа/дЬсп/да-Цу/геадте. ЪЛ
Доступные функции
* сопуег1_1о_со1иг1П5: Преобразовывает строку текста в список
Педиггетеп15
* Модуль С8У
# Начинаем с импортирования библиотекы С87
1прог1: С87
деГ па1п():
# Переменная для сопоставления названия выходного файла названию входного файла
Шепапе = "дЬспб-з^а^топз"
# Открываем файл данных для чтения, указывая режим г
8оигсе_ГИе = ореп(Г1Лепапе+".Ь<1:", "г")
# Метод "геасПлпезО" пе1±ос1 преобразовывает содержимое текстового файла в список
# строк
51аМоп8_1л81: = 5оигсе_ГИе. геасЛлпезО
# Создаем выходной файл для записи модифицированного набора данных
ои1ри1_^Ие = ореп(П1епапе+".с57","м")
# Используем библиотеку С87 для создания средства записи, которое предоставляет
# нам удобные функции для создания нового файла в формате С57
ои1риО/г1Лег = С87.иг11:ег(ои1:ри1:_Г1Ле)
# Эти заголовки нужно создавать "вручную", используя содержимое файла геаскпеЛх!'
Ьеас1ег5 = ["ГО", "1_АТ1Т1ЮЕ", "ЮМС1Т1ЮЕ", "Е1_Е7АТЮМ", "5ТАТЕ", "МАМЕ",
"С5М_Е1_АС", "НСМСРМ_Е1_АС", "НМ0_ГО" ]
# Создаем список "кортежей", содержащих указатель начала и конца каждого столбца
со1ипп_гапде$ = [(1,11),(13,20),(22,30),(32,37),(39,40),(42,71),(73,75),
(77,79),(81,85)] О
# Записываем заголовки в выходной файл
ои1:ри1:_мг11:ег.мг11:егом(Ьеас1ег8)
# Обрабатываем в цикле каждую строку файла
1"ог Ппе 1п 21аНоп5_1л51:
# Отправляем данные для форматирования
пем_гом = соп7ег1:_1:о_со1итп5(11пе, со1ипп_гапдез)
# Записываем пем_гом в выходной файл, используя метод мгтЛегоиО
ои^ри'Мл/гтЛег. мг11:егом( пем_гом)
# На всякий случай закрываем выходной файл
ои1ри1_Гг1е. с!озе()
бе!2 соп7ег1:_1:о_со1итп5(ба1:а_ипе, соТитпЗп^о, гего_1пс1ех=Еа15е): О
"""Преобразовывает строку текста в список на основе предоставленных пар указателей
Параметры
с1а1а_Ппе : 51г
Строка текста для обработки
со1итп_гпТо : Список кортежей
Каждый кортеж содержит указатель начала и конца каждого столбца данных
гего_гпдех: ЬооТеап, орНопа!
Если ЕаХзе (состояние по умолчанию), указатель начальной позиции уменьшается
на единицу
Возвращает
Список
Список значений с удаленными начальными и конечными пробелами
1ПП!
пем_гом = []
# Функция полагает, что предоставляемые указатели имеют НЕНУЛЕВОЕ начальное
# значение, поэтому уменьшаем начальные значения указателей на 1
1пс1ех_оН5е1: = 1
# Если со1ипп_1пГо имеет начальное нулевое значение, то тогда начальные значения
# указателей не уменьшаем на 1
1Г гего_1пс1ех:
1пбех_оГГзеГ = 0
# Обрабатываем в цикле список указателей столбцов
Гог 1пс1ех_ра1г 1п со1ипп_1пГо:
# Извлекаем начальное значение, модифицированное значением 1пбех_оГГзеГ
зГагГ_1пс1ех = 1пбех_ра1г[0]-1пбех_оГГзеГ
# Извлекаем конечное значение
епс1_1пс1ех = 1пс1ех_ра1г[1]
# Удаляем пробелы вокруг данных
пем_гом. аррепб ((с1аГа_1Лпе [ зГагГ_1пс1ех: епс1_1пс1ех]). зГг1р())
# Возвращаем данные с удаленными пробелами
геГигп пем_гом
1Г___папе__ == "__па1п__
па1п()
О Заключение описания основного сценария и функции соп7егГ_Го_со1итпз() в три
двойные кавычки (""") выделяет их визуально от остальных комментариев в коде и
делает их доступными для отображения в окне командной строки, исполняя коман-
ду рус!ос9:
рубос Г1хеб_м1бГЬ_8Гг1р_рагз1пд_геГасГогеб
В результате исполнения этой команды в окне консоли отобразятся описания ос-
новного сценария и функции. Если весь текст не помещается в окно консоли, то по
нему можно перемещаться построчно, используя стрелки вверх и вниз, или постра-
нично, используя клавиши <Р§Лр> или <Р§Вп>.Чтобы завершить просмотр доку-
ментации и возвратиться в режим командной строки, нажмите клавишу <Р>.
О Вместо того чтобы создавать уникальную строку кода для извлечения каждого
столбца данных из заданной строки текста, я поместила все начальные и конечные
значения для каждого столбца в список кортежей РуШоп (Ьйр://Дос8.ру1:Ьоп.ог§/
3.3/ПЬгагу/81с11уре8.И1т1?И1цИПцИ1=1ир1е#1ир1е8), которые представляют собой, по
сути, неизменяемые списки.
О Передавая функции соп7егГ_Го_со1ипп5() вместе с каждой строкой информацию о
начале и конце столбца, мы можем преобразовать текст в столбцы посредством
9 В данном случае я использую смесь разных стилей структурирования и форматирования, взятых из
руководства, доступного по адресу ЬПр8:// геа1руЩоп.сош/(1оситепНп§-руЩоп-со(1е/#(1оситепНп§-
уо11г-ру1:Ьоп-сос1е-Ьа8е-и8т§-с1ос81тт§8. И если при работе в большой команде может быть важным
использование стандартного метода, то для самостоятельной работы или работы в небольшой группе
предпочтительнее использовать стиль, наиболее удобный для вас.
цикла ^ог.Лп. Это делает наш основной сценарий более удобочитаемым, а также
создает функцию, которую можно использовать повторно с любой строкой текста,
при условии передачи ей пар указателей начала и конца столбца в правильном
формате. Я даже добавила флаг гего_1пс1ех, который позволяет использовать эту
функцию с парами указателей с нулевым значением первой позиции (по умолчанию —
значение первой позиции, равное 1, как это сделано и в данном наборе данных).
Обратите внимание на то, что кроме просмотра документации для всего сценария
посредством команды рус!ос можно просматривать документацию и для отдельной
функции (например, для функции соп7егГ_1:о_со’1итп8)), исполнив ее следующим об-
разом:
рус1ос Г1хес1_\л/1с11:11_51:г1р_раг81пд_ге12ас1:огес1. соп7ег1:_1:о_со1итп8
Перемещение по документации отдельной функции осуществляется тем же самым
образом, как и по документации для всего сценария.
Перемещения по документации в командной строке
Документацию для любого сценария РуЙюп (при условии, что она была от-
форматирована должным образом) можно просмотреть в консоли командной
строки, исполнив следующую команду:
рус1ос название_файла_6ез_расширения_.ру
Подобным же образом можно просмотреть и документацию для любой функ-
ции в сценарии, исполнив следующую команду:
рус1ос название_файла_6ез_расширения_.ру.название_функции
Поскольку по отображаемому таким образом тексту нельзя перемещаться при
помощи мыши, для этого используются следующие клавиши на клавиатуре:
Стрелка вверх/стрелка вниз
Перемещение на одну строку вверх или вниз.
Клавиша пробела
Перемещение на одну страницу вниз.
Клавиша <О>
Завершение просмотра документации.
О полезности аргументов командной строки
Кроме рефакторинга длинного сценария в набор функций существуют и другие
способы для повторного использования кода выпаса данных. Для многошаговых
процессов выпаса данных (например, содержащие загрузку данных, как в сценарии
в листинге 5.8, или для преобразования изображений РВР в текст, как в сценарии в
листинге 4.20) разбиение кода на несколько сценариев будет еще одним способом
сэкономить время и усилия. Прежде всего, этот подход позволяет свести к мини-
муму выполнение ресурсозатратных задач, таких как, например, загрузка данных
или преобразование страниц РВГ-документа в изображения. Более того, такие за-
дачи, как правило, довольно рутинные. Нет большой разницы, какие именно дан-
ные загружать или какой РВБ-документ преобразовывать в изображения, и наш
сценарий, по большому счету, будет делать одно и то же. Нам требуется всего лишь
несколько дополнительных приемов, чтобы преобразовать наши специальные сце-
нарии для выпаса данных в автономный код, пригодный для многократного по-
вторного использования без каких-либо дополнительных модификаций.
Для примера рассмотрим снова сценарий из листинге 5.8. Основная исполняемая в
нем задача — загрузка содержимого веб-страницы, но в данном случае адрес целе-
вого файла (йКр://ууеЬ.т1а.1пГо/йеуе1орег8/1игп8Й1е.Ыт1) жестко прописан в сце-
нарии. Подобным образом, код в листинге 5.1 для загрузки файлов ХМЬ и 18ОХ
почти идентичен данному, с единственной существенной разницей, состоящей в
использовании других 1ЖЬ-адресов и названий локальных файлов. В долгосрочной
перспективе мы могли бы сэкономить много времени и усилий, выполнив рефакто-
ринг этих сценариев, чтобы они работали подобно функции.
К счастью, благодаря библиотеке РуЙюп аг§рагзе, это вполне достижимая цель для
автономных сценариев РуЙюп. Эта библиотека позволяет создавать сценарии, ко-
торые как требуют, так и используют аргументы, передаваемые им из командной
строки. Таким образом, нам не нужно создавать новый сценарий для каждой от-
дельной веб-страницы, которую мы хотим загрузить, поскольку мы можем указать
как целевой ИКЬ-адрес, так и название выходного файла из командной строки. Со-
ответствующий сценарий приводится в листинге 8.11.
Сценарий 8.11. Сценарий меЬрадезауег.ру
""" НеЬ раде 8ауег!
Загружает и сохраняет содержимое веб-страницы на локальном устройстве
Использование
руЬЬоп иеЬраде_зауег.ру 1агде1_иг1 РПепате
Параметры
1агде1_иг1 : 51г
Полный 1Ж1_-адрес веб-страницы, подлежащей загрузке
ТПепате : 51г
Желаемое название файла локальной копии данных
Требования
* Модуль агдрагзе
* Модель гедиез^з
11111!
# Импортируем библиотеку гециез^з для получения данных из Интернета
Тпрог!: гециез^з
# Импортируем агдрагзе для извлечения аргументов из командной строки
Тпрог!: агдрагзе
# Создаем новый экземпляр Агдипеп1:Раг5ег()
рагзег = агдрагзе.Агдипеп1:Раг5ег()
# Аргументы будут присваиваться в порядке их предоставления
рагзег. абб_агдитеп1:( "-|:агде1:_иг1.", Ье1р="Ри1Л 11К1_ о!7 меЬ раде 1о Ье боипТоабеб")О
рагзег.абб_агдитеп1("Г11епате", ЬеТр="ТЬе безтгеб Шепате о!2 ±Ье ТосаТ сору")
агдз = рагзег.раг5е_агдз()
# Извлекаем из предоставленных аргументов 0Р1_-адрес веб-страницы, из которой следует
# выполнять загрузку
1агде1_иг1 = агдзЛагде1:_иг1
# Извлекаем из предоставленных аргументов требуемое название локального файла
# для хранения полученных данных
ои^ри^-ГИепапе = агдз.ТИепапе
# Создаем заголовочную информацию для нашего запроса веб-страницы
Ьеабегз = {
'Озег-АдепС : 'МогШа/5.0 (XII; СгО5 х86_64 13597.66.0) ' + \
'Арр1е1л1еЬК11:/537.36 (КНТМ1_, Ике Сеско) ' + \
'СИготе/88.0.4324.109 8аТаг!/537.36',
’Егогп’: 'У011Р МАМЕ НЕРЕ - уоигета11аббгез8@етаТ1рго71.с1ег .зот'
}
# Поскольку мы загружаем обычную веб-страницы, отправляем по 0Р1_-адресу запрос СЕТ
# вместе с заголовочной информацией
шеЬраде = гедиез^з.де^агде^иг!, Ьеабегз=Ьеабегз)
# Открываем локальный файл в режиме записи для сохранения содержимого загруженной
# веб-страницы
ои1ри1_Н1е = ореп(ои1:ри1:_Т11епате,"м")
# Код веб-страницы имеет свойство 1ех1 ответа веб-страницы, поэтому записываем
# это в наш файл
ои^ри^НТе. мг11:е(меЬраде. 1ех1)
# Закрываем выходной файл
ои1ри1_Н1е. с!озе()
О Здесь мы присваиваем название параметра, которое можно использовать для об-
ращения к значениям, передаваемым из сценария через командную строку. Спра-
вочный текст имеет важность! Делайте его описательным, но кратким.
На помощь!
Данный код использует позиционные аргументы в командной строке вместо
необязательных аргументов, потому что он не сможет делать ничего полезно-
го без наличия хотя бы аргументов целевого 1ЖЬ-адреса 1агде1_иг1 и ГИепапе
для хранения полученных данных.
Но каким образом пользователь, который не знаком с нашим сценарием, мо-
жет знать об этом? Очевидно, что у нас имеется документация для просмотра
при помощи команды рус!ос, но у нас также есть текст справки, добавленный к
каждому аргументу. Таким образом, новый пользователь узнает требуемые ар-
гументы, исполнив следующую команду:
ру1±оп меЬраде_8ауег.ру -Ь
В результате в консоли командной строки выводится краткая инструкция по
использованию нашего сценария (по крайней мере, что касается требуемых ар-
гументов).
Теперь у нас должен быть легкий способ для загрузки любой веб-страницы на ло-
кальное устройство, без необходимости создавать сценарий для каждого нового
1ЖЬ-адреса. Например, исполнение следующей команды:
ру1±оп меЬраде_за7ег. ру " Н11р: //шеЬ. п1:а. т.пГо/с^е’Еорегз/ГигпзШе. ЫтП"
\"МТА_1:игп5Ше8_1пс1ех.Ы:т1"
дает точно такие же результаты, как и сценарий в листинге 5.8. Но исполнив ко-
манду:
ру1±оп меЬраде_за7ег.ру \
"Ы=1:р8: /. сШЫкепус. соп/зуз^еп-^а1:а/орега1:1пд- герог1=5" \
"сН1Ь1ке_орега1:1пд_герогТ5.
мы сможем получить операционные отчеты компании СШ В1ке без необходимости
даже открывать, не то что модифицировать наш сценарий. Удобно, не так ли?
Использование аргументов командной строки со сценариями, специфич-
ными для определенного типа задач, может сэкономить время, но не в
том случае, если ИКЬ-адреса нужно вводить в командную строку вруч-
ную с клавиатуры. Чтобы упростить себе жизнь, рассмотрим вкратце, как
можно копировать требуемые аргументы и вставлять их в командную
строку, в зависимости от используемой операционной системы.
1лпих (включая СкготеЪоок)
Выделите ХЖЬ-адрес, который нужно скопировать, затем щелкните по
выделению правой кнопкой мыши и в открывшемся контекстном меню
выберите опцию Копировать. В командной строке просто щелкните
мышью, и скопированный текст вставится автоматически.
У^1П(1о^8/МаС1П1О8к
Выделите ХЖЬ-адрес, который нужно скопировать, затем щелкните по
выделению правой кнопкой мыши и в открывшемся контекстном меню
выберите опцию Копировать. В окне командной строки снова щелкните
правой кнопкой мыши и в контекстном меню выберите опцию Вставить.
Отличия во взаимодействии со сценариями
в автономных файлах и блокнотах
Вы, возможно, заметили, что в предыдущих разделах я не описывала способа пере-
дачи аргументов через командную строку в блокнотах 1иру1ег. Я также не очень
обсуждала создание документации для сценариев и функций для блокнотов 1иру1ег
и последующее взаимодействие с этой документацией. Но это объясняется не не-
возможностью таких действий, а тем, что взаимодействие пользователей с РуЙюп в
блокнотах 1иру1ег осуществляется по-другому, нежели в автономных сценариях,
поэтому некоторые из этих концепций менее применимы к ним. Я начала работать
с РуЙюп до появления блокнотов 1иру1ег (ранее ГРуЙюп), поэтому по-прежнему
склоняюсь к использованию автономных сценариев для большинства своих повсе-
дневных задач выпаса данных на РуЙюп. Хотя блокноты (обычно) замечательно
подходят для тестирования и настройки фрагментов кода, после определения рабо-
чего подхода для конкретной задачи по выпасу данных, в конечном итоге я почти
всегда перехожу к автономным сценариям. Это объясняется тем, что по мере уг-
лубления в проект у меня часто не хватает терпения даже для открытия и модифи-
цировании сценария (если только это абсолютно не необходимо), не говоря уже о
запуске веб-сервера или ожидании загрузки веб-страницы. (К тому же, может ока-
заться так, что даже нет подключения к Интернету!) Это несколько из тех причин,
по которым я предпочитаю использовать аргументы командной строки и автоном-
ные сценарии для повседневных, несложных задач.
Но, как мы вскоре увидим, когда дело доходит до экспериментирования (и осо-
бенно совместного использования) с анализом данных и визуализации нашей
работы по выпасу данных, то интерактивные возможности блокнотов 1иру1ег де-
лают их несколько предпочтительней автономных сценариев РуЙюп. Поэтому
блокнотам 1иру1ег уделяется больше внимания в главах 9 и 10, в которых рас-
сматриваются эти темы.
Заключение
В этой главе мы значительно отошли от предмета непосредственно выпаса данных,
чтобы пересмотреть некоторые аспекты нашей предыдущей работы, где код стал
громоздким. Посредством процесса рефакторинга мы исследовали возможность
реорганизации просто работающего кода в хорошо работающий код, поскольку
такой код удобно читать и повторно использовать. По мере развития наших проек-
тов по выпасу данных это поможет нам создать и использовать собственную кол-
лекцию пользовательских функций, служащих для удовлетворения наших конкрет-
ных требований по выпасу данных. Подобным образом, немного повысив уровень
структуризации нашей документации, мы сделали ее доступной непосредственно
из командной строки, чтобы можно было найти требуемый нам сценарий или
функцию, не открывая для этого ни одного файла сценария. Мы применили к на-
шим сценариям логику рефакторинга, чтобы можно было настраивать их функцио-
нальность, также не открывая их.
В следующей главе мы снова обратим наше внимание на работу с данными и при-
ведем обзор некоторых важных методов анализа данных. Затем, в главе 10, мы
вкратце рассмотрим основные методы визуализации, которые помогут вам эффек-
тивно понимать и представлять свои данные, чтобы можно было лучше поделиться
своими знаниями, полученными в результате выпаса данных, с остальным миром.
ГЛАВА 9
Введение в анализ данных
Выше в этой книге основное внимание уделялось способам нахождения данных, а
также их оценке, преобразованию и дополнению. Мы исследовали, как написать
код, достающий данные из Интернета, извлекающий их из неудобных форматов,
оценивающий их полноту и учитывающий, есть ли в данных какая-либо несогласо-
ванность. Мы даже обдумали, как гарантировать, что используемые нами инстру-
менты (наши скрипты на РуЙюп) действительно со всем этим справятся, и как оп-
тимизировать их под наши потребности, в том числе — на будущее.
Но в данный момент пришло время вернуться к вопросу, почему все это работает.
Еще в разд. "Что такое выпас данных" главы 1 я описала, зачем нужен выпас дан-
ных, т. е. преобразование ”сырой” информации в осмысленный материал, на основе
которого могут быть сделаны выводы. Если не углубляться в анализ, то мы не уз-
наем, были ли достаточны наши усилия по выпасу данных — и какие выводы на их
основе можно было бы сделать. Поэтому остановить выпас наших данных на этапе
аугментации/преобразования — все равно, что разложить все нужное для готовки —
и выйти из кухни. Никто не будет битый час шинковать овощи и подбирать другие
ингредиенты, если не собирается готовить. То же касается и анализа данных: берем
все красиво очищенные и препарированные данные — и превращаем их в новые
открытия и знания.
Если вы опасаетесь, что мы вновь скатываемся в абстракции, не волнуйтесь —
основы анализа данных просты и достаточно конкретны. Как и оценка данных,
анализ данных примерно на три четверти состоит из рассуждений и на четверть —
из технических деталей. Да, в основы анализа данных заложена бесспорная матема-
тика в стиле ”2 + 2 = 4”, но выводы зависят от интерпретации результатов этих
весьма простых формул. Именно здесь в работу включаются логика и изыскания —
наряду с человеческим здравым смыслом и экспертным опытом — в качестве свя-
зующего звена.
В рамках этой главы мы исследуем основы анализа данных — в частности, такие
простые показатели, как центральная тенденция и распределение, помогающие
поместить данные в информативный контекст. Мы также рассмотрим эмпириче-
ские правила, помогающие делать верные выводы о данных на основе этих показа-
телей и на основе роли визуального и числового анализа в понимании тенденций и
аномалий в рамках нашего датасета. В завершение этой главы мы поговорим об
ограничениях анализа данных и постараемся объяснить, почему для перехода от
”что” к ”почему” одним анализом данных как таковым не обойтись. Мы также рас-
смотрим, как РуЙюп может помочь нам с решением этих задач и почему он так
уместен для любых дел — от быстрых расчетов до подробных визуализаций.
Вся суть — в контексте
Если бы я прямо сейчас предложил вам яблоко за 50 центов, вы бы купили? Ради
примера предположим, что вам нравятся яблоки и сейчас вы не прочь перекусить.
А еще это красивое яблоко: лоснящееся, ароматное, тяжеленькое. Также будем ис-
ходить из того, что вы уверены: я предлагаю вам яблоко без злого умысла. Это про-
сто классное свежее яблоко за 50 центов. Вы бы купили?
Большинство людей ответит: по ситуации. От чего зависит решение? От множества
вещей. Независимо от того, насколько вы мне доверяете (и безотносительно моего
почти идеального яблока), если рядом со мной окажется кто-нибудь, продающий
”не менее хорошие яблоки” по 25 центов за штуку, вы решите купить яблоко у не-
го, а не у меня. Почему? Очевидно, потому, что у него яблоки дешевле, и даже мое
невероятно аппетитное яблоко, пожалуй, не лучше двух яблок конкурента. В то же
время, если бы по другую руку от меня стоял ваш кузен и продавал вкусные яблоки
по 60 центов за штуку, то вы, вероятно, предпочли бы его яблоки, просто из соли-
дарности с ним, желая поддержать его ”стартап” по продаже яблок.
Такой процесс принятия решений может показаться весьма сложным, если речь
идет всего-то о выборе фрукта, но на самом деле мы принимаем такие решения все
время, и результаты порой бывают удивительны. Некоторые экономисты, например
Дэн Ариели и Тим Харфорд, проводили исследования, показывающие, каково
влияние ''простого” подарка, пусть вам и придется на него немного потратить, или
как может падать наша удовлетворенность собственным жалованьем, если мы уз-
наем, сколько зарабатывают наши коллеги1. Большинство наших приоритетов и
решений зависят от оценочных суждений, и чтобы эти суждения были эффективны,
мы должны знать, какие у нас есть варианты. Купил бы я сказочно бесподобное
яблоко за 50 центов? Может быть. Но, пожалуй, только если бы спешил, и если бы
за другим яблоком мне пришлось пройти еще километр. Хотя все мы понимаем, что
такое "по ситуации”, правильнее в данном случае говорить "зависит от контекста".
Именно по причине важности контекста разрозненные точки данных сами по себе
бессмысленны. Даже если данные фактически "верны", единичный фрагмент дан-
ных не поможет нам принять решение. Генерация и усвоение новых знаний, в
принципе, сводится к привязке новой информации к той, что нам уже известна.
Иными словами, знания заключены не "в данных" как таковых, а в соотношении
данных с контекстом. Поскольку невозможно исчерпывающим образом исследо-
вать контекст каждой ситуации, в которой требуется принимать решение (в том
числе, каковы ваши соображения по поводу яблочного стартапа кузена и важности
1 Для дополнительной информации читайте "Предсказуемо иррациональный" Дэна Ариели.
поддержать семейное дело), часто приходится ограничиваться изучением тех эле-
ментов контекста, которые можно (или решено) измерить и количественно выра-
зить. Иными словами, обратимся к данным.
Как мы извлекаем контекст из данных? Например, нужно исследовать, откуда они
взялись, задаться вопросом, кто их собрал, когда и зачем — эти вопросы помогают
уточнить, что включено в данные и чего в них может недоставать. Кроме того, изы-
скиваем способы систематически сравнить каждую точку данных со всеми остав-
шимися, чтобы было понятно, насколько эта точка соответствует любым законо-
мерностям, присутствующим в целостном датасете, либо нарушает их. Разумеется,
все это не приведет нас к однозначным ”ответам”, но многое для нас прояснит и
выведет нас на идеи, которыми мы сможем поделиться с другими. Эти выводы на-
толкнут нас на следующий вопрос о том, что происходит в окружающем мире.
Одинаковые, но не совсем
Насколько важно выстраивать контекст для извлечения ценной информации из
данных и откуда узнать, какие контексты важны? Учитывая, что насчитывается
бесконечно много типов отношений, которые мы могли бы выявить — даже среди
точек данных в совсем небольшом датасете — как нам определиться, на которые из
них обратить внимание? Возьмем, к примеру, данные из листинга 2.10, где содер-
жится простой (выдуманный) список номеров страниц:
раде_соип1=8 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
В данном случае, имея дело даже с небольшим количеством чисел, можно предста-
вить немало типов ”контекста”. К примеру, можно отметить, что здесь встречаются
как четные, так и нечетные числа, а также, что некоторые числа здесь делятся без
остатка на 8. Проблема в том, что большинство из этих отношений не особо инте-
ресны и важны. Как же нам найти среди них заслуживающие внимания?
Оказывается, человеческий мозг особенно хорошо подходит для того, чтобы заме-
чать и отыскивать отношения двух типов: одинаковости и несхожести. Наше вни-
мание привлекают тенденции и аномалии такого рода, каков бы ни был конкретный
стимул — от усмотрения закономерностей в контурах облаков или результатах ло-
терей до быстрого выявления отличий в ориентации похожих объектов. Это озна-
чает, что тенденции и отклонения от них интересны практически по определению.
Поэтому, когда мы хотим выстроить осмысленный контекст для наших данных,
весьма целесообразно для начала выяснить, каким образом отдельные записи в
конкретном датасете схожи друг с другом или отличаются друг от друга.
Что типично? Оценка центральной тенденции
Что такое ”среднее” значение какого-либо показателя? Употребляя этот термин в
обыденной жизни, мы часто понимаем ”среднее” как ''непримечательное”, "ожи-
даемое” или "типичное”. С учетом таких особенно заурядных ассоциаций, зачастую
”среднее” также может стать синонимом ”скучному”.
Однако, когда речь заходит об анализе данных, оказывается, что именно ”среднее”
нас и интересует, так как именно на основе среднего производится сравнение, а
сравнения — одна из тех вещей, которые особенно интересны человеку. Помните
исследование о зарплатах? Чисто по-человечески мы хотим знать, как соотносятся
затрагивающие нас вещи с показателями, ''типичными” для других людей. Поэто-
му, даже если мы никогда и не стремились быть "средними”, в целом, мы все равно
хотим знать, чему равно "среднее” и как с ним соотносится "наше”.
Что это значит?
Возможно, вам уже знакомо, как вычисляется "среднее” от множества чисел: скла-
дываем их все и делим на количество слагаемых. Данный конкретный параметр
центральной тенденции более известен как среднее арифметическое и подсчиты-
вается именно так, как мы только что описали. Итак, для нашей переменной
раде_соип1=5 математика будет такова:
теап_рдз = (28+32+44+23+56+32+12+34+30)/9
что даст нам среднее значение в
32,333333333333336
Такой подход кажется весьма разумным для представления "типичной” длины гла-
вы; во многих наших главах действительно плюс-минус 30 страниц, есть даже две
главы ровно по 32 страницы. Поэтому среднее количество страниц по главам чуть
выше 32 страниц кажется относительно правильным.
В данном случае этот подход действительно нам помог, однако во многих ситуаци-
ях полагаться на среднее как на меру "типичности” бывает ошибочно. Допустим,
что в книгу добавилась одна очень длинная глава (скажем, 100 страниц). Метод
подсчета среднего не изменится:
теап_рдз = (28+32+44+23+56+32+12+34+30+100)/10
Но теперь среднее значение составит:
39,1
Совершенно внезапно наша "средняя” длина главы увеличилась почти на 6 стра-
ниц, хотя мы и добавили всего одну новую главу, а половина наших глав имеет
длину 28-34 страницы. Настолько ли "типична” в данном случае глава длиной 39
страниц? Однозначно — нет.
Даже на данном небольшом примере мы четко видим, что хотя в некоторых случа-
ях среднее является адекватной мерой "типичности”, на этот показатель сильно
влияют экстремумы — и всего одного такого значения достаточно, чтобы среднее
арифметическое по датасету полностью девальвировалось. Но какие у нас есть аль-
тернативы?
Берем на вооружение медиану
Еще один подход к определению ”типичных” значений по датасету — выявить, ка-
кое из них (в буквальном смысле) находится посередине. В анализе данных "сре-
динное” значение в последовательности записей называется медианой, и для его
нахождения требуется даже меньше математических расчетов, чем для вычисления
среднего арифметического. В данном случае нам понадобится всего лишь отсорти-
ровать и посчитать. Например, в нашем исходном множестве длин глав отсортиру-
ем значения от наименьшего к наибольшему2:
раде_соип1=5 = [28, 32, 44, 23, 56, 32, 12, 34, 30]
раде_соип1=8. зог1:()
р г1п1: (раде_соип1=5)
Имеем:
[12, 23, 28, 30, 32, 32, 34, 44, 56]
Теперь нам всего лишь остается выбрать ”срединное” значение — то, что располо-
жено на полпути между первой и последней позицией в списке. Поскольку в этом
списке девять элементов, это будет пятое значение (по обе стороны от него будет
по четыре элемента). Следовательно, медианное значение нашего датасета
раде_соип1: равно 32.
Теперь давайте посмотрим, что произойдет с медианой, если мы добавим в книгу
сверхдлинную главу. В отсортированном виде наши данные примут вид:
[12, 23, 28, 30, 32, 32, 34, 44, 56, 100]
А что же медиана? Поскольку теперь список состоит из четного количества эле-
ментов, мы можем просто взять два ”средних” значения, сложить их и разделить на
два3. В данном случае речь идет о значениях на позициях 5 и 6, их сумма равна 32.
Соответственно, имеем медианное значение (32 + 32)/ 2 = 32. Пусть мы и добави-
ли еще одну очень длинную главу, медианное значение не изменилось!
На первый взгляд может показаться: "Постойте, это же неверно. Добавилась целая
новая глава — очень длинная глава, а значение медианы совсем не изменилось.
Разве не должно оно было сдвинуться, хотя бы чуть-чуть?”
Реальная разница между средним и медианным значением заключается в том, что
на среднее сильно влияют конкретные значения, содержащиеся в датасете, — на-
сколько они высокие или низкие. В свою очередь, на медиану влияет в основном
частота встречаемости определенных значений. Таким образом, подход с медиа-
ной гораздо ближе к понятию ”одно значение, один голос”, тогда как при использо-
вании среднего крайние значения могут получаться "слишком громкими". По-
скольку в настоящий момент мы стремимся понять, какие значения являются "ти-
пичными" для нашего датасета, медиана всегда будет давать наиболее репрезен-
тативный результат.
2 В частности, для сумм, расположенных непосредственно ниже или выше этих значений.
3 Подробнее см. на сайте кПр8:/Лу\ууу.8Ьа.§оу/с1оситеп1:/8иррог1>Гад-ррр-Ьогг(лтег8-1еп(1ег8.
Поразмыслим нестандартно: выявляем выбросы
В разд. "Одинаковые, но не совсем " я отметила, что человеческие существа в прин-
ципе интересуются "схожестью” и ”несхожестью”. Рассматривая две наши возмож-
ные меры центральной тенденции, мы исследуем, в каком отношении значения в
датасете могут быть похожи. Но что насчет качеств, которыми они отличаются?
Возвращаясь к исходному списку раде_соип1=, мы полагаем, что 32-страничная глава
вполне может считаться типичной, а может быть, даже 30-, 28- и 34-страничные
главы — тоже. А что насчет 12-страничной главы? А 56-страничной? Они явно ка-
жутся нетипичными, но откуда нам знать, какие значения достаточно отличаются
от массы, чтобы считать их по-настоящему ”необычными”?
Именно здесь мы начинам объединять математику и здравый смысл. Меру цен-
тральной тенденции можно вычислить вполне однозначно, но определить, какие
значения в датасете по-настоящему необычны — т. е. являются аномалиями или
выбросами. — средствами одной лишь арифметики невозможно. По мере увеличе-
ния и усложнения датасетов человеку становится все сложнее эффективно интер-
претировать их как наборы точек данных4. Поэтому в самом ли деле можно судить
о больших датасетах с точки зрения человеческого здравого смысла? Здесь нам
придется прибегнуть к крупнейшему и самому универсальному механизму обра-
ботки данных: человеческому зрению5.
Визуализация для анализа данных
Роль визуализации в работе с данными является двоякой. С одной стороны, визуа-
лизация может помочь проанализировать данные и осмыслить их; с другой — по-
могает передать те выводы, которые удалось получить по результатам сделанного
анализа. Использование данных именно со второй целью — в качестве средства
коммуникации, позволяющего поделиться выясненной нами информацией о дан-
ных с более широкой аудиторией — это тема, подробно рассматриваемая в главе
10. Здесь мы сосредоточимся на том, каким образом визуализация может помочь
нам яснее увидеть уже имеющиеся у нас данные.
Чтобы понять, как при помощи визуализации идентифицировать экстремальные
значения в наших данных, придется — в самом буквальном смысле — посмотреть
на данные. Здесь я не имею в виду, что мы должны открыть С8У-файл и присту-
пать к прочесыванию строк с данными. Вместо этого мы создадим особую столбча-
тую диаграмму, которая называется гистограмма, где каждый столбец показывает,
4 Хотя точные оценки количества объектов, которые человек способен хранить в рабочей памяти,
разнятся, исследователи согласны с тем, что эта способность ограничена. См. на сайтах
ЬПр8://псЬ1.п1ш.тЬ.§оу/ртс/агНс1е8/РМС2864034 и ЬПр8://рпа8.ог§/соп1еп1:/113/27/7459.
5 Хотя до достижения эффективных результатов еще далеко, ведутся разнообразные исследования в
области тактильной графики. Эти решения позволят снизить зависимость визуальных подходов от
качества зрения, особенно для слепых или слабовидящих людей. См. на сайте ЬНр://8Ьаре.81апГог(1.е(1и/
ге8еагсЬ/соп81гисйуеУ1гАссе88/а88е1820-88.р(1Г.
сколько раз конкретное значение встречается в нашем датасете. Например, на рис. 9.1
показана очень простая гистограмма нашего (расширенного) датасета раде_соип1=.
Количество
Рис. 9.1. Простая гистограмма
Гистограмма для столь малого датасета, как в нашем примере с раде_соип1=, ничего
особого нам не скажет. На самом деле, когда в датасете всего 10 значений, его, по-
жалуй, не хватит ни для формулировки центральной тенденции (ни по среднему,
ни по медиане), ни для выявления выбросов. Но даже на рис. 9.1 усматриваются
зачатки потенциального паттерна: две равновеликие главы образуют двухвершин-
ный пик, тогда как уникальные значения количества страниц для оставшихся глав
получаются отдельно стоящими столбиками, сгруппированными в кластер доста-
точно близко от пика. В то же время далеко справа расположена наша 100-стра-
ничная глава, и поблизости от этой точки данных нет никаких значений. Мы, веро-
ятно, уже и так склонялись к выводу, что 100-страничная глава является аномалией
— выбросом, — опираясь на одну лишь математику, но данная гистограмма опре-
деленно подкрепляет нашу интерпретацию.
Разумеется, чтобы в полной мере оценить всю силу визуализации при анализе дан-
ных, давайте взглянем на датасет, значения которого по определению нельзя "под-
метить”, как в случае с постраничным размером глав. К счастью, у нас есть данные
по программе гарантии выплаты заработной платы (РРР), с которыми мы работали
в главе 6. и они в этом отношении определенно полноценны, поскольку в этом
множестве данных содержатся тысячи кредитных записей. Чтобы понять, что мож-
но узнать о типичности и ”нетипичности” таких ссудных сумм, одобренных по про-
грамме РРР, напишем экспресс-скрипт, и он сгенерирует гистограмму тех значений
ссуд, которые к настоящему времени одобрены по РРР. Затем пометим эту гисто-
грамму как средними, так и медианными значениями, чтобы проверить, насколько
хорошо каждый из этих показателей может послужить для потенциального опреде-
ления центральной тенденции. После этого вернемся к вопросу идентификации
возможных выбросов среди одобренных ссудных сумм, подкрепив наши наблюде-
ния как визуализацией данных, так и математикой.
Приступая к этой работе, я вновь вооружусь несколькими мощными библиотеками
РуШоп — в частности, шаф1оШЬ и зеаЪогп. В обеих есть функции для вычисления и
визуализации данных. В то время как библиотека ша1р1оШЪ остается основопола-
гающей для создания диаграмм и визуализаций на РуШоп, мы также воспользуемся
зеаЬот, так как она очень пригодится нам при работе с более продвинутыми вы-
числениями и форматами. Поскольку две эти библиотеки очень хорошо совмести-
мы друг с другом (фактически, зеаЪогп можно считать надстройкой над ша1р1о1НЬ),
такая комбинация обеспечит нам гибкость, которая понадобится нам как для быст-
рого создания нужных здесь простейших визуализаций, так и для их кастомизации,
чтобы представлять данные еще эффективнее, — этим мы займемся в главе 10.
Пока давайте сосредоточимся на аналитическом размере нашего процесса визуали-
зации. Для начала создадим простейшую гистограмму данных по нашим РРР-
ссудам, для этого воспользуемся значениями СиггепГАррго7а1_АтоипГ. Также для рас-
ширения контекста добавим среднюю и медианную линии, как показано в примере 9.1.
Листинг 9.1. ррр_1оап_сеп1га1_теа8иге8.ру
# 'рапбаз'для считывания и оценки наших данных
нпрог! рапйаз аз рй
# 'зеаЬогп' для использования встроенных тем и типов диаграмм
нпрог! зеаЬогп аз зпз
# 'та1р1о11лЬ' для кастомизации визуальных деталей
пирог! ша!р1о!11Ъ.рур1о! аз рИ
# считываем данные
ррр_ба1:а = рб. геаб_С87(1 риЫ1с_150к_р1из_221. С871)
# задаем базовую цветовую тему для нашей визуализации
зпз. 5е1_1Нете( 81у1е=" иНИед г1б")
# используем методы 'пеап()' и 'пеб1ап()', встроенные в 'рапбаз
пеан = ррр_ба1:а['С^ггеп1:АрргоVа^А^^о^п1:,].пеап() // О
пебтап = ррр_ба1:а[ ,С^ггеп1:АрргоVа^Ато^п1:, ] .пеб1ап()
# создаем гистограмму значений в столбце 'Сиггеп1:Аррго7а1Атоип1:'
аррго7еб_1озп_р!о1: = 8пз.Ыз1:р1о1:(ба1:а=ррр_ба1:а, х="Сиггеп1:Аррго7а1Атоип1:")
# получаем минимальное и максимальное значение по оси у в нашей гистограмме
у_ах1з_гапде = аррго7ед_1оап_р!о1:.де1:_у11т()// О
# ставим вертикальные линии там, где они нужны
аррго7ес1_1оап_р1о1:.7Г1пе8(теап, 0, у_ах1з_гапде[1], со1ог=,сг1т5оп', 1з=':')// €>
аррго7ес1_1озп_р1о1:.711пе8(тес11ап, 0, у_ах1з_гапде[1], со1ог='дгееп', 1з='-')
# Метод 'зЬошО' из та1р1оНлЬ - тот самый, который отображает визуализацию
рИ.8Ь01\/()//О
О Столбец Сиггеп1:Аррго7а1Атоип1: в данных о программе РРР сообщает, какова сум-
ма каждой из одобренных в настоящее время ссуд в долларах (независимо от того,
была ли уплачена эта сумма).
О Метод де!_у1лт() возвращает в виде списка наименьшее и наибольшее значение
по оси у. Как правило, он используется для того, чтобы задать удобочитаемую дли-
ну для строк с медианными и средними значениями.
О В любой части гистограммы (или другой визуализации) можно добавить верти-
кальные линии, указав позицию по оси ”х”, начальную точку по оси ”у”, конечную
точку по оси ”у”, цвет и оформление линии. Обратите внимание: единицы для зна-
чений ”х” и ”у” даются относительно датасета, а не видимого размера диаграммы.
О Притом, что в ноутбуках 1иру1ег не требуется явно вызывать метод зНонО из
библиотеки ша1р1оШЪ, в отличие, например, от инструкций ргтп±(), я предпочитаю
включать зЬом() для ясности и единообразия. По умолчанию диаграмма будет ото-
бражаться в "интерактивном” режиме (в 1иру1ег потребуется включить "магиче-
скую” команду ^а^рХоШЬ по1:еЬоок, как я сделал в предоставленных файлах). С ее
помощью можно уменьшать и увеличивать, панорамировать и другими способами
исследовать гистограмму, без написания дополнительного кода.
Скорее всего, вы рассматриваете диаграмму, получившуюся в результате запуска
именно этого скрипта (и выглядит она, надеюсь, примерно как на рис. 9.2), — и ду-
маете: ”а дальше что?” Признаться, эта первичная визуализация выглядит не бле-
стяще — а то и совершенно запутывает нас. Не бойтесь! Как бы то ни было, счи-
тайте, что на этом примере вы впервые смогли увидеть, почему визуализация для
анализа и коммуникативная визуализация — совсем не одно и то же. Так, аналити-
ческие визуализации, подобные этой, сравнительно сложно читать, понимать и
уточнять, нежели любую коммуникативную визуализацию, которую вы показали
бы широкой аудитории. Однако, чтобы извлечь информацию из данных, начинать
нужно именно с такого примера.
Прежде чем двинуться далее с нашим анализом данных, давайте уделим время и
оценим подчеркнуто олдскульный6, но невероятно полезный интерфейс, предостав-
6 Ставший классикой. — Примеч. ред.
ляемый нам в РуЛоп именно для этой диаграммы. Этот скрипт на РуЙюп предос-
тавляет нам не только статическое изображение, но и целую панель инструментов
(показанную на рис. 9.2), при помощи которой можно работать с изображением:
увеличивать, уменьшать, панорамировать, модифицировать и даже сохранять вы-
вод. И хотя мы, конечно же, можем (и в конечном итоге будем) кастомизировать
содержание и оформление этой диаграммы в коде, интересен сам факт, что данные
можно эффективно исследовать, не занимаясь при этом постоянным изменением и
перезапуском кода — получается громадная экономия времени. Чтобы попробо-
вать, каковы в данном случае наши возможности, уделите несколько минут и сами
поиграйте с элементами управления. Когда будете готовы продолжить анализ дан-
ных, щелкните по ярлыку йоте (домой), чтобы вернуть диаграмму в исходный вид —
и читайте следующие разделы.
О* В
в □ X
х-4.03е+О6 у-2,57е+О4
Рис. 9.2. Гистограмма ссуд по программе РРР
Какова форма наших данных?
Учимся понимать гистограммы
При работе с данными в табличном формате мы привыкли считать, что данные
представлены в виде некоторого количества строк и столбцов (именно эту инфор-
мацию возвращает свойство рапс1а8.8Ьаре объекта ВаШРгаше). В данном контексте
интересующая нас "форма” буквально означает контуры гистограммы и позволяет
нам заметить потенциально интересные паттерны или аномалии.
Первым делом в таких случаях мы будем обращать внимание вот на что:
Симметрия
Симметричны ли наши данные по вертикали? То есть можем ли мы провести
где-нибудь поверх наших визуализированных данных такую вертикальную ли-
нию, чтобы узор столбцов по одну сторону от нее выглядел (примерно) как их
отражение с другой стороны?
Плотность
Где кластеризована большая часть значений наших данных (и есть ли такая об-
ласть)? Имеется несколько кластеров или всего один?
Неудивительно, что эти вопросы касаются не только эстетики. Контур гистограммы
датасета иллюстрирует ее свойство, называемое распределением. Поскольку опре-
деленные распределения обладают конкретными свойствами, распределение наших
данных может помочь нам выявить, что в данной диаграмме типично, что необыч-
но, а что следует рассмотреть внимательнее.
Важность симметрии
В природе симметрия встречается повсюду. Разнообразная симметрия присуща жи-
вотным и растениям. Скажем, и собачья морда, и дубовый лист в равной степени
обладают так называемой зеркальной симметрией — можно сказать, что одна сто-
рона "отражает” другую. В популяциях организмов также встречается симметрия в
распределении определенных характеристик — например, роста особей или длины
крыльев. Гистограмма позволяет наблюдать эту симметрию, показывая частоту
встречаемости тех или иных показателей роста и длины крыльев в пределах попу-
ляции. Классический пример такого рода приведен на рис. 9.3, где показана длина
крыльев обыкновенной мухи на материале особей, измеренных командой биологов
в середине XX века7.
Симметричная колоколообразная кривая, показанная на рис. 9.3, иногда также име-
нуется ''нормальным”, "стандартным” или "гауссовским” распределением. Если вы
когда-либо видели "кривую” академических оценок, то это было распределение, в
которое вписываются оценки всего вашего потока. По краям распределения нахо-
дятся немногочисленные отличники и самые неуспевающие, а большинство значе-
ний сконцентрировано в середине.
Но мощь гауссовского распределения не только в его красивых очертаниях, а в
том, что можно сделать, ориентируясь на этот контур. Датасеты, демонстрирую-
щие гауссовские распределения, можно как описывать, так и сравнивать друг с
другом такими способами, какие неприменимы к несимметричным распределени-
7
См. датасет под названием А Могркоте1пс Апа1у818 о/РА1-Ке8181ап1 апс1 1Уоп-Ке8181ап1 Нои8е Ну 81гат8,
авторы КоЬеН К. 8ока1 и Рге§1оп Е. НшДег, ЬПр8://(1о1.ог§/10.1093/ае8а/48.6.499 — в нем представлены
соответствующие данные.
ям, поскольку в гауссовском распределении можно осмысленно вычислить два
показателя: стандартное отклонение, количественно выражающее тот числовой
диапазон, в котором находится большинство значений данных, и показатель 2-
оценки каждой точки данных, описывающий расстояние этого значения от сред-
него в терминах стандартных отклонений. Поскольку гауссовским распределени-
ям присуща фундаментальная симметрия, можно пользоваться стандартным от-
клонением и г-оценкой для сравнения двух множеств функционально подобных
данных, даже если у них отличается шкала. Например, если оценки студентов
укладываются в гауссовское распределение, то можно вычислить и сравнить
/-оценки отдельных студентов (то есть, их успеваемость на фоне потока), даже
среди разных курсов и для разных преподавателей, критерии оценки у которых
могут отличаться. Иными словами, даже если у одного преподавателя студент
получает оценки от 90 до 100 баллов, а у другого — от 70 до 80 баллов, то, если
оба множества студентов описываются истинно гауссовским распределением, то
все равно можно определить, какие студенты на всех потоках успевают лучше
всех либо нуждаются в помощи. Эту информацию никак не узнать из номиналь-
ных оценок (например, 74 или 92).
Рис. 9.3. Распределение длин мушиных крыльев
Эти характеристики также подсказывают, как подходить к измерению центральной
тенденции и выбросов. Например, в ”идеальном” гауссовском распределении сред-
нее и медианное значения будут равны. Более того, /-оценка значения позволяет
быстро определить, что в данном значении типично, а что необычно, поскольку
четко определен процент точек данных, у которых ожидается конкретное /-
значение. Запутались? Не волнуйтесь. Эта информация, как и любые сложные от-
ношения между данными, станет гораздо понятнее, если ее визуализировать.
Как показано на рис. 9.48, если распределение ваших данных является гауссовским,
то более 2/3 точек данных (34,1% + 34,1% = 68,2%) находится в пределах одного
стандартного отклонения (часто обозначаемого, как здесь, греческой буквой о) од-
ного среднего. Еще 27,2% находится между одним и двумя стандартными отклоне-
ниями от среднего, а последние 4,2% отделены от среднего на 2-3 стандартных от-
клонения. Таким образом, при гауссовском распределении 99,7% всех значений
находятся в пределах 3 стандартных отклонений от среднего.
Рис. 9.4. Гауссовское распределение, демонстрирующее,
какой процент значений приходится на 1-е, 2-е и 3-е стандартные отклонения (о) от среднего
И что же? Как вы помните, одна из фундаментальных целей анализа данных за-
ключается в том, чтобы понять, какие значения ”типичны” для нашего датасета, а
какие по-настоящему экстремальны. Тогда как среднее и медианное значения по-
зволяют наскоро прикинуть ”типичное” значение данного датасета, такие показате-
ли, как стандартное отклонение и рассчитываемые на его основе /-значения, помо-
гают систематически оценивать, какие значения могут или не могут оказаться по-
настоящему необычными.
Неудивительно, что вычислить эти значения при помощи РуЙюп не составляет тру-
да. Воспользовавшись либо рапбаз, либо статистической библиотекой, можно бы-
стро найти, каково стандартное отклонение (о) нашего датасета, а затем на его ос-
нове провести по нашей гистограмме линии там, где находятся соответствующие
/-значения. Здесь будем основываться на данных, из которых был сгенерирован
рис. 9.3, как показано в листинге 9.2.
Листинг 9.2. мпп8_1еп§1к_нп1к_$(1.ру
# Библиотека 'рапбаз' для считывания наших данных
1прог1: рапбаз аз рб
# Библиотека 'зеаЬогп' для встроенных тем и типов диаграмм
1прог1: зеаЬогп аз зпз
8 СС ВТ 2.5, автор М. УУ. Тоелуз. Краткое описание см. на ЬПр8://сгеаЛуесоттоп8.ог§/Ксеп8е8/Ьу/2.5, с
использованием АУйатебха Соттопз.
# Библиотека ' гпаГрХоГНЬ' для кастомизации визуальных деталей
ГгпрогГ паГр1оГ11Ь.рур1оГ аз рИ
# Библиотека 'зГаГГзГтсз' для легкого вычисления статистических показателей
ГгпрогГ зГаПзГгсз
# считывание наших данных
м1пд_баГа = рб.геаб_С57( ,1л/1пд_1епд1± - з057.сз7')
# устанавливаем базовую цветовую тему для нашей визуализации
зпз. 5е1_1Нете(51у1.е="н1п1е")
# создаем гистограмму, позволяющую 'зеаЬогп' выбирать задаваемые по умолчанию
# значения "Ып"
м1пд_р!оГ = 5П5.ЫзГр1оГ(баГа=м1пд_баГа, х="\л/1_пд_1епд1± (0.1пп)", кс1е="Тгие") // О
# вычисляем стандартное отклонение, воспользовавшись методом 'зГбе7()' из библиотеки
# 'зГаГГзГГсз'
зб = зГаГ1зГ1с5.5Гбе7(ш1пд^аГа['иГпдЗепдГЬ (0.1пп)']) // О
# получаем для нашей гистограммы минимальное и максимальное значения по оси у
у_ах1з_гапде = м1пд_р1оГ.деГ_у11п()
# проводим сплошную линию, соответствующую среднему значению
пеан = м1пд_баГа[11л/1пд_1епдГЬ (0.1пп)1 ] .пеап()
1ппд_р1ог.71лпе5(теап, 0, у_ах1з_гапде[1], со1ог='дгау1, 1з='-’)
# наносим граничные линии трех стандартных отклонений по обе стороны от среднего
Гог I 1п гапде(-3,4): // О
# находим актуальное граничное значение
2_уа1ие = пеан + (1*зб)
# не отрисовываем вторую линию поверх линии среднего значения
1Г 2_7а1ие != пеан:
# проводим прерывистую линию из точек, соответствующую каждому граничному значению
м1пд_р1оГ.71Лпез(2_7а1ие, 0, у_ах1з_гапде[1], со!ог='дгау', 15=':')
# выводим график!
ри.зНон()
О Каждый ”Ып” — это диапазон конкретных данных, которые будут объединены в
один столбец гистограммы; именно благодаря параметру кбе в нашей визуализации
появляется сглаженная линия. Эта линия аппроксимирует паттерн, который мы бы
увидели, будь в нашем датасете бесконечное количество точек данных.
О Здесь у нас также мог бы использоваться метод 5±с1() из библиотеки рапбаз:
н1пд_с1а1а[ 'и1пд_1епд1±(0.1гт1)1 ]. з±с1().
О Не забывайте, что наш цикл закончится прежде, чем методу гап§е() будет предос-
тавлено второе значение. Поэтому, чтобы получить три положительных значения,
устанавливаем в качестве второго значения 4. Если начинать с отрицательного зна-
чения, то, фактически, на первом шаге мы будем вычитать из среднего — мы будем
так делать, поскольку хотим захватить значения, расположенные как выше, так и
ниже среднего.
Просто просмотрев вывод листинга 9.2, можно подумать: ”Отлично, значит, мы
провели несколько линий по множеству данных о багах. Как это поможет нам при
интерпретации реальных данных?” В конце концов, тот пример с мушиными
крыльями, который мы взяли в качестве прототипа, обладавший гауссовским рас-
пределением, не слишком похож на вывод, который мы получили, строя диаграмму
данных по РРР-ссудам. График ссуд отчетливо асимметричен, и именно такой ри-
сунок, вероятно, будет у большинства наших данных.
Что же делать, когда в распределении наших данных отсутствует симметрия? Мы
уже знаем, как найти ”середину” асимметричного распределения, такого, как в лис-
тинге 9.1: нужно вычислять медианное значение, а не среднее. Но как же находить
экстремальные значения? Поскольку асимметричные, они же скошенные распреде-
ления, понятно, симметрией не обладают, у них нет единого "стандартного” откло-
нения, а также для них нельзя рассчитать х-значения. Но мы по-прежнему можем с
пользой подразделить асимметричный датасет таким образом, что нам удастся рас-
смотреть в нем потенциально необычные или экстремальные значения.
Этот процесс подразделения, как и нахождение медианы, на самом деле очень
прост. Сначала находим в отсортированном датасете срединное значение — иными
словами, медиану. Далее рассматриваем сначала одну, а затем вторую половину
множества, как если бы они были отдельными датасетами, и находим их медиан-
ные значения. Медиана нижней половины традиционно обозначается Р1, а медиана
верхней половины — РЗ. На данном этапе мы уже разделили наш датасет на четы-
ре части, или квартили, и в каждом квартиле содержится равное количество значе-
ний данных.
Что это нам дает? Напомню, что серьезная часть той информации, которую сооб-
щают х-значения, заключается в том, каков процент точек данных, значения кото-
рых являются схожими. Например, из рис. 9.4 понятно, что точка данных с х-
значением 0,75 (как и следовало ожидать) отстоит менее чем на одно стандартное
отклонение от медианы, — и мы знаем, что то же самое будет истинно примерно
для 68,2 % данных во всем датасете. Поделив данные на квартили, мы избрали
примерно такой же путь. Например, любое значение в нашем датасете, которое в
числовом выражении меньше значения из ()1, по определению будет меньше, чем
примерно 75% значений имеющихся у нас данных.
Все же реально нас интересуют способы, которыми можно было бы идентифициро-
вать потенциально необычные значения. Знание, что величина больше — или
меньше — чем 75 % остальных данных — это уже что-то, но едва ли такое значе-
ние будет экстремальным. Идентифицировать границы наших квартилей также
будет недостаточно.
К счастью, можно воспользоваться нашими значениями р1 и РЗ, чтобы вычислить
так называемые верхнюю границу и нижнюю границу нашего датасета. Если подра-
зумевалось, что наше распределение данных было гауссовским, то эти границы
почти идеально совпадут со значениями, расположенными в трех стандартных от-
клонениях выше и ниже медианы. Так что мы ориентируемся на эти границы имен-
но потому, что наши данные не являются гауссовскими. Я привожу здесь это срав-
нение, чтобы проиллюстрировать: верхнюю и нижнюю границу можно использо-
вать для выявления экстремальных значений в асимметрично распределенном
датасете.
Расчет верхней и нижней границы весьма прост, как и нахождение медианы. Для
начала находим значение, именуемое "межквартилъный размах"9 (ЩК), — вычур-
ное название для числовой разницы между значениями РЗ и ()1. Затем мы умножа-
ем это значение на 1,5, вычитаем его из — и так получаем нижнюю границу.
Чтобы узнать верхнюю границу, складываем вышеупомянутое произведение и РЗ.
Вот и все!
1РК (межквартильный размах) = РЗ —
Нижняя граница = Р1 — (1,5 х 1РК)
Верхняя граница = РЗ + (1,5 х 1РК).
При гауссовском распределении значения верхней и нижней границы будут нахо-
диться на три стандартных отклонения выше или ниже медианы — но значит ли
это, что любые значения за пределами верхней или нижней границы являются вы-
бросами? Нет. Но, найдя эти границы, мы можем сузить область, в которой следует
искать выбросы. Что не менее важно, эти меры помогают нам понять, почему неко-
торые значения выбросами не являются, даже если на уровне чисел может пока-
заться, что они весьма отличаются от ”типичного” или "ожидаемого” значения, ес-
ли судить по среднему или медианному.
Для примера давайте вернемся к нашим данным по РРР-ссудам. Ссуда в $1 млн ка-
жется очень большой, даже если — как и в нашем случае — нас интересуют только
ссуды в $ 150 000 и выше. Но в самом ли деле ссуда в $1 млн необычна? Вот где
нам могут по-настоящему пригодиться наши показатели центральной тенденции и
дисперсии, в данном случае — медиана, квартили, а также значения верхней и
нижней границы. Давайте посмотрим, какой вид примет наша гистограмма, когда в
нее добавятся эти значения (см. листинг 9.3), и подумаем об этом.
Листинг 9.3. ррр_1оап_сеп1га1_апс1_с1|51ру
# 'рапбаз' для считывания и оценки наших данных
1прог1: рапс!а8 аз рс!
9 Или интерквартильный размах.
# 'зеаЬогп' понадобится нам, так как в нее встроены нужные темы и типы диаграмм
Тпрог!: зеаЬогп аз зпз
# 'та1р1о11лЬ' для кастомизации визуальных деталей
Тпрог!: та1р‘1оМЛЬ.рур‘1о1: аз рИ
# считывание данных
ррр_ба1:а = рб. геаб_С87(1 риЫЛс_150к_р1из_221. сзу 1)
# установка базовой цветовой темы для визуализации
зпз.зе1:_1±ете(81:у1е="шЬ11:едг1б")
# изе ±Не Ьи11±-1п 'пеап()' апб 'пес11ап()' пе1±ос18 1п 'рапбаз
пеап = ррр_ба1:а[ ,С^ггеп1:АрргоVа^Ато^п1:, ] .пеап()
песНап = ррр_баТа[1 СиггепТАррго7а1Атоип1:1 ] .теб1ап()
# §1- это такое значение в нашем датасете, левее которого находится # 25%
# всех считанных данных
()1 = ррр_ба1:а['СиггепЗДрргоуаТАпоип^ ] .циапШе(0.25)
# 03 - это такое значение в нашем датасете, левее которого находится # 75%
# всех считанных данных
()3 = ррр_ба1:а[ ,Сиггеп1:Аррго7а1Аггюип1:'] .циап1лЛе(0.75)
# - это разница между значениями ()3 и ()1
= рЗ-01
# Теперь вычисляем верхнюю и нижнюю границы
Томег_Ьоипб = ()1 - (1.5*1()К)
иррег_Ьоипб = рз + (1.5*1()Р)
# при помощи 'зеаЬогп' вычерчиваем гистограмму
аррго7еб_Тозп_рТо1: = 8П5.Ыз1:р1о1:(ба1:а=ррр_ба1:а, х="Сиггеп1:Аррго7а1Атоип1:")
# получаем минимальное и максимальное значение по оси у в гистограмме
у_ах1з_гапде = аррго7еб_Тоап_рТо1:.де1:_у11т()
# обозначаем среднее серой линией
аррго7еб_1оап_рТо1:.71Лпе8(гпеап, 0, у_ах1з_гапде[1], со!ог='дгау', 18='-')
# другие линии черные (медиана сплошная, остальные точечные)
аррго7еб_Тоап_р1оТ.711_пе8(тебТап, 0, у_ах1з_гапде[1], со1ог='Ыаск',
аррго7еб_Тозп_рТо1:.711пе8(1омег_Ьоипб, 0, у_ах1з_гапде[1], соТог=,Ыаск', 1з=':')
аррго7еб_Тоап_рТо1:.71Лпе8(()1, 0, у_ах1з_гапде[1], со1ог=' Ыаск', 18=':')
аррго7еб_’1оап_р1_о'1:.71Лпе8(()3, 0, у_ах1з_гапде[1], соТог='Ыаск', 1з=':')
аррго7ес1_1оап_р1о1:.711пе8(иррег_Ьоипс1, 0, у_ах1з_гапде[1], со!ог='Ыаск', 1з=':')
# отобразим, что у нас получилось!
рИ.зНон()
Как видите на увеличенном представлении получившегося графика (показанного на
рис. 9.5), в принципе, не подтверждается гипотеза о том, будто ссуда в $1 млн явля-
ется из ряда вон выходящей; эта сумма оказывается значительно ниже верхней гра-
ницы, рассчитанной нами для данного датасета, хотя сумма этой ссуды выше, чем у
трех четвертей ссуд, оформленных к данному моменту (так как отметка в $1 млн,
сейчас обозначенная на оси х этой диаграммы как 1.0 1еб, находится правее линии
РЗ). Тем не менее это не так много, чтобы каждую ссуду в $1 млн требовалось до-
полнительно проверять. Как минимум, если и начинать проверки, то не с такой
суммы.
о
о
а
б
0)
Рис. 9.5. Подробности по гистограмме с суммами РРР-ссуд с указанием медианы, квартилей,
границами (нанесены черным) и медианой (нанесена серым)
Итак, где именно нам отыскивать потенциально интересные паттерны данных?
Прямо перед нами, на диаграмме, которая у нас уже есть. Ведь, хотя мы могли бы
поискать более сложные статистические показатели для расчета и оценки, даже
эта простейшая визуализация открывает нам в данных некоторые занятные зако-
номерности. Первая, о которой стоит упомянуть, — пусть даже, чтобы лишний
раз убедиться в том, что мы не ошиблись с выбором статистических показателей,
— в том, что среднее от этого датасета находится в распределении практически
на той же позиции, что и значение РЗ. Если бы у нас были какие-либо сомнения,
что предпочесть в качестве показателя центральной тенденции — среднее или
медиану — то вышеупомянутый факт их бы не оставил. Что еще заметно в пред-
ставлении данных на рис. 9.5, особенно, если прокрутить вправо, — так это инте-
ресные маленькие всплески в наших данных, означающие, что ссуду на опреде-
ленную сумму одобряли относительно часто10 11. Учитывая, как ясно эти значения
проступают в паттернах окружающих данных, далее, пожалуй, стоит присмот-
реться именно к этим значениям.
Подсчет "кластеров"
Допустим, вы идете по людной улице — и замечаете группу людей, собравшихся
на углу через дорогу. Что вы сделаете? На шумном проспекте, где большинство
пешеходов волнует, только как добраться из точки ”А” в точку ”В”, не один-два
человека, а больше остановились в одном и том же месте в одно и то же время. Это
уже явно свидетельствует: что-то там происходит. "Повод” может быть самым раз-
ным: возможно, там играет уличный музыкант или кто-то продает особенно попу-
лярные снеки, либо стоит коробка с котятами11. Факт есть факт: наша зрительная
система настроена подмечать аномалии, именно потому, что отклонение от тренда
указывает на какие-нибудь, как минимум слегка неординарные события.
Вот почему визуализация данных так помогает при их анализе. Наши глаза и мозг
заточены и на быстрое восприятие паттернов, и на столь же быстрое улавливание
отклонений от них. Иногда просто угадать, почему именно складывается тот или
иной паттерн, иногда нет. Но любой предсказуемый паттерн, скажем, поток людей
на улице, колоколообразное распределение данных или распределение, образую-
щее пологую кривую, — заслуживает изучения.
В случае, показанном на рис. 9.5 заметен целый диапазон таких отклонений от пат-
терна. Во-первых, резкая линия в левой части графика, выразительно напоминаю-
щая, что в нашем множестве данных содержатся только одобренные ссуды на сум-
му $150 000 и более, а не все без исключения ссуды, одобренные по данной про-
грамме. Если мы упустили это из виду, то нам об этом хорошо напомнит четкая и
очевидная линия ”отреза” ближе к левому краю.
Но здесь прослеживается и еще одна совокупность нарушений паттерна: неболь-
шие всплески в окрестности конкретных точек по оси х, в районе таких точек дан-
ных, как $2 млн. Откуда они берутся? Просмотрев нашу гистограмму, можем с оп-
ределенностью сказать, что подобные всплески встречаются с интервалом пример-
но в $500 000, особенно по мере увеличения сумм ссуд. В некоторой степени эта
тенденция может объясняться естественным тяготением сумм к круглым числам:
если вы запросите $1 978 562,34, почему бы просто не "округлить” эту сумму до
$2 млн? Разумеется, вы в таком случае получите на $21 437,66 больше, чем вам
требуется, — а для большинства из нас это, в любом случае, много денег. Учиты-
вая, что РРР-ссуды выдаются на обеспечение специфических расходов, должно по-
10 Особенно относительно сумм кредитов, находящихся немного выше или ниже этих значений.
11 И так бывает.
казаться немного странным, что так много ссуд, почти 2000 из общего количества,
судя по получившемуся графику, оказывается, составляют ровно по $2 млн.
Что же здесь происходит? Вот где нам понадобится провести дополнительное ис-
следование, чтобы эффективно интерпретировать то, что мы видим в данных. Мой
опыт подсказывает, что первым делом нужно посмотреть правила выдачи РРР-ссуд
и попытаться по ним понять, почему запросы на $2 млн так популярны. Например,
могут ли $2 млн оказаться минимальной или максимальной допустимой суммой,
обусловленной конкретными аспектами бизнеса или той цели, на которую запра-
шивается поддержка?
Немного поискав на сайте ййр8://уууууу.8Ьа.§оу/ГипШп§-рго§гат8/1оап8/соу1с1-19-
ге11еГ-орйоп8/раусЬеск-рго1:есйоп-рго§гат/8есоп(1-(1галу-ррр-1оап Управления по
делам малого бизнеса (8ВА), находим как минимум часть ответа:
Для большинства заемщиков максимальная сумма ссуды на второй раунд
помощи в 2,5 раза выше средних ежемесячных затрат на рабочую силу за
2019 г. или 2020 г. до $2 млн. Для заемщиков из сектора "Гостиницы, жилье
и общественное питание" (источник: ГА1С8 72) максимальная сумма ссуды
на второй раунд помощи в 3,5 раза выше средних ежемесячных затрат на
рабочую силу за 2019 г. или 2020 г. до $2 млн.
Поскольку $2 млн — потолок практически для всех отраслей бизнеса, для которых
применим второй раунд помощи по программе РРР, в том числе для тех случаев,
где исходно предусматривались более серьезные суммы, — объяснимо, почему так
велик кластер ссуд, одобренных ровно на $2 млн.
Этот "ответ”, конечно же, оставляет больше вопросов, чем дает ответов. Согласно
документации, сумма $2 млн была потолком для второго раунда ссуд по программе
РРР; ссуды, выданные в рамках первого раунда, могли достигать до $10 млн. Если
так много компаний запрашивают максимально возможную ссуду именно во вто-
ром раунде, то это значит, что многие компании: 1) уже получили ссуду в первом
раунде и 2) ссуда, полученная ими в первом раунде, могла даже превышать $2 млн.
Ведь во втором раунде им пришлось округлять ссуду вниз до $2 млн, если в первом
раунде они получили одобрение на более крупную ссуду. Иными словами, можно
ожидать, что те компании, которые запрашивали во втором раунде сумму ровно в
$2 млн, — как раз таковые, которым также были одобрены наибольшие общие сум-
мы облегчения суммы задолженности по программе РРР. Естественно, если они
действительно получили самые большие денежные мешки, то мы (и многие другие)
определенно хотим об этом знать!
Вопрос за $2 миллиона
Чтобы понять, какие общие характеристики могут быть у компаний, запросивших
ссуды по $2 млн во втором раунде программы РРР (и есть ли они вообще), сначала
нужно эффективно вычленить записи о них из нашего датасета. Как это можно сде-
лать? Что ж, нас интересуют компании, о которых известно, что им одобрили более
одной ссуды. Это значит, что соответствующее имя ВоггсмегМагпе должно фигуриро-
вать в наших данных более одного раза. Мы также знаем, что до 13 января 2021
года никакого "второго раунда” выдачи ссуд не было. Сопоставив вместе два этих
факта, мы, пожалуй, можем воспользоваться нашими навыками выпаса данных и
уверенно идентифицировать, какие компании во втором раунде кредитования за-
просили ссуду ровно на $ 2 млн.
Чтобы этого добиться, давайте выполним в нашем датасете пару ключевых преоб-
разований:
1. Создадим для каждого займа новый столбец с меткой ^1гз1:_гоопб или пауЬе_зесопб,
в зависимости от того, когда был выдан займ — до или после 13.01.2021. При-
том, что мы не можем быть уверены, что все займы, выданные после этой даты,
относятся ко ”второму раунду”, мы можем быть уверены, что все займы, выдан-
ные до этой даты, относятся к ”первому раунду”.
2. Посмотрим, есть ли в нашем датасете дублирующиеся записи. Для каждого
одобренного займа создается отдельная запись, поэтому если для одной и той же
записи одобрено два займа, это значит, что данная компания будет фигуриро-
вать в наших записях дважды.
Логика здесь такова: если название конкретной компании дважды попадется в на-
ших данных, и у этих записей будут отличаться ”округляющие” метки, то это озна-
чает, что эта компания получила одобрение на два разных займа.
Как обычно, воспользуемся в данном случае библиотеками РуЙюп. Как и ранее, нам
потребуется воспользоваться рапбаз, но здесь мы поработаем и с другой библиоте-
кой, питру, в которой содержится множество полезных функций для работы со
списками и массивами (на самом деле, рапбаз при работе серьезно опирается ”под
капотом” на питру). Кроме того, здесь я снова подтяну зеаЬогп и ша1р1оШЬ, чтобы у
нас была возможность сгенерировать визуализации, которые помогли бы нам в
оценке нашего датасета по мере его развития.
Хотя все, что мы пытались проделать с этими данными, в концептуальном отноше-
нии тривиально, этот анализ делается с привлечением выпаса, а выпас данных —
процесс многоэтапный, как показано в листинге 9.4.
Листинг 9.4. мЖо_доС2_1оап8_Ьу_с1а1е.ру
# 'рапбаз' для загрузки и преобразования данных
1прог1: рапбаз аз рб
# 'зеаЬогп' для визуализации
1прог1: зеаЬогп аз зпз
# 'па1р1о11лЬ' для детальной поддержки визуализаций
1прог1: па1р‘1о1НЬ.рур'1о1: аз рИ
# 'попру' для манипуляций над массивами и списками
1прог1: попру аз пр
# загрузка наших данных
ррр_ба1:а = рб.геаб_С87('риЫ1с_150к_р1из_Ьоггомег_Г1пдегрг1п1:_а.С87') // О
# преобразуем столбец '0а1:еАррго7есГ в конкретный тип данных для даты и времени
ррр_ба1:а['0а1:еАрргсл/есГ ] = рб.1:о_ба1:е1:1те(ррр_ба1:а[' 0а1:еАрргсл/ес1']) // О
# создаем переменную, в которой будет содержаться дата начала второго раунда
зесопб_гоипб_з1:аг1: = рд.1о_да1е1:гте( '2021-01-13')
# будем считать сегодняшнюю дату как "верхний" предел для всех возможных ссуд
# в рамках второго раунда
1=обауз_ба1:е = рс1.1о_с1а1е1!те( Чобау')
# Используем дату 1/1/2020 в качестве "нижнего" предела, поскольку программа РРР была
# запущена позже
ргодгап_з1:аг1: = рд. 1о_да1:е1:гте( '2020-01-01')
# передаем наши граничные значения и метки категорий функции 'си1:()' из библиотеки
# рапбаз
1оап_гоипб = рб.си1:(ррр_ба1:а.0а1:еАррго7еб,
Ыпз=[ргодгат_з1:аг1:,5есопб_гоипб_51:аг1:, 1=обауз_ба1:е],
1аЬе1з=[1 Г1гз1:_гоипб1, 1 тауЬе_зесопс1 ’ ])// О
# вставляем новый столбец на той позиции, которую укажем
ррр_с1а1:а.1п5ег1:(2, ’ 1_оап РоипсГ ,1оап_гоипб)
# эта "сводная таблица" вернет Ряд, демонстрирующий, сколько раз
# конкретный 'ВоггомегМапеР1пдегрг1п1:' фигурирует в датасете
1оап_соип1: = ррр_ба1:а.р17о1:_1:аЫе(1пбех=['ВоггомегМапеР1пдегрг1п1:'], аддГипс='зтге')
# Преобразуем наш Ряд в 0а1:аРгате и даем ему имя
1оап_соип1:_бГ = 1оап_соип1:.1:о_Ггате(' 1_оап Соип1:') // О
# при помощи метода 'безсг1Ье()' выводим на экран сводную статистику
рг1п1:("0е8сг1р1:1оп оГ бирНса1е Ьоггомег 1:аЫе:")
рг1п1:(1оап_соип1:_бГ. безсг1Ье()) //О
О Этот файл мы сгенерировали, выполнив процесс фингерпринтинга для ВоггомегИапе,
как описано далее во врезке "Нахождение фингерпринта ".
О Нам требуется узнать, какие ссуды были одобрены до 13 января 2021 года. Бы-
стрейший способ сделать это — преобразовать наши строки 0а1еАрргсл/ес1 в "реаль-
ные” даты и сравнить с ними даты выдачи ссуд.
О При помощи функции си1() из библиотеки рапбаз можно создать новый столбец
на основе имеющегося, применив к имеющемуся столбцу граничные значения и
метки. В данном случае мы размечаем все столбцы в зависимости от того, когда
именно была выдана ссуда: до или после 13 января 2021 года.
О Это делается для удобства, поэтому здесь можно воспользоваться методом
с1е8Сг1Ье().
0 Ожидается, что максимальное значение в таблице будет равно 2, так как ни од-
ной компании не разрешено получать более двух ссуд по программе РРР.
Если вы запустите код из листинга 9.4, и примерно за минуту ничего не произойдет —
не отчаивайтесь. У меня на СйгошеЬоок на выполнение этого скрипта уходит от 40
до 90 секунд (в зависимости от того, сколько еще приложений под Ыпих у меня
одновременно работает)12. Когда скрипт закончит работу, вы увидите на экране
примерно следующий вывод:
ОезсгтрЕЕоп оГ дир1лса1е Ьоггомег 1:аЫ.е:
1_оап Соип!:
соип!: 694279.000000
пеап 1.104022
0.306489
п1п 1.000000
25% 1.000000
50% 1.000000
75% 1.000000
пах 12.000000
Представляется, что в этой первой попытке чего-то... не хватает. По выводу ко-
манды .безсгтЬеО можно быстро собрать почти всю обобщающую статистику, ко-
торая нас интересует (здесь ()1, медиана и РЗ размечены в соответствии с процент-
ным количеством значений, которые будут фигурировать в диаграмме левее той
или иной отметки — т. е. 25%, 50% и 75%, соответственно). Эти значения подска-
зывают, что более одной ссуды получили менее 25% всех компаний (в противном
случае значение для 75% было бы больше 1) — и это логично. Но максимальное
значение заставляет поволноваться, поскольку правила РРР, по-видимому, не по-
зволяют одной компании получить более двух ссуд, а это гораздо меньше 12! Да-
вайте внимательнее присмотримся к этой ситуации, добавив к примеру 9.4 код,
приведенный в примере 9.5, — и посмотрим, что получится.
Листинг 9.5 мГю_доС2_1оап8_Ьу_с1а(е.ру (продолжение листинга 9.4}
# для начала отсортируем 0а1:аЕгате с количеством ссуд от наибольшего к наименьшему
зог1:ес1_1оап_соип1:5 = 1оап_соип1:_сЛ2.8Ог1:_7а1ие8(Ьу=[' 1_оап СоиггЬ' ], а8сепсйпд=Еа18е)
12 Если их слишком много, то в выводе появится надпись «К111ес1». Это признак того, что вам нужно
закрыть некоторые приложения или перейти в облако.
# Создадим новый ОаГаЕгате *только* с теми компаниями, которые получили более
# двух ссуд
поге_1±ап_Ыо = зогГеб_1оап_соипГз[8ОгГес1_1оап_соипГ5[' 1_оап СоипГ'] > 2]
# выводим по одному экземпляру каждой компании, попадающей в категорию 'поге_1±ап_Ъл/о'
ргГпГС'ВизГпеззез ±На1: зеет Го Ьауе доГГеп тоге ГЬап 2 Гоапз:")
рг1пГ(тоге_ГЬап_Ыо.8Ьаре)
рг1пГ("МитЬег оГ ЬизГпеззез ГЬаГ арреаг Го Ьауе доГГеп ргесГзеГу 2 Гоапз:")
ргес1зе!у_Гмо = зогГеб_1оап_соипГз[зогГес1_1оап_соипГ8[11_оап СоипГ1] == 2]
рг1пГ(ргес1зе1у_Гмо.зЬаре)
Теперь получаем дополнительный вывод, показанный здесь:
ВизГпеззез ГЬаГ зеет Го Ьауе доГГеп тоге ГЬап 2 Гоапз:
(58, 1)
МитЬег оГ ЬизГпеззез ГЬаГ арреаг Го Ьауе доГГеп ргесГзеГу 2 Гоапз:
(72060, 1)
Это говорит о том, что существует (относительно) немного компаний, которым
может быть одобрено более двух ссуд, и такие случаи, пожалуй, можно связать с
сочетанием выбранного нами подхода с фингерпринтингом (комбинация
ВоггсмегМате, ВоггсмегСГГу, и Вог гемегБГаГе), а также возможности, что в одном городе
найдется несколько точек одной и той же франшизы, которой было одобрено фи-
нансирование по программе РРР13. Мы не будем тратить свои ресурсы на поиск
подробностей об этих компаниях. В любом случае, их достаточно мало, чтобы су-
щественно изменить результаты анализа. По крайней мере, второй результат, пока-
зывающий, что 72 060 отдельных предприятий получили ровно два кредита, пока
кажется разумным, так как это определенно менее 25% общего датасета. Следова-
тельно, такой результат соответствует сводной статистике, полученной из Ва1а-
Ргаше 1_оап СоипГ (поскольку значение РЗ все еще равно 1, а это означает, что менее
25% всех названий предприятий появились в нашем датасете более одного раза).
Конечно, это все еще только оценка. Было бы гораздо лучше, если бы у нас был
более официальный подсчет кредитов второго раунда, с которым мы могли бы ра-
ботать. Как отмечалось в конце главы 6, Управление по делам малого бизнеса дей-
ствительно выпустило Официальный словарь данных14 для данных по ссудам РРР.
И хотя он не содержит всей информации, на которую мы могли бы надеяться, сло-
варь указывает, что поле РгосеззГпдМеГЬос! различает кредиты первого (РРР) и второго
(РР5) раундов. Давайте посмотрим на данные с этой точки зрения и сравним их с
оценкой на основе сопоставления имен, добавив код из листинга 9.6 далее в нашем
файле.
13 Подробнее см. на ЬПр8://8Ьа.§оу/(1оситепЦ8иррогЬГад-ррр-Ьоггоууег8-1еп(1ег8 и
кПр8://8Ьа.§оу/(1ос11теп1:/811ррог1>8ЬаГгапсЫ8е-(Нгес1:огу.
14 ЬПр8://(Ша.8Ьа.§оу/(Ша8е1:/ррр-й)1а/ге8О11гсе/ааЬ8е919-36с11-42е1-ЬЗЬа-е59с79Пс17Г0
Листинг 9.6. \л/Ьо до12 1оапз Ьу с1а1е.ру (опять продолжение)
# используем значение 'РгосеззтпдМеГЬосТ для идентификации ссуд второго раунда
ррз_1оапз = ррр^аГа[ррр^аГа['Ргосез81пдМеГЬосГ] == ' РР5' ]
# выводим на экран значение 'зЬаре' из этого объекта ОаГаРгапе, чтобы узнать,
# сколько у нас компаний
рггп!:("1\1итЬег о!2 Тоапз ТаЬеТеб аз зесопб гоипб:")
рг1п1:(рр5_1оап8. зЬаре)
Повторный запуск нашего скрипта дает дополнительный результат:
МипЬег о!2 Хоапз 1аЬе1ес1 аз зесопб гоипб:
(103949, 52)
Вот это да! Даже используя слишком слабый метод фингерпринта, мы все равно не
смогли найти более 300 000 компаний, получивших обе ссуды. Что же делать?
Прежде всего, признайте, что это не необычная ситуация. Мы имеем дело пример-
но с 750 000 записей данных, каждая из которых представляет собой комбинацию
данных, введенных несколькими лицами, включая заемщика, кредитора и, возмож-
но, 8ВА. Тот факт, что существует так много расхождений, на самом деле не уди-
вителен (я проиллюстрирую некоторые из них в следующей врезке), но еще не все
потеряно. Помните, что изначально нас интересовали те предприятия, которые по-
лучили именно $2 млн на второй раунд ссуды, что, скорее всего, представляет со-
бой лишь малую частью всех предприятий, получивших две ссуды. Мы все еще
можем продолжить эту часть анализа, чтобы: 1) проверить, насколько эффективной
была наша оценка ссуд второго раунда на основе даты, и 2) посмотреть, что мы
можем узнать о конкретной подгруппе предприятий, получивших ровно $2 млн во
втором раунде.
Нахождение фингерпринта
Поскольку в предыдущих главах описано, как использовать библиотеку
йп§егргт1з, я пропустила точный процесс, применяемый для подготовки рас-
сматриваемых здесь данных15. Однако, как всегда, эффективность процесса со-
поставления зависит не только от самого алгоритма фингерпринтинга, но и от
данных, к которым он применяется. Хотя сочетание ВоггсмегМапе, Во г геме г С! Гу и
ВоггомегЗГаГе явно далеко от совершенства, я остановилась на нем только после
того, как сначала попыталась сопоставить ссуды на основе значения
ВоггомегМапе непосредственно, а затем на сочетании фингерпринтинга значений
ВоггомегМапе и Вогомег21р. В обоих случаях я смогла найти совпадения менее
чем для половины кредитов второго раунда.
15 Но вы можете найти его в файле ррр_Гт§егрпп1_Ьогго\уег8.ру.
Интересно, как выглядят различия в данных? Вот один из примеров:
Во гго^егМаше
СОРРЕН МУЕВ. БЕАГО005, 1НС.
СОРРЕН Н1УЕВ 5ЕАГ0005 Г1ЧС
0оггоыегМатеЕ1пдегрг!п1
ВоггочегДййгеБЗ
Во ггоыегс1Vу
Воггоуег51а1е
соррег 1пг гЬ/ег зеаТоойв апсНогаде АК соррег 1пс ггчег БеаГаобБ апс!1огаде АК
111В 5ТН АУЕ
АМСНОКАБЕ
АК
кйп№д1Ь₽г
|а^еЛН|*’0*й
;&.*О<Т-ксвСсйе
зз?зз171ва
М/11/Ж4
Вог гп«гПд₽е
Ног т.гл,е г р г 1л1
Наг гтшчгЛйй геи
№г гашкпу
гряргг
Виг гивгЛр
1-а>П^'11а'1ис
111а 5ТМ м*
«Кгй1Мй
АХ
ГМ1
тт
Н1*'Е» зел! ош» .
•1/30/Ж1
1084
111В Е 5ГП Дие
АпсИогаде
АК
111И В ЫН ДкЕ
Лп{1т г<1р.г
АХ
ййЧ1-7?5
---------яшм:
Д|ГТ-11«| [1л—Й г щЬ а г
цэтл ил.
5М0Я1ИГВ ПГуРеГ'грг.тгЕ|де
1зЦ|ррг лч в Ычпипт
Сиг гоп!Арр п№я.1Ларип1
игнахзьмгяЕвлмипт
РгапсГИзеИаше
5егУ1С1пдкепс1ег1осаГ1оп10
5еп/1с1пд1_епс1е гМате
5е г V1 с 1п д 1_е п Йе гАд <1 ге 5 з
5егУ1С1пдЬепс1е гС11у
5еп/1с1пд1_еп(1е г5Га!е
5е гу1с1пд1_епйе г21р
Ви гаШгЬапХпсНса^ог
НиЬхопе1п(11са1ог
1_М11пс11са1:ог
ВиБхпеззАдеОезсггрИоп
Рго] есГСИу
Рго] ес1Соип1уМате
Рго]есС51а!е
Рго]есГ21р
СО
ЗоЬзАерогЕей
МА1С5Сойе
НасеЕ1Ьп1с11у
1ГГ11_1Т1Е5_РЙ0СЕЕ0
РАУК011__РН0СЕЕ0
М0ЯГ6А6Е_1МТЕКЕ5Т_РК<)СЕ ЕО
КЕМТ.РМСЕЕВ
!«•
ЛаЛ
119918
Еаз! ИезГ Вапк
РАЗАОЕНА
СА
91101—4525
ИМ
ЭДМйМ.Й
2В0ШЮ.0
2МВДО.0
ЛаЛ
119918
ЕазГ Иез! Вапк
135 Л коз КоЫез Ауе, 71В Р1
РА9АБЕЛА
91101-4525
ЛаЛ
Ех1511пд ог тоге 1Нап 2 уеагз о!<1
АЛСНОКАбЕ
АЛСН0КА6Е
АК
99501-2759
АК-
303.0
311710.0
Цпапзыегей
ЛаЛ
6382400.0
ЛаЛ
ЛаЛ
ЕххзНпд о г тоге 1Ьап 2 уеагз а!й
АпсЬогаде
АЛСН0ВА6Е
АК
99501-2759
АК-
236.0
311710.0
Цпапзыегей
1999998.0
МаЛ
МаЛ
Как видите, хотя поле ВоггсмегМате аналогично, оно не такое же, как значение в
ВоггомегАс1с1ге85 или капитализация ВоггсмегСтЛу. Почтовые индексы одинако-
вые, но многие другие столбцы, такие как Сепс1ег, \/е1:егап и даже ЬМПпЛсаЬэг, не
совпадают. Вот вам и реальные данные!
На данном этапе мы собираемся использовать информацию из столбца
Раутеп1:Ргосе881пдМе1±ос1 для подтверждения нашей предыдущей работы с использо-
ванием сопоставления имен и оценки раунда кредитования на основе даты. Для
этого произведем слияние ВаШГгаше 1_оап Соип1: обратно с исходным датасетом. За-
тем отберем только те ссуды на сумму $2 млн, которые, по нашим оценкам на ос-
нове даты выдачи, были ссудами второго раунда. Наконец, мы сравним это количе-
ство ссуд с количеством ссуд на сумму $2 млн, которые, как мы узнали, были по-
вторным кредитованием, основываясь на их значении РгосеззтпдМе^Ьос! из РР8.
Очевидно, что это означает добавление дополнительного кода в наш файл, как по-
казано в листинге 9.7.
Листинг 9.7. \л/Ьо до12 1оапз Ьу с1а1е.ру (снова продолжение)
# сколько ссуд в нашей производной выборке данных было одобрено именно
# на сумму $2 млн во время (возможно) второго раунда?
# слияние '1оап_соип1_с1Г' с исходными данными для отслеживания компаний,
# которые мы пометили как имеющие ровно два кредита
ррр_с1а1а_м_1с = рс1.тегде(ррр_с1а1:а, 1_оап_соип1:_с1Г,
оп=[1 ВоггомегМапеЕ1пдегрг1п1:1 ], Ьом=1ТеГГ1)
# теперь получим *все* записи о названиях компаний, которые мы связывали
# с двумя ссудами
та1сЬес!_1\л/о_1оап8 = ррр_ба1:а_и_1с[(ррр_ба1:а_и_1с[11_оап Соип^1] == 2)]
# выберем ссуды со значением $2 млн, которые могут быть вторыми ('тауЬе_зесопс1')
пауЬе_гоипб2_2М = та1сЬеб^=1юДоап5[(та1сЬес^1шоДоап5[
'С^ггеп1:АрргоVа^Ато^п1:, ] == 2000000.00) &
(па1:сЬеб_Ъл/о_1оап5 [
'1_оап ЯоипсГ] == ' пауЬе_зесопс11) ]
рггп1("0ег1л/еб $2М зесопб-гоипб Тоапз:")
рггп1(пауЬе_гоипс12_2М. зЬаре)
# выберем те ссуды, которые, как мы *знаем*, являются ссудами второго
# раунда со значением $2 млн
ррз_до1:_2М = рр8_1оапз[рр5_1оап5['Сиггеп^АрргоуаТАтоип^'] == 2000000.00]
ргитЬС'Ас^иа! $2М зесопб-гоипб Тоапз:")
ргитЬ:(рр5_до1:_2М.8Ьаре)
Добавив этот код в основные файлы, мы получим еще несколько строк вывода:
0ег1л/ес1 $2М зесопс1-гоипс1 Тоапз:
(1175, 53)
Ас1иа1 $2М зесопб-гоипб Тоапз:
(1459, 52)
Если сравнить эти результаты с предыдущими, то кажется, что дела обстоят немно-
го лучше. По всем кредитам у нас есть совпадение 72 060 из 103 949 реальных кре-
дитов второго раунда, или около 70%. Среди организаций, получивших одобрение
на получение кредитов в размере $2 млн во втором раунде, мы нашли 1 115 из
1 459, или около 80%.
Что же можно сказать о предприятиях, получивших $2 млн во втором раунде? Мы
не можем сказать ничего со 100% уверенностью, пока не найдем совпадения для
тех 284 компаний, у которых значение ВоггомегМапеЕ1пдегрг1п1: не совпадает между
ссудами первого и второго раунда. Необходимо понять, что можно обнаружить при
просмотре нашей 80-процентной выборки.
Для этого я собираюсь выполнить следующие действия16:
1. Найти все уникальные значения ВоггомегМапеЕ1пдегрг1п1: для компаний, которые
точно получили кредиты второго раунда на сумму $2 млн.
2. Создать Ва1аГгаше (Ы2_папез_с1Г) на основе этого списка и заполнить его значе-
нием флага 2Мт12пс1Кп(±
3. Объединить этот ВаШГгаше с моим датасетом и использовать значение флага
для получения всех записей о ссудах для этих компаний (как первого, так и вто-
рого раунда).
4. Провести базовый анализ того, сколько денег было одобрено этим предприятиям
в обоих раундах, и визуализировать эти суммы, сравнивая официальное обозна-
чение второго раунда (то есть Ргосезз1пдМе1±ос1 == ' РР5') с нашей производной
категорией на основе даты.
И, конечно, теперь, когда я выписала в виде списка шаги, которые должен выпол-
нить мой скрипт (это именно то, что вам необходимо записать в дневник данных
и/или план программы), осталось только запрограммировать его под существую-
щую задачу. Для ясности я поместила этот код во второй файл скрипта, его полный
код показан в листинге 9.8.
Лиситнг 9.8. ^Ьо_до(_2М_^1й1_У12.ру
# 'рапбаз' для загрузки/преобразования данных
гпрог! рапбаз аз рс1
# 'зеаЬогп' для визуализации
гтрог! зеаЬогп аз зпз
# 'та1р1о11лЬ' для поддержки детальной визуализации
гпрог! паГрТоГПЬ.рурТоГ аз рН
# 'попру' для работы с массивами/списками
гпрог! попру аз пр
# загружаем наши данные
ррр_ба1:а = рб. геаб_С57(1 роЫ1с_150к_р1оз_Ьоггомег_Г1пдегрг1п1:_а. С871)
# преобразуем столбец 'Оа^еАрргоуеб' в актуальный тип данных ба^еИпе
ррр_ба1:а[ 'Оа1:еАррго7еб' ] = рб.1:о_ба1:е1:1.пе(ррр_ба1:а[ 'ОаГеАррго7еб1 ])
# создаем переменную для хранения даты начала второго раунда
зесопб_гоипб_з1:аг1: = рбЛо_ба1:е1:1пе( '2021-01-13')
16 Обратите внимание, что я намеренно делаю это немного окольными путями. Это позволяет проде-
монстрировать еще несколько стратегий сбора данных и визуализации, но не стесняйтесь переде-
лывать этот код для большей эффективности в качестве упражнения!
# обрабатываем сегодняшнюю дату, чтобы использовать ее в качестве "верхнего"
# предела для возможных ссуд второго раунда
1:обау8_ба1:е = рб.1о_ба!е1гте( Чобау1)
# используем 1/1/2020 в качестве "нижнего" лимита, так как это дата до запуска РРР
ргодгат_з1:аг1: = рбЛо_ба1:е11те( '2020-01-01')
# передаем наши границы и метки категорий в функцию 'си1:()' библиотеки рапбаз
1оап_гоипб = рб.си^(ррр_ба-1:а.Оа1:еАррго7еб,
Ыпз=[ргодгат_81:аг1,5есопб_гоипб_81:аг1:, 1=обауз_ба1:е],
1_аЬе1з=[ 'Г1гз1_гоипб1, ' тауЬе_зесопб' ])
# добавляем новый столбец в указанную позицию
ррр_ба1:а.1п5ег1:(2, '1_оап Роипб' ,1оап_гоипб)
# эта таблица "ртл/о! 1:аЫе" вернет последовательность, показывающую количество
# раз, когда определенное значение 'ВоггомегМатеР1пдегрг1п1:' появляется в датасете
1оап_соип1: = ррр_ба1:а. р17о1:_'1:аЫе(1пбех=[' ВоггомегМатеР1пдегрг1п1:' ],
адд^ипс='з12е')
# преобразуем нашу последовательность в 0а1:аРгате и даем ей имя
Тоап^оип^бР = 1оап_соип1:.1:о_(тате(' 1_оап Соип1:')
# используем метод 'безсг1Ье()' для вывода сводной статистики
рггп1("0езсг1р1:1оп о!2 бир1лса!е Ьоггомег 1:аЫе:")
рг1п1(1оап_соип1_бГ.безсг1Ье())
# начнем с сортировки нашего 0а1:аРгате количества ссуд от наибольшего
# к наименьшему
зог^ебЗоаП-Соип^з = 1оап_соип1:_б1:.5Ог1:_7а'1ие8(Ьу=[' 1_оап Соип!'],
азсепб1пд=Ра1зе)
# создаем новый объект 0а1:аРгате, содержащий *только* тех, у кого более
# двух кредитов
поге_1±ап_ил/о = зог1:еб_1оап_соип1:5[5ог1еб_1оап_соип1:5[' 1_оап Соип!:' ] > 2]
# выводим на экран по одному экземпляру каждого названия компании,
# которое встречается в 'тоге_1±ап_Ыо'.
рггп-ЬС'Визтпеззез 1±а1: зеет 1о Ьауе до11еп тоге ±Кап 2 Тоапз:")
рг1п!(тоге_1±ап_Ъл/о. зЬаре)
рггп-Ц"МитЬег О12 Ьизтпеззез ±На± арреаг 1о Ьа7е доИеп ргес1зе!у 2 1оапз:")
ргес1зе1у_Ъл/о = зог1:еб_1оап_соип1:5[5ог1:еб_1оап_соип1:5[' 1_оап Соип!:' ] == 2]
рггп-Ь (р гес1 зе1у_1злю. з К а ре)
# используем значение 'Ргосезз1пдМе1±об' для идентификации ссуд второго раунда
ррз_1оапз = ррр_ба1а[ррр_ба1а[1 Ргосез81_пдМе1±об'] == ' РР8' ]
# выводим на экран значение 'зЬаре' из этого объекта 0а1:аЕгате, чтобы узнать,
# сколько у нас компаний
рггп!:("1\1итЬег оГ Тоапз 1аЬе1ес1 аз зесопб гоипб:")
ргитЦррзЗоапз. зЬаре)
# сколько ссуд в нашей производной выборке данных было одобрено именно
# на сумму $2 млн во время (возможно) второго раунда?
# слияние '1оап_соип1:_с1Г' с исходными данными для отслеживания компаний,
# которые мы пометили как имеющие ровно два кредита
ррр_ба1:а_м_1с = рб.пегде(ррр_ба1:а, 1оап_соип1:_с1Г,
оп=[1 ВоггомегМапеЕ1пдегрг1п1:1 ], Ьом=11еГ1:1)
# теперь получим *все* записи о названиях компаний, которые мы связывали
# с двумя ссудами
таТхЬесРЬл/оДоапз = ррр_ба1:а_и_1с[(ррр_ба1:а_и_1с[11_оап Соип^1] == 2)]
# выберем ссуды со значением $2 млн, которые могут быть вторыми ('пауЬе_зесопсГ)
пауЬе_гоипс12_2М = та1:сЬес1_1зл/о_1оап5[
(та^сЬеОмо^оапзЕ'Сиггеп^АрргоуаТАтоип!:1 ] == 2000000.00) &
(па1:сЬеб_Ъ\/о_1оап8[11_оап Роипс!' ] == 'пауЬе_зесопсГ)]
ргитЬ:("0ег1л/ес1 $2М зесопб-гоипб Тоапз:")
рггп1(пауЬе_гоипс12_2М. зЬаре)
# выберем те ссуды, которые, как мы *знаем*, являются ссудами второго раунда
# со значением $2 млн
ррз_до1:_2М = ррз_1оап8[ррз_1оап8['С^ггеп1:АрргоVа^А^тюип1:, ] == 2000000.00]
ргитЬС'Ас^иа! $2М зесопб-гоипс! Тоапз:")
рггп-Ь(ррз_до1:_2М. зЬаре)
# отделим фингерпринты компаний, получивших одобренные кредиты второго раунда
# на $2 млн
Ы2_папез = рс1. ип1дие(ррз_до1:_2М[' ВоггомегМатеЕ1пдегрг1п1:1 ])
# преобразуем этот список в 0а1:аЕгате
Ы2_папез_с1Г = рс1.0а1:аЕгате(Ы2_пате5, со1иппз=['ВоггомегМапеЕ1пдегрг1п1:'])
# создаем новый массив той же длины, что и Ы2_папез_с1Г, и заполняем его
# значением флага
ГШ_со1ипп = пр.Гии((1еп(Ы2_патез),1), '2М112пс1Рпс1')
Ь12_папез_бГ['Со^ЗесопсГ ] = П11_со1ипп
# теперь объединим этот новый 0а1:аЕгате из двух столбцов с нашим списком
# ЛД1_с1а1а, чтобы (надеемся) найти их ссуды первого раунда.
зесопб_гоипс1_тах = рб.пегде(ррр_ба1:а_м_1с, Ы2_папез_с1Г,
оп=' ВоггомегМапеЕ1пдегрг1п1:1)
# теперь все ссуды, имеющие общие фингерпринты с теми, которые получили
# максимальную сумму во втором раунде, должны содержать значение флага
# ' 2М1Л2пбРпб' в столбце 'СкЧЗесопб'
8есопб_тах_аи_1оап5 = зесопб_гоипб_пах[
зесопб_гоипб_пах[1 Со1:5есопб1 ] == 12МН2пбРпб1 ]
# мы ожидаем, что это в два раза больше, чем число предприятий, получивших
# кредиты второго раунда в размере $2 млн
рггп!('То±а1 # оТ7 Тоапз аррго7еб 1"ог поз!: огдз 1На1: до± $2М 1"ог зесопб гоипб:')
рггп1(8есопс1_тах_а1Л_1оап8. зЬаре)
# сколько денег эти предприятия получили от РРР, в общей сложности?
1=о1:а’1_'Гипб8 = зесопб_тах_а1Л_'1оап5[1 Сиггеп1:Аррго7а1Атоип1:' ]. зип()
рггп-еС'То^а! ^ипбз арргоуеб 1^ог т.беп1т/!еб огдз 1Ьа1: соиТб Ьауе " + \
"зесопб-гоипб пах:")
рп.п-е(1о1а1_Рипб8)
# построим график наших данных с пометкой '1_оап Роипб' на основе даты рядом
# с записями, разделенными 'Ргосезз1пдМе1±осГ. Получим ли мы те же результаты?
# установим тему зеаЬогп
зпз.зе1_1Ьете(з1у1е="иЬг1едг1б")
# используем 'гпа^рХоИгЬ' 'зиЬрХоЪзО' для построения графиков рядом друг с другом
# используем ЧирТез' для последующего доступа к различным значениям зиЬр1о1з
Ч1д, ((гом1со!1, гом1со!2)) = рИ=.зиЬр1о1:8(пго\л/8=1, псо!з=2)
# построим гистограмму нашего анализа на основе дат
ба1=е_Ьазеб = 8П8.Ь1з1:р1о1:(ба1:а=8есопб_тах_а'1Л_'1оап5, х='Сиггеп1:Аррго7а1Аггюип1:',
Ьие='1_оап Роипб', ах=гом1со!1)
# построим гистограмму нашего анализа на основе дат
ба1=а_Ьазеб = 8П8.Ь181:р1о1:(ба1:а=8есопб_тах_а'1Л_1оап5, х='Сиггеп1:Аррго7а1Атоип1:',
Ьие='РгосеззтпдМе^Ьоб', ах=гом1со!2)
# выводим график!
рН=.зЬом()
Запустив этот скрипт, мы получим все результаты предыдущих примеров, а также
еще несколько строк дополнительной информации:
То^а! # Тоапз аррго7еб -Гог поз!: огдз 1Ьа1: до± $2М 1"ог зесопб гоипб:
(2634, 54)
То^а! -Гипбз арргоуеб 1"ог 1беп1лЧ1еб огдз 1Ьа1: сои!б Ьауе зесопб-гоипб пах:
6250357574.44
Сначала кажется, что что-то не так, потому что ожидалось, что общее количество
ссуд составит 2 х 1 175 = 2 350. Но помните, что мы сопоставляли ссуды на основе
того, получили ли компании одобрение на сумму ровно $2 млн во втором раунде, и
не смогли сопоставить 284 ссуд по значению ВоггомегМапеЕ1пдегрг1п1=.
Это означает, что у нас есть все кредиты второго раунда, но в этих цифрах не хва-
тает 284 кредитов первого раунда. Другими словами, мы ожидаем получить (2 х
1,175) + 284 = 2,634 — и получаем! Хорошо! Всегда приятно, когда что-то совпа-
дает. Это означает, что наша ”общая” цифра, хотя и не будет на 100% точной, пред-
ставляет собой в определенной степени обоснованную оценку минимального зна-
чения общего количества ссуд, одобренных этой группе компаний в рамках РРР:
около $6 млрд.
Наконец, давайте посмотрим на визуализацию, показанную на рис. 9.6, которая
представляет собой вид графика, созданного скриптом. На этом графике мы можем
сравнить, как наша классификация 1_оап Роипб соответствует указанным РР8 ссудам.
Это грубый (но все же полезный) способ проверки нашей работы, и результаты вы-
глядят довольно хорошо17!
Рис. 9.6. Сумма в долларах большинства одобренных ссуд для компаний,
получивших два кредита РРР, в разбивке по раундам кредитования
Интересно, однако, что рис. 9.6 иллюстрирует и другое: похоже, что значительное
число компаний, получивших кредиты на сумму $2 млн во втором раунде, нару-
шают нашу предыдущую гипотезу о том, что компании, получившие кредиты на
сумму $2 млн во втором раунде, получили больше этой суммы в первом раунде,
когда лимиты были выше. Как обычно, отвечая на один вопрос, мы породили другой!
17 Если сравнить результаты численно, то можно заметить, что они идентичны по крайней мере для
нашей подгруппы компаний, получивших $2 млн во втором раунде.
И, конечно, уже проделанная работа даст нам фору на пути к ответу на этот вопрос.
Однако прежде чем приступить к следующему раунду вопросов и ответов, необхо-
димо поговорить еще об одном важном компоненте анализа и интерпретации дан-
ных: пропорциональности.
Пропорциональный ответ
Представьте, что идете в кафе с друзьями. Вы недавно поели, поэтому заказываете
только напиток, но трое ваших друзей проголодались и заказали по полному обеду.
Как вы решаете, кто сколько должен оплатить, когда приносят счет? Большинство
из нас согласится, что наиболее разумным было бы вычислить — или хотя бы оце-
нить — в каких пропорциях заказ каждого человека составляет общий счет, а затем
попросить каждого человека оплатить эту долю, а также такую же долю, скажем,
налога и чаевых.
Такая же логика применима и при анализе данных. В разд. "Вопрос за $2 миллиона"
мы рассмотрели общий объем средств, утвержденных для определенной группы
предприятий в рамках РРР, и хотя 6 млрд долларов звучит внушительно, нас, веро-
ятно, должно больше интересовать то, как эти предприятия использовали предос-
тавленные средства, а не абсолютное количество полученных ими денег. Посколь-
ку программа РРР была разработана для того, чтобы сохранить людям заработную
плату, мы могли бы узнать, сколько денег получили эти предприятия по отноше-
нию к сохраненному ими количеству рабочих мест, — процесс, который я считаю
рационализацией данных18.
К счастью, процесс рационализации наших данных чрезвычайно прост: мы вычис-
ляем отношение между двумя значениями данных путем деления одного числа на
другое. Например, если мы хотим узнать, сколько долларов на одно рабочее место
потратили компании, определенные в разд. "Вопрос за $2 миллиона", можно (после
некоторой проверки на вменяемость) разделить значение в РА7Р0Ы_РР0СЕЕ0 на значе-
ние в ЗоЬзРерог1:ес1 для каждой записи, как показано в листинге 9.9.
Листинг 9.9. боНаг8_ре^оЬ_2М_гпс12.ру
# 'рапбаз' для загрузки/преобразования данных
гпрог! рапбаз аз рб
# 'зеаЬогп' для визуализации
гпрог! зеаЬогп аз зпз
# 'та^рХоПлЬ' для кастомизации визуальных деталей
гтрог!: таГрТоГНЬ.рурТо!: аз рП
18 У этого термина в мире бизнеса и статистики/науки о данных есть более конкретное определение —
пропорционализация, но оно звучит как-то неуклюже. Кроме того, мой вариант лучше соответствует
реальному процессу расчета!
# 'потру' для работы с массивами/списками
гтрог! потру аз пр
# загружаем наши данные
ррр_ба1:а = рб. геаб_С87(1 роЫ1с_150к_р!о8_Ьоггомег_Г1пдегрг1п1:_а. С871)
# сначала проверим данные на вменяемость
рг1п!(ррр_ба1:а[ррр_ба1:а[1 ЗоЬзРерог1:есГ ] <= 0]) О
# отбрасываем записи, не имеющие значения в поле'ЗоЬзРерог1:есГ
ррр_ба1:а.с1гор(1аЬе18=[437083,765398], ах1з=0) О
# рассчитываем затраты на одно рабочее место
скэШагз_рег_доЬ = ррр_ба1:а[ 'Соггеп1:Аррго7а1Атооп^ ]/ррр_ба1:а[1 ЗоЬзРерог1:есГ ]
# вставляем новый столбец в наш исходный датасет
ррр_ба1:а.1п5ег1:(3, ’ОоШагз рег ЗоЬ1, бо11аг5_рег_доЬ)
# используем значение 'РгосеззтпдМе^Ьоб' для идентификации ссуд второго раунда
рр8_1оапз = ррр_ба1:а[ррр_ба1:а['РгосеззтпдМе^Ьоб'] == ' РР8' ]
# выбираем все ссуды второго круга, значение которых составляет $2 млн
ррз_до1:_2М = рр8_1оапз[рр8_1оап5['Соггеп1:Аррго7а1Атооп1:'] == 2000000.00]
ргитЬС'Ас^оа! $2М зесопб-гоипс! Тоапз:")
ргитЬ:(рр5_до1:_2М.5Ьаре)
# получаем фингерпринты всех компаний, получивших кредиты второго раунда
# на сумму $2 млн
Ы2_патез = рб. ип1дие(ррз_до1:_2М[1 ВоггомегМатеР1пдегрг1п1:' ])
# преобразуем этот список в 0а1:аРгате
Ы2_патез_с1Г = рб.0а1:аРгате(Ы2_пате5, со1итпз=['ВоггоVVег^атеР^пдегрг^пI:,])
# создаем массив той же длины, что и 'Ы2_патез_с1Г'; заполняем значением флага
ГШ_со1итп = пр.Гии((1еп(Ы2_патез),1), 12М1Л2пс1РпсГ)
Ы2_патез_с1Г['Со^Зесопб1 ] = Г111_со1итп
# теперь объединим этот новый 0а1:аРгате из двух столбцов с нашим списком Ги11_ба1:а
зесопб_гоипс1_тах = рб.тегде(ррр_ба1:а, Ы2_патез_с1Г, оп=,ВоггоV7ег^атеР^пдегрг^пI:,)
# все ссуды, фингерпринты которых совпадают с таковыми у компаний, получивших
# $2 млн во втором раунде, должны содержать значение 12МП2пс1Рпс11
# в столбце 'Со1:8есопс1'
зесопс1_тах_а11_1озп5 = зесопс1_гоипс1_тах[
зесопс1_гоипс1_тах[1 Со1:5есопс1' ] == 12М1Л2пс1Рпс1' ]
# предприятий, получивших одобрение на $2 млн во втором раунде, должно быть
# в 2 раза больше
рггп-Ц'То±а1 # о-Г Тоапз арргсл/еб 1"ог поз!: огдз 1±а1 до!: $2М 1"ог зесопб гоипсI:,)
рггп!:(8есопс1_тах_а11_1озп5. зЬаре)
# сколько денег эти предприятия получили от РРР в общей сложности?
1о1а1_Лтб5 = 8есопб_тах_а1Л_1оап5[1 Сиггеп1:Аррго7а1Атоип1:' ]. зип()
рггп-1:("То1а1 ^ипбз аррго7еб 1"ог тбегтЫНеб огдз 1:На-С соиХб Ьа7е " + \
"зесопб-гоипб пах:")
рг1п!(1:о1:а1_Гипс15)
# теперь давайте построим график этого нового столбца на выбранном датасете
# установим тему зеаЬогп
зпз. зе1_1Ьете( 51у1е=" иЬНед г1б")
# 'та^рХоНЛЬ' 'зиЬр1о1=8()' для построения графиков рядом друг с другом
Г1д, ((гом1со!1)) = рИ.зиЬр1о1:8(пгом8=1, псо!з=1)
# построим гистограмму нашего анализа на основе дат
ба1=е_Ьазеб = 8П5.Ы5Гр1оГ(баГа=5есопб_тах_а11_1оап8, х='0о11агз рег ЗоЬ1,
Ьие='Ргосезз1пдМе1±об', ах=гом1со!1)
# выводим график!
рН=.зЬом()
О Оказалось, что несколько предприятий не сообщили о наличии рабочих мест, что
нарушит наши расчеты. Поскольку причиной послужили только две записи, мы
просто отбросим их, используя их метки строк, назначенные библиотекой рапбаз.
Хотя текстовый вывод подтверждает, что мы рассматриваем тот же набор ссуд, что
и в разд. "Вопрос за $2 миллиона", наши рационализированные данные выявляют
примечательные аномалии в некоторых ссудах первого раунда. В этих случаях гор-
стка компаний, похоже, одобрила кредиты, которые выделяли больше средств на
фонд заработной платы, чем разрешенный лимит в 100 000 долларов на одно рабо-
чее место, как показано на рис. 9.7.
Что можно с этим сделать? Вы можете заметить, что к этому моменту мы довольно
далеко отошли от вопроса, заданного в разд. "Пандемия и программа РРР" главы 6,
где мы оценивали, помогла ли программа РРР ”спасти” американский бизнес. Хотя
то направление помогло в работе над оценкой качества данных, проведение кон-
текстного анализа открыло ряд новых вопросов и направлений, что, как мне кажет-
ся, представляется довольно распространенным явлением, когда речь идет выпасе
данных. Надеюсь, это побудит вас продолжить работу с новыми датасетами, чтобы
увидеть, что еще вы можете найти!
Рис. 9.7. Детализация в долларах на одно рабочее место компаний,
получивших кредиты второго раунда в размере $2 млн
Заключение
После всех этих анализов мы узнали несколько новых вещей — некоторые из них
относятся именно к этому датасету, но многие из них имеют гораздо более широкое
применение:
♦ относительно небольшое число компаний получили одобрение на максимально
допустимую сумму ссуды второго раунда в рамках программы кредитования
РРР. Хотя многие из них подали заявку на получение в первом раунде гораздо
большей суммы, некоторые компании этого не сделали;
♦ несколько компаний, получивших одобрение на получение кредита второго ра-
унда в размере $2 млн, заявили, что в первом раунде они получили более $100
тыс. на каждое заявленное рабочее место;
♦ данные, введенные человеком, всегда беспорядочны. Вот почему очистка дан-
ных — это постоянный, итерационный процесс. Документирование работы не-
обходимо для того, чтобы иметь возможность защитить свои результаты.
Итак, вводный курс анализа данных оставил нам гораздо больше вопросов, чем от-
ветов. На данный момент у нас есть только один способ узнать больше: поговорить
с людьми. Конечно, некоторые из выявленных нами закономерностей выглядят со-
мнительно19, но у нас слишком много неизвестных, чтобы делать какие-либо обос-
19 кПр8://рарег8.88гп.сот/8о13/рарег8.сГт?аЬ81тас1:_и1=3906395
нованные заявления на данном этапе. Например, многие из ссуд второго раунда в
размере $2 млн еще не были выданы на момент публикации этих данных, поэтому
некоторые компании могли получить или использовать гораздо меньше этой сум-
мы. Поскольку правила программы РРР требуют, чтобы минимальный процент
кредита расходовался только на заработную плату, компании, получившие одобре-
ние на слишком большую сумму, могли просто использовать разницу на другие
допустимые расходы, такие как проценты по ипотеке или траты на здравоохране-
ние. Это позволило бы им удовлетворить условиям выдачи ссуды. Другими слова-
ми, хотя мы можем узнать что-то нового из такого типа анализа числовых данных,
этой информации не будет достаточно, чтобы получить представление о материале
в целом — как или почему. Для этого нам нужны данные, полученнные непосред-
ственно от людей.
Когда мы проделали эту работу и четко определили, какими знаниями хотим поде-
литься, мы готовы подумать о наиболее эффективном способе донести то, что мы
узнали, до других. Подобно тому, как наш анализ данных опирается на данные и на
человеческие суждения и предоставляемые данные, наиболее эффективные типы
передачи данных почти всегда подразумевают баланс между словами и визуализа-
цией. В следующей главе показано, что при тщательном подборе слов и визуализа-
ции можно гарантировать, что наше послание действительно будет услышано.
ГЛАВА 10
Представление данных
После всех усилий, затраченных на получение доступа, оценки, очистки, преобра-
зования, дополнения и анализа данных, мы наконец-то достигли того момента, ко-
гда готовы подумать о передаче полученных выводов другим. Будь то официальная
презентация для коллег или пост в социальных сетях для друзей и последователей;
обмен информацией, полученной в результате работы по выпасу данных, — всё это
возможность оказать влияние результатами нашей работы на кого-то другого.
Как и любая другая часть процесса выпаса данных, эффективная и точная передача
наших выводов предполагает применение всего нескольких жестких правил, но при
этом содержит очень много суждений. Это относится как к письменным сообщени-
ям, так и касается такого аспекта передачи данных, как визуализация, которой часто
уделяется больше всего внимания.
Как уже говорилось в разд. "Визуализация для анализа данных" главы 9, создание
визуализаций для эффективного обмена нашими данными с другими людьми тре-
бует иной направленности и подхода, чем при создании визуализаций для получе-
ния этих данных. Например, если вы не пытаетесь обратиться к достаточно специа-
лизированной аудитории (скажем, через академическую публикацию), очень мало-
вероятно, что гистограмма попадет в ваш арсенал визуализации, когда придет
время поделиться своими результатами с миром. В то же время, весьма вероятно,
что в итоге вы будете использовать какую-либо форму линейчатой или столбчатой
диаграммы1, чтобы поделиться своими мыслями с неспециализированной аудито-
рией, поскольку эти широко используемые и хорошо читаемые графические формы
относительно легко поддаются точной интерпретации для большинства аудитории.
Другими словами, способ визуального представления результатов анализа данных
должен зависеть не только от того, какие данные у нас есть (или какие мы исполь-
зовали для получения выводов), но и от аудитории, на которую мы пытаемся воз-
действовать. Многие программные пакеты (включая некоторые из применяемых в
этой книге) с удовольствием создадут диаграммы и графики, если вы просто ука-
жете им на структурированный датасет и передадите несколько дополнительных
параметров. Хотя такой подход создаст необходимый минимум для визуализации,
результат будет больше похож на машинный перевод, чем на поэму. Да, на высо-
ком уровне результат может соответствовать ”правилам” языка, но прозрачность
1 Из которых гистограмма является особым типом.
смысла повествования (не говоря уже о его эффективности или красноречии) вы-
зывает сомнения. Таким образом, использование визуализации для того, чтобы
сделать наши данные действительно доступными для других, требует тщательного
обдумывания. Необходимо определить, что вы хотите передать, какая визуальная
форма подходит для этого лучше всего и как вы можете адаптировать ее к своим
конкретным потребностям.
В этой главе мы рассмотрим каждую из этих задач по очереди, начиная с некото-
рых стратегий, призванных помочь определить ключевые моменты в полученных
данных. После этого мы проведем обзор наиболее распространенных (и полезных!)
визуальных форм для данных. В каждом случае мы рассмотрим правила и лучшие
практики использования визуальных форм, а также представим базовый код для их
визуализации в РуЙюп с использованием комбинации библиотек зеаЪош и ша!р1оШЪ.
Наконец, мы возьмем базовую визуализацию реальных данных и рассмотрим, как
настроить и доработать различные ее элементы, чтобы превратить (обычно) по
умолчанию пригодную презентацию в нечто одновременно точное и привлекатель-
ное. Я надеюсь, что попутно вы изучите хотя бы несколько новых инструментов и
подходов, которые помогут вам более критически подойти к визуализации данных
в целом — независимо от того, являются ли они результатом вашей работы по вы-
пасу данных или нет.
Основы визуального красноречия
Как уже несколько раз упоминалось, процесс написания хорошего кода является
зеркальным отражением процесса в большинстве других обстоятельств. Например,
в главе 8 мы потратили время на пересмотр и реструктуризацию кода — хотя он
уже работал — и превратили его в скрипты и функции, которые в итоге стали более
четкими, чистыми и пригодными для повторного использования. Этот процесс по
сути не отличается от того, как пересматривается эссе или статья: собрав все клю-
чевые идеи в одном месте, можно вернуться к материалу позже и посмотреть, как
его стоит переформулировать и реорганизовать, чтобы он стал более лаконичным, а
концепции сливались в повествовании более логично.
Хотя этот же цикл ”написать-редактировать-отшлифорвать” применим и к визуали-
зациям данных, однако блок, с которым мы работаем, гораздо меньше похож на
эссе и гораздо больше на отдельный абзац — потому что в целом одна визуализа-
ция должна использоваться для передачи одной-единственной идеи. Это касается
как печатной, так и цифровой, статичной или интерактивной визуализации, части
длинного выступления или отдельного поста в социальных сетях. Одна визуализа-
ция = одна ключевая идея.
Я подчеркну это сейчас, потому что если вы открыли эту главу в надежде найти
примеры построения интерактивных визуализаций в стиле (Эарштбег2 или проду-
2 кПр8:/Лт\ууу.§оо§1е.сот/риЫ1сс1а1:а/(Нгес1:огу
манного потокового графика3, хочу вас сразу разочаровать: в этой главе я сосредо-
точусь на наиболее часто используемых типах визуализации, таких как гистограм-
мы, столбцы и линейные диаграммы. Причина отчасти в том, что они остаются са-
мым простым и наиболее интерпретируемым способом представления данных, ко-
торые содержат только одну независимую переменную, — а это в любом случае
все, что вы будете пытаться представить большую часть времени. Да, более слож-
ные визуализации могут использоваться для построения графиков нескольких неза-
висимых переменных — оригинальную визуализацию (Эарштбег можно считать
редким хорошим примером — но без обаятельного шведского мужчины, который
проводит зрителей через материалы в реальном времени4, такие визуализации
больше похожи на красивые игрушки, чем на информативные инструменты. Имен-
но поэтому мы сосредоточимся на совершенствовании доступных визуальных
форм до уровня, который мне нравится называть красноречивыми графиками, —
визуализации, которая, как и лучший текст, передает информацию четко, просто и
доступно. Хотя создание красноречивых графиков не исключает визуальной слож-
ности или даже интерактивности, этот процесс требует, чтобы каждый аспект ви-
зуализации способствовал ясности и раскрытию смысла графика.
Достижение такого визуального красноречия означает размышление о визуализа-
ции данных на трех основных этапах:
Уточнение фокуса внимания
Что именно вы пытаетесь донести до людей? В некотором смысле это парал-
лельно процессу выбора первоначального вопроса для выпаса данных: незави-
симо от того, что выявил ваш выпас данных и анализ, вы должны передать одну
идею в каждой визуализации. Как узнать, эффективно ли вы это сделали? Чаще
всего это означает, что вы сможете выразить идею в одном предложении. Как и
в предыдущем вопросе выпаса данных, составленный вами отчет о данных бу-
дет служить своего рода "базовой истиной” для принятия решений о визуализа-
ции. Все, что помогает вам более четко донести свою идею, сохраняется; все ос-
тальное сокращается.
Поиск своей визуальной формы
Как лучше отображать данные — в виде столбцов или линейных диаграмм? В
виде диаграммы связей? Будет ли это круговая диаграмма? Диаграмма рассея-
ния или пузырьковая диаграмма? Определение наилучшей визуальной формы
для ваших данных всегда связано с некоторыми экспериментами. В то же время
выбор визуальной формы для выражения ваших данных — это не только вопрос
предпочтений или вкуса; существует несколько неоспоримых правил, как опре-
деленные типы и отношения данных должны быть визуально закодированы. Да,
эстетика играет определенную роль в эффективности визуализации, но она не
может отменить необходимость точности.
3 ЬНр8://Лоу\1п«с1аЫ.сот/2008/02/25/еЬЬ-апс1-Поу\-оГ-Ьох-оП1се-гесе1р18-о\ег-ра8Ь20-уеаг8
4 ЬН:р8://уои1:иЬе.сот/ууа1:с11?у=]Ьк8КЕУ8о]’о
Повышение ясности и смысла
Даже если определена основная визуальная форма, существует множество спо-
собов, позволяющих деталям вашей визуализации улучшить или ухудшить ее
ясность, доступность, визуальную привлекательность и красноречие. Как мини-
мум, вам придется принимать решения о цвете, рисунке, шкалах и легендах, а
также об подписях, заголовках и аннотациях. Если формулировка ваших данных
особенно сложна, то для отражения этих нюансов вам потребуется тщательно
продумать еще более наглядную структуру, например, линии ошибок или диапа-
зоны неопределенности, или, возможно, прогнозируемые и/или отсутствующие
данные.
В следующих разделах мы не только концептуально обсудим каждый из этих эта-
пов, но и на реальных данных посмотрим, как они применяются на практике с по-
мощью РуЙюп.
Сформулируйте свои данные
Много лет назад мне посчастливилось пригласить Аманду Кокс5 из Тке Иеуу Уогк
Птез на один из моих курсов по визуализации данных, где она поделилась отлич-
ным советом, как оценить, действительно ли данные подходят для визуализации:
”Если в заголовке нет глагола, у вас проблемы”.
При поверхностном рассмотрении, конечно, это требование легко выполнить6. Од-
нако смысл ее заявления подразумевает нечто гораздо более строгое: заголовок
графика должен четко формулировать какую-то важную взаимосвязь или утвер-
ждение, а подтверждающие доказательства должны быть видны в самом графике.
Почему это так важно? Во-первых, если вы поместите свои утверждения прямо в
заголовок, это побудит читателей сразу же посмотреть на ваш график внимательно.
Четкое именование поможет убедиться, что зрители — в буквальном смысле —
будут знать, где искать доказательства этих утверждений. Конечно, наша работа
как информационных дизайнеров заключается в том, чтобы убедиться, что все ви-
зуальные подсказки графики также способствуют хорошему восприятию, но часто
именно заголовок привлекает людей7.
Если у вас не получается найти глагол действия для заголовка графика, это признак
того, что визуализация, вероятно, не лучший способ передать ваши мысли. Правда,
в правильных обстоятельствах люди могут обрабатывать визуализации невероятно
быстро8, но это преимущество реализуется только в том случае, если визуализации
есть что "сказать”. Другими словами, хотя вы можете создать визуально точный
5 кПр8://еп.лт1к1ре(На.ог§Лу11а/Атапс1а_Сох
6 Особенно если вы отдаете себе отчет, что используете глаголы-связки йПр8://тегпат-ууеЬ81:ег.сот/
(Нсйопагу/1ткт§%20уегЬ.
7 ЬПр8://р8усЬо1о§1са18С1епсе.ог§/пеуу8/1юуу-Ьеа(Шпе8-сЬап§е4Ье-ууау-луе-Щтк.Ы:п11
8 кПр8://пеуу8.тк.ес1и/2014/т-1:11е-Ытк-оГ-ап-еуе-0116
график под названием ”Один год ежедневных долгосрочных ставок казначейства”,
реальность такова, что даже самый большой специалист по политике будет зада-
ваться вопросом, почему он должен утруждать себя просмотром этого графика. Не
настаивайте на визуализации, если это не подходящий инструмент! Помните, что
наша цель — как можно эффективнее передать свои идеи по выпасу данных, а не
выразить их визуально любой ценой. Сосредоточившись в первую очередь на уточ-
нении формулировки данных и подтвердив, что она обладает необходимой вам си-
лой, вы сможете избежать больших временных затрат на разработку и создание ви-
зуализации, которая на самом деле не делает того, что вы хотите, или того, что
нужно вашей аудитории. Конечно, базовые визуализации данных можно быстро
создать с помощью датасета и надежной библиотеки визуализации (например,
зеаЪот). Но создание действительно красноречивых визуализаций требует тща-
тельного обдумывания, а также детальной кастомизации даже самых лучших биб-
лиотечных графиков, предоставляемых по умолчанию. Прежде чем тратить столько
времени и энергии, стоит убедиться, что ваша сложная визуализация не была бы
лучше в качестве отдельной, выделенной статистики из вашего анализа.
После создания мощного ''заголовка” формулировки данных пришло время опреде-
лить графическую форму, которая поможет вам наиболее эффективно представить
данные, подтверждающие ваше заявление.
Диаграммы, графики и картограммы — вот это да!
Даже если мы ограничимся более простыми графическими формами, все равно ос-
тается достаточно вариантов, способных сделать процесс поиска оптимального ва-
рианта для наших данных немного ошеломляющим. Следует выбрать линейчатую
или столбчатую диаграмму? Если подходит вариант гистограммы, должна ли она
быть горизонтальной или вертикальной? Подходят ли вообще круговые диаграм-
мы? К сожалению, это одна из ситуаций, когда библиотеки РуЙюп по большей час-
ти не могут нам помочь, поскольку они будут просто пытаться создать любой за-
прошенный тип графика с предоставленными данными. Поэтому нам нужен более
эффективный способ.
Вот здесь-то и пригодится хорошо сфокусированная формулировка данных. Ссылается
ли ваша формулировка на абсолютные значения — например, на Сиггеп1:Аррго7а1Атоип1:
в наших данных о кредитах РРР — или же она сосредоточена на взаимосвязи меж-
ду значениями, как, например, диаграмма Тке рап^етгс си1 апписй ТО1 /1о\\?8 Ъу опе-
В то время как утверждения об абсолютных значениях часто лучше всего
выражаются с помощью линейчатых диаграмм, формулировка взаимосвязей дан-
ных может быть подкреплена с помощью более широкого спектра визуальных
форм. Например, если ваша формулировка данных включает утверждение об изме-
нении во времени, хорошо было бы начать с линейного графика или диаграммы *
9 ЬНр8 ://есопот18Гсот/«гарЫс-(1еЫП/2021/06/21 ДЬе-рап(1ет1с-сиЬаппиа1ЛсП-Ло\У8-Ьу-опеЛЫгс1
рассеяния. Ведь некоторые визуальные формы, такие как круговые диаграммы и
картограммы, трудно адаптировать к чему-либо, кроме данных, полученных в один
момент времени.
На самом деле, для визуализации данных не существует жестких и неизменных
правил10 11, но я описала применяющиеся в следующих разделах, наряду с некоторы-
ми общими советами по созданию графики. Хотя эти рекомендации помогут вы-
брать форму для визуализации, которая не будет работать против ваших данных,
это только следующий шаг. Варианты, которые могут действительно поднять вашу
графику на более высокий уровень, мы рассмотрим в разд. "Элементы красноречи-
вых визуальных эффектов". В этом разделе мы выйдем за рамки (все еще довольно
отличных) стандартных настроек библиотеки зеаЪош и начнем больше копаться в
ша1р1оШЪ, чтобы управлять такими вещами, как метки, цвета и аннотации, которые
могут действительно выделить вашу визуализацию.
Круговые диаграммы
Круговые диаграммы — удивительно спорная тема в визуализации. Хотя круговые
диаграммы полезны для обучения детей дробям11, есть много людей, которые счи-
тают, что им практически нет места в лексиконе эффективной визуализации12.
Лично я считаю, что есть определенные, пусть и ограниченные ситуации, в кото-
рых круговая диаграмма может стать наилучшей визуализацией для поддержки ва-
шей формулировки данных. Например, если вы пытаетесь объяснить, какая доля
или часть ваших данных имеет определенное значение, а остальные значения мо-
гут быть разумно сгруппированы в четыре или менее категорий, то круговая диа-
грамма вполне может вам подойти. Это особенно верно, если на результирующем
графике выделяются значения, соответствующие некоторой узнаваемой13 доле це-
лого (например, 1/4, 1/3, 1/2, 2/3 или 3/4), поскольку человеческий глаз способен
обнаружить такие различия в расположении линий без особых усилий14.
Например, глядя на результаты демократических выборов мэра Нью-Йорка в июне
2021 г.15, можно представить, что мы сформулируем данные следующего содержа-
ния: ''Несмотря на обширную область проведения голосования, 3 лучших кандида-
та набирают почти 3/4 голосов за первый тур выборов”. Поскольку только четыре
кандидата набрали более 10% голосов после первого тура выбора, целесообразно
10 Хотя даже эти немногочисленные правила часто нарушаются публично, например 81аг1 а1 Хего 1т-
ргочез ТЫ8 Скаг1 Ьи1 Оп1у 8Н§Ы1у, автор Лтк СЬагГ к11р8://1ипкскаг18.1урера(1.сот/111пк_с11аг18/2021/
06/81аг1-а1-гего-1тргоуе8-1Ы8-с11аг1-Ь111-оп1у-811§1111у.111т1.
11 к11р8://рЬ8.ог§/рагеп18/гес1ре8/ре§8-р1гга-1гас11оп8
12 Ь11р8://81огу1е11т§уу1111с1а1а.сот/Ь1о§/2011/07/(1еа111-1о-р1е-с11а1Т8
13 кПр8://8Щге.тота.ог§/Гог-1:11е-11оте/кйс11еп-(Нтп§/соокутаге-кйс11еп-1:оо18/у1811а1-теа811гт§-сир8/
8711-802262. Шт1
14 Ы1р8://с8с2.пс8и.е(1и/Гаси11у/11еа1еу/РР
15 Ы1р8://ууа8Ып§1опро81.сот/е1ес11оп8/е1ес11оп-ге8и118/пеуу-уогк/пус-рптагу
также объединить всех остальных кандидатов в одну категорию "Другие”. В дан-
ном случае круговая диаграмма послужит вполне разумным способом точного
представления результатов и поддержки нашего утверждения. Отчасти потому, что
она позволяет легко увидеть, насколько сильно лидирующие кандидаты опережают
других.
Учитывая спорный характер круговых диаграмм, не удивительно, что в достаточно
универсальной библиотеке зеаЪот нет опции круговых диаграмм. Однако можно
напрямую использовать библиотеку ша1р1оШЪ для получения очень удобной круго-
вой диаграммы16. Однако в функции построения круговых диаграмм шаф1оШЬ есть
несколько своеобразных моментов, которые нам предстоит преодолеть. Например,
круговые диаграммы строятся так, чтобы самый большой раздел начинался с ”12
часов”, а остальные добавлялись в порядке убывания размера по часовой стрелке.
Однако в библиотеке ша1р1оШЪ первый раздел начинается с ”3 часов”, а дополни-
тельные разделы добавляются против часовой стрелки. Следовательно, нам нужно
указать значение з1:аг1:апд1е=90 и изменить порядок следования сегментов от наи-
большего к наименьшему17. Аналогично, ша1р1оШЪ по умолчанию присваивает ка-
ждому ”кусочку” пирога свой цветовой оттенок (например, фиолетовый, красный,
зеленый, оранжевый и синий) в определенном диапазоне цветов, который может
быть недоступен людям с некоторыми видами дальтонизма. Поскольку в нашей
формулировке данные концептуально группируются по трем лучшим кандидатам, я
сделала их всех одинакового оттенка (зеленого). И, поскольку все кандидаты пред-
ставляют одну политическую партию, я оставила все кусочки в зеленой гамме.
Чтобы увидеть, как программируется такой тип графика (включая эту небольшую
кастомизацию), посмотрите листинг 10.1 и полученную визуализацию на рис. 10.1.
Листинг 10.1. а_ЬитЫе_р!е.ру
1трог1: та^рБоИтЬ. рур!о1: аз рИ
# та^рХоИгЬ работает против часовой стрелки, поэтому нам нужно изменить
# порядок отображения "кусочков" нашего пирога
сапб1ба1:е_пате8 = ['Абапз', 11л111еу1, 'багета', 'Уапд', '0±Негз']
сапЛс1а1:е_пате8. геуегзе()
уо1:е_рс1: = [30.8, 21.3, 19.6, 12.2, 16.1]
701е_рс1.ге7егзе()
СО1ОГ8 = [' #006с12с',' #006с12с', ' #006с12с', ' #31а354',' #74с476' ]
со1огз.ге7егзе()
Нд1, ах! = р1Л.зиЬр1о1:5()
16 Поскольку библиотеки рапбаз и зеаЬогп в значительной степени опираются на та1р1о1НЬ, существу-
ет множество случаев, когда значительная кастомизация требует прямого использования функций
ша1р1оШЬ. Более подробно это показано в разд. "Элементы красноречивых визуальных эффектов".
17 Конечно, можно просто изменить порядок первоначального представления данных, но я предпочи-
таю, чтобы порядок данных и возможный визуальный порядок совпадали.
# по умолчанию начальной осью является ось х; значение 90 гарантирует,
# что вместо нее будет вертикальная линия
ах1.рге(уо1е_рс1, 1аЬе1з=сапс11с1а1:е_пате8, аи^орс^ХИЭДб', з1:аг1:апд1е=90,
со1ог8=со1ог8) О
ах1.ах1з('едиа!') # равное соотношение сторон гарантирует, что пирог будет
# нарисован в виде круга.
# выводим график!
ри.зЬоиО
О Аргумент, который мы передаем айсоре!:, должен быть "форматированным стро-
ковым литералом”, также известным как /-строка13. В этом примере указано, что
дробь должна быть выражена как число с плавающей точкой (десятичное) с точно-
стью до одного знака. Двойной знак процента (%%) используется здесь для того, что-
бы вывести один знак (экранируя зарезервированный символ знака процента дру-
гим символом).
Рис. 10.1. Круговая диаграмма первого тура выборов в Нью-Йорке
Вкратце, если вы рассматриваете круговую диаграмму:
Правило
Категории ваших данных должны концептуально (и буквально) складываться в
единое "целое”.
Рекомендация
Количество категорий должно быть сокращено до пяти или менее. 18
18 ЬНр$://с1ос8.руГЬоп.ог«/3/ШЩпа1/1приЩифиГкИт1#ГогтаНес1-$Ц1п<*-1кегак
Рекомендация
Доля данных, которые вы хотите выделить, должна составлять 1/4, 1/3, 1/2, 2/3
или 3/4 от общего количества.
Однако если ваши данные не соответствуют одному или нескольким из этих требо-
ваний, следующей графической формой для рассмотрения может стать гистограмма.
Линейчатые и столбчатые диаграммы
Гистограммы часто становятся наиболее эффективным способом выделения взаи-
мосвязей (включая различия) между дискретными, номинальными (в отличие от
пропорциональных) значениями данных. В отличие от круговых диаграмм, гисто-
граммы могут точно представлять датасеты, в которых значения не складываются в
”целое”. Они также могут эффективно представлять как положительные, так и от-
рицательные значения (одновременно, если необходимо) и способны отображать
как сравнение различных категорий данных в момент времени, так и значения дан-
ных с течением времени. Столбцы также могут быть ориентированы вертикально
(часто такие диаграммы называют столбчатыми) или горизонтально (линейчатые),
чтобы сделать метки и взаимосвязи данных более разборчивыми.
Другими словами, гистограммы невероятно гибкие и предлагают множество вари-
антов для эффективного представления доказательств утверждений о данных. Од-
нако существует одно жесткое и непреложное правило для работы с гистограмма-
ми: значения данных ДОЛЖНЫ начинаться с нуля\ Исключений из этого правила
действительно нет, хотя это не останавливает некоторых людей от попыток19. По-
чему это правило так важно? По причине того, что начало столбцов графика с чис-
ла, отличного от нуля, означает, что их визуальная разница в длине больше не бу-
дет пропорциональна их фактической разнице в значении.
Например, представьте, что вы добиваетесь повышения зарплаты на работе, где в
настоящее время получаете минимальную федеральную зарплату в размере $7,25 в
час. Вы просите своего начальника повысить вашу почасовую ставку до $9,19 в
час20, чтобы учесть влияние инфляции с момента последнего повышения мини-
мальной заработной платы в 2009 г.
”Что ж, — говорит начальник, — давайте посмотрим, как будет выглядеть такая
прибавка”, — и затем показывает вам график, подобный тому, что показан на рис. 10.2.
Вы видите проблему? Благодаря тому, что столбики начинаются с 5, а не с нуля,
график на рис. 10.2 выглядит так, будто 9,19 доллара в час — это почти вдвое
больше вашей нынешней зарплаты. Но, конечно, простая математика (9,19 - 7,25 /
7,25 = ~,27) показывает, что это чуть более чем на 25% больше текущей ставки. Как
видите, начало гистограмм с нуля — это не вопрос вкуса, эстетики или семантики
— это предотвращение визуального обмана21.
19 ЬН:р8://(1а1:а1оигпа118т.сот/геа(1/1оп§геа(18ДЬе-ип8рокеп-ги1е8-оГ-у18иа118айоп
20 ЬПр8://(1а{а.Ы8.§оу/с§1-Ьт/срка1с.р1?со811=7.25&уеаг1=200907&уеаг2=202107
21 ЫТр 8 ://Яо\ут§с1а1:а. сот/2017/02/09/Ьо\у4о-8р (Л- У18иаПгайоп-Пе8
ю
Текущая
Запрашиваемая
Временные рамки
Рис. 10.2. Неточное сравнение заработной платы
Рис. 10.3. Неточное сравнение пенсионного возраста
Тем не менее даже профессиональные графические команды иногда ошибаются.
Возьмем этот пример, приведенный в блоге Кайзера Фунга на сайте 1ипк Сйаг^з под
заголовком ”А^огкт§ СиЙиге”22 н показанный на рис. 10.3.
На рис. 10.3 "ломаная” гистограмма претендует на то, чтобы показать, когда муж-
чины в разных странах прекращают работать, по сравнению с официальным пенси-
онным возрастом. Как и на рис. 10.2, начало столбиков не с нуля сильно искажает
реальную разницу этих значений: согласно маркировке данных, мужчины во Фран-
ции выходили на пенсию примерно на 10 лет раньше мужчин в Японии — разница
в годах работы составляет 15%. Однако фактически изображенная линия для Япо-
нии более чем в два раза длиннее, чем для Франции. Конечно, если бы все линии
начинались с нуля, разница в их значениях не казалась бы такой драматичной.
Что же случилось? Действительно ли дизайнеры этой визуализации пытаются об-
мануть нас, заставляя думать, что мужчины в Японии работают в два раза дольше,
чем мужчины во Франции? Почти наверняка нет. Графики, подобные приведенно-
му на рис. 10.3, послужили причиной появления условия, говорящего о важности
выражения одной идеи в каждой визуализации: пытаясь добавить больше, вы, ско-
рее всего, столкнетесь с проблемами и получите в итоге неточную и вводящую в
заблуждение визуализацию. На рис. 10.3 дизайнеры пытаются показать два разных
показателя (разница между официальным пенсионным возрастом и возрастом, ко-
гда мужчины перестают работать, и каков этот возраст), шкалы которых несовмес-
тимы. Начните шкалу с нуля, и читатели не смогут распознать возраст, в котором
расположены точки; измените масштаб, чтобы расположение точек было разборчи-
вым, и шкала станет неточной. В любом случае, вы заставляете читателя проделать
большую работу — тем более, что заголовок графика не говорит ему о том, что
следует искать ;-)
Давайте посмотрим, что произойдет, если мы начнем с четкого заголовка с глаго-
лами действия и используем его для изменения дизайна этого графика: ”Мужчины
в Японии работают годами после достижения пенсионного возраста, в то время как
другие прекращают работу задолго до этого”. В данном случае формулировка заго-
ловка/данных говорит о том, что мы хотим подчеркнуть разницу между официаль-
ным выходом на пенсию и фактическим выходом на пенсию. Теперь можно разра-
ботать горизонтальную гистограмму, которая одновременно подтверждает это ут-
верждение и точно представляет основные данные, как показано в листинге 10.2 и
на рис. 10.4.
Листинг 10.2. гейгетепСаде.ру
1трог1: та^рХоИгЬ. рур!о1: аз рИ
1трог1: рапдаз аз рс1
1трог1: зеаЬогп аз зпз
1трог1: потру аз пр
22 ЬПр8://]ипксЬаг1:8Лурера(1.соп1/1ипк_сЬаг1:8/2005/11/йпа11у_а 1ап§е.Шт1
# список стран (сокращенный)
соип1:г1е8 = [1 Зарап', ' ТсеТапб', '5\л/1±гег"1апс1', Тгапсе', 'ТгеТапб', 'Сегпапу',
'1±а1у', 'ВеТдтип']
# разница в годах между официальным и фактическим выходом на пенсию
геШепеп1:_дар = [9, 2, 2, -1, -2, -2, -7, -8]
# соедините два списка вместе и укажите имена столбцов, как мы создаем 0а1:аЕгате
ге1Лгетеп1:_с1а1:а = рб.0а1:аЕгате(1181:(21р(соип1:г1е8, ге1л.гетеп1:_дар)),
соТиппз =[ ’соип±гу', ' ге^тгетеп^дар' ])
# на практике может быть предпочтительнее написать функцию, которая генерирует
# этот список, основываясь на значениях наших данных
Ьаг_со1огз = ['#б01с8Ь', '#б01с8Ь1, '#б01с8Ь1, '#4бас261,'#4бас261,'#4бас261,
' #4с1ас261,' #4с1ас261 ]
# передаем наши данные и палитру в функцию 'Ьагр1о1:()' 'зеаЬогп'
ах = зпз.Ьагр1о1:(х="ге1:1гетеп1:_дар", у="соип1:гу",
С1а1:а=ге1:1гетеп1:_с1а1:а, ра!е1:1:е=Ьаг_со1ог5) О
# выводим график!
рИ=.зЬом()
О Присвоив числовые значения оси х, а значения категорий — оси у. зеаЪот ото-
бразит это в виде горизонтальной, а не вертикальной гистограммы.
Япония
Исландия
Швейцария
Ирландия
Франция
Германия
Италия
Бельгия
-7.5 —5.0 -2.5 0.0 2.5 5.0 7.5
геИгетепЬдар
Рис. 10.4. Горизонтальная гистограмма пенсионного разрыва
Поскольку моя формулировка данных/заголовка теперь четко указывает на разницу
в годах между официальным и фактическим пенсионным возрастом, я решила ото-
бразить эту разницу на графике, а также изменить порядок данных: мужчины во
Франции выходят на пенсию только на один год раньше, в то время как мужчины в
Бельгии выходят на пенсию примерно на восемь лет раньше. Чтобы еще больше
подчеркнуть различие между возрастом до и после официального выхода на пен-
сию, я также выделила цветом столбики в соответствии с их положитель-
ным/отрицательным значением.
Сочетание положительных и отрицательных значений в этом датасете, а также бо-
лее длинные метки названий стран делают этот график более читаемым в виде го-
ризонтальной гистограммы, а не вертикальной. Однако если мы хотим проверить
представление в качестве вертикального графика для сравнения, необходимо поме-
нять местами столбцы данных, которые мы передаем в качестве аргументов хну
функции Ьагр1о1:(). Например, изменив это:
ах = 8П8.Ьагр1о1:(х="ге1:1гетеп1:_дар", у="соип1:гу", с1аГа=геГ1_гетеп1:_с1а1:а,
ра!е1:1:е=Ьаг_со1ог5)
на это:
ах = 8П8.Ьагр1о1:(х="соип1:гу", у="геГ1гетеп1:_дар", с!а1:а=ге1:1гетеп1:_с1а1:а,
ра!е1:1:е=Ьаг_со1ог5)
Хотя это изменение достаточно легко сделать, могут возникнуть реальные различия
в читабельности данного датасета при вертикальном или горизонтальном отобра-
жении. В частности, вертикальные столбики, как правило, лучше подходят для
данных с более короткими метками, меньшим разбросом и/или небольшим количе-
ством или полным отсутствием отрицательных значений, в то время как горизон-
тальные столбики обычно лучше подходят для расходящихся данных (особенно
при высокой доле отрицательных значений) и/или данных с более длинными метками.
Хотя визуализация, показанная на рис. 10.4, довольно проста, если вы запустите
код, то сами увидите разницу в качестве, которую, например, привносит в визуали-
зацию тщательная работа с цветовой палитрой. Хотя здесь я выбрала двоичное ко-
дирование цвета (пурпурный/зеленый), я также могла бы указать одну из 170 цве-
товых палитр библиотеки зеаЬогп (например, ра1еГГе=' ВиСп'), что (по большей части)
выровняло бы интенсивность цвета со значением каждой полосы.
Напомним, что при работе с гистограммами:
Правило
Столбцы должны начинаться с нулевой отметки!
Рекомендация
Вертикальные столбцы хороши для более плотных данных с меньшим разбро-
сом.
Рекомендация
Горизонтальные линии лучше использовать при применении более разнообраз-
ных и/или длинных меток данных.
Линейные диаграммы
Если в вашей формулировке данных речь идет о темпах изменения, а не о разнице
значений, самое время изучить линейные графики. Как и гистограммы, линейные
диаграммы могут эффективно отображать несколько категорий числовых данных,
но только по мере их изменения во времени. Однако, поскольку они не кодируют
визуально абсолютные значения данных, шкалы линейных диаграмм не обязатель-
но должны начинаться с нуля.
Сначала это может показаться приглашением к манипуляциям — и действительно,
линейные диаграммы были в центре некоторых крупных политических споров23.
Однако и для гистограмм, и для линейных диаграмм масштаб оси у определяется
данными: точно так же, как нельзя начать гистограмму со значения, отличного от
нуля, было бы абсурдно масштабировать ось у во много раз больше значения само-
го большого показателя данных, как показано на рис. 10.5.
Текущая
Запрашиваемая
Временные рамки
Рис. 10.5. Еще один плохой график сравнения зарплат
Несмотря на техническую точность, чрезмерно растянутая ось у на рис. 10.5 на-
столько сжала значения данных, что наши глаза больше не могут точно и эффек-
тивно различать разницу между ними. Для гистограмм наибольшее значение оси у
обычно должно находиться на следующем ”целом” приращении метки (подробнее
об этом в разд. "Выбор масштаба"). Для линейных диаграмм эксперты по визуали-
23 См. Тке Носкеу 8Иск: Тке Мо$1 Соп1гоуег8га1 Скаг1 т Зеленее, Ехркипе, автор С11Г18 Моопеу,
ЬНр8:/ЛЬеаНапНс.сот/ЫсЬпо1о«у7агсЫ\е/2013/05/ГЬе-Ьоскеу-8Нск-Ше-то81-сопЦо\ег81а1-сЬагЫп-
8с1епсе-ехр1атес1/275753; но, тем не менее, организационные схемы тоже (предупреждение о руга-
тельстве) свое отжили, ЬПр8://Д1скг.сош/рЬо1:о8/гоЬег1:ра1тег/3743826461.
зации, такие как Дона Вонг, рекомендуют, чтобы диапазон значений данных зани-
мал примерно две трети пространства оси у24.
Конечно, такой подход подчеркивает влияние, которое оказывает выбор точек дан-
ных для линейного графика на общий посыл. Например, рассмотрим график из
журнала Тке Есопотгз!25, показанный на рис. 10.6.
Пандемия сократила годовой поток ЕС1 на одну треть
Низкие приливы
Приток прямых иностранных инвестиций, $ трлн
Весь мир
2007 08 09 10 11 12 13 14 15 16 17 18 19 20
Рис. 10.6. Потоки прямых иностранных инвестиций (РЮ1), 2007-2020 гг.
В данном случае заголовок "Пандемия сократила годовой поток ЕВ1 на одну треть”
(Тйе рапбепйс си! ашша! ЕЭ1 До\у§ Ьу опе-Ймгс!) на самом деле довольно эффекти-
вен; он одновременно активен и конкретен. Но хотя данные, о которых говорится в
этом заголовке, включены в сопроводительную диаграмму, они не очень выделены
— визуализация включает данные более чем за десятилетие, хотя описываемые из-
менения произошли между 2019 и 2020 гг. Если пересмотреть график и сосредото-
читься только на том, что произошло между этими двумя годами, как показано на
рис. 10.7, мы можем более четко подтвердить формулировку данных и выявить до-
полнительное измерение данных: хотя прямые иностранные инвестиции значи-
тельно сократились в "развитых” странах, в ”развивающихся” регионах они в ос-
новном остались на прежнем уровне. Как говорится в самой статье, ”приток
средств в богатые страны падал быстрее, чем в развивающиеся, — на 58% против
всего 8%”. Этот двухточечный линейный график, известный также как диаграмма
наклона, не только облегчает читателям восприятие доказательств, лежащих в ос-
нове утверждения, вынесенного в заголовок, но и позволяет сделать вывод о нерав-
24 Подробнее см. в книге Тке \Та1181гее1 ,Тоигпа1 Сии1е 1о 1п/огтаИоп Сгарклсх, автор Попа М. \Уоп§.
25 ЬН:р8://есопот181:.сот/§гарЫс-(1е1:аП/2021/06/21/1:Ье-рап(1ет1с-си1:-аппиа1-Г(11-Поуу8-Ьу-опе4Ь1Г(1
номерном воздействии пандемии на ГВ1 в отношении "развитых” и "развивающих-
ся” стран, тем самым предоставляя доказательства и для последующего утвержде-
ния статьи. Как показано в листинге 10.3, построение этого типа базового линейно-
го графика занимает всего несколько строк кода.
Рис. 10.7. Пандемия сократила годовой поток Е01 на одну треть
Листинг 10.3. с(мс1_ЕО1_1трас1.ру
гтрог! таТр1о1:11Ь.рур1о1: аз рН
1трог1 рапбаз аз рб
гтрог! зеаЬогп аз зпз
1трог1 литру аз пр
# каждый отдельный массив - это строка данных
НИ = пр.аггау([[0.8, 0.7], [0.3, 0.6]])
Ебт._с1а1а = рб.Оа1:аЕгате(с1а1:а=ЕО1,
со!иппз=[1ОеуеТореб', '0е7е1ор1пд'])
ах = 5П5.11пер1о1:(с]а1:а=Гсй_с1аГа)
# выводим график!
рИ=.зЬом()
На этом этапе у вас может возникнуть вопрос — не будет ли включение данных
только за два года ошибкой. В конце концов, у нас в запасе есть десятилетний запас.
Настоящий вопрос, конечно, заключается не в том, есть ли у нас больше данных, а в
том, не искажают ли данные, которые мы утверждаем, более широкую тенденцию.
Если посмотреть на исходный график на рис. 10.6, становится ясно, что за последние
15 лет или около того объемы ЕВ1 падали так быстро только дважды: с 2007 г. по
2008 г. и снова с 2016 г. по 2017 г. Почему? Мы не знаем точно — ни оригинальный
график, ни полный текст статьи (я проверила!) не проясняют этого. Мы знаем, что
абсолютное значение изменения (около $500 млрд) и пропорциональные изменения
значения достаточно велики и уникальны, поэтому концентрация внимания только
на годовом изменении не вводит в заблуждение. Если мы хотим уверить наших чита-
телей в этом, лучше представить дополнительные подробности в таблице, где они
смогут подробно рассмотреть точные цифры, не отвлекаясь от главного.
Линейные диаграммы представляют собой важные визуальные формы, когда ско-
рость изменения (отраженная в наклоне каждой линии) занимает центральное ме-
сто в формулировке данных. Хотя этот тип графика не обязательно должен начи-
наться с нуля, он может использоваться только для представления данных с тече-
нием времени. Для обзора при работе с линейными графиками применяются
следующие правила:
Правило
Точки данных должны представлять значения с течением времени.
Рекомендация
Линии данных должны занимать примерно 2/3 площади диаграммы по вертикали.
Рекомендация
Четыре или менее линий должны быть четко окрашены/маркированы.
Хотя поначалу это может показаться нелогичным, на самом деле большое количе-
ство линий на линейной диаграмме — это вполне нормально. Конечно, при усло-
вии, что они оформлены так, чтобы не конкурировать с доказательствами нашей
формулировки данных. В следующем разделе показано, что такого рода ”фоновые”
данные могут быть полезным способом предоставления дополнительного контек-
ста для читателей, тем самым еще более эффективно поддерживая утверждения
вашего заголовка.
Диаграмма рассеяния
Хотя диаграммы рассеяния не часто используются в общем обмене данными, они
могут быть незаменимы в качестве точечного аналога линейных диаграмм, особен-
но когда у вас есть большое количество точек данных, которые иллюстрируют чет-
кую тенденцию или отклонение от этой тенденции.
Рассмотрим график на рис. 10.8, который воспроизводит одну из статей Иечч Уогк
Ттез26, и иллюстрирует, как даже среди тысяч показаний температуры, получен-
26 Ы1р8://пу11те8.сот/1п1егас11уе/2021/06/29/ир8Ьо1/рог11ап(1-8еа111е-уапсоиуег-луеа111ег.Ыт1
ных в течение более чем четырех десятилетий, три дня подряд в июне 2021 г. выхо-
дили далеко за пределы ожидаемого диапазона в городах от Портленда, штат Оре-
гон, до Ванкувера, Канада. Хотя заголовок, безусловно, мог бы быть более ярким,
сама визуализация содержит четкое послание: максимальные температуры, зафик-
сированные в Портленде, штат Орегон, в течение трех дней в конце июня, были
выше, чем в любой другой день за последние 40 лет.
Максимальные показатели температуры
в Портленде по дням за 1979-2021 гг.
120 градусов по Фаренгейту
Янв. Февр. Март Апр. Май Июнь Июль Авг. Сент. Окт. Нояб. Дек.
Рис. 10.8. Максимальные показатели температуры в Портленде по дням за 1979-2021 гг.
В большинстве случаев диаграммы рассеяния используются для отображения зна-
чений данных, полученных либо в течение времени (как на рис. 10.8), либо по от-
дельным членам некоторой ”группы” (например, ”школы”, ”болыпие города”, "озе-
ра, питаемые реками” или ”фильмы МагуеГ). Иногда диаграммы рассеяния могут
включать рассчитанные линии тенденций. Такие линии служат эталоном для срав-
нения отдельных точек данных с ''ожидаемыми” средними значениями. В иных
случаях эталоном может быть значение, определяемое профессиональными, юри-
дическими или социальными нормами.
Например, черпая вдохновение из статьи Рюпеег Рге$8 о школах, где ученики пока-
зывают лучшие результаты, чем можно было бы предположить27, можно использо-
вать библиотеку зеаЪогп для построения исторических данных школьной системы
Калифорнии, чтобы создать диаграмму рассеяния и выделить необычную точку
данных. Код для этого примера находится в листинге 10.4.
27 Вскоок Тка1 \Уогк: ЭезрНе Арреагапсез, ВскооЕ Оотр ВеИег 1кап Е.\рес1ес1 Нахе ТгаНз т Соттоп,
автор Ме§ап ВоШ1 е! а1., 9 июля 2010 г., ЬПр8:/ЦлУ1псШе8.сот/2010/07/09/8сЬоо18-Ц1аЬууогк-(1е8рйе-
арреагапсе88с!юо18-(1от§-ЬеН:ег4Ьап-ехрес1:е(1-Ьауе4гаЙ8-1п-соттоп.
Листинг 10.4. зсЬоо18_Л1а(_^огк.ру
гпрог! та-ЬрХоМгЬ.рурХо-Ь аз рИ
гпрог! зеаЬогп аз зпз
1прог1: рапдаз аз рд
# импортируем данные школьного тестирования
зсЬоо!_с1а1:а = рс1.геас1_С87("ар1Ь12Ъ<.с87")
# строим график зависимости результатов тестов от процента учащихся,
# обеспеченных питанием
8П8.зса1:1:егр1о1:(с1а1:а=8сЬоо1_с1а1:а, х="МЕА1_$", у="АРИ2В", а!рЬа=0.6, 1гпеигс11:Ь=0) О
# выделяем школу с высокими показателями
ЫдЫ1дЫ=_зсЬоо1 = 8сЬоо1_с1а1:а[8сЬоо1_с1а1:а[' 5МАМЕ' ] == \
"СЫп (ЗоЬп УеЬаИ) Е1епеп1:агу"]
ри.зса^егСЬтдЬИдЬ^зсЬооЦ1МЕА151 ], ЫдЫ1дЫ:_5сЬоо1['АР112В' ],
со1ог='огапде', а!рЬа=1.0) О
# выводим график!
рИ=.зЬом()
О Аргумент а!рЬа регулирует непрозрачность точек; значение 60% (0,6 в десятич-
ном исчислении) оказалось правильным балансом для разборчивости как отдель-
ных, так и перекрывающихся точек. Аргумент 11пем1сЛ:Ь=0 устраняет контуры во-
круг каждой точки, которые мешают эффекту тепловой карты при регулировке не-
прозрачности.
О Чтобы "выделить” школу, мы, по сути, просто создаем диаграмму рассеяния с
одной точкой в координатах хну выбранной точки данных.
Одним из основных вызовов при использовании диаграмм рассеяния стала пробле-
ма окклюзии, когда точки данных могут накладываться друг на друга, тем самым
скрывая истинную плотность данных. Один из подходов к решению этой проблемы
заключается в добавлении джиттера — небольшого количества неупорядоченно-
сти в размещении отдельных точек, призванного минимизировать это визуальное
перекрытие. Однако, начиная с версии зеаЬогп 0.11.2, джиттер указан как опцио-
нальный аргумент23, но числится как ”не поддерживаемый”. К счастью, можно со-
хранить точность данных без потери их интерпретируемости, регулируя непро-
зрачность или свойство а1рка точек данных. Если сделать все точки в нашей визуа-
лизации в некоторой степени прозрачными, перекрывающиеся точки данных
превращаются в своего рода тепловую карту, управляемую непрозрачностью, ко-
торая проясняет тенденции без потери конкретности, как показано на рис. 10.9.
28 ЬПр8://8еаЬогп.рус1а1:а.ог§/§епега1:ес1/8еаЬогп.8саН:егр1о1:.111:п11
40 60
МЕА1Я
Рис. 10.9. Некоторые школы не подпадают под общий паттерн
Когда же имеет смысл использовать диаграммы рассеяния?
Рекомендация
Данные должны быть достаточно объемными, чтобы были видны тенденции и
выбросы.
Рекомендация
Необходимо визуализировать соответствующие контрольные показатели, не
важно, вытекают ли они из данных или из внешних правил.
Рекомендация
Большая часть данных должна быть ”фонового” цвета, с выделением не более
нескольких точек.
Рекомендация
Отрегулируйте непрозрачность или примените джиттер для минимизации окк-
люзии данных.
Картограммы
Для многих из нас картограммы — это одни из самых привычных типов визуали-
зации. В зависимости от обстоятельств вы можете использовать картограммы для
планирования маршрута на работу или в школу, для поиска нового магазина или
ресторана, а также для поиска нового парка или велосипедной дорожки. Карто-
граммы также стали распространенной визуальной формой в массовых коммуника-
циях, где они появляются в виде карт погоды, карт выборов или даже карт "локато-
ров”, которые обеспечивают ориентиры для незнакомых мест. Если в данных есть
географическая составляющая, то вполне естественно рассмотреть возможность их
картографирования.
Однако в действительности, если ваши данные не связаны с географией, вам не
стоит наносить их на карту. Почему? Потому что карты представляют участок зем-
ли. Не преобладание и даже не численность населения, о чем обычно говорят наши
данные. Например, давайте вспомним данные по ссудам РРР из главы 6. Если бы
вы сгруппировали количество одобренных кредитов по штатам, используя функ-
цию Vа^^е_соип1:5(,Рго^ес1:81:аI:е,), то получили бы следующий результат (перефор-
матированный в столбцы для экономии места):
СА 99478 УА 18682 СТ 10197 ИН 4197 АК 2076
ТХ 60245 ИС 18022 А1_ 9025 Ю 3697 РР 2032
МУ 54199 МИ 16473 ОК 8598 ИМ 3524 УТ 1918
Е1_ 46787 СО 15662 5С 8522 МЕ 3490 ЫУ 1791
И 33614 МО 15170 ОТ 7729 Н1 3414 си 305
РА 30768 1л11 14729 КУ 7623 ОС 3175 VI 184
ОН 26379 1И 13820 1А 7003 Р1 3012 МР 55
М3 24907 МО 13511 К5 6869 1л(У 2669 А5 18
М1 24208 ТИ 12994 ИУ 6466 МТ 2648
МА 21734 А2 12602 ИЕ 4965 ИО 2625
СА 20069 ОР 10899 АР 4841 0Е 2384
1л!А 18869 1_А 10828 М5 4540 50 2247
Без особых усилий можно догадаться, что порядок появления штатов в этой табли-
це похож на их порядок в таблице29, где штаты ранжированы по численности насе-
ления. Другими словами, если бы мы "нанесли на карту” данные об одобрении кре-
дитов РРР, то в итоге получилась бы карта населения. Но, допустим, мы исправили
это и нормализовали количество ссуд по численности населения, и таким образом
создали новый столбец под названием "Одобренные кредиты на человека" или что-
то подобное. Даже теперь, когда мы преобразовали свои данные из населения в
преобладание, мы на самом деле просто создали себе проблему гистограммы в гео-
графической форме: фактическая визуальная площадь, занимаемая конкретным
штатом, просто не пропорциональна отображаемым данным. Как бы мы ни выби-
рали цветовую палитру или диапазоны данных, Делавэр будет занимать 1/50 визу-
ального пространства Вайоминга, несмотря на то, что у него на 25% больше одоб-
ренных кредитов. Составляя карту этих данных, мы просто гарантируем, что наше
визуальное представление работает с реальными данными.
Очевидно, что существует множество визуализаций карт, и многие из них, вероят-
но, допускают несколько из описанных мной ошибок. Картографирование предла-
гает такой, казалось бы, простой принцип визуальной организации, что многие лю-
ди не могут устоять перед соблазном его применения. Но использование его для
негеографических данных на самом деле наносит вред как самим данным, так и
вашим читателям.
29 ЬНр$://еп.уу1к1реЩа.ог«/уу1к1/Г1${_оГ_Г.8._$ЫЫ8_ап(1_ЫгпЩпе8_Ьу_рор111аНоп
Конечно, существуют прекрасные карты, посвященные действительно географиче-
ским явлениям: например, эти карты переписи населения в И ем? Уогк Итез предла-
гают продуманный подход к представлению данных переписи населения30, а работа
РгоРиЪНсаз по зонам затопления в Хьюстоне31 иллюстрирует, насколько важна гео-
графия (как природная, так и созданная человеком) во время экстремальных погод-
ных явлений. А для красивого и оригинального представления данных о ветре по-
смотрите эту карту ветров на Ып1.йп32.
Учитывая, насколько редко карты оказываются подходящей визуализацией для
поддержки ваших утверждений о данных (а также то, насколько сложным может
быть их построение в РуШоп), не будем приводить здесь пример кода для построе-
ния карт. Если вы работаете с данными, которые, по вашему мнению, действитель-
но необходимо картографировать, я рекомендую обратить внимание на библиотеку
§еорапба§33. Она предназначена для легкого объединения информации о форме,
связанной с картографированием, с ВаШЕгашез библиотеки рапбаз для создания
визуализаций.
Возьмите в руки бумагу
Хотя вашим визуализациям, вероятно, суждено существовать в цифровом
формате, я настоятельно рекомендую, когда придет время начать процесс про-
ектирования, отойти от клавиатуры и взять в руки ручку (карандаш или ме-
лок). Не волнуйтесь, если вы ”не умеете рисовать”. Ваша цель на этом этапе —
выявить (и, возможно, решить) визуальные проблемы без предвзятого отноше-
ния к тому, что могут или не могут сделать ваши цифровые инструменты. Если
ваша визуализация "бумажных прототипов”34 в итоге больше похожа на набро-
сок, чем на иллюстрацию, это даже лучше: исследования пользовательского
опыта показывают, что если образец дизайна слишком подробный или точный,
зрители скорее всего будут зацикливаться на деталях, таких как конкретный
размер шрифта или используемые цвета, а не на ”более широкой картине” во-
просов о расположении и смысле. Наброски идей визуализации на бумаге (или
доске) в качестве первого шага станут еще и эффективным способом опробо-
вать различные визуальные формы для ваших данных, оставаясь при этом де-
шевыми и простыми.
Лично я считаю, что создание эскиза помогает быстрее понять, действительно
ли данная визуальная форма (например, гистограмма) иллюстрирует нужную
взаимосвязь между элементами данных.
30 ЬПр8://пуНте8.сош/1п1:егасйуе/2015/07/08/и8/сеп8и8-гасе-тар.Ы:т1
31 ЬНр8 ://р го] ес18.ргориЫ1са.ог§/§гарЫс8/11агуеу-тар8
32 ЬНр://ЫпЩт/у\1пс1
33 ЬН:р8://§еорап(1а8.ог§/т(1ех.Ы:т1
34 ЬПр8://еп.уу1к1ре(На.ог§Лу1к1/Рарег_рго1:о1урт§
Это также облегчает рассмотрение и изменение таких вещей, как выделение
данных, аннотации, шкалы и метки осей, которые часто составляют разницу
между приемлемым и отличным графиком. Я также заметила, что если начать
с рисования визуализации, то когда приходит время приступать к программи-
рованию, создание окончательной (цифровой) визуализации происходит в два-
три раза быстрее.
Один совет относительно этого процесса: нет необходимости заботиться о
тщательном учете каждой точки данных, когда вы делаете эти наброски. Вам
необходимо получить общее представление о том, какой тип визуализации
лучше всего подходит для ваших данных, и принять ключевые решения о том,
что будет выделено, а что останется за кадром. Даже не имея под рукой точ-
ных значений данных, наброски идей позволят сделать обзор, поделиться и
получить мнения о них. Этого нельзя будет сделать, когда вы уже зафиксируе-
те свои решения в коде.
Элементы красноречивых визуальных эффектов
Хотя большую часть своей карьеры я была информационным дизайнером, я не счи-
таю себя настоящим графическим дизайнером. Я не смогу сделать хороший лого-
тип для вашей компании, так же как и вы, получив достаточно знаний по програм-
мированию на РуЙюп, не сможете устранить неполадки с принтером. К счастью,
благодаря учебе, большому количеству прочитанной информации, нескольким кур-
сам и щедрости многих талантливых дизайнеров, с которыми я работала, я узнала
достаточно много об эффективном визуальном дизайне в целом и информационном
дизайне в частности. Мои выводы, сделанные за несколько десятилетий работы,
изложены здесь.
"Мелкие" детали действительно имеют значение
Миллион лет назад, когда я только начинала работать фронтенд-программистом в
веб-стартапе, моим приоритетом номер один было создание вещей, которые рабо-
тают, и, честно признаться, я была очень рада, когда они работали. Со временем я
даже доработала и провела рефракторинг своего кода некоторыми из способов,
применявшихся в главе 5, и я была вполне довольна этими артефактами програм-
мирования. С точки зрения программиста, моя работа была довольно чистой.
Но я работала в команде по разработке дизайна, и коллеги дизайнеры постоянно
подталкивали меня к исправлению мелких деталей, которые казались мне скорее
"приятным дополнением”, чем необходимостью. Неужели действительно так важ-
но, плавно ли сменяются фотографии в слайд-шоу или слегка перескакивают, когда
занимают свои места? В то время программирование подобных эффектов означало
написание и корректировку (очень приблизительную) уравнений физики, чего я
делать не любила. Кроме того, вся эта кастомизация загромождала код.
Но больше всего в программировании мне нравится решать проблемы, поэтому в
конце концов я смирилась и внесла изменения, о которых они просили. И как толь-
ко эти изменения были запущены, я поняла, насколько более отшлифованным и
удовлетворительным был конечный результат. Я начала понимать, что качество
дизайна заключается в этих ”маленьких” деталях и что такие "мелочи”, как измене-
ние цвета при тапе или щелчке мышью, на самом деле делают мои интерфейсы и
графику более понятными и удобными для использования. Другими словами, не
отмахивайтесь от ”деталей” дизайна — они нужны не только для того, чтобы все
"выглядело красиво". Дизайн — это то, как все работает.
Доверяйте своим глазам (и экспертам)
Визуальные элементы, отображаемые в цифровом виде, обязательно выражаются в
количественных терминах: математическое происхождение шестнадцатеричных
цветовых кодов35 и позиционирования координат х/у может создать впечатление,
что поиск "правильных" цветов для графика или правильного позиционирования
метки описания — это вопрос правильной математики. Это не так. Например, вос-
приятие цвета является одновременно сложным и очень индивидуальным — мы не
можем быть уверены, что другие люди видят "тот же самый" цвет, что и мы, и, ко-
нечно, существует множество видов цветовой "слепоты", которая может помешать
некоторым людям вообще воспринимать разницу между определенными парами
взаимодополняющих цветов (например, красный/зеленый, синий/оранжевый, жел-
тый/фиолетовый). Не существует уравнения, способного это учесть.
На самом деле, каждый "цвет", с которым мы можем работать, характеризуется
тремя различными свойствами: оттенком (например, красный или синий), яркостью
(или светимостью) и насыщенностью. Цвета, которые "сочетаются" друг с другом,
выравнивают или контрастируют по этим признакам определенным образом. Когда
дело доходит до визуализации, необходимо, чтобы цвета не просто хорошо смотре-
лись вместе, — они должны содержательно кодировать информацию. Но что зна-
чит для цвета "на 20% более синий", чем другой? Что бы это ни было, это не просто
вопрос перестановки цифр в шестнадцатеричном значении цвета36.
К счастью, можно обратиться за помощью к экспертам. Более двух десятилетий
все, кто ищет советы по выбору цвета (в основном для картограмм, хотя это также
отличная сфера для других типов диаграмм), могут обратиться к работе Синтии
Брюэр37, картографа и профессора географии в университете Репп 81а1е, чей инст-
румент Со1огВге\уег38 предоставляет отличные бесплатные распределения цветов
для визуального дизайна. Аналогичным образом, в превосходной книге Донны Вонг
Тке 1Та11 81гее1 Тоигпа! Сшс1е 1о 1п/огтаИоп СгарЫсз (МоЧоп) приведены некоторые
из моих личных фаворитов, когда речь идет о цветовых сочетаниях для графики.
35 ЬПр8://сотриЫгЬоре.сот/Ытсо1ог.Ыт
36 Поверьте, я пыталась.
37 ЬПр://рег8опа1.р8и.е(1и/саЬ38
38 ЬПр8://со1огЬгелуег2.ог§/
Если вам очень хочется выбрать свою собственную цветовую палитру, то наиболее
оптимальный подход, который у нас есть, — это обратиться к самому большому
авторитету в области цвета: к природе. Найдите фотографию из мира природы (фо-
тографии цветов работают особенно хорошо) и используйте инструмент захвата
цвета, чтобы выбрать контрастные цвета, если они вам нужны, или несколько от-
тенков одного цвета. Используйте их по умолчанию для обновления настроек в лю-
бом пакете визуализации, и вы оцените, насколько более привлекательной и про-
фессионально выглядящей стала ваша графика.
Когда дело доходит до таких вещей, как размер шрифта и расположение надписей,
здесь также нет никаких уравнений, которым нужно следовать. Обычно достаточно
просто визуально оценить свою графику и подправить ее, если она кажется не со-
всем удачной. Например, я отчетливо помню, как программировала дизайн для од-
ного из моих коллег в Тке ТУаП $1гее1 Л>игпа1. В том проекте был список элементов
макета, и, естественно, я написала цикл Гог для их точного размещения. Проблема в
том, что при выполнении кода и его рендеринге что-то выглядело не так. Убедив-
шись, что я просто плохо оценила расстояние между элементами, я спросила его,
сколько пикселей белого пространства должно быть между каждым элементом. ”Я
не знаю, — сказал он, — я просто определяю расстояние на глаз”.
Хотя я понимаю, что вы можете растеряться от подобных советов, когда только
начинаете заниматься визуальным дизайном, но могу уверить, что если вы найдете
время поэкспериментировать, то в конце концов научитесь доверять собственным
глазам. С таким подходом, практикой и вниманием к (четко определенным) дета-
лям, изложенным в следующих разделах, вы вскоре будете создавать визуализации,
которые будут одновременно и точными, и красноречивыми.
Выбор масштаба
В разд. "Диаграммы, графики и картограммы — вот это да!" мы рассматривали
вопрос масштаба в отношении точности визуализаций. А здесь мы сосредоточимся
на ясности и читаемости. Такие графические пакеты, как зеаЪогп и ша1р1оШЪ, авто-
матически выбирают масштабы и пределы осей на основе ваших данных, но эти
значения по умолчанию по разным причинам могут потребовать корректировки.
После определения подходящего числового диапазона для данных необходимо
проанализировать, как на самом деле отображается ваш график, и убедиться, что он
соответствует общим правилам:
♦ пределы осей и отмеченные значения должны быть целыми числами и/или крат-
ными 5 или 10;
♦ для обозначения значений нельзя использовать сугубо научную терминологию;
♦ единицы измерения должны быть указаны только на последнем элементе каж-
дой оси;
♦ метки могут потребовать редактирования или (что менее предпочтительно) на-
клона, чтобы оставаться читаемыми.
Выбор цветовой гаммы
Помимо обращения к специалисту за советом по выбору конкретного цвета, поду-
майте о том, сколько цветов должно быть у элементов данных. Цвет может стать
бесценным способом выделить конкретную точку данных или провести различие
между измеренными и прогнозируемыми значениями. Выбирая цвета для диаграм-
мы или графика, помните:
Один цвет для каждой категории данных
Если вы, к примеру, отображаете данные за несколько месяцев о программе кре-
дитования РРР, все столбцы должны быть одного цвета. Аналогично, различные
значения одной и той же переменной должны быть оттенками одного цвета.
Избегайте непрерывного распределения цветов
Хотя настройка цвета каждого визуального элемента в соответствии с его значе-
нием может показаться более точной, подобно визуальному сжатию данных, ко-
торое мы видели на рис. 10.5, непрерывные цветовые палитры (или рампы) соз-
дают настолько незначительные цветовые различия, что человеческий глаз не
может их воспринять. Именно здесь пригодятся ваши расчеты распределения
(вы ведь делали их, верно?): создайте цветовую шкалу (или рампу) до пяти цве-
тов, а затем присвойте каждый из них одному квинтилю ваших данных39.
Используйте расходящиеся цветовые шкалы с осторожностью
Расходящиеся цветовые шкалы подходят только тогда, когда есть истинное
"нейтральное” значение, вокруг которого варьируются данные. В некоторых
случаях этот показатель может быть равен нулю. В других случаях это может
быть согласованное значение в данной области (например, Федеральная резерв-
ная система США считает уровень инфляции около 2%40 идеальным).
Никогда не выделяйте цветом более четырех отдельных категорий данных
Но включение контекстных данных в серый фон — это нормально.
Проверяйте цвета на доступность
Такие инструменты, как Со1огВге\уег, поддерживают возможность создания
комбинаций цветов, различимых для людей с дальтонизмом. Если вы используе-
те собственные цвета, протестируйте выбранные варианты, преобразовав графи-
ку в оттенки серого. Если вы все еще можете различать все цвета в своей визуа-
лизации, то и ваши читатели должны быть в состоянии это сделать.
Прежде всего делайте аннотации!
Цель процесса визуализации — поделиться знаниями о данных и поддержать свои
утверждения. Хотя заголовок с глаголами действия должен отражать основную
39 Вы можете быстро выполнить это в библиотеке рапбаз, используя функцию диапШе() и передав
значения 0.2, 0.4, 0.6 и т. д. Для более общего ознакомления с тем, как рассчитать эти значения и что
они собой представляют, см. листинг 9.3 в главе 9.
40 ЬПр8://Геаега1ге8егуе.§оу/Гад8/5В58Е72Г066А4ВВВА80ВВА659С55Г774.Ыш
идею графика, бывает полезно выделить или добавить контекст к конкретным точ-
кам данных в самой визуализации. Здесь не место для звездочек и сносок. Инфор-
мация, необходимая для точного или эффективного понимания графика, должна
быть частью основного визуального поля графика. Некоторые из основных спосо-
бов сделать данные в визуализации более понятными включают следующее:
Непосредственно помечать различия категорий
Вместо того чтобы создавать отдельную легенду, разместите метки данных не-
посредственно на визуализации. Это избавит читателей от необходимости пере-
ходить от одного графика к другому, чтобы понять смысл представленной ин-
формации.
Выделять цветом важные точки данных
Если одна ключевая точка данных является основной для вашего общего утвер-
ждения, выделите ее контрастным цветом.
Добавлять контекстные аннотации
Эти небольшие объемы текста (при необходимости соединенные с соответст-
вующим элементом данных связующей линией) могут быть надписью, объясне-
нием или важной контекстуальной информацией. Что бы это ни было, убеди-
тесь, что оно отображается как можно ближе к данным и всегда находится в
пределах визуальных границ самой графики.
От базового к красивому: настройка визуализации
с помощью зеаЬогп и таф1о(НЬ
И последнее слово о дизайне — визуальном или ином. Хотя в разд. "Элементы
красноречивых визуальных эффектов" я постаралась разложить элементы эффек-
тивной визуализации на составные части, по-настоящему красноречивая графика —
это не набор взаимозаменяемых частей. Изменение одной ее части — перемещение
метки, изменение цвета — означает, что многие, если не все, оставшиеся элементы
необходимо скорректировать, чтобы привести целое в равновесие. Именно поэтому
я не привожу примеры кода для каждого отдельного аспекта дизайна, описанного
ранее. Изолированное рассмотрение отдельных элементов может затруднить пони-
мание, почему тот или иной элемент так важен. Но если рассматривать их как часть
единого целого, то (надеюсь) станет ясно, как каждый из них вносит свой вклад в
воздействие графики на человека.
Чтобы реализовать индивидуальные визуальные эффекты в РуШоп, мы по-
прежнему будем полагаться на библиотеки зеаЪот и таф1оШЬ. Но хотя в преды-
дущих случаях мы позволяли зеаЪогп делать большую часть тяжелой работы, в
этом примере именно тонкий контроль, предлагаемый ша1:р1оШЬ, будет действи-
тельно блистать. Да, мы по-прежнему будем позволять библиотеке зеаЬогп выпол-
нять задачи высокого уровня, такие как построение графиков данных в масштабе.
Но шаф1оШЬ даст нам инструмент, необходимый для указания всех тонкостей: от
размещения и ориентации меток до единиц измерения, меток, аннотаций и выде-
ляемых значений — т. е. всего, что нам нужно, чтобы действительно адаптировать
нашу визуализацию к своим требованиям.
В этом примере мы отвлечемся от данных РРР и вместо этого будем работать с
данными по СОУГО-19, собранными группой исследователей на основе данных
Университета Джона Хопкинса и размещенными на сайте Оиг АУогМ ш Эа1а41. Цель
этой визуализации — подчеркнуть всплеск подтвержденных случаев заболевания
СОУГО-19 в США в июле 2020 г., который объяснялся ускоренным возобновлени-
ем работы42 во многих штатах, произошедшим весной43, а также массовыми меро-
приятиями во время празднования 4 июля44. Чтобы понять разницу между исполь-
зованием значений по умолчанию и настройкой диапазонов, меток и цветов осей,
сравните рис. 10.10 с рис. 10.11. Код для создания рис. 10.11 показан в листинге 10.5.
202 с? лид и—гаи—мттщрмтпто1 о о о
Дата
Рис. 10.10. Гистограмма, созданная библиотекой визуализации
со значениями по умолчанию
41 ЬПр8://оипуог1сПпс1а1:а.ог§/согопау1г118-8О11гсе-с1а1:а
42 Но\у Согопсгмгиз Сазез Наче Клзеп 8тсе 5Ме8 Кеорепей, автор Ьахаго Оашю, Мт’ Уогк Ите8, 9
июля 2020 г., ЬПр8://пуйте8.сош/1п1:егасйуе/2020/07/09/и8/согопау1ги8-са8е8-георешп§4геп(18.Ы:т1.
43 Согопач1ги8 Са8в8 Ко8е Ъу Ееаг1у 50 Регсеп1 Ьа8( Моп1к, 1М Ъу 5Ме8 Тка1 Кеорепей Е1г81, авто-
ры Аппе Оеагап, Вегек На\укт§ и ЗюЫгап О’Огас1у \Уа81гт§1оп Ро§1, 1 июля 2020 г.,
ЬНр8://у\а8Ь1п«Гопро8Гсот/роПНс8/согопа\1ги8-са8е8-го8е-Ьу-пеаг1у-50-регсепЫа8ЬтопШ-1е(1-Ьу-
8ЫЫ81ЬаЬгеорепес1-Г1Г8Ц2020/07/01/3337Пес-ЬЬ96-11еа-80Ь9-40есе9а701с1с8Гогу.Ыт1.
44 СаИ/огта СОУЮ-19 Са8в8 Зр1кес1 а/1ег Му 41к. РатИу (Мкепп§8 Не1рес1 1ке 8ргеас1, ЕхрегК 8ау.
авторы Магк О1аШе и №со1е Нау<1еп, Ц$А ТоАау, 2 августа 2020 г., ЬПр8://и8аЩ(1ау.сот/81:огу/
пехУ8/пайоп/2020/08/02/ соу1с!-1 9-8 р1ке-са1Иогп1а-аПег-|и1у-4-ПпкесМ‘аггп1у-<*а1Ъепп<>8/5569158002.
Всплеск заболеваемости СОУЮ-19
после ослабления ограничений весной 2020 года
2020 год
2021 год
Месяц
Рис. 10.11. Кастомная визуализация
Листинг 10.5. гейпес!_соу|с1_ЬагсЬаг1.ру
# 'рапбаз' для загрузки данных; 'зеаЬогп' и 'па^рХоПлЬ' для визуальных эффектов
гпрог! рапбаз аз рб
1трог1 зеаЬогп аз зпз
гтрог! та^рХоШ.Ь.рурХо!: аз рН
# ' ЕипсЕогпа^ег' для форматирования меток осей
Тгоп па1р1о1'1тЬ.1:1скег гтрог! ЕипсЕогпа^ег
# 'ба^еМте' для интерпретации и настройки дат
Тгоп ба^еИте гпрог! ба^еШге
# загружаем данные
7асс1пе_ба1:а = рб. геас1_С87( ’омтб-со71б-ба1:а.С87')
# преобразуем столбец 'ба1:е' в "реальную" дату
7асс1пе_ба1:а[1 ба^е1 ]= рбЛо_ба1:е1лте(7асс1пе_ба1:а[1 ба^е1 ])
# сгруппируем данные по стране и месяцу
соипТгу_апб_топ1± = 7асс!пе_ба1:а.дгоирЬу(' 1_зо_собе'). гезатр1е(’М',
оп='с1а1:е' ).зип()
# используем ' гезеТ_1пбех()' для "сглаживания" заголовков ОаТаЕгапе
соипТгу_апб_поп1:Ь_ирба1:е = соипТгу_апб^оп1±.ге5е1:_1пс1ех()
# выберем данные только по Соединенным Штатам
дизТ_118А = соипТгу_апб_поп1±_ирба1:е[соипТгу_апб_топТЬ_ирс1а1:е['1зо_сос1е' ]==' К5А1 ]
# создадим базовую гистограмму с помощью 'зеаЬогп'.
ах = зпз.Ьагр1о1:(х="ба1:е", у="пем_сазез", ра!е1:1:е=['#ЬаЬаЬа'], баТа=диз1:_118А)
# переберем циклом прямоугольники столбцов и установим цвет для столбца
# июля 2020 г. в красный
Тог I, Ьаг гп епипега^еСах.раТсЬез):
гТ I == 6:
Ьаг.зеТ_со1ог('#са0020')
# устанавливаем максимальное значение оси у на 7 млн
ах. зеТ_у Ип( 0,7000000)
# устанавливаем метки осей
рИ.х'ЕаЬеЦ 'МопТЬ')
рИ.у1аЬе1('Мем сазез (М)')
# изменяем цвет, расположение и ориентацию "меток осей"
ах.Т1ск^эагатз(б1гесТ1оп='ои1:', 1епдТЬ=5, м1бТЬ=1, со1ог='#404040',
со!огз='#404040',раб=4, дг1б_со1ог=' #404040', дг1б_а!рЬа=1,
гоТаТ1оп=45) О
# функции для форматирования "меток осей"
# 'пПИопзО' преобразует научные понятия в миллионы случаев заражения
беТ п1111опз(7а1, роз): О
посНТтесК/а! = 7а1*1е-6
ТогтаТТесН/а! = з^гСпосНТтесН/а!)
!Т 7а! == ах.деТ_у1Лп()[1]:
ТогтаТТесМ/а! = ТогтаТТесН/аХ+'М'
1Т 7а! == 0:
ТогтаТТес1_7а1 = "0"
геТигп Тогта-СТесМ/а!
# 'сиз1:от_с1а1:е5()' будет сокращать даты для более удобного чтения
деТ сизТоП-баТез^а!, роз): О
ба^езЗт-^Т = зизТ_и8А. баТе.ТоИз^О
баТе_1аЬе! = ""
1Г роз 1з по! Мопе: О
сиггеп^уаТие = с1а1е5_1л51[ро5]
сиггеп1:_топ1:Ь = ба1:еНте.81:гГНте(сиггеп1:_7а1ие, '%Ь')
с1а1:е_1аЬе1 = сиггеп1:_топ1±
1Г с1а1:е_1аЬе1 == ' Зап':
ба1е_1аЬе1 = с1а1:е_1аЬе1 + " '"+ с!а1:еНте.81:гГНте(сиггеп1:_7а1ие,
'%У')
ге^игп с1а1:е_1аЬе1
# назначаем функции ^огтаНег
у_ГогпаНег = ЕипсЕогпа1:1:ег(т1111оп8)
х_ГогпаНег = ЕипсЕогпаНег(сиз1:от_с1а1:е8)
# применяем функции ГогпаНег к соответствующей оси
ах.уахтз.зе^дпадоМ'огтаНегСу-ГогтаНег)
ах. хах1 з. 5е1:_та д о г_Го гпа Не г (х_Го гпа Не г)
# создаем и располагаем текст аннотации
ах.1ех!(4, 3000000, "СопПгтес! сазез\поНеп 1ад 1пГесНоп\пЬу зеуега! меекз.")О
# получаем значение всех столбцов в виде списка
Ьаг_7а1ие = диз^ЗА.пе^азезЛоиз^О
# создаем связующую линию
ах.7Ипез( х = 6, со!ог='#404040', Нпем1с11:Ь=1, а!рЬа=.7,
ут!п = Ьаг_7а1ие[6]+100000, углах = 3000000-100000)
# устанавливаем заголовок графика
рН.ННе("С0\/10-19 сазез зртке ГоИомтпд геТахес! гез1т1сНоп5\п" + \
"1п ±Ке зргтпд оГ 2020", Гоп1зл/е1дЫ:="Ьо1с1")
# выводим график!
рН.зЬогл/О
Л Настройка ориентации, цвета и других атрибутов ”меток осей”, маркирующих
значения на каждой оси графика, может быть выполнена с помощью метода
Нск_рагатз() из библиотеки ша1р1о1НЪ45.
О Функции ЕипсЕогта-Нег будут назначаться пользовательские функции. Любая из
таких функций будет получать «значение» и позицию «метки оси» в качестве аргу-
ментов.
О В ”интерактивном режиме”, если роз будет содержать значение Мопе, эта функ-
ция будет выдавать ошибки.
45 ЬПр8://та1р1оШЬ.ог§/81:аЫе/ар1/_а8_§еп/та1:р1оШЬ.ахе8.Ахе8.йск_рагап18.Ы:п11
О По умолчанию позиционирование текстовых элементов, расположенных поверх
графика46, осуществляется по ”координатам данных”, например, значение 1 вырав-
нивает начало текста влево по центральной точке первого столбца. Введенное зна-
чение ”у” закрепляет нижнюю часть текстового поля.
Как видно из листинга 10.5, процесс кастомизации нашей визуализации в РуЙюп
представляется несколько сложным; адаптация внешнего вида и настроек вывода
по умолчанию почти в три раза увеличила количество необходимых строк кода.
В то же время вывод по умолчанию в этом случае был практически нечитаемым.
Смягчив цвета, выделив важные точки данных и (что, возможно, наиболее важно)
усовершенствовав метки данных, нам удалось создать график, который может за-
нять достойное место в большинстве контекстов публикации. И очевидно, что
большую часть приведенного кода можно доработать и перепрофилировать, чтобы
сделать эту степень кастомизации гораздо менее сложной для создания будущей
графики.
Выйдите за рамки основ
Хотя в этой главе я постаралась охватить основные принципы эффективной и точ-
ной визуализации данных, правда в том, что ценные идеи для визуализации могут
прийти откуда угодно и далеко не всегда из книг или блогов, посвященных этой
теме. Одним из моих любимых графиков времен работы в \^а11 81гее11оита1 стала
визуализация безработицы47, представленная на рис. 10.12. На создание этой визуа-
лизации меня вдохновила похожая форма, которую я встретила на музейной вы-
ставке, посвященной изменению климата, — геометрический формат тепловой кар-
ты позволяет читателям наглядно сравнить десятилетия ежемесячных уровней без-
работицы. Если вы интересуетесь дизайном, вы можете многое узнать о том, что
работает (и не работает), просто внимательно взглянув на средства массовой ин-
формации, постоянно окружающие вас — будь то реклама в Интернете или меню в
ресторане. Если что-то не кажется отшлифованным или привлекательным, внима-
тельно изучите все составляющие. Цвета несочетаемы? Трудно ли читать шрифт?
Или просто слишком много элементов теснятся в слишком маленьком пространстве?
Критиковать работу других, конечно, легко. Если вы действительно хотите улуч-
шить свои визуализации, бросьте себе вызов и попробуйте создать лучшие реше-
ния, и вы узнаете очень много нового. И если вы заметите, что ищете подходящую
лексику для описания того, что не работает, можете просмотреть некоторые ресур-
сы в приложении Г. Там указаны некоторые из моих любимых ресурсов для расши-
рения и улучшения работы по визуализации.
46 ЬПр8://та1р1оШЬ.ог§/81:аЫе/ар1/_а8_§еп/та1:р1оШЬ.рур1оЩехЬЫ:т1
47 кПр8://1еппИегуа1епИпос1еупе8.сот/2009/09/16/§п(1-§гарЫс-и-8-ипетр1оутеп1>га1:е
Январь
Февраль
Март
Апрель
Май
Июнь
Июль
Август
Сентябрь
Октябрь
Ноябрь
Декабрь
Рис. 10.12. Безработица в США с течением времени
(первоначально разработано для \Л/аП 81гее1 Зоигпа!)
Заключение
Как и программирование, визуализация — это прикладное искусство: единствен-
ный способ повысить свои навыки — это практика. Если у вас нет под рукой про-
екта, над которым можно поработать, начните с ”найденной” визуализации, кото-
рую вы считаете некачественной, и переделайте ее самостоятельно. Можно исполь-
зовать РуШоп, или другие вычислительные инструменты, или просто карандаш и
бумагу — важно попытаться решить проблемы, которые вы видите в оригинале. На
этом пути вы на собственном опыте убедитесь в компромиссах, присущих каждому
дизайнерскому решению, и начнете развивать навыки, необходимые для миними-
зации этих компромиссов в интересах достижения ваших целей визуализации. И
когда вы столкнетесь с собственными проблемами визуализации данных, у вас бу-
дет портфолио предыдущих работ. К такому архиву можно будет обратиться при
размышлениях о том, как лучше представить имеющиеся данные и выполнить свою
работу.
На данный момент мы выполнили практически все поставленные задачи по работе
с данными с помощью РуШоп. Но за пределами РуШоп существует целый мир ин-
струментов для работы с данными и визуализации, которые могут быть удивитель-
но полезны для поддержки вашей работы по выпасу и повышению качества дан-
ных. Мы обратимся к ним в главе 11.
ГЛАВА 11
За пределами Ру№оп
РуЙюп — исключительно мощный и универсальный инструмент для работы с дан-
ными, и если вы следили за упражнениями в этой книге, то, надеюсь, уже чувствуе-
те себя более уверенно, используя его для продвижения своих собственных проек-
тов по выпасу данных. Благодаря активному сообществу РуЙюп и постоянно разви-
вающемуся набору полезных библиотек, создаваемых и поддерживаемых членами
сообщества, работа, которую вы затратили на изучение основ, изложенных в этой
книге, будет ценной независимо от того, будет ли ваш следующий проект по выпа-
су данных завтра или через год. Кроме того, хотя РуЙюп как язык программирова-
ния во многом уникален, навыки программирования и приобретенный здесь сло-
варный запас дадут вам преимущество при работе с другими языками программи-
рования, особенно такими объектно-ориентированными, как 1ауа8спр1.
Тем не менее на протяжении всей книги я старалась разъяснить одну вещь: бывают
случаи, когда ”программное” решение проблемы фактически будет не самым эф-
фективным. Например, работа с файлами Ехсе1 и ХМЬ в главе 4 показала, что ино-
гда попытки сделать что-то программно просто не имеют смысла. Например, хотя в
листинге 4.12 можно было бы написать скрипт на РуЙюп для просмотра всего на-
шего ХМЬ-документа с целью выявления его структуры, несомненно, быстрее и
проще было просто просмотреть данные, определить интересующие элементы и
написать программу на РуЙюп непосредственно для них. Более того, бывают слу-
чаи, когда написание программы на РуЙюп может в целом потребовать больше уси-
лий, чем действительно необходимо для конкретного проекта по выпасу данных,
особенно если он меньше или носит исследовательский характер. Хотя рапбаз —
невероятно полезная библиотека, вам все равно может понадобиться написать дос-
таточно много кода только для того, чтобы получить базовое представление о со-
держимом нового датасета. Другими словами, хотя я полностью уверена, что мощь
и универсальность РуЙюп делает его незаменимым инструментом для работы с
данными, я хочу выделить несколько других бесплатных инструментов и/или инст-
рументов с открытым исходным кодом, которые будут полезными1 для вас как до-
полнение к РуЙюп в проектах по работе с данными.
1 Я определенно их таковыми считаю!
Дополнительные инструменты для анализа данных
РуЛоп обеспечивает высокую скорость и гибкость при работе с данными, но он не
очень хорошо подходит для того, чтобы получить возможность реально посмотреть
на ваши данные. Поэтому на протяжении всей этой книги мы полагались на основ-
ные текстовые редакторы (и иногда на веб-браузеры), когда хотели визуально про-
смотреть датасеты. Хотя текстовые редакторы отлично подходят для первого шага
в этом направлении, вам также стоит освоить хотя бы одну из следующих про-
грамм, чтобы получить быстрый первоначальный обзор данных — особенно если
обрабатываемые файлы не слишком большие.
Программы для работы с электронными таблицами
Вполне возможно, что еще до того, как начать читать эту книгу, вы уже были зна-
комы с программами электронных таблиц — будь то онлайн-версии, такие как
Ооо§1е 8Ьее18, платные локальные варианты программного обеспечения, такие как
М1СГО8ОЙ Ехсе1, или альтернативы с открытым исходным кодом, такие как 1лЪге
ОГйсе Са1с. Программы для работы с электронными таблицами обычно поставля-
ются в комплекте с программными пакетами ”офисного” типа и предлагают базо-
вые функции расчетов, анализа и построения графиков. В целом, нет большого раз-
личия в том, что могут делать эти программы, хотя некоторые из них более гибкие.
Например, я предпочитаю программу ЫЪгеОГйсе2, потому что она бесплатная, с
открытым исходным кодом и работает на всех платформах (включая менее распро-
страненные, такие как 1лпих). У нее даже есть сертифицированное приложение для
СйгошеЪоокз и устройств АпбгоМ под названием СоПаЬога3. Тем не менее, если у
вас уже есть определенная программа для работы с электронными таблицами или
вы хорошо знакомы с ней, нет причин для перехода на какую-либо другую, если
только вас не разоряет оплата текущего приложения. Что бы вы ни делали, не ис-
пользуйте пиратское программное обеспечение. В мире, где троян-вымогательские
программы распространяются в огромных количествах, риск для ваших устройств
и данных просто огромен, и это не стоит того!
Хотя многие программы для работы с электронными таблицами поддерживают
расширенные функции, которые примерно соответствуют возможностям РуЙюп (в
гораздо меньшем масштабе), я обычно обращаюсь к ним для решения очень специ-
фических задач по выпасу и оценке данных. В частности, я часто использую про-
грамму электронных таблиц для быстрого выполнения следующих действий:
Изменение форматов файлов
Например, если мои данные предоставлены в виде многолистового файла ХЬ8Х,
я могу открыть его в программе обработки электронных таблиц и сохранить
только интересующий меня лист в формате .С8У.
2 ЬПр8://ПЬгеоЖсе.ог§/
3 ЬПр8://со11аЬогаоЖсе.сот/рге88-ге1еа8е8/со11аЬога-оШсе-8Ыр8-Гог-сЬготеЬоок8
Переименование столбцов
Если столбцов не так много, я могу заменить столбцы с неудобными или неяс-
ными заголовками на что-то более читабельное и/или интуитивно понятное для
моих целей.
Получение "ощущения"значений данных
Являются ли представленные в столбце ”йа1е” значения датами на самом деле?
Или здесь просто обозначены годы? Много ли явно отсутствующих значений
данных? Если мой датасет относительно невелик, простого визуального изуче-
ния данных в программе электронных таблиц иногда бывает достаточно, чтобы
определить, есть ли требуемые данные или стоит продолжать поиск.
Создание базовой сводной статистики
Конечно, я могу сделать это в РуЙюп, и в основном я так и делаю. Но если у ме-
ня всего несколько сотен строк данных, набрать выражение =МЕО1АМ() и затем
выбрать интересующие ячейки иногда быстрее, особенно если исходный файл
данных содержит метаданные, которые в противном случае пришлось бы уда-
лить (как показано в главе 4 и снова в главах 7 и 8).
Конечно, у каждого инструмента свои недостатки, и предварительный просмотр
данных в программе для работы с электронными таблицами может привести к не-
ожиданным результатам. Как вы могли догадаться из наших продолжительных
приключений с "датами” в стиле ХЕ8, предварительный просмотр файла, содержа-
щего значения, похожие на даты, может привести к тому, что они будут отобра-
жаться очень по-разному. Это зависит от конкретной программы электронных таб-
лиц и ее способов обработки и отображения дат по умолчанию. Поэтому всегда на-
до проверять значения, похожие на дату, с помощью текстового редактора, если
исходный формат данных был текстовым (например, .С8У, .18У или .Ш). Аналогич-
ным образом, обязательно проверьте форматирование (по умолчанию или приме-
ненное) в любых ячейках, содержащих числа, поскольку сокращение или округле-
ние значений может скрыть важные изменения в данных.
Ореп Кейпе
Один из инструментов, к которому я чаще всего обращаюсь для первоначального
изучения больших структурированных датасетов, — это ОрепКейпе4. По личному
опыту могу сказать, что ОрепКейпе представляет собой уникальный программный
инструмент, помогающий преодолеть разрыв между традиционными программами
электронных таблиц и полноценными языками программирования, такими как Ру-
Йюп. Как и программы для работы с электронными таблицами, ОрепКейпе работает
через графический интерфейс пользователя (Сг1Л), поэтому большая часть вашей
4 Ы1р8://орепгейпе.ог§/
работы с ним будет заключаться в наведении и щелчках мышью. Однако, в отличие
от электронных таблиц, вы не прокручиваете строки данных, чтобы получить пред-
ставление о том, что они содержат. Вместо этого вы можете использовать опции
меню для создания фасетов, которые предоставляют сводную информацию, анало-
гичную предоставляемой методом 7а1ие_соип1=5()5 из библиотеки рапбаз, — но без
необходимости писать какой-либо код. ОрепКейпе также поддерживает пакетное
редактирование, реализует несколько алгоритмов для сопоставления строк (вклю-
чая метод фингерпринтинга, который мы использовали в листинге 6.11) и может
импортировать большие файлы по сегментам (например, 100 000 строк за раз). По
правде говоря, ОрепКейпе часто становится моей первой остановкой при выпасе
нового датасета, поскольку он легко открывает различные форматы данных и даже
предлагает удобный ”живой” предварительный просмотр того, как данные, напри-
мер, будут разобраны на основе вашего выбора разделителей. После загрузки ваше-
го датасета ОрепКейпе также может практически в один клик дать ответы на во-
просы типа "Какое значение наиболее часто встречается в столбце х?”. Наконец,
каждый раз, когда вы вносите фактические изменения в файл данных в ОрепКейпе
(в отличие от кластеризации или фасетирования), он автоматически записывает
ваши действия в экспортируемый файл в формате .)зоп. Его затем можно приме-
нить к другому файлу ОрепКейпе, чтобы эти действия автоматически повторились
(обычно в течение нескольких секунд). Это невероятно полезно, если вам нужно
переименовать или переставить столбцы в датасете, который регулярно обновляет-
ся поставщиком данных. При этом он еще более полезен, если вам нужно, чтобы
кто-то другой мог это сделать, а у этого человека нет навыка или возможности ис-
пользовать РуШоп.
В основном я использую ОрепКейпе, чтобы легко выполнять следующие действия:
Предварительный просмотр небольших фрагментов больших датасетов
Инструмент ОрепКейпе позволяет загружать (и пропускать) требуемое количе-
ство строк в датасете. Это особенно удобно для больших датасетов, когда я хочу
получить разумное представление о том, что они содержат, но не знаю о них
практически ничего. Я могу начать с загрузки 50 000 или 100 000 строк и ис-
пользовать фасеты и другие функции, чтобы получить общее представление,
скажем, о типах данных и об организации всего датасета.
Получение быстрой оперативной информации о датасете
Какой тип разрешения на съемки в Нью-Йорке запрашивают чаще всего6? А ка-
кой район самый популярный? Как показано на рис. 11.1, ОрепКейпе может
предоставить эти подсчеты за один или два щелчка мыши и позволяет так же
быстро создавать перекрестные таблицы.
5 ЬПр8://рап(1а8.ру(1а{а.ог§/(1ос8/геГегепсе/ар1/рапс1а8.$епе8.уа1ие_со11п1:8.1Нт1
6 ЬН:р8://(1а1:а.сйуоГпеуууогк.и8/Сйу-СоуегптепЦЕ11т-Регтк8/1:§4х-Ь46р
Выполнять основные преобразования, которые не поддерживают программы
электронных таблиц
В некоторых программах по работе с электронными таблицами не хватает функ-
циональности, например, возможности разделить строку на определенные сим-
волы, или поддержка регулярных выражений может быть ограничена. Одна из
моих любимых функций ОрепКейпе — пакетное редактирование, которое мож-
но легко и быстро выполнить с помощью окна фасетного поиска слева.
Автоматическая генерация макросов
Многие программы электронных таблиц позволяют записывать макросы, авто-
матизирующие определенные действия, но ОрепКейпе записывает их по умол-
чанию, что делает его более мощным инструментом с меньшей кривой обучения
для выполнения такого рода задач.
Рис. 11.1. Разрешения на съемку в Нью-Йорке в программе ОрепКейпе
Конечно, есть некоторые аспекты работы с ОрепКейпе, к которым нужно привык-
нуть. Во-первых, хотя процесс установки становится все более удобным, он зависит
от наличия на вашем компьютере языка программирования под названием 1ауа,
поэтому запуск процесса установки может быть многоэтапным. Запуск программы
после ее установки также немного необычен: вам необходимо щелкнуть (или дваж-
ды щелкнуть) мышью на значке программы для запуска и, в некоторых случаях,
открыть окно браузера с указанием адреса вашего локального хоста (обычно
НН:р://127.0.0.1:3333/ или Ы:1:р://1оса1Ьо81::3333). Как и приложение 1иру1ег Мо1е-
Ьоок, ОрепКейпе фактически работает через крошечный веб-сервер на вашем ком-
пьютере, а интерфейс — это просто веб-страница, которая ведет себя как мощная
программа обработки электронных таблиц. Несмотря на эти причуды, ОрепКейпе
невероятно полезен и часто служит отличной стартовой площадкой для проведения
первоначального исследования (потенциально грязного) датасета.
Дополнительные инструменты для обмена
и представления данных
Глава 10 была посвящена тому, как выбирать и совершенствовать визуализации с
помощью РуЙюп и ключевых библиотек зеаЪот и таф1оШЬ. Хотя степень доступ-
ной с помощью этих инструментов кастомизации впечатляет, бывают случаи, когда
необходимо внести небольшие изменения в визуализацию, а вы не хотите или не
можете восстановить ее из исходного источника данных с помощью РуЙюп.
Если требуется быстро добавить или изменить что-то незначительное в визуализа-
ции, наличие доступа к программному обеспечению для редактирования изображе-
ний очень важно. И хотя вы, вероятно, знакомы с очень мощными и очень дороги-
ми коммерческими программными приложениями для редактирования изображе-
ний, вы можете не подозревать, что существуют такие же мощные инструменты,
только в бесплатном виде с открытым исходным кодом.
Редактирование изображений в форматах ^6, РМС и С1Р
Если вы ищете что-то мощное, но не можете (или не хотите) много за это платить,
для редактирования пиксельных изображений особенно хорошо подойдет ОМЕГ 1ш-
а§е Машргйайоп Рго§гат (СИМР). Редактор СИМР7 является бесплатным с откры-
тым исходным кодом и работает на всех платформах. Хотя стиль пользовательско-
го интерфейса явно устарел (на момент написания этой книги интерфейс находился
в процессе модернизации), реальность такова, что сама программа может выпол-
нять любое базовое (и не очень базовое) высококачественное редактирование изо-
бражений, особенно если вы просто хотите добавить (или удалить) текст или анно-
тации на изображение, обновить метки осей и т. д.
Не секрет, что у О1МР, возможно, несколько крутая кривая обучения. Сочетания
клавиш могут быть неожиданными, а расположение некоторых меню и внешний
вид их значков не соответствуют тому, что вы видели в коммерческих программах.
Тем не менее, если вы не эксперт в другой программе редактирования изображений
и не готовы платить (и продолжать платить) за доступ к ней, любое затраченное на
изучение СНМР время будет того стоить. Особенно если вам нужен лишь периоди-
ческий доступ к программам для редактирования изображений, СИМР — это мощ-
ный и гибкий вариант.
Программное обеспечение для редактирования 8У6
и других векторных форматов
Если планируется использовать визуализации для печати или в других контекстах с
высоким разрешением (или гибким разрешением), вы вполне можете сохранить их
в векторном формате. Хотя размеры файлов больше, векторная графика гораздо
более гибкая, чем ее пиксельные аналоги. Векторные изображения можно увеличи-
вать и уменьшать без потери качества, без пикселизации или размытости. Однако
их нельзя эффективно редактировать с помощью программ для работы с растровы-
ми изображениями, таких как 61МР.
И снова, если у вас есть бюджет на коммерческое программное обеспечение, стоит
использовать его, но и здесь есть бесплатный вариант с открытым исходным кодом.
Как и СЛМР, приложение 1пк§саре8 — бесплатное, с открытым исходным кодом и
кроссплатформенное. И, как и СИМР, оно обладает почти всеми теми же функция-
ми, что и дорогое коммерческое программное обеспечение для редактирования
векторных изображений. Более того, если вы уделите время работе с векторными
программами, они позволят вам не только подправить свою графику в цифровом
формате, но и станут вашими помощниками для печати на футболках, лазерной
резки и многих других цифро-физических работ. Если вы только начинаете рабо-
тать, то 1пк§саре также определенно подходит по цене: бесплатно!
Немного о ЕО88
ЕО88 — это сокращение от Егее апб Ореп 8оигсе 8ой\уаге (бесплатное про-
граммное обеспечение с открытым исходным кодом), и многие программы
ЕО88, такие как СНМР и 1пк§саре, существуют уже несколько десятилетий. Не-
смотря на то, что большинство из нас больше знакомы с фирменным про-
граммным обеспечением таких компаний, как Арр1е, М1СГО8ОЙ, Сюо§1е и дру-
гих, факт заключается в том, что большая часть программного обеспечения, на
котором сегодня работают компьютеры и интернет, основана на бесплатных
программах, пакетах и библиотеках. Пакеты и библиотеки РуШоп, используе-
мый в этой книге, также бесплатны для всех. Во многих случаях даже создание
и продажа продуктов, в основе которых лежит программное обеспечение
ЕО88, законно — и этим занимаются многие компании.
Хотя бесплатное программное обеспечение с открытым исходным кодом га-
рантирует, что инструменты для инноваций не будут закрыты как интеллекту-
альная собственность крупных компаний, реальность такова, что создание,
поддержка и совершенствование этих инструментов на самом деле не бесплат-
ны. Это стало возможным благодаря работе добровольцев — некоторые из них
могут тратить на это рабочее время, оплачиваемое их компанией, но многие
делают это безвозмездно.
Тот факт, что инструменты ЕО88 бесплатны для всех, помогает сделать работу
по выпасу данных доступной, и я (разумеется) рекомендую по возможности
работать с ними. В то же время, если вы регулярно пользуетесь ими и имеете
возможность, я также рекомендую делать небольшие пожертвования в пользу
проектов, которыми вы пользуетесь, чтобы помочь поддержать их работу.
Размышления об этике
Основное внимание в этой книге уделено развитию навыков выпаса данных — в
значительной степени для того, чтобы поддержать интерес к оценке и улучшению
качества данных. Попутно мы затронули более широкое влияние качества данных,
как абстрактное, так и конкретное. Низкое качество данных может привести к ана-
лизу, который дает неверное, искаженное или дискриминационное представление о
мире, а в сочетании с масштабным и повсеместным распространением управляе-
мых данными систем9 это может привести к серьезным последствиям и нанести
существенный вред. Хотя вы можете использовать методы, описанные в этой кни-
ге, для проверки и улучшения качества ваших данных, к сожалению, остается еще
много возможностей для получения данных ”хорошего качества” неэтичным путем.
И, как и в любой другой части процесса выпаса данных, вы сами должны решить, с
какими данными вам удобно работать и для каких целей их использовать.
Одна из стратегий, позволяющих убедиться в том, что ваша работа по выпасу дан-
ных не приведет к непреднамеренному нарушению ваших собственных этических
норм, заключается в разработке чеклиста. Разработав список вопросов, которые вы
задаете себе об источниках данных и о том, как будут использоваться результаты
вашего анализа, вы сможете уже на ранней стадии определить, стоит ли вам брать-
ся за тот или иной проект по выпасу данных. Следующий чеклист — это выдержка
из списка экспертов по данным Ди Джея Патила, Хилари Мейсон и Майка Лукиде-
са10. Как и в случае со списком характеристик данных в главе 3, цель здесь не в том,
чтобы отклонить проект данных, если ответ на каждый вопрос ”да”; цель — крити-
чески осмыслить все аспекты качества данных, включая находящиеся вне нашего
контроля. Правда, мы можем только отклонить (а не изменить) проект, который не
соответствует нашим этическим стандартам, но если вы выскажете свои опасения,
вы можете помочь другим сделать то же самое. В худшем случае проектом займет-
ся кто-то другой, и ваша совесть будет (в некоторой степени) чиста. В лучшем слу-
чае, вы можете вдохновить других задуматься об этических последствиях своей
работы, прежде чем они возьмутся за следующий проект. Вот некоторые вопросы,
которые вам, возможно, стоит включить в свой чеклист:
1. Отражает ли дизайн сбора данных принципы сообщества, о котором идет речь?
2. Знают ли члены этого сообщества о том, что данные были собраны, и был ли у
них реальный способ отказаться?
3. Оценивались ли данные на предмет репрезентативности?
4. Есть ли способ проверить данные на предвзятость?
9 ЬПр8://реп§и1пгап(1оп1Ьои8е.соп1/Ьоок8/241363/ууеароп8-оГ-таЩ-(1е81:гисйоп-Ьу-саЩу-опеП
10 ЬПр8://огеП1у.сот/га(1аг/оГ-оаЩ8-ап(1-сЬеск1181:8
5. Являются ли предоставленные данные точными прокси для тех явлений, кото-
рые мы хотим описать?
6. Будет ли анализ заменяться, если и когда данные устареют?
В конце концов, вы можете решить, что ваши собственные опасения по поводу
данных имеют другую направленность.
Однако, какие бы вопросы вы ни решили включить в свой собственный чеклист,
вам будет гораздо легче придерживаться принципов работы с данными, если вы
изложите их заранее.
Заключение
В этой книге мы рассмотрели всё — от основ программирования на РуЙюп и оцен-
ки качества данных до выпаса данных из полудюжины форматов файлов и АР1. Мы
применили свои навыки к некоторым типично грязным и проблематичным реаль-
ным данным и усовершенствовали код, чтобы облегчить будущие проекты. Мы да-
же изучили, как проводить базовый анализ данных и наглядно представлять данные
в поддержку своих идей.
Если вы дошли до этого этапа, то я полагаю, что вы уже поймали какой-то ”баг”: в
программировании, в данных, в анализе и визуализации — или, возможно, во всем
вышеперечисленном. Что бы ни привело вас к прочтению этой книги, я надеюсь,
что вы нашли хотя бы часть того, что искали, включая уверенность, чтобы сделать
следующий шаг. Ведь что бы ни изменилось в мире выпаса данных в ближайшие
годы, одно остается неизменным: миру нужно как можно больше людей, делающих
эту работу критически и вдумчиво. Почему бы вам не стать одним из таких людей?
Другие ресурсы
по программированию на РуИтоп
Надеюсь, эта книга показала, что РуШоп — это надежный и гибкий язык програм-
мирования с широким спектром применения. Хотя в предыдущих главах я предста-
вила множество ключевых концепций и популярных библиотек, это приложение
создавалось, чтобы порекомендовать вам несколько полезных ресурсов и ссылок
для перехода вашей работы на языке РуШоп на новый уровень.
Официальная документация Ру1Ьоп
Да, всегда есть поисковые системы и ЗШскОуегДоуу1, но есть смысл научиться чи-
тать официальную документацию — будь то по языку РуШоп или по таким попу-
лярным библиотекам, как рапбаз, шаф1оШЬ или зеаЬогп. Хотя я не советую вам са-
диться и читать всю документацию по программированию, просмотр параметров и
опций типов данных или функций, которые вы хотите использовать, может дать
лучшее представление о том, что (в целом) можно сделать с помощью этого эле-
мента, а также представление об устройстве его механизмов. Это может быть осо-
бенно полезно, когда вы хотите сделать что-то совершенно новое, потому что эта
информация даст вам представление о том, где искать пути дальнейшего развития.
Например, когда я начинала писать эту книгу, я знала, что зеаЪот и рапбаз были
построены на основе библиотеки ша1р1о111Ъ, и я немного поработала над созданием
и настройкой графики с помощью обеих библиотек. Однако только после изучения
документации последней я поняла разницу между объектами 1Чдиге и ахез, на кото-
рые я так часто ссылалась в коде примера, и это понимание помогло мне быстрее
найти решение, когда я экспериментировала со способами более полной кастоми-
зации своих визуализаций. Не менее важно и то, что официальная документация
обычно поддерживается в актуальном состоянии, в то время как нередко самые по-
пулярные сообщения на форумах по той или иной теме могут быть месячной или
даже многолетней давности, а это означает, что содержащиеся в них советы иногда
могут оказаться сильно устаревшими.
1 ЬПр8://81аскоуегПолу.сот/дие8Йоп8Да§§е(1/руШоп
Я также рекомендую регулярно обращаться к стандартной библиотеке РуЙюп 81ап-
йагй ЫЬгагу2, потому что вы можете удивиться количеству встроенной в нее функ-
циональности. Многие из методов, которые вы можете узнать из работы с библио-
теками, построены на основе (или для имитации) функций, существующих в ”ва-
нильном” РуЙюп. И хотя библиотеки часто бывают уникальными и полезными, нет
никаких гарантий, что они будут продолжать развиваться или поддерживаться. Ес-
ли вы можете получить необходимую функциональность, работая с ”обычным” Ру-
Йюп, то снижается вероятность устаревания кода по причине того, что он полагает-
ся на библиотеку, которая больше не обновляется.
Установка ресурсов Ру№оп
Существует множество способов установки пакетов РуЙюп, в зависимости от ва-
шей среды программирования. Независимо от того, используете ли вы НошеЬге\у
на МасО8, работаете на машине с системой АУшсЕпуз или используете Со1аЬ, самым
надежным способом установки пакетов РуЙюп практически всегда будет использо-
вание какой-либо версии рхр3. На самом деле, вы даже можете использовать р!р для
установки пакета на ноутбук Соо§1е Со1аЪ (если вам удастся найти таковой без уже
установленных пакетов), используя следующий синтаксис4:
!р1р т-Пз^аН. имя_библиолеки
Однако, какой вариант бы вы ни выбрали, я советую сделать свой выбор и придер-
живаться его — ситуация может стать довольно непредсказуемой, если вы начнете
использовать несколько инструментов для установки и обновления среды РуЙюп.
Где искать библиотеки
Обычно я рекомендую устанавливать пакеты РуЙюп, доступные в РуЙюп Раска§е
1пбех5, также известном как РуР1. У каталога РуР1 чистая структура и надежный
интерфейс тегирования и поиска, что позволяет легко находить полезные пакеты
РуЙюп, а тот факт, что документация по пакетам РуР1 (обычно) выполнена по стан-
дартному макету (как показано на рис. А.1), действительно способствует экономии
времени, если необходимо просмотреть множество вариантов.
Некоторые проекты (например, Веаиййй 8оир 1хп1) могут по-прежнему хранить
большую часть своей документации в отдельном месте, но их страница РуР1 обыч-
но все же содержит полезное краткое описание того, что делает проект, и даже не-
которые советы по началу работы. Мне лично нравится просматривать раздел "Ис-
тория выпуска” (Ке1еа§е Н181огу). Здесь показано, когда проект был создан, а также
2 ЬНр$://(1ос$.руШоп.ог«/3/11Ьгагу/1пс1ех.кИт1
3 ЬПр8://раска§т§.руЩоп.ог§ДЩопа18/т81:аШп§-раска§е8
4 11Н:р8://со1аЬ.ге8еагс11.§оо§1е.сот/по1:еЬоок8/8трре1:8/1трог1:т§_11Ьгапе8лрупЬ
5 Ы1р8://рур1.ог§/
как часто и когда в последний раз пакет обновлялся. Конечно, долговечность нель-
зя считать идеальным показателем для оценки надежности выбранного пакета —
поскольку любой может публиковать пакеты РуЙюп (и добавлять их в РуР1) — но
существующие дольше и/или обновляющиеся чаще (и недавно), обычно служат
хорошей отправной точкой.
$еагсЬ ргсцеси
не|р 5роЛ50г$ Ю81П
а
ехсе!гс! 2.0.3
ртр 1П5Га11 ехсеТгд I*
ке1еэм*и: 4зп 12,2020
ИЬгагу Гог сГеуекореге 1о ехиасГ дага Ггот М!сго$ой Ехсе! (кт) $ргеас1зйее1 Л1ез
Маи^агюп
Е РГ01СС1 ЙС5СГ1рЙ0П
Э Йе1еа5е Ь15й>гу
Д Оотелк>а(1 П1е$
Рго|ес( безспрйоп
ехсекй
• еханмз
• ршск^аК
• ТгапзИкт Ггот х1гд Го ексекН
• АлоШег ди1ск$и>1
• АскпоиНе^етеп»
Рис. А.1. Пример целевой страницы пакета РуР!
Следите за остротой своих инструментов
Инструменты программирования постоянно обновляются, поскольку сообщество
выявляет (и в основном устраняет) новые проблемы или приходит к согласию, что
некоторые аспекты программного инструмента (или даже сам РуЙюп!) должны ра-
ботать по-другому. Если вы следовали инструкциям по установке РуЙюп и 1иру1ег
Хо1еЬоок, приведенным в главе 7, то можете использовать команду сопс!а для об-
новления обоих. Вам просто нужно будет время от времени выполнять команды
сопс1а Ордазе ру1±оп и сопс1а ирс!а1:е диру1:ег. Поскольку эта книга предназначена для
начинающих, я не затронула в главе 1 вопрос об окружении РуЙюп. В этом контек-
сте определенное окружение определяется в основном базовой версией Руйюп, ко-
торая будет использоваться по умолчанию (например, 3.9 или 3.8) при использова-
нии команды ру1±оп. Хотя выполнение команды сопс!а ирс!а±:е ру1±оп обновит, ска-
жем, версию 3.8.6 до 3.8.11, оно никогда не будет автоматически обновлять вашу
версию до другого базового выпуска (например, 3.9). Часто это не вызывает про-
блем, если только ваша установка РуЙюп не устарела на несколько лет (и, соответ-
ственно, базовых версий). Если и когда это произойдет, следует создать новую сре-
ду для новой версии РуЙюп, главным образом потому, что в противном случае
компьютеру может быть трудно разобраться в ситуации. К счастью, когда придет
время обновить РуШоп до следующей базовой версии, вы сможете узнать все необ-
ходимое о том, как это сделать, на странице документации сопба6 7.
Где получить больше информации
Поскольку РуШоп стал таким популярным языком программирования, существуют
тысячи ресурсов в Интернете и библиотеках для перехода на следующий уровень
программирования, независимо от того, есть ли у вас конкретный проект для вы-
полнения или вы просто хотите узнать больше о таких продвинутых темах, как ма-
шинное обучение и обработка естественных языков.
Для практического и емкого введения в более сложные темы науки о данных я бы
рекомендовала прежде всего книгу ”Ру1коп для сложных задач'” Джейка Вандер
Пласа (О’КеШу). Я тоже использовала эту книгу, когда пыталась разобраться в не-
которых более сложных методах машинного обучения, доступных в РуШоп. Четкий
стиль изложения и лаконичные примеры Джейка позволяют получить общее пред-
ставление о машинном обучении на РуШоп и познакомиться с конкретными мето-
дами (например, кластеризацией к-шеапз). Более того, вы можете получить доступ
ко всей книге бесплатно онлайн8 — хотя, если у вас есть возможность, я настоя-
тельно рекомендую приобрести печатное издание.
Однако, возможно, еще более ценным, чем поиск нужных книг, учебников или кур-
сов, является поиск сообщества людей, с которыми вы можете работать и учиться,
продолжая свой путь по выпасу данных. Будь то в школе, общественной организа-
ции или на встрече, найти небольшую группу людей, с которыми можно обсудить
свои проекты или обратиться за советом (а иногда и за поддержкой!), — это, пожа-
луй, самый бесценный ресурс для расширения своих навыков в РуШоп и работе с
данными. Существует также множество бесплатных программ, способных помочь
поддержать и продвинуть вашу работу, особенно это касается тех людей из сооб-
ществ, которые недостаточно погружены в технические области. Загляните в такие
группы, как Аба Веуе1орег§ Асабешу9 или Ргее Собе Сашр10.
6 ЬПр8://(1ос$.соп(1ало/рго]’ес{8/соп(1а/еп/1а{е8Ци$ег-§ш(1еЦа8к$/тапа§е-епу1гоптеп1:8.1Нп11
7 ЬНр8://у\у\у\.оге111у.сот/ПЬгагуЛ1еу\7ру1Ьоп-с1аЫ-8С1епсе/9781491912126/
8 ЬНр8://]'акеу с1р.«ИЬиЬ.1о/РуТЬопВаЫ8с1епсеНапс1Ьоок
9 ЬН:р8://ас1ас1еуе1орег8асас1ету.ог§/
10 кПр8://Ггеесос1есатр.ог§/
Еще несколько слов о СИ
По большей части, работа с Сгй, когда вы единственный программист в проекте,
довольно проста: вы вносите изменения в свой код, делаете коммит, отправляете на
СгйНиЪ (или в другой удаленный репозиторий), и все.
До тех пор, пока... это условие не изменится. Возможно, вы обновили файл
КЕАПМЕ.шб на СгйНиЪ и забыли выполнить команду дН риП перед внесением из-
менений в тот же файл на устройстве. Возможно, забыли добавить сообщение, ко-
гда выполняли команду дП сопгтИ. Хотя справиться с подобными трудностями на
самом деле не так уж сложно, некоторые сообщения об ошибках и стандартное по-
ведение Сгй в терминале могут быть сложными, особенно если вы никогда не стал-
кивались с ними раньше. Хотя руководство в этом приложении далеко не всеобъ-
емлющее, по моему опыту, базового рабочего знания Ой более чем достаточно, если
только вы не работаете с относительно большой командой в крупной организации.
Поэтому, если вы планируете использовать Ой с несколькими сотрудниками в
сложных проектах, то следующие примеры охватывают ситуации, в которых чаще
всего могут оказаться разработчики и для выхода из которых требуется помощь.
Вы запускаете команду дП: ризК/риН
и оказываетесь в странном текстовом редакторе
Как и я, Ой неумолим в вопросах документирования своей работы, вплоть до того,
что он не позволит вам выполнить коммит изменения без сопровождающего сооб-
щения. Это означает, что если выполнить команду согтгП без указания -п «Сообщение
коммита», например:
дП: сопгтИ имя_файла
вы, скорее всего, обнаружите, что окно терминала занято и показывает текст, по-
добный тому, что показан на рис. Б.1.
Это может вызвать тревогу, особенно когда происходит в первый раз. Но поскольку
это рано или поздно произойдет, вот как быстро с этим разобраться:
1. Начните с ввода буквы г. Хотя не все редакторы требуют ввода I для входа в
режим 1И8ЕРТ, большинство все же примут первый введенный символ, так что вы
можете начать с него. На рис. Б.2 показано, как выглядит этот режим.
I
# Р1еазе епХет 1Ье соттИ теззаде Тог уоиг сЬапдез. Ипез зТагИпд
# «ИН «111 Ье гдпогед, апс! ап етрТу теззаде аЬогТз ХЬе соттИ.
#
# Оп ЬгапсЬ тахп
# Уоиг ЬхапсИ 18 аЬеас! оТ 'ох1д1п/та1п' Ьу 2 соттИз.
# (изе "дП ризЬ" 1о риЬИзЬ уоиг 1оса1 соттгСз)
#
# СЬапдез То Ье соттИТед:
# тцй1Г1сд: КЕАОМЕ.тс!
#
# ОпСхаскес! ТПез:
# сЬар1ех_11_ехатр1ез/
# сЬарСех_7_ехатр1ез/81апс1а1опе_Т11е5/ррр_ас1с!_Т1пдехр11п1:8_с1ето.ру
# сЬар1ег_7_ехатр1ез/з1:апс1а1опе_Т11е5/риЬ11с_150к_р1и5_Т1пдегрг1п1з. сзу
# сЬар1ег_7_ехатр1ез/51апс1а1опе_Т11е5/риЬ11с_150к_р1и8_гесеп1.С5V
# сЬар1ех_9_ехатр1ез/
#
"-/0КЕ1ИУ/с!а1а_«хапд1±пд_ехехс18е8/.д±Т/С0ММ1Т_Е01ТМ56" 181, 586С
Рис. Б.1. Редактирование сообщений коммитов с помощью терминала
Г
# Р1еазе ептег 1Ие соттП теззаде Тог уоиг сЬапдез. Ипез зТагИпд
# мгСЬ '#' «И1 Ье 1дпохес1, апс! ап етрТу теззаде аЬогТз 1Ье соттИ.
#
# Оп ЬгапсЬ та±п
# Уоиг ЬхапсИ 1з аЬеас! оТ 'охгдхп/тахп' Ьу 2 соттИз.
# (изе "дИ ризЬ" То риЬИзЬ уоиг 1оса1 сотгтйТз)
#
# СЬапдез То Ье соттИТед:
# : КЕАОМЕ.тс!
#
# КпТгаскес! ТПез:
# сЬарТег_11_ехатр1ез/
# сЬарТег_7_ехатр1ез/зТапс!а1опе_Т11е8/ррр_адЬ_Т1пдегрт1пТз_дето.ру
# сЬарТег_7_ехатр1е8/5Тапда1опе_Т11е8/риЫ1с_150к_р1и8_Т1пдегрг1птз.сзу
# сЬарТег_7_ехатр1ез/зТапс1а1опе_Т11е8/риЬ11с_150к_р1и8_гесепТ.сз7
# сЬарТег_9_ехатр1ез/
#
-- 11М5ЕКТ --
Рис. Б.2. Редактор терминала в режиме 1№ЕРТ
2. Чтобы выйти из режима 1М8ЕРТ, нажмите клавишу <Е§с> или <Е§саре>. Затем
введите :х, как показано на рис. Б.З, после чего нажмите клавишу <Еп1ег> или
<КеШт>. Это сохранит ваше сообщение коммита, а также позволит выйти из
текстового редактора и вернуться в знакомое и любимое окно терминала.
Обратите внимание, что редактор, в котором вы окажетесь, может выглядеть не
так, как показано на рис. Б.2 и Б.З. Если что-то выглядит не так — не паникуйте.
Сейчас самое время поискать в Интернете, как редактировать, сохранять и выхо-
дить из программы, в которую вы попали. Независимо от особенностей, цель одна
и та же.
1Гои.г соттИ чеьБадв йеге
# РЗеазе еп1ег 1Ье соттИ теззаде Тог уоиг сЬапдез. 1Лпез зТагИпд
# мНИ М111 Ье 1дпогес1, апс1 ап етрТу теззаде аЬоПз 1Ье соттИ.
#
# Оп ЬгапсЬ та1п
# Уоиг ЬгапсЬ 15 аЬеас! оТ 'оНдхп/тагп' Ьу 2 соттИз.
# (изе "дН ризЬ" То риЬИзЬ уоиг 1оса1 соттИз)
#
# СЬапдез То Ье соттИТей:
# КЕАОМЕ.тд
#
# 11п1гаскес1 ТИез:
# сйар1ег_11_ехатр1ез/
# сЬарСег_7_ехатр1ез/зСапс1а1опе_Т±1е8/ррр_ас1с1_Т1пдегрг1пТ5_с1ето. ру
# сЬарТег_7_ехатр1е8/51апс1а1опе_Т11е8/риЫ1С_150к_р1и8_Т1пдегрг1пТ5. С8V
# сйар1ег_7_ехатр1ез/ 5±апс1а1опе_Т11е5 / риЬ11с_150к_р1из_гесеп1. с з V
# сЬарСег_9_ехатр!ез/
#
’х|
Рис. Б.З. Редактор терминала с командой "сохранить и выйти"
Ваша команда дй ризЬ/риП отклоняется
Это случается с каждым из нас. Вы думаете, что старательно выполняли коммиты
своего кода в конце каждой рабочей сессии, — даже писали отдельные коммит-
сообщения, чтобы отслеживать изменения, внесенные в определенные файлы, вме-
сто того, чтобы просто выполнить команду д11: сопттЛ -а. Даже в этом случае ино-
гда вы запускаете команду дх!: ризЬ и она отклоняется. Что же делать в таком случае?
Если команда дтЛ: ризЬ не сработала, вы, вероятно, увидите ошибку, подобную этой:
! [гедес1=ес1] патп -> патл (поп-‘Газ1>'Гопл/агс1)
еггог: Еа11ес1 1о ризЬ зоне ге^з 1о 'Ы:1:р5://д11:ЬиЬ.сот/уоиг_и5ег_пате/уоиг_г
еро_пате.д1Л'
Ып1: Орсеев меге гедес1:ес1 Ьесаиве ±Не -Ыр о!2 уоиг сиггеп!: ЬгапсЬ 1в ЬеЫпс!
Ып1: тЛз геноме соип1:еграг1:. 1п1едга1е ±Ье геноме сЬапдез (е.д.
Ып1: 'дН: ри1Л ...') ЬеЕэге ривЫпд адатп.
Ып1: 8ее ±Ье 'Мо±е аЬои!: ^^ав1:-ГогмагсIв, 1п 'дт1= ризЬ --Не1р' Еэг с1е1ат1в.
В общем случае, это происходит, когда один из файлов в вашем локальном репози-
тории был изменен с момента последнего коммита, но также была изменена вер-
сия в вашем удаленном репозитории, так что Ой не знает, какой из файлов должен
быть приоритетным. Не волнуйтесь!
Выполните команду дИ ри II
Если изменения в файле(ах) можно объединить автоматически, Ой сделает это. Это
разрешит конфликты файлов, но может привести к тому, что вы попадете во встро-
енный текстовый редактор командной строки, который сам по себе может быть до-
вольно запутанным. Если при запуске команды д!1: риИ перед вами внезапно поя-
вилось что-то похожее на рис. Б. 1, см. раздел «Вы запускаете команду ризк/риП
и оказываетесь в странном текстовом редакторе».
Если изменения в файле(ах) невозможно объединить автоматически, вы получите
сообщение примерно следующего вида (вы также увидите это сообщение, если
просто запустите команду дИ: ри11 без предварительного выполнения д!1: ризЬ):
Аи1:о-тегд1пд имя_файла
СОМЕЫСТ (соп!еп1): Негде сопГПс!: 1п имя_файла
Аи^ота-Ыс негде ЕаИес!; Нх сопНлс^з апс1 1±еп соппт.1: ±Ье гезиН.
Это означает, что вы должны определить, у какого файла будет приоритет, и либо
вручную привести их в соответствие (путем прямого редактирования одного или
обоих файлов), либо просто заставить один из них перезаписать другой.
Устранение конфликтов вручную
Представьте, что у вас есть репозиторий с файлом КЕАВМЕ.шб. Вы выполняли
коммиты изменений в него, как на ОйНиЬ.сош, так и на своем устройстве. Вы по-
пытались выполнить команду д1Л ризЬ и получили ошибку, поэтому попытались
выполнить д1Л ри11, но автоматическое слияние не удалось. Чтобы исправить кон-
фликты вручную, начните с открытия локальной копии файла в предпочитаемом
текстовом редакторе. То, что вы можете увидеть в файле разметки, показано в лис-
тинге Б.1.
Листинг Б.1. Пример конфликта в файле разметки
# Заголовок одинаков в обоих файлах
Содержимое также одинаково.
«««< НЕАЭ
Здесь - то, что в настоящее время находится в локальном файле.
Здесь - то, что в данный момент находится в удаленном файле.
»»»> 1Ьаа345а02с17сЫг8а212сс1е164еЗЬ5ее1Ьсе84ЫЬ
Чтобы разрешить конфликт, просто отредактируйте содержимое файла по своему
усмотрению, обязательно удалив строки, начинающиеся с ««<« и »»»>, а также
строку =======; если вы оставите эти строки, они появятся в вашем итоговом файле.
Теперь сохраните файл как обычно. Далее выполните команду:
дП: ас1с1 имя_файла
Затем выполните:
дП: согтпТ -п "Ном I Нхес! ±Не сопНлс!."
Обратите внимание, что нельзя указывать имя файла в команде дх1: сопттЛ, иначе Ой
пожалуется, что вы попросили его сделать частичный коммит во время слияния.
Наконец, выполните команду:
д1Л ризЬ
И все будет готово!
"Устранение" конфликтов путем принудительной перезаписи
Хотя всегда полезно просмотреть все файлы, в которых возникли конфликты, ино-
гда вы выполняете выгрузку в точно устаревший репозиторий и просто хотите пе-
резаписать то, что находится в вашем удаленном репозитории. В этом случае мож-
но перенести изменения без ручной сверки файлов, выполнив команду:
д1Л ризЬ ---Гогсе
Она просто перезапишет удаленный файл вашей локальной версией, но имейте в
виду, что все записи о перезаписанной версии будут потеряны, включая историю
коммитов. Это означает, что после применения команды с выражением ---Гогсе пу-
ти назад уже не будет — содержимое удаленного файла исчезнет. Очевидно, это
означает, что вам никогда не следует использовать эту опцию, если другие люди
вносят вклад в тот же репозиторий, — используйте ее, только если вы единствен-
ный, кто работает над файлом, и вы (полностью) уверены, что материал в удален-
ном репозитории может быть безопасно перезаписан.
Краткое руководство по СИ
В табл. Б.1 представлен краткий обзор наиболее полезных/распространенных ко-
манд д!Г. Более полный список можно найти на сайте СИНиЫ.
Таблица Б.1. Наиболее используемые команды терминала у И
Текст команды Результат выполнения команды
дх1: 51а1и5 Выводит текущее состояние репозитория; ничего не изменяет. Перечисляет все файлы2 в репозитории, сгруппированные по признакам: новые, неотслеживаемые или измененные
дх!: абб имяфайла Индексирует определенный файл, который в настоящее время носит неотслеживаемый статус. Перед выполнением коммита в репозиторий файл должен быть индексирован командой д1Л абб. После этого команда д1Л з-Га-Гиз пометит проиндексированные файлы как новые
1 ЬПр8:/Дга1п1п§.§кЬиЬ.сот/(1оууп1оа(18/§йЬиЬ-§й-сЬеаЬ8Ьее1:
2 Если у вас есть активный файл .§Ш§поге для репозитория, все игнорируемые файлы не будут ото-
бражаться в перечислении команды дтЛ
Таблица Б.1 (окончание)
Текст команды Результат выполнения команды
д1Л асИ -А Выполняет индексирование всех файлов в статусе неотслежи- ваемый сразу. После этого команда д1Л 51а1из пометит проин- дексированные файлы как новые
дН: сопттЛ -п "Сообщение коммита."' имяфайла Выполняет коммит определенного, уже индексированного файла, прикрепив сообщение, указанное между двойными кавычками
д11: соттЛ -а -п "Сообщение коммита." Выполняет коммит всех проиндексированных на данный момент файлов, прикрепляя ко всем из них одно и то же сообщение ком- мита
дх1: ризЬ Выгружает все локальные коммиты в удаленный репозиторий. Добавление команды - - Пэгее перезапишет все конфликтующие коммиты в удаленном репозитории прошедшими локальный коммит файлами
дх!: ри1Л Извлекает все удаленные файлы в локальное хранилище. Добав- ление команды - -’Гогсе перезапишет все конфликтующие ком- миты в локальном репозитории удаленными файлами
Поиск данных
В целом, существует четыре основных "источника” данных, к которым можно об-
ратиться при поиске ответа на вопрос о мире. Я взяла слово ”источники” в кавычки,
потому что на самом деле это типы источников, а не конкретные веб-сайты, базы
данных или даже организации. Вместо этого они представляют собой механизмы,
которые журналисты, исследователи и другие специалисты используют для сбора
данных о мире, чтобы получить ответ на свои вопросы.
Репозитории данных и АР1
Доступ к ”открытым данным” все чаще становится характерной чертой многих
правительственных и научных организаций, стремящихся повысить прозрачность,
подотчетность и — особенно в научном сообществе — воспроизводимость. Это
означает, что многие государственные учреждения, некоммерческие организации и
научные журналы поддерживают веб-сайты, на которых можно получить доступ к
структурированным данным, имеющим отношение к их работе. Например, простой
поиск в Интернете по словам ”пус ореп ба1а” или "ЪаШшоге ореп ба1а” приведет вас
на порталы ''открытых данных” этих городов. Аналогичный поиск по словам ’^о-
йаппезЪигд ореп ба1а” приведет вас сначала на сайт 8оиЙ1 Айтсап СШез Ореп Эа1а
А1шапас (8СОЭА), но несколькими ссылками ниже вы найдете больше данных от
организации под названием "ВаШГпъГ, а также портал данных Южной Африки,
расположенный по адресу ййр://орепс1а1:аГогаГг 1са.ог§. На всех этих сайтах есть
определенные данные, хотя, как обсуждалось в главе 3, качество этих данных, в том
числе их пригодность для ответа на ваш конкретный вопрос, может сильно разли-
чаться.
АР1 — еще один ценный источник данных, поскольку они могут обеспечить доступ
к очень подробным и специализированным датасетам, которые не будут отобра-
жаться в результатах поисковых систем. Это связано с тем, что данные АР1 пред-
ставляют собой часть того, что иногда называют "глубокой сетью” (беер мюЪ): дан-
ные, которые доступны через Интернет, но только через специально созданные по-
исковые запросы, отправленные на определенные ИКЪ-адреса. Например, поиск в
Интернете по запросу "сштепйу ауайаЫе сШЫкез, пус” приведет вас к интерактив-
ной карте, где вы можете нажать на станцию СШ В1ке, чтобы увидеть, сколько ве-
лосипедов доступно. А запрос к СШ В1ке АР1 позволит получить информацию о
доступных велосипедах на всех станциях СШ В1ке в Нью-Йорке. Более того, АР1
вернет эти данные в файле, достаточно хорошо структурированном для анализа, в
отличие от интерактивной карты или, если на то пошло, большинства веб-страниц.
Репозитории данных и АР1 обычно представляют собой отличный способ доступа к
большому количеству структурированных данных, относительно чистых и простых
в использовании для анализа. Однако в то же время у этих источников данных есть
существенное ограничение: у компании или агентства, управляющих порталом или
АР1, полный контроль над тем, какие данные содержатся в АР1. Это означает, что
данные могут быть устаревшими, неполными или недостаточно контекстуальными,
или в них может просто отсутствовать информация, которую владелец данных хо-
чет скрыть. Другими словами, то, что данные могут быть чистыми и ”готовыми к
использованию”, не означает, что эти данные обязательно хорошего качества. Кри-
тическая оценка их качества по-прежнему необходима.
Однако чаще всего вся информация, необходимая для ответа на ваш вопрос, не мо-
жет быть доступна через один из предыдущих типов интерфейсов, поэтому вам
придется обратиться к другим источникам.
Эксперты по предметным вопросам
Как уже говорилось в главе 3, все данные так или иначе исходят от людей. Тем не
менее на начальном этапе часто может показаться, что проще положиться на авто-
матизированные интерфейсы, такие как описанные ранее веб-порталы и АР1. В
действительности эти ресурсы доступны практически постоянно — при структури-
ровании вызова АР1 нет необходимости беспокоиться о времени ожидания по те-
лефону, рабочих часах или графике отпусков. В то же время общение с экспертами
в предметной области часто становится самым быстрым способом выявления наи-
более богатых и релевантных данных для ответа на ваш вопрос. Это связано с тем,
что многие общедоступные источники данных, даже если они доступны в Интерне-
те, являются достаточно нишевыми. По этой причине вы можете столкнуться с
трудностями, не зная оптимальных поисковых запросов, тогда как специалисты
используют их на регулярной основе. Таким образом, они не только смогут указать
вам на порталы и АР1, которые почти никогда невозможно найти с помощью веб-
поиска, но и помогут отличить на первый взгляд похожие источники данных и пре-
доставить важную контекстную информацию об их содержимом. Более того, они
могут добровольно предоставить вам датасеты, которых в противном случае вы
могли бы ждать несколько недель или месяцев в ответ на запрос БО1А/Б.
В целом, есть три места, где можно найти экспертов по нужной тематике, способ-
ных помочь вам найти или понять данные. Колледжи и университеты служат до-
мом для головокружительного количества экспертов по, казалось бы, бесконечному
кругу вопросов. Начните со звонка или электронного письма в отдел по связям с
общественностью — или просто начните читать онлайн-профили преподавателей и
исследователей. Аналогичным образом, неправительственные организации (НПО),
такие как некоммерческие организации и аналитические центры, нанимают экспер-
тов в соответствии со своей направленностью. Эти эксперты также могут быть зна-
комы со специализированными датасетами, которые иначе было бы трудно найти;
у них также могут быть лично собранные и/или проанализированные данные, кото-
рыми они готовы поделиться. Наконец, государственные служащие могут быть по-
лезным источником данных и информации, поскольку зачастую именно им прихо-
дится собирать и обрабатывать данные от имени правительства. И хотя негативных
стереотипов предостаточно, по моему опыту, государственные служащие — одни
из самых добрых и открытых из всех возможных "помощников”.
Запросы РО1А/1-
Запросы по Закону о свободе информации (РО1А, Ргеебош оГ ТпГогшабоп Ас1 или
РО1Ь, Ргеебош оГ ТпГогшабоп Ьа\у) — это механизм, с помощью которого любой
человек может запросить записи о деятельности правительства на федеральном,
государственном или местном уровне, соответственно1, что может стать источни-
ком данных, которые еще не доступны через существующие порталы или публика-
ции. Эти запросы представляют собой специализированное письмо, которое вы
можете отправить в определенный офис или отдельному сотруднику агентства,
указав в нем свой запрос на получение информации.
Ключевое требование для эффективного использования запросов РО1А/Р — сфор-
мировать запрос максимально конкретно и узко: хотя законы РО1А/Р гарантируют
ваше право запрашивать данные, большинство из них лишь обязывают вас полу-
чить ответ в течение определенного срока (часто от 5 до 20 рабочих дней). В этих
законах нет требования, чтобы ответ включал запрошенные данные. В большинстве
случаев первоначальный ответ будет таким: 1) отклонять ваш запрос на основании
допустимых исключений и освобождений от обязательств1 2; 2) признавать ваш за-
прос, но классифицировать его как "сложный”, что требует увеличения сроков его
выполнения; или 3) информировать, что поисковый запрос был выполнен, но не
дал результатов. Для успешного получения нужных вам данных, скорее всего, по-
требуется как минимум одна "апелляция” и много-много терпения. Всестороннее
введение в запросы РО1А/Р выходит за рамки этой книги, но в Интернете можно
найти ряд руководств, содержащих шаблоны и советы о том, как составить запрос
РО1А/Р, и даже сайты, такие как МискКоск, которые предоставляют инструменты,
помогающие составлять, подавать и отслеживать поданные запросы. Размышляя
над тем, о чем просить, помните о следующем:
Запросы РО1А/Ь относятся только к правительственным документам
Вам придется творчески подойти к вопросу о том, как интересующее вас явление
может породить такие записи. Подумайте о любой деятельности, связанной с ва-
шим вопросом, которая может потребовать разрешений, лицензирования или нало-
1 В некоторых штатах требуется, чтобы податель запроса был резидентом этого штата.
2 ЬПр8://(1Ь8.§оу/Го1а-ехетрНоп8
гообложения, — все это приведет к появлению государственных документов. Так-
же следует помнить, что деятельность государственных служащих, включая их
электронную почту, календари и другие записи, также подпадает под действие за-
конов ЕО1А/Е.
Не "объединяйте"запросы
Если вы ищете записи, содержащие несколько терминов, отправьте отдельный за-
прос ЕО1А/Е для каждого термина и типа файла (например, электронная почта, ка-
лендарь и т. д.). Это увеличивает количество запросов ЕО1А/Е, которые вам прихо-
дится отслеживать, но также уменьшает вероятность того, что ваш запрос будет
задержан из-за того, что он был классифицирован как ”сложный”.
Используйте общедоступные формы
Если вы не знаете, какую информацию собирает агентство, используйте общедос-
тупные формы — например, те, которые требуются для уплаты налогов, получения
лицензий и разрешений, — чтобы получить представление о данных. Затем вы мо-
жете ссылаться на эти конкретные номера и поля формы в своем запросе.
Изучите прошлые запросы
Запросы ЕО1А/Е сами подпадают под действие этих законов. Если вы ищете ин-
формацию, которая была частью ранее выполненного запроса, обращение к ней
может ускорить выполнение вашего запроса.
Вежливо обратитесь к соответствующему персоналу
Сотрудники ЕО1А/Е могут помочь вам с запросами или усложнить процесс. Поста-
райтесь установить с ними теплые, если не дружеские, отношения — по электрон-
ной почте или по телефону. Они могут дать вам важные советы о том, как усовер-
шенствовать запрос для достижения успеха.
Кастомные методы сбора данных
Иногда на ваш вопрос просто невозможно ответить с помощью комбинации суще-
ствующих датасетов, независимо от того, сколько экспертов или АР1 вы используе-
те. Это обстоятельство подчеркивает именно человеческую природу данных: они
существуют только потому, что кто-то где-то решил их собрать, очистить и сделать
доступными. Если ваш вопрос особенно своевременный или уникальный, может
оказаться, что этот человек — вы.
В университетах можно найти множество курсов, полностью посвященных различ-
ным методам сбора данных, от опросов до сенсоров. Выбор метода (методов) сбора
данных зависит как от характера вашего вопроса, так и от ресурсов (включая вре-
мя), которые вы можете выделить на процесс сбора данных. Если вы ищете иссле-
довательские или эпизодические данные, то ваш процесс сбора данных может быть
простым — разместить вопрос или ”опрос” в социальных сетях и проанализировать
полученные ответы. С другой стороны, если вы хотите получить возможность де-
лать обобщающие утверждения, то есть утверждения о более масштабных явлени-
ях, чем те, для которых у вас есть данные, то ваши данные должны быть репрезен-
тативными, как обсуждалось в главе 3.
Какой бы тип данных вы ни собирали — эпизодические или репрезентативные —
для этого существуют два основных метода: интервью и инструментарий. Методы
интервью подразумевают обращение к человеку с просьбой дать ответ, независимо
от того, являются ли эти ответы открытыми, с множественным выбором или в ка-
ком-либо другом формате. Инструментальные методы предполагают определенную
степень автоматизированного сбора данных, будь то привычки просмотра веб-
страниц или уровень угарного газа. В любом случае, вам необходимо ознакомиться
с передовым опытом или проконсультироваться с экспертами, чтобы понять огра-
ничения своих методов измерения и соответствующие методы анализа и интерпре-
тации данных. Хотя сбор собственных данных может быть сложной задачей, он
также может стать для вас невероятно полезным.
Ресурсы для визуализации
и информационного дизайна
Развитие средств визуализации за последнее десятилетие превратило процесс соз-
дания красивой, информативной графики из того, что требует дорогостоящего спе-
циализированного программного обеспечения и специальной подготовки, в то, что
может сделать практически каждый. Как уже говорилось в главе 10, создание крас-
норечивых визуализаций — это не только инструменты, имеющиеся в вашем рас-
поряжении, но и выбор дизайна, который наилучшим образом улучшит и прояснит
ваше сообщение. Хотя существует множество книг и курсов, посвященных визуа-
лизации для конкретных целей (например, для делового или научного общения),
следующий список можно считать скорее универсальным — это отличные книги и
ресурсы для развития и понимания истории, целей и потенциала визуализации и
информационного дизайна, если вы хотите расширить свои знания в этой области.
Основополагающие книги
по визуализации информации
Вероятно, самый известный человек в мире визуализации информации — это Эд-
вард Тафти. Он написал множество первых и наиболее влиятельных книг по этой
теме. Хотя все они являются прекрасными работами, вероятно, наиболее поучи-
тельными будут первые три: ’Тйе У18иа1 Э18р1ау оГ РиапШайуе 1пГогшабоп”,
”Епу18Юшп§ ТпГогшабоп” и ”У18иа1 Ехр1апабоп8” (изд-во СггарЫсз Рге88). Тафти так-
же опубликовал несколько коротких докладов и брошюр, в которых приводятся
выдержки из его книг и дается острая критика программы РоууегРопй. Все его пуб-
ликации можно приобрести на его личном сайте, Ъйр8://е(1ууаг(ИиЙе.сот, но мно-
гие из них доступны в публичных библиотеках.
Хотя формально это книги о типографике, ”ТЫпкт§ уугЙт Туре” Эллен Луптон1
(Рппсейт АгсййесШга! Рге88) — это отличное введение в общие принципы графи-
ческого дизайна, которые применимы везде, где есть текст, — а это большинство
мест, включая визуализации на основе данных. Книга Луптон знакомит читателей
1 ЬПр://е1ирЩп.сот/
со всеми основными аспектами проектирования с использованием текста — от вы-
бора шрифта и его насыщенности до размера, цвета и интервалов. Эта книга —
прекрасный учебник и справочник, который стоит иметь под рукой. Если у вас нет
возможности приобрести экземпляр этой книги, многие из основных материалов
доступны бесплатно на сайте ййр://€Ыпкт§ууйй€уре.сот.
Краткое руководство, за которым вы потянетесь
Моим справочником номер один по визуализации информации остается ’Тйе ЛМа11
81гее1 1оигпа1 Сгшбе 1о ТпГогшайоп СгарЫсз” (Хог1оп), написанный моей бывшей
коллегой Доной Вонг. В этой книге собраны все советы и рекомендации, которые
вам понадобятся для точной и красивой визуализации данных. Нужна приятная
цветовая палитра? Есть. Напоминание о том, как рассчитать процентное измене-
ние? Есть. Идеи о том, как обрабатывать отрицательные или отсутствующие значе-
ния? Есть. Легко читаемый и бесценный справочник. Если бы нужно было приобре-
сти только одну книгу с информацией по визуализации, то это была бы именно она.
Источники вдохновения
Существуют сотни книг и блогов о визуализации информации, и не все они созда-
ны одинаково — потому что не все дизайнеры сосредоточены на точном представ-
лении данных. Хотя многие предлагают платные семинары по визуализации (неко-
торые из них могут быть весьма хорошими), есть также много талантливых практи-
ков, которые делятся своей работой и идеями бесплатно.
Блог Кайзера Фунга под названием ’Чипк СйаЧз”2 — это ресурс по критике и реди-
зайну визуализации с внушительной историей. Здесь вы найдете более чем 15-
летнюю коллекцию осознанных взглядов Фунга на информационный дизайн,
включая освещение ошибочных данных, которые иногда появляются в визуализа-
циях даже крупных новостных организаций и предприятий. Отличный ресурс для
тех, кто хочет узнать о том, как даже благонамеренные визуализации могут ока-
заться ошибочными.
Работа Моны Чалаби для Тке СиагсИап3 и других изданий демонстрирует, как вели-
колепные визуализации данных могут быть сделаны вручную и отражать индиви-
дуальность и человечность людей, о которых эти данные говорят.
Стефани Посавец4 — художник и дизайнер, создающий объекты и визуализации,
основанные на данных, в различных медиа.
2 кПр8://]ипкскаг{8.1урерас1.сот/
3 ЬПр8://Ще§иаг(11ап.сот/ргой1е/топа-сЬа1аЬ1
4 ЬПр://81еГап1еро8ауес.сот/
Об авторе
Сьюзен Макгрегор — исследователь Института науки о данных Колумбийского
университета, где она является сопредседателем Центра данных, медиа и общества.
На протяжении более десяти лет она совершенствует свой подход к преподаванию
программирования и анализа данных для студентов, не являющихся специалистами
в области технических наук, на уровне специалистов, аспирантов и студентов. Мак-
грегор — штатный преподаватель и исследователь Колумбийского университета с
2011 г., когда она присоединилась к Колумбийской школе журналистики и Центру
цифровой журналистики То\у. Там она разработала первую в школе учебную про-
грамму по журналистике данных и служила главным академическим консультан-
том программы двойного диплома по журналистике и компьютерным наукам. Ее
научные исследования посвящены затрагивающим журналистов и медийные орга-
низации вопросам безопасности и конфиденциальности. Данные вопросы стали
темой ее первой книги 1п/огтаИоп ЗесигИу ЕззепИак: А <3шс1е/ог Керог1ег8, ЕсШог8,
апА №уу$гоот Ьеас1ег8 (СИР, Со1ишЫа Ншуегзйу Ргезз).
До работы в Со1ишЫа Макгрегор несколько лет проработала старшим программи-
стом в группе новостной графики в 1Уа1181гее1 Аоигпсй. Она была названа лауреатом
премии Джеральда Леба 2010 г. за работу над оригинальной серией ”\^11а1
Тйеу Кпо\у”, а также выступала и публиковалась на ряде ведущих научных конфе-
ренций по безопасности и конфиденциальности. Ее работа получила поддержку
Национального научного фонда, Фонда Найта, компании Сгоо§1е, нескольких школ
и отделов Колумбийского университета. Макгрегор также интересуется тем, как
искусство может помочь стимулировать критическое мышление и представить но-
вые взгляды на технологические вопросы. Она получила степень магистра в облас-
ти образовательных коммуникаций и технологий в Нью-Йоркском университете и
степень бакалавра в области интерактивного информационного дизайна в Гарвард-
ском университете.
Колофон
Животное, изображенное на обложке книги ”Обработка данных на РуШоп. Ва1а
\Угап§1т§ и Ва1а РиаШу”, — подковчатый полоз (Нешогг1ю18 Ырросгер18).
Природные места обитания этой змеи расположены в юго-западной Европе и се-
верной Африке, но ее можно встретить в самых разных местах, включая кустарни-
ковую растительность, скалистые и песчаные берега, пастбища, плантации, сель-
ские сады и некоторые городские районы. Взрослые особи могут достигать до 1,5 м
в длину. У подковчатого полоза гладкая чешуя и желтовато-красное тело с рядом
крупных черных или темно-коричневых пятен, окаймляющих спину. Свое название
он получил благодаря светлой подковообразной отметине на шее и затылке.
Подковчатый полоз хорошо лазает и охотится на птиц, мелких рептилий и неболь-
ших млекопитающих с верхушек деревьев, крыш или скалистых утесов. Хотя эта
змея может быть агрессивной и обладает сильным укусом, она не ядовита и не
опасна для человека.
Считается, что подковчатый полоз обладает высокой адаптивностью и имеет теку-
щий природоохранный статус — "Вызывающие наименьшие опасения”. Многие
животные на обложках О’КеШу находятся под угрозой исчезновения, и все они
важны для окружающего мира.
Иллюстрация на обложке выполнена Хосе Марзаном-младшим на основе старин-
ной гравюры из книги Ричарда Лидеккера1.
1 Коуа1 Иа1ига! НШогу, К.1с11агс1 Ьус1еккег. — Примеч. ред.
Предметный указатель
I X
180№ ХМЬ:
• объект, 161 • пара ключ/значение, 161 • список, 161 • форматер: • атрибуты, 151 • спецификация, 153 0 БРИВ, 154 0 КМБ, 154
0 РогшаИег, 164 0 Ж(ЖБт1, 164 0 К88, 154 0 БУО, 154 • теги, 151
X • формат: 0 .а!ош, 154
ИА1С8, 292 • элемент, 151 0 дочерний, 152
К 0 корневой, 152 0 прародитель, 152
геас11те8(), 318 0 родитель, 152
А Б
Автозаполнение: Байткод, 190
• команда, 32 Авторизация: Безработица: • показатель:
• конечная точка, 202 • цикл, 193 Арифметическое: 0 133, 133, 134 0 И6, 134 Библиотека:
• среднее, 111 Атрибуты ХМБ, 151 Аугментация данных, 115 Аутентификация: • агдрагзе, 322 • ВеаийГи! Боир, 214 • сзу, 88, 129 • Гш§егрпп18, 246, 285
• ключ, 50 . 1хш1, 154, 155, 156, 157, 159
• Маф1оШЬ, 26 • ЖХК, 26 • пишЬег8, 276 • ХшпРу, 26 • орепсу-руШоп, 167 • орепрух!, 137, 139 • 08, 216 • рапдаз, 26, 255, 291 • рсИ2ппа§е, 167 • рорр1ег, 167 • руехсе1-ос18, 137, 140 • ру!е88егас1, 167 • гедие818, 175 • 8с1кй-Ьеагп, 26 • ЗеаЬогп, 26 • 1е88егас1-осг, 167 • бите, 216 • хШ, 137, 141, 142,279 Блокнот РуЙюп, 28 • надежность, 98, 257 • непротиворечивость, 244 • неструктурированные, 121, 122 • очистка, 115 • первичная обработка, 19 • происхождение, 106 • размерность, 114 • репрезентативность, 98, 102 • словарь, 145, 239 • соответствие, 23, 96, 97, 104, 107 • стабильность, 257 • структуризация, 119 • структурированные, 120, 121 • тип: 0 ОгбегесЮю!, 140 0 кортеж, 139 0 массив, 59 0 сложные, 60 0 список, 59 0 числовой, 59
в • точность, 257 • файловый, 117
Веб-извлечение данных, 208 Верблюжий регистр, 62 Видимость: • формат: 0 .С8У, 123 0 ]8ОП, 119
• область, 276 Вложение, 74, 152 Внешнее соединение, 231 Возвращаемые значения, 303 Встроенные функции, 63 Выборка, 102 Выражения регулярные, 265 0 3804 147, 148 0 ХМЬ, 147, 148 • целостность, 23, 96, 104 Диспетчер Мпнсопба, 33, 36, 37, 38 Дневник данных, 45, 116 Документ: • тип: 0 объявление, 150
г Документирование, 96 Достоверность:
Глобальные переменные, 312 Гранулярность, 111 • данных, 98, 253 • конструкта, 99 • содержимого, 100
д Доступ: • маркер, 202, 204
Данные: • токен, 193, 202
• веб-извлечение, 208 • дневник, 45, 116 • достоверность, 98, 253 • канальные, 118 • качество, 19, 22, 95 • машиночитаемые, 120 • многомерные, 111 Дочерний элемент: 0 ХМЬ, 152 3 Запрос: • строка, 179, 184
Знаменатель: • проблема, 259 Значения: • возвращаемые, 303 и Инспектирование: • средства, 211 Инструмент ТаЬи1а, 171 Интерфейс: • АР1, 173 • командной строки, 30 Исключение, 82 Итератор, 128 Конечная точка, 149, 180, 184 • авторизации, 202 Конкатенация, 63 Константа символьная, 63 Конструкт: • достоверность, 99 • операционализация, 99 Контекст: • коллапс, 104 Корневой элемент: • ХМЬ, 152 Кортеж: • тип данных, 139 м
к Канальные: • данные, 118 • форматы: 0 данных, 118 Качество данных, 19, 22, 95 Ключ: • $$Н, 50, 52, 53, 54 • аутентификации, 50 Ключевое слово: • с!еГ, 66 • Ггот, 189 • Ите, 93 Код: • ХА1С8, 292 • редактор А1от, 29, 34, 35, 38, 40, 41 • рефакторинг, 170 Коллапс контекста, 104 Команда: • ей, 31 • §И ас!с1, 54 • §И с1опе, 53 • §й ризЬ, 54 • §И 81а1п8, 54 • 18,31 • шкейг, 31 • рубос, 320, 321 • автозаполнение, 32 Командная строка: • интерфейс, 30 Комментарии, 44 Маркер доступа, 202, 204 Массив: • тип 0 данных, 59 Машиночитаемые данные, 120 Метаданные, 107, 239 Метод: • .кеу8(), 156 • сопуег!Гготра1Ь(), 169 • е1гее.1081гт§(), 156 • Иет8(), 140 • йег(), 159 • Иегбе8сепбап18(), 159 • ]от, 170 • кеу8(), 159 • к\уаг§8.§е1(), 303 • 1еп(), 227 • 1оаб_\уогкЬоок(), 138 • тах(), 245 • тт(), 245 • ро81, 202, 203 • ге.сотрйе(), 268 • геаб_с8у(), 237 • 8рН1(), 63, 263 • 81г(),227 • 81г.соп1ат8(), 255 • 81гГ11те(), 279 • 81пр(), 281 • 1о_с8у(), 229 • шпдие(), 247 • уа1ие_соип18(), 242 • \уеекбау(), 270, 272, 307
• мтйего\у(), 140
• х1с1а1е_а8_с1а1ебте(), 278
• отпечатков пальцев, 245
Методы строковые, 63
Многомерные данные, 111
н
Надежность данных, 98, 257
Национальное управление по проблемам
океана и атмосферы, 143
Непротиворечивость данных, 244
Неструктурированные данные, 121, 122
Носитель:
• токен, 199, 204
о
Область видимости, 276, 298, 299, 300
Обработка данных:
• первичная, 19
Объект:
• с1а1е1ппе, 270
. 3804 161
Объявление:
• типа документа, 150
Операторы, 63
• йС.еЬе, 73
• присваивания, 62
• условный:
0 1Г...е18е, 74, 89
Операционализация конструкта, 99
Определяемые пользователем функции, 64
Организация РгоРиЬНса, 24
Очистка данных, 115
п
Пакет офисных приложений:
0 ЫЬгеОГйсе, 125
0 ОрепОГйсе, 125
Папка:
• русасйе_, 190
• с1сиа^гап^1п^, 32
• Ыпих, 32
• текущая, 32
Пара 18ОК
• ключ/значение, 161
Параметр тсйса1ог=Тгие, 232
Первичная обработка данных, 19
Переменная, 59
• логическая, 275
• флаговая, 275, 276
Показатель:
• безработицы
о ЫЗ, 133, 134
о Ы6, 134
Пользовательские функции, 67
• названия, 297
Прародитель:
• элемент:
0 ХМЬ, 152
Присваивание:
• оператор, 62
Проблема знаменателя, 259
Программа велопроката
• СШ В1ке, 85
Происхождение данных, 106
Псевдокод, 86
р
Размерность данных, 114
Разработка среды:
• Со1аЬ, 42
• 1иру1ег Хо1еЬоок, 30, 35, 37, 38, 41
Расширение файла
• .ру, 29, 41
Регистр верблюжий, 62
Регулярные выражения, 265
Редактор кода А1ош, 29, 34, 35, 38, 40, 41
Репрезентативность данных, 98, 102
Рефакторинг, 296
• кода, 170
Родитель:
• элемент ХМЬ, 152
с
Сводка статистик, 111
Секьюритизация, 224
Сеть О1оЬа1 Н181опса1 С1ппа1о1о§у Хе1мюгк,
143
Символы управляющие, 33
Символьная константа, 63
Система:
. С0МРА8, 24
. ОйНиЬ, 47
Словарь данных, 145, 239
Слово:
• ключевое:
О с1еГ, 66
О Ггош, 189
О Игле, 93
Сложные типы данных, 60
Содержимое:
• достоверность, 100
Соединение внешнее, 231
Соответствие данных, 23, 96, 97, 104, 107
Спецификации:
. ХМЬ, 153
0 БРИВ, 154
0 КМЬ, 154
0 К88, 154
о 8У6, 154
Список:
• 38(Ж, 161
• Тип данных, 59
Среда разработки:
• Со1аЬ, 42
• 1иру1ег Хо1еЬоок, 30, 35, 37, 38, 41
Среднее арифметическое, 111
Средства инспектирования, 211
Средство Сгй, 50
Стабильность данных, 257
Статистики, сводка, 111
Стек функций, 306
Столбец шег§е, 232
Строка запроса, 179, 184
Строковые методы, 63
Структура Оа1аБгаше, 228
Структуризация данных, 119
Структурированные данные, 120, 121, 122
т
Теги ХМЬ, 151
Текущая папка, 32
Тип:
• данных:
0 ОгбегесЮю!, 140
0 кортеж, 139
0 массив, 59
0 сложные, 60
0 список, 59
0 числовой, 59
• документа:
0 объявление, 150
Токен:
• доступа, 193, 202
• носителя, 199, 204
Точка конечная, 149, 180, 184
Точность данных, 257
Транспонирование, 237
Тренды, 118
У
Управляющие символы, 33
Условный оператор ЙС.еЬе, 74, 89
Ф
Файл:
• .§й1§поге, 188, 190
• §11спс1-81а1юп8.1х1, 145
• §й1§поге, 188, 192
• МТА_Шт8Ше8_тс1ехЪ1т1, 211
• ТмлйегсгебепйаЬ.ру, 199
• расширение
О.ру, 29,41
Файловые данные, 117
Файлы С8У, 88
Флаговая переменная, 275, 276
Формат:
• РОБ, 165
• ХМЬ:
0 .а!ош, 154
• данных:
0 .С8У, 123
0 ]8ОП, 119
о 38ОХ, 147, 148
0 ХМЬ, 147, 148
0 канальные, 118
Форматер:
• Богшайег, 164
• ЖОХБпй, 163
Функции, 63
• встроенные, 63
• сгеа1е_ше1а_1ех1(), 313
• сгеа1е_1аЫе_го\у(), 313
• О1с1Кеас1ег(), 129
епшпега1е(), 138 §е!_го\у(), 142 §1оЬ(), 170 §гее1_ше(), 304 18_\уеекс1ау(), 308 йег_го\У8(), 139 1181(), 163 шат(), 308 таке_§гее1т§(), 304 пех!(), 129 ореп(), 88, 127 рс1.тег§е(), 232 рудое, 317 гап§е(), 127, 128 8е1(), 247 81еер(), 217 8р1И(), 170 81г(), 170 81г(), 205 81пр(), 217, 279 8ит(), 72 • 1о_с1а1е1ш1е(), 226 • определяемые пользователем, 64 • пользовательские, 67, 297 0 названия, 297 • стек, 306 ц Целостность данных, 23, 96, 104 Цикл: • Гог, 71 • Гог...т, 89 • авторизации, 193 э Элемент ХМЬ, 151 • дочерний, 152 • корневой, 152 • прародитель, 152 • родитель, 152
КРОК
СОЗДАЁМ НАСТОЯЩЕЕ,
ИНТЕГРИРУЕМ БУДУЩЕЕ
сгос.ги
КРОК — технологический партнер с комплексной
экспертизой в области построения и развития
инфраструктуры, внедрения информационных
систем, разработки программных решений
и сервисной поддержки.
Центры компетенций КРОК фокусируются
на ключевых отраслевых кластерах — промышлен-
ность, финансовый сектор, розничные продажи,
муниципальное управление, спорт и культура.
Ежегодно сотни проектов КРОК становятся
системообразующими для экономики
и социально-культурной сферы.
ИНТЕРНЕТ-МАГАЗИН
вм
КНИГИ, РОБОТЫ,
ЭЛЕКТРОНИКА
Интернет-магазин издательства *6ХВ»
Более 30 лет на российском рынке
Книги и наборы по электронике
и робототехнике по издательским ценам
Электронные архивы книг
и компакт-дисков
Ответы на вопросы читателей
БХВ-Электроника Ыъ/.ги/е1етеп1з
Электронные компоненты
для мейкеров