{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: визуализация",
    "home_page_url": "http:\/\/test.leftjoin.ru\/tags\/vizualizaciya\/",
    "feed_url": "http:\/\/test.leftjoin.ru\/tags\/vizualizaciya\/json\/",
    "icon": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg",
    "author": {
        "name": "Николай Валиотти",
        "url": "http:\/\/test.leftjoin.ru\/",
        "avatar": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg"
    },
    "items": [
        {
            "id": "142",
            "url": "http:\/\/test.leftjoin.ru\/all\/quantitative-vs-qualitative-color-scales\/",
            "title": "Неочевидный выбор: количественная или качественная цветовая палитра?",
            "content_html": "<p class=\"note\">Перевод статьи <a href=\"https:\/\/blog.datawrapper.de\/quantitative-vs-qualitative-color-scales\/\"><span style=\"color:#0071a1\" class=\"has-inline-color\">«When to use quantitative and when to use qualitative color scales»<\/span><\/a><\/p>\n<p>Лиза Шарлотта Мут, дизайнер из Берлина, опубликовала на Datawrapper статью о сложностях выбора между качественной и количественной цветовой палитрой при визуализации данных разных типов. Заинтересовавшись ее мнением и аргументами по данному вопросу, мы решили перевести ее для вас.<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales1.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Эта статья попытается ответить на следующий вопрос: когда следует использовать <i>количественную<\/i> цветовую палитру (последовательную или расходящуюся; например, голубой, синий, темно-синий), а когда — <i>качественную<\/i> палитру (например, красный, желтый, голубой)?<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales2.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>И небольшой дисклеймер: чтобы мне не оперировать длинными терминами <i>количественная цветовая схема<\/i> и <i>качественная цветовая схема<\/i> в этой статье, я буду часто употреблять менее корректные, но более удобные для чтения слова «оттенки» и «градиенты» для количественных цветовых схем и «тона» для качественных.<\/p>\n<p>Итак, когда надо использовать оттенки? А когда — тона?<\/p>\n<h2>Используйте тона, когда у значений нет изначального порядка<\/h2>\n<p>Тона необходимо использовать, когда невозможно упорядочить данные. Например, если в графике представлены промышленности или страны (Иран, Марокко, Пакистан): Марокко ничем не уступает Пакистану, как и наоборот.<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales3.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<h2>Используйте оттенки или градиенты, когда у значений есть порядок<\/h2>\n<p>А вот когда вы можете упорядочить данные, представляемые цветом, используйте последовательную или расходящуюся цветовую палитру. Если, например, необходимо передать данные о безработице (4%; 6%; 8%), лучше отдать предпочтение именно количественной цветовой палитре.<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales4.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Это правило применимо не только к тексту или числам. У шкалы Лайкерта («полностью не согласен», «не согласен», «где-то посередине», «согласен», «полностью согласен») или размеров одежды (XS, S, M, L, XL, XXL) также есть специфический порядок. Они тоже относятся к количественному типу данных, поэтому для их визуализации крайне рекомендуется использовать именно оттенки.<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales5.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Но давайте пойдем дальше. Если мы изучим другие примеры визуализации данных, то заметим, что качественные данные иногда передаются с помощью количественных цветовых палитр и наоборот. А вот и ответ почему:<\/p>\n<h2>Используйте оттенки, чтобы подчеркнуть подразумеваемый порядок<\/h2>\n<p>Здесь разобраться помогут древовидные карты:<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales6.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Да, можно красить категории в дереве тоном (например, чтобы определить страны) как на примере слева. Но дерево станет более удобным для чтения, если покрасить категории <i>по размеру ячейки<\/i>, который выбран для передачи определенного численного значения (например, показателя безработицы в разных странах).<\/p>\n<p>И тут вы могли бы подумать: «Но я хочу обозначить цветом совершенно новое значение!» Например, если вы отсортируете ячейки по показателю безработицы, возможно, вам захочется покрасить их по ВВП на душу населения. Но такой график было бы крайне тяжело прочитать:<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales7.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Дерево слева пытается убить одним выстрелом слишком много зайцев. Даже при наличии отличной цветовой легенды, такой график все равно остается тяжелым для восприятия.<\/p>\n<p><b>Старайтесь не красить графики по значениям, которые не отображаются дополнительно с помощью, например, позиции или порядка.<\/b><\/p>\n<p>А если все-таки покрасить подразумеваемые значения в удобочитаемом графике?<\/p>\n<p>Предварительно убедитесь, что эти значения уже заметны не прибегая к цвету. Это непростая задача, ведь использование цвета для передачи новой переменной часто делает график тяжелым для прочтения.<\/p>\n<p>Давайте посмотрим на один пример, чтобы прокачать ваше интуитивное понимание, когда стоит прибегнуть к такому решению.<\/p>\n<p class=\"note\">Страница с деталями графика «Алгоритм Google» из <a href=\"https:\/\/www.economist.com\/graphic-detail\/2019\/06\/08\/google-rewards-reputable-reporting-not-left-wing-politics\"><span style=\"color:#0071a1\" class=\"has-inline-color\">The Economist<\/span><\/a> от 8 июня, 2019<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales8.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Сосредоточимся на точечной диаграмме в левом верхнем углу. Точечные диаграммы — это один из немногих видов графиков, где покраска по скрытым данным без их представления каким-либо дополнительным способом (позицией, длиной, и т. д.) удивительно хорошо работает.<\/p>\n<p class=\"note\">Точечную диаграмму проблематично разобрать в частности из-за того, что точки покрашены расходящейся, а не последовательной цветовой палитрой. По сути, The Economist использует цвета для передачи двух различных переменных: тон для презентации политической идеологии («левые»\/«правые») и насыщенность и яркость для демонстрации политической крайности. Тот же самый график, который передавал лишь политическую крайность (не важно, в сторону «левых» или «правых») с помощью последовательной цветовой палитры было бы проще читать.<\/p>\n<p>И все же, чтобы понять этот график из The Economist, требуется несколько секунд. Обратите внимание, что точечную диаграмму справа проще прочитать, так как в ней совмещены два приема визуализации: политическая идеология показана позицией («левые» и «правые») <i>и<\/i> оттенком.<\/p>\n<p>Но я готова поспорить, что диаграмма слева все же работает — именно благодаря диаграмме справа и нормированной линейчатой диаграмме снизу. Они обе как цветовые ключи к тому, как следует читать цвета на первой точечной диаграмме.<\/p>\n<p>И эти большие цветовые подспорья здесь критически важны. <b>Используйте цвет только тогда, когда читателю будет легко понять, какое значение ваши оттенки отображают, даже если вы используете какой-то другой прием визуализации для этого же значения.<\/b><\/p>\n<p>Итак, мы рассмотрели древовидные карты, столбчатые графики и точечные диаграммы. Теперь, давайте возьмем в качестве примера линейный график:<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales9.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>В этих линейных графиках оттенки использованы для того, чтобы продублировать значение порядка линий. Но в графике справа это гораздо проще заметить, так как линии еще и упорядочены. График же слева может читателя только запутать.<\/p>\n<h2>Используйте оттенки для выделения подкатегорий<\/h2>\n<p>Есть и другие причины красить качественные данные количественными цветовыми палитрами, а не качественными. Например, чтобы выделить <b>под<\/b>категории. Ниже представлен график из The Economist, который показывает, как это сделать:<\/p>\n<p class=\"note\">Население в занимаемых территориях, сгруппированное по религиозной принадлежности (The Economist, <a href=\"https:\/\/infographics.economist.com\/2019\/AChristmasGiftForYou\/AYearInGraphicDetail.pdf\"><span style=\"color:#0071a1\" class=\"has-inline-color\">PDF<\/span><\/a>)<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales10.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Мы видим две категории: «евреи» (голубой) и «арабы» (желтый). <b>Под<\/b>категориями же являются территории, которые представлены оттенками синего и желтого.<\/p>\n<p>Вот похожий пример:<\/p>\n<p class=\"note\">График <a href=\"https:\/\/www.nzz.ch\/gesellschaft\/schweizerdeutsch-viel-wandel-im-freiburger-dialekt-kaum-in-baar-ld.1390868\"><span style=\"color:#0071a1\" class=\"has-inline-color\">NZZ<\/span><\/a><\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales11.jpg\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>NZZ использует оттенки (зеленый и синий), чтобы помочь читателям заметить разницу между двумя категориями: Ältere («пожилые») и Jüngere («молодые»). Оттенки помогают различать подкатегории: какие слова пожилые люди и молодежь используют, чтобы сказать «сердцевина яблока».<\/p>\n<p>(Кстати, и тому и другому графику цвета, в целом-то, и <i>не нужны<\/i>: все секции могут быть серыми с разделяющими их белыми линиями, так как каждая из подкатегорий объяснена текстом. Слова <i>Gaza<\/i>, <i>Israel<\/i>, <i>Ältere<\/i>, <i>Gräutschi<\/i> и так далее показаны на самих графиках, делая цветовое решение лишним.)<\/p>\n<p>Когда вы работаете с подкатегориями, ваш читатель будет подсознательно понимать, что функция оттенков заключается в том, чтобы помочь ему видеть разницу между подкатегориями.<\/p>\n<h2>Используйте оттенки, чтобы разграничить первостепенные и второстепенные категории<\/h2>\n<p>Вам не нужно давать всем категориям одинаковый вес. Если вы хотите выделить какую-то из них, вы вполне можете покрасить остальные в один цвет (зачастую, градиентами серого):<\/p>\n<p class=\"note\">График <a href=\"https:\/\/daten.berlin.de\/datensaetze\/einwohnerregister-berlin\"><span style=\"color:#0071a1\" class=\"has-inline-color\">Лизы Шарлотты Рост<\/span><\/a><\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales12.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>По сути, этот график превращает категории («в браке», «не замужем\/не женат», «разведен\/а», «вдова\/вдовец») в подкатегории, так как они все поделены на две большие группы («первостепенные» и «второстепенные»). Если покрасить подкатегории градиентом, наш читатель все отлично поймет.<\/p>\n<h2>Используйте оттенки, чтобы сделать отдельные категории менее яркими<br \/>\nи более удобными для чтения для людей, страдающих дальтонизмом<\/h2>\n<p>Существует такая мантра в визуализации данных, которая призвана помочь специалистам сделать их графики более доступными для людей с проблемами зрения: «Сделай так, чтобы визуализация работала в черно-белом исполнении». Суть в том, что цвета должны иметь разные уровни яркости, чтобы их было легко отличить при переводе в серую шкалу.<\/p>\n<p>Ну, допустим. Тогда какой же цвет темнее, этот <span style='color:#5794b2'>⬤<\/span> или этот <span style='color:#e62600'>⬤<\/span>? И как насчет <span style='color:#60dfc4'>⬤<\/span> и <span style='color:#ffff00'>⬤<\/span>? Сюрприз! Два первых превратятся в <span style='color:#6b6b6b'>⬤<\/span> в серой градации. А два последних будут выглядеть как <span style='color:#c1c1c1'>⬤<\/span>.<\/p>\n<p>Для простых смертных судить о светлоте двух цветов, если у них разные тона, не так-то и просто. Поэтому «сделать так, чтобы визуализация работала в черно-белом исполнении» проще, если специалист по дата визуализации использует градиенты только одного тона.<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales13.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Две причины — доступность и строгость — могут убедить вас выбрать оттенки, а не тона, при работе с категориями. Вот пример из The Financial Times:<\/p>\n<p class=\"note\">График из <a href=\"https:\/\/www.ft.com\/content\/1ae439b1-75e7-4b55-876c-66533ac37db8\"><span style=\"color:#0071a1\" class=\"has-inline-color\">The Financial Times<\/span><\/a><\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales14.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Но имейте в виду следующие особенности: во-первых, <i>скорее всего, некоторые читатели будут пытаться придать цветам какой-то смысл<\/i>. Даже если это не входило в ваши планы, они могут попытаться найти объяснение тому, почему использован градиент. «США изображены более темным цветом, так как имеют самые высокие показатели». Или: «... так как их данные имеют большую значимость в этой статье». Красить наобум нельзя.<br \/>\nСинтия Брюэр отмечала то же самое, когда говорила о представлении бинарных данных («да»\/«нет», «общественный»\/«частный», «присутствует»\/«отсутствует») на картах:<\/p>\n<p class=\"note\">Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994. Курсив автора статьи.<\/p>\n<blockquote>\n<p>«Создатель карты решает, какая из категории значительней, основываясь на цели создания карты, и <i>самая важная из двух категорий должна быть темнее цветом<\/i>. Если бинарные категории обладают одинаковым весом, красьте график качественной цветовой палитрой с незначительной разницей по светлоте между тонами».<\/p>\n<\/blockquote>\n<p>Во-вторых, опыт показывает, что <b>чем больше оттенков одного тона для передачи категорий, тем сложнее прочитать такой график<\/b>. Вполне реально различить между двумя, тремя оттенками. Но если их будет четыре, пять или шесть, ваши читатели полезут на стенку. Особенно если цветовые индикаторы не упорядочены и\/или прокомментированы и\/или выполнены лишь в одном тоне (от светло-голубого до темно-синего) вместо нескольких (от светло-желтого до темно-синего).<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales15.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>В-третьих, если хочется использовать градиент, <b>старайтесь использовать оттенки только одного тона, если нет необходимости вводить еще один<\/b>.<\/p>\n<p>Спросите себя, какое ключевое послание вы хотите передать, и выделите участки, которые нужно сделать более заметными, с помощью другого тона и его оттенков, как «Europe» и «US» в этом графике:<\/p>\n<p class=\"note\">График <a href=\"https:\/\/www.ft.com\/content\/1caa7059-2e4e-4b9e-b365-8944478509a4\"><span style=\"color:#0071a1\" class=\"has-inline-color\">The Financial Times<\/span><\/a>, который я слегка отредактировала<\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales16.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Если вы поймали себя на том, что используете слишком много тонов, используйте другой тип визуализации.<\/p>\n<p>До этого момента, мы в основном смотрели на причины использовать <i>оттенки<\/i> вместо тонов. А вот причина использовать <i>тона<\/i> вместо оттенков:<\/p>\n<h2>Используйте тона, чтобы лучше обособить категории<\/h2>\n<p>В 2020 году, The Financial Times опубликовали график, в котором отобранные страны Европы были сгруппированы в зависимости от значительного\/умеренного\/небольшого роста чисел новых случаев заболевания Covid-19.<\/p>\n<p class=\"note\">График <a href=\"https:\/\/www.ft.com\/content\/bcddc297-b7f2-444d-908f-54e8ce6f4f98\"><span style=\"color:#0071a1\" class=\"has-inline-color\">The Financial Times<\/span><\/a><\/p>\n<p><img src=\"http:\/\/test.leftjoin.ru\/pictures\/quantitative-vs-qualitative-color-scales17.png\"  border=\"0\" width=\"100%\" height=\"100%\"><\/p>\n<p>Стивен Бернард и Джон Берн-Мердок, создатели этого графика, решили использовать тона для разных стран в каждой группе.<\/p>\n<p>Я спросила Стивена Бернарда, почему он предпочел тона вместо оттенков. И вот что он ответил:<\/p>\n<blockquote>\n<p>«Логика в такой покраске в том, чтобы сделать линии <b>максимально отличными друг от друга<\/b> на каждом графике, но, в то же время, сохранить сортировку во всех четырех графиках. [...] Тот подход, который я применил, позволяет зрителю понять из первого графика, что темно-синий представляет собой первое место, розовый — второе, голубой — третье, а зеленый — четвертое. Так, зритель может легко считать данные с других трех графиков».<\/p>\n<\/blockquote>\n<p>Когда я посмотрела на график Стивена, я не поняла, что темно-синяя линяя расценивается как первая, как и то, что розовая линия идет второй, и так далее. Читатели (как и я) не ожидают, что категории, которые отображаются тонами, могут обыгрывать еще и порядок. Если бы у категорий был порядок, их стоило бы покрасить градиентом.<\/p>\n<p>И все же мне кажется, что тона неплохо работают в графике Стивена. Ведь, как всегда, стоит вопрос: чем оправдан ваш выбор типа палитры? В данном случае, The Financial Times выбрали качественную палитру, чтобы помочь читателям лучше различать линии, переплетающиеся на графике, вместо того, чтобы подчеркнуть их порядок.<\/p>\n<h2>Выводы<\/h2>\n<p>Итак, благодаря Лизе Шарлотте Мут, мы можем подвести следующие итоги:<br \/>\n• Используйте <i>тона<\/i>, когда у значений <i>нет<\/i> изначального <i>порядка<\/i><br \/>\n• Используйте <i>оттенки<\/i> или градиенты, когда у значений <i>есть порядок<\/i><br \/>\n• Используйте <i>оттенки<\/i>, чтобы подчеркнуть <i>подразумеваемый порядок<\/i><br \/>\n• Используйте <i>оттенки<\/i> для выделения <i>под<\/i>категорий<br \/>\n• Используйте <i>оттенки<\/i>, чтобы разграничить <i>первостепенные<\/i> и <i>второстепенные<\/i> категории<br \/>\n• Используйте <i>оттенки<\/i>, чтобы сделать отдельные категории менее яркими и более удобными для чтения для людей, страдающих <i>дальтонизмом<\/i><br \/>\n• Используйте <i>тона<\/i>, чтобы лучше <i>обособить категории<\/i><\/p>\n<p><i>Список литературы<\/i><\/p>\n<ol start=\"1\">\n<li>Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994.<\/li>\n<li>Adobe Spectrum: <a href=\"https:\/\/spectrum.adobe.com\/page\/color-for-data-visualization\/\"><span style=\"color:#0071a1\" class=\"has-inline-color\">Цвет для визуализации данных<\/span><\/a>, 2019.<\/li>\n<li>Юусо Копонен, Джонатан Хильден: «Руководство по визуализации данных», 2019, с. 73-79.<\/li>\n<\/ol>\n",
            "date_published": "2022-11-04T12:00:31+03:00",
            "date_modified": "2022-11-03T11:36:13+03:00",
            "_date_published_rfc2822": "Fri, 04 Nov 2022 12:00:31 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "142",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "111",
            "url": "http:\/\/test.leftjoin.ru\/",
            "title": "Анализируем речь в Python: О чем говорят гости youtube-канала вДудь",
            "content_html": "<p>Сегодня при помощи ML мы будем анализировать прямую речь. В качестве данных используем интервью, которые журналист Юрий Дудь берет для своего YouTube-канала.<br \/>\nВыход практически каждого ролика на канале «вДудь» считается событием, а некоторые из этих релизов даже сопровождаются скандалами из-за неосторожных высказываний его гостей.<br \/>\nПосмотрим с помощью Python и лемматизации о чем таком интересном рассказывали герои роликов канала «вДудь».<\/p>\n<p><b>Парсим тексты субтитров<\/b><br \/>\nВ этом проекте мы будем использовать библиотеки, которые обрабатывают тексты, но сначала нам нужно эти тексты добыть. Импортируем API-интерфейс Python <span class=\"inline-code\">youtube_transcript_api<\/span>, который скачивает субтитры из видео на YouTube.<\/p>\n<pre class=\"e2-text-code\"><code>import pandas as pd\r\nimport numpy as np\r\n\r\nfrom youtube_transcript_api import YouTubeTranscriptApi\r\nimport json<\/code><\/pre><p>Предобработаем URL видео для скачивания субтитров. Всего мы собрали 100 роликов с интервью. В некоторых из интервью нет подготовленных субтитров. В файле <span class=\"inline-code\">‘dud.csv’<\/span> заранее подготовлен список гостей канала вДудь с ссылками на их интервью.<\/p>\n<pre class=\"e2-text-code\"><code>def new_url(s):\r\n    return s.replace('watch?v=','').replace('be.com','.be').replace('www.','')\r\n\r\ndef url_to_id(s):\r\n    return s.partition('be\/')[2]\r\n\r\ndf = pd.read_csv('dud.csv')\r\ndf['URL'] = df['URL'].apply(new_url)\r\ndf['video_id'] = df['URL'].apply(url_to_id)\r\ndf = df.set_index(keys='Гость')<\/code><\/pre><p>У нас теперь есть датафрейм, в котором пока только информация о гостях и ссылка на видео. Но это пока.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/--2021-06-01--10.49.04.png\" width=\"638\" height=\"407\" alt=\"\" \/>\n<\/div>\n<p>Загрузим в нашу таблицу субтитры интервью. Если субтитры найти не удалось, то выведем на экран имена людей, к интервью с которыми их нет или они отключены (или субтитры есть, но не на русском языке).<\/p>\n<pre class=\"e2-text-code\"><code>texts = []\r\nno_sub = []\r\nfor speaker in df.index:\r\n    video_id = df.loc[speaker,'video_id']\r\n    try:\r\n        data = YouTubeTranscriptApi.get_transcript(video_id, languages=['ru', 'ru'])\r\n        data = ' '.join([words['text'] for words in data])\r\n    except Exception:\r\n        print('Нет Субтитров для: ', speaker)\r\n        no_sub.append(speaker)\r\n        data = &quot;&quot;\r\n    texts.append(data)\r\ndf['text'] = texts\r\ndf.to_csv('df_dud.csv')<\/code><\/pre><p>У девяти из 100 интервьюируемых субтитров не оказалось и нам вернулся такой текст:<\/p>\n<pre class=\"e2-text-code\"><code>Нет Субтитров для:  L'one\r\nНет Субтитров для:  Шнур\r\nНет Субтитров для:  Ресторатор\r\nНет Субтитров для:  Амиран\r\nНет Субтитров для:  Ильич\r\nНет Субтитров для:  Соболев\r\nНет Субтитров для:  Иван Дорн\r\nНет Субтитров для:  Навальный\r\nНет Субтитров для:  Noize MC<\/code><\/pre><p><b>Анализируем тексты<\/b><br \/>\nАнализ текстовой информации сложен в той степени, в какой сложен язык, на котором написан текст. Самый популярный способ решения такой аналитической задачи — стемминг. Стеммингом называют процесс нахождения стема — основы слова. Для стемминга используют библиотеку NLTK (Natural Language Toolkit), которая содержит правила образования стемов.<br \/>\nЭтот метод хорошо работает с английскими словами, но у русского языка слишком сложно устроена морфология образования слов, что повышает вероятность ошибки. Стемминг будет хорошим выбором для анализа строк, содержание которых вы примерно представляете себе (например, когда пользователя просят заполнить форму).<br \/>\nДля нашего кейса лучше выбрать лемматизацию — приведение слова к его словарной форме. Проведя лемматизацию текстовых данных по правилам русского языка мы получим существительные в именительном падеже единственного числа (кошками — кошка), прилагательные в именительном падеже мужского рода (пушистая — пушистый), а глаголы в инфинитиве несовершенного вида (бежит — бежать). В этом проекте мы используем MyStem и Pymorphy. Обе библиотеки представляют собой морфологические анализаторы.<br \/>\nКроме того, поскольку при анализе  мы будем использовать алгоритмы машинного обучения, то нам нужно избавиться от слов, которые часто встречаются, но не несут какой-то ценности для анализа. В противном случае они могут повлиять на работу модели. Список таких стоп-слов возьмем из библиотеки <span class=\"inline-code\">nltk.corpus<\/span>.<br \/>\nМаксимально подробно о подготовке текста к анализу мы рассказывали в материале <a href=\"http:\/\/test.leftjoin.ru\/all\/borderline-text-analysis\/\" class=\"nu\">«<u>Python и тексты нового альбома Земфиры<\/u>»<\/a>. Тут была проведена идентичная работа подготовка текстов, после чего мы посчитали количество уникальных слов (’Unique Words’) и записали, как часто они встречаются в речи собеседников Дудя (‘PPT Unique Words’).<\/p>\n<pre class=\"e2-text-code\"><code>df['Total Words'] = df['text'].apply(number_words)\r\ndf['Unique Words'] = df['text'].apply(set).apply(len)\r\ndf['PPT Unique Words'] = df['Unique Words'] \/ df['Total Words'] * 100\r\ndf['PPT Unique Words'] = df['PPT Unique Words'].apply(lambda x: round(x,2))\r\ndf.to_csv('df_dud.csv')<\/code><\/pre><p><b>Строим облако слов<\/b><br \/>\nАвтоматизируем построение облака слов для каждого гостя Дудя. Таким образом мы узнаем какие слова встречаются в их речи чаще всего. Для визуализации инсталлируем <span class=\"inline-code\">wordcloud<\/span>, а <span class=\"inline-code\">word_tokenize<\/span> подсчитает количество слов, которые будут встречаться чаще всего.<\/p>\n<pre class=\"e2-text-code\"><code>import nltk\r\nfrom wordcloud import WordCloud\r\nimport pandas as pd\r\nimport matplotlib.pyplot as plt\r\nfrom nltk import word_tokenize, ngrams\r\n\r\ndef word_cloud(df, occup=None, general=True):\r\n    if occup:\r\n        df = df[df['Род деятельности'] == occup] \r\n    if general:\r\n        data_source = zip([occup], [' '.join([el for el in df['Prepared Text']])])\r\n        col_count, row_count = 1, 1\r\n    else:\r\n        data_source = zip([el for el in df.index], df['Prepared Text']) \r\n        col_count = max(1, df.shape[0] \/\/ 3)\r\n        row_count = df.shape[0] \/\/ col_count + 1\r\n        \r\n    fig = plt.figure()\r\n    plt.figure(figsize=(10, 10))\r\n    fig.patch.set_facecolor('white')\r\n    plt.subplots_adjust(wspace=0.3, hspace=0.2)\r\n    i = 1\r\n    for name, text in data_source:\r\n        tokens = word_tokenize(text)\r\n        text_raw = &quot; &quot;.join(tokens)\r\n        wordcloud = WordCloud(colormap='PuBu', background_color='white', contour_width=10).generate(text_raw)\r\n        plt.subplot(row_count, col_count, i, label=name,frame_on=True)\r\n        plt.tick_params(labelsize=10)\r\n        plt.imshow(wordcloud)\r\n        plt.axis(&quot;off&quot;)\r\n        plt.title(name,fontdict={'fontsize':12,'color':'grey'},y=1.0)\r\n        plt.tick_params(labelsize=10)\r\n        i += 1\r\n    plt.savefig(f'.\/word_cloud\/{occup}.png', dpi=900)<\/code><\/pre><p>У нас получились вот такие облака слов по каждому из гостей программы:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/--2021-06-02--18.58.17.png\" width=\"1198\" height=\"678\" alt=\"\" \/>\n<\/div>\n<p><b>Работа с Word2vec<\/b><br \/>\nС помощью библиотеки <span class=\"inline-code\">gensim<\/span> вызываем модуль, который должен представить слова в наших текстах как векторы.<\/p>\n<pre class=\"e2-text-code\"><code>import plotly.graph_objects as go\r\nimport plotly.figure_factory as ff\r\nfrom scipy import spatial\r\nimport collections\r\nimport pymorphy2\r\nimport gensim\r\n\r\nmorph = pymorphy2.MorphAnalyzer()<\/code><\/pre><p>Для работы модели используем бинарный файл <span class=\"inline-code\">‘model.bin’<\/span>:<\/p>\n<pre class=\"e2-text-code\"><code>model = gensim.models.KeyedVectors.load_word2vec_format('model.bin', binary=True)<\/code><\/pre><p>Модель <b>Word2Vec<\/b> основана на нейронных сетях и позволяет представлять слова в виде векторов, учитывая семантическую составляющую. Ее мы уже использовали в анализе лирики Земфиры. Косинусная мера семантически схожих слов будет стремиться к 1, а  у двух слов, не имеющих ничего общего по смыслу, она близка к 0.<br \/>\nНапишем функцию, которая будет принимать список слов из наших интервью, распознавать для каждого часть речи, а затем получать и суммировать вектора — так мы сможем находить вектора не для одного слова, а для целых предложений и текстов.<\/p>\n<pre class=\"e2-text-code\"><code>def get_vector(word_list):\r\n    vector = 0\r\n    for word in word_list:\r\n        pos = morph.parse(word)[0].tag.POS\r\n        if pos == 'INFN':\r\n            pos = 'VERB'\r\n        if pos in ['ADJF', 'PRCL', 'ADVB', 'NPRO']:\r\n            pos = 'NOUN'\r\n        if word and pos:\r\n            try:\r\n                word_pos = word + '_' + pos\r\n                this_vector = model.word_vec(word_pos)\r\n                vector += this_vector\r\n            except KeyError:\r\n                continue\r\n    return vector<\/code><\/pre><p>Для каждого интервью находим вектор и собираем соответствующий столбец в датафрейм:<\/p>\n<pre class=\"e2-text-code\"><code>vec_list = []\r\nfor word in df['Prepared Text']:\r\n    vec_list.append(get_vector(word.split()))\r\ndf['Vector'] = vec_list<\/code><\/pre><p>Напишем функцию, который будет подсчитывать N-граммы для каждого гостя:<\/p>\n<pre class=\"e2-text-code\"><code>def get_top_five_ngrams(text, n):\r\n    counter = collections.Counter()\r\n    bigrams = list(ngrams(text, n))\r\n    counter.update(bigrams)\r\n    return counter.most_common()[:10]<\/code><\/pre><p>Построим топ N-грамм в соответствии с группой:<\/p>\n<pre class=\"e2-text-code\"><code>top_words = dict.fromkeys(df.index)\r\nfor person in df.index:\r\n    text = df.loc[person,'Prepared Text']\r\n    n_gram = get_top_five_ngrams(text.split(), 1)\r\n    n_list = []\r\n    for item in n_gram:\r\n        n_list.append(item[0][0])\r\n    top_words[person] = n_list\r\nordered_pesrons = df.index\r\ntop_2_words = []\r\nfor person in ordered_pesrons:\r\n    top_2_words.append(top_words[person])\r\ndf['Top bigramms'] = top_2_words<\/code><\/pre><p>Напишем функцию, которая будет добавлять самые часто встречающиеся слова в речи интервьюируемого:<\/p>\n<pre class=\"e2-text-code\"><code>def top_similar(df, occup=None, agg='Person'):\r\n    if occup:\r\n        df = df[df['Род деятельности'] == occup]\r\n    if agg == 'Person':\r\n        top_words_person = dict.fromkeys(df.index)\r\n        for person in df.index:\r\n            vec = df.loc[person, 'Vector']\r\n            words = model.similar_by_vector(vec, topn=10)\r\n            top_words_person[person] = [el[0].split('_')[0] for el in words]\r\n        df_person_words = pd.DataFrame(columns=[agg,'Top Words'])\r\n    elif agg == 'Total':\r\n        top_words_person = {'Total':0}\r\n        vec = df['Vector'].sum()\r\n        words = model.similar_by_vector(vec, topn=10)\r\n        top_words_person['Total'] = [el[0].split('_')[0] for el in words]\r\n        df_person_words = pd.DataFrame(columns=[agg,'Top Words'])\r\n    \r\n    for k,v in top_words_person.items():\r\n        df_person_words = df_person_words.append({agg:k, 'Top Words':v},ignore_index=True)\r\n    df_person_words = df_person_words.set_index(keys=agg) \r\n    \r\n    return df_person_words<\/code><\/pre><p>Для дальнейшей работы группируем гостей по цеховой принадлежности. Наверное, можно ожидать, что режиссеры будут обсуждать кино и все, что с ним связано, а музыканты — музыку.<\/p>\n<pre class=\"e2-text-code\"><code>df_occup = pd.DataFrame(columns=['Occupation', 'Top Words'])\r\nfor occup in df['Род деятельности'].unique():\r\n    words = top_similar(df, occup=occup, agg='Total')['Top Words'][0]\r\n    df_occup = df_occup.append({'Occupation':occup, 'Top Words’:words},ignore_index=True)\r\n\r\nfor i in range(10):\r\n    df_occup[f'Top {i+1} word'] = df_occup['Top Words'].apply(lambda x: x[i])<\/code><\/pre><p>У нас получится новый датафрейм с топом слов для каждой категории гостей (музыкант, политик, актер и тд).<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/--2021-06-02--20.55.04.png\" width=\"1104\" height=\"631\" alt=\"\" \/>\n<\/div>\n<p><b>Анализ риторики гостя<\/b><br \/>\nИспользуя метод <span class=\"inline-code\">similar_by_vector<\/span> для каждого из видов деятельности интервьюируемых, мы получаем список слов, которые наиболее точно описывают тематику текстов.<br \/>\nСтоит отметить, что слово «государство» стоит на первом месте не только в интервью политиков и бизнесменов, но и дизайнеров с писателями. Очевидно, что тема разговора у всех профессиональных групп смещена в сторону политики.<br \/>\nАктёры, кинокритики и музыканты описываются вполне закономерными для их сфер деятельности словами. А вот у фотографов нет ни слова про фотографию или творчество, но есть «работа», «трудоустройство», «существовать» и “семья”.<br \/>\nСравним риторику героев, построив box plot для каждой категории с помощью <span class=\"inline-code\">plotly<\/span>.<\/p>\n<pre class=\"e2-text-code\"><code>import plotly.express as px\r\n\r\nl = []\r\nfor el,ind in zip(df['Род деятельности'].value_counts(), df['Род деятельности'].value_counts().index):\r\n    if el &gt; 1:\r\n        l.append(ind)\r\n\r\ndf_kpi = df[df['Род деятельности'].isin(l)]\r\nfor kpi in ['Total Words', 'Unique Words','PPT Unique Words']:\r\n    buf_df = df_kpi[['Род деятельности',kpi]]\r\n    fig = px.box(df_kpi, \r\n                 x='Род деятельности',\r\n                 y=kpi,\r\n                )\r\n    fig.show()<\/code><\/pre><iframe id=\"igraph\" scrolling=\"no\" style=\"border:none;\" seamless=\"seamless\" src=\"https:\/\/plotly.com\/~Bespalova\/3.embed?link=false\" height=\"650\" width=\"100%\"><\/iframe>\n<p>Наиболее разговорчивыми гостями оказались блогеры — и в среднем, и по медиане они наговорили больше всего слов. И опередили по этому показателю даже писателей. А вот самыми немногословными оказались рэперы, хотя, казалось бы, вот кто должен быть хорош в импровизации.<\/p>\n<iframe id=\"igraph\" scrolling=\"no\" style=\"border:none;\" seamless=\"seamless\" src=\"https:\/\/plotly.com\/~Bespalova\/5.embed?link=false\" height=\"650\" width=\"100%\"><\/iframe>\n<p>Что касается количества уникальных слов, то и тут блогеры значительно ушли вперед. Согласно медианным значениям, тройка лидеров выглядит так — блогер, журналист и писатель. А вот словарный запас рэперов оставляет желать лучшего.<\/p>\n<iframe id=\"igraph\" scrolling=\"no\" style=\"border:none;\" seamless=\"seamless\" src=\"https:\/\/plotly.com\/~Bespalova\/1.embed?link=false\" height=\"650\" width=\"100%\"><\/iframe>\n<p>Если говорить об отношении уникальных слов к общему количеству, то у всех групп гостей примерно одинаковый медианный показатель. Наиболее вариативными оказались музыканты — усы от их ящика показываю наибольший разброс значений.<\/p>\n",
            "date_published": "2021-06-07T11:26:28+03:00",
            "date_modified": "2023-05-19T15:03:12+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/Total-Words.png",
            "_date_published_rfc2822": "Mon, 07 Jun 2021 11:26:28 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "111",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/Total-Words.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/Unique-Words.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/PPT-Unique-Words.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/--2021-06-01--10.49.04.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/--2021-06-02--18.58.17.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/--2021-06-02--20.55.04.png"
                ]
            }
        },
        {
            "id": "103",
            "url": "http:\/\/test.leftjoin.ru\/all\/outliers-detection-in-r\/",
            "title": "Обнаружение статистических выбросов в R",
            "content_html": "<p class=\"note\">Этот материал — перевод статьи <a href=\"https:\/\/statsandr.com\/blog\/outliers-detection-in-r\/\" class=\"nu\">«<u>Outliers detection in R<\/u>»<\/a>. А ещё у нас есть материал про <a href=\"http:\/\/test.leftjoin.ru\/all\/outliers-detection-in-python\/\">обнаружение выбросов в Python<\/a>.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/outliers-detection-in-R.jpeg\" width=\"700\" height=\"467\" alt=\"\" \/>\n<\/div>\n<p>Выбросы — значения или наблюдения, отклоняющиеся от других данных. Всегда нужно сравнивать наблюдение с другими значениями, полученными тем же способом, прежде чем называть их выбросами. Действительно, человек с ростом 200 см, скорее всего, будет считаться отклонением по сравнению с остальным населением, но этот же человек не будет считаться статистическим выбросом, если мы измерим рост баскетболистов.<\/p>\n<p>Выбросы могут быть вызваны изменчивостью, присущей наблюдаемому явлению. Например, при сборе данных о заработной плате часто возникают выбросы, поскольку некоторые люди зарабатывают гораздо больше остальных. Выбросы также могут возникать из-за экспериментальной ошибки, ошибки измерения или кодирования. Например, вес человека 786 кг явно является ошибкой при кодировании веса объекта. Её или его вес, скорее всего, составляет 78,6 кг или 7,86 кг в зависимости от того, был измерен вес взрослого человека или ребёнка.<\/p>\n<p>По этой причине иногда имеет смысл формально выделять два класса выбросов: экстремальные значения и ошибки. Экстремальные значения интереснее, потому что они возможны, но маловероятны.<\/p>\n<p>В этой статье я представлю несколько подходов к обнаружению выбросов в R от простых методов, таких как описательная статистика (включая минимальные, максимальные значения, гистограмму, прямоугольную диаграмму и процентили), до более формальных методов, таких как фильтр Хэмпеля, тесты Граббса, Диксона и Рознера.<\/p>\n<p>Не существует строгого и однозначного правила насчет того, следует ли удалять выбросы из набора данных перед проведением статистического анализа. Довольно часто переменные значения, вызванные экспериментальной ошибкой или ошибкой измерения удаляются или заменяются на новые значения. Некоторые статистические тесты требуют их отсутствия, чтобы сделать обоснованные выводы, но удаление выбросов рекомендуется не во всех случаях и должно выполняться с осторожностью.<\/p>\n<p>Эта статья поможет обнаружить и проверить выбросы, но вы не узнаете, следует ли удалять, изменять или оставлять такие значения. После проверки вы можете исключить их или включить в свой анализ (а это обычно требует вдумчивого размышления со стороны исследователя). Удаление или сохранение выбросов, в основном, зависит от трех факторов:<\/p>\n<ol start=\"1\">\n<li>Область \/ контекст вашего анализа и вопрос исследования. В некоторых областях обычно удаляют посторонние значения, поскольку они часто возникают из-за сбоев в процессе. В других областях отклонения сохраняются, потому что они содержат ценную информацию. Также бывает, что анализ выполняется дважды, один раз с посторонними значениями и один раз без них, чтобы оценить их влияние на результаты. Если результаты резко изменятся из-за некоторых определяющих значений, это должно предостеречь исследователя от чрезмерно амбициозных утверждений.<\/li>\n<li>Устойчивость тестов. Например, наклон простой линейной регрессии может значительно варьироваться даже с одним выбросом, тогда как непараметрические тесты, такие как <a href=\"https:\/\/statsandr.com\/blog\/wilcoxon-test-in-r-how-to-compare-2-groups-under-the-non-normality-assumption\/\">тест Уилкоксона<\/a>, обычно устойчивы к ним.<\/li>\n<li>Дальность выбросов от других наблюдений. Некоторые наблюдения, рассматриваемые как выбросы, на самом деле не являются экстремальными значениями по сравнению со всеми другими наблюдениями, в то время как другие потенциальные выбросы могут быть действительно отстающими от остальных наблюдений.<\/li>\n<\/ol>\n<p>Мы будем использовать набор данных mpg из библиотеки ggplot2, чтобы проиллюстрировать различные подходы к обнаружению выбросов в R, и в частности, мы сосредоточимся на работе с переменной hwy (пробег в милях на галлон израсходованного топлива).<\/p>\n<h2>Минимальные и максимальные значения<\/h2>\n<p>Первое, что необходимо для обнаружения выбросов в R — начать с описательной статистики, и, в частности, с минимальных и максимальных значений.<\/p>\n<p>В R это легко сделать с помощью функции summary():<\/p>\n<pre class=\"e2-text-code\"><code>dat &lt;- ggplot2::mpg\r\nsummary(dat$hwy)\r\n\r\n##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \r\n##   12.00   18.00   24.00   23.44   27.00   44.00<\/code><\/pre><p>Минимум и максимум — первое и последнее значения в выходных данных выше. В качестве альтернативы, их также можно вычислить с помощью функций min() и max():<\/p>\n<pre class=\"e2-text-code\"><code>min(dat$hwy)\r\n\r\n## [1] 12\r\n\r\nmax(dat$hwy)\r\n\r\n## [1] 44<\/code><\/pre><p>Явная ошибка кодирования, такая как, например, человеческий вес в 786 кг уже будет легко обнаружена с помощью этой простой техники.<\/p>\n<p>Гистограмма<\/p>\n<p>Другой базовый способ обнаружения выбросов — построение <a href=\"https:\/\/statsandr.com\/blog\/descriptive-statistics-in-r\/#histogram\">гистограммы данных<\/a>.<\/p>\n<p>При помощи внутренних инструментов R:<\/p>\n<pre class=\"e2-text-code\"><code>hist(dat$hwy,\r\n  xlab = &quot;hwy&quot;,\r\n  main = &quot;Histogram of hwy&quot;,\r\n  breaks = sqrt(nrow(dat))\r\n) # set number of bins<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-3-1@2x.png\" width=\"672\" height=\"480\" alt=\"\" \/>\n<\/div>\n<p>При помощи ggplot2:<\/p>\n<pre class=\"e2-text-code\"><code>library(ggplot2)\r\n\r\nggplot(dat) +\r\n  aes(x = hwy) +\r\n  geom_histogram(bins = 30L, fill = &quot;#0c4c8a&quot;) +\r\n  theme_minimal()<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-4-1@2x.png\" width=\"672\" height=\"480\" alt=\"\" \/>\n<\/div>\n<p>Пара полосок справа в отрыве от основного графика — значения, которые больше остальных.<\/p>\n<p>#Box plot<br \/>\nПомимо гистограмм, box plot (ящик с усами) также полезен для обнаружения потенциальных выбросов.<\/p>\n<p>Используя R:<\/p>\n<pre class=\"e2-text-code\"><code>boxplot(dat$hwy,\r\n  ylab = &quot;hwy&quot;\r\n)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-5-1@2x.png\" width=\"672\" height=\"480\" alt=\"\" \/>\n<\/div>\n<p>или используя ggplot2:<\/p>\n<pre class=\"e2-text-code\"><code>ggplot(dat) +\r\n  aes(x = &quot;&quot;, y = hwy) +\r\n  geom_boxplot(fill = &quot;#0c4c8a&quot;) +\r\n  theme_minimal()<\/code><\/pre><p>Box plot помогает визуализировать количественную переменную, отображая пять общих сводных данных (минимальное значение, среднее значение, первый и третий квартили и максимальное значение) и любое значение, которое было классифицировано как предполагаемый выброс с использованием критерия межквартильного размаха (IQR). Критерий межквартильного размаха означает, что все единицы значения больше q₀,₇₅+ 1.5 ⋅ IQR или меньше q₀,₂₅ — 1,5⋅ IQR рассматриваются R, как потенциальные выбросы. Другими словами, все наблюдения за пределами следующего интервала будут рассматриваться как потенциальные выбросы:<\/p>\n<p>I = [q₀,₂₅ — 1.5 * IQR; q₀,₇₅ + 1.5 * IQR]<\/p>\n<p>Выбросы отображаются в виде точек на прямоугольной диаграмме. Исходя из этого критерия, есть 2 потенциальных выброса (смотрите на 2 точки над вертикальной линией в верхней части диаграммы размаха).<\/p>\n<p>Даже если наблюдение рассматривается как потенциальный выброс по критерию IQR, это не означает, что его следует удалять. Удаление или сохранение выброса зависит от контекста вашего анализа, от того, являются ли тесты, которые вы собираетесь проводить с наборами данных, устойчивыми к выбросам или нет, и насколько далеки выбросы от других наблюдений.<\/p>\n<p>Также возможно извлечь потенциальные выбросы на основе критерия IQR благодаря функции boxplot.stats()$out:<\/p>\n<pre class=\"e2-text-code\"><code>boxplot.stats(dat$hwy)$out\r\n\r\n## [1] 44 44 41<\/code><\/pre><p>Как видите, на самом деле есть 3 точки, которые считаются потенциальными выбросами: две со значением 44 и одна со значением 41.<\/p>\n<p>Благодаря функции which() можно извлечь номер строки, соответствующий этим посторонним значениям:<\/p>\n<pre class=\"e2-text-code\"><code>out &lt;- boxplot.stats(dat$hwy)$out\r\nout_ind &lt;- which(dat$hwy %in% c(out))\r\nout_ind\r\n\r\n## [1] 213 222 223<\/code><\/pre><p>Имея эту информацию, вы теперь можете легко вернуться к определенным строкам в наборе данных, чтобы проверить их, или напечатать все переменные для этих выбросов:<\/p>\n<pre class=\"e2-text-code\"><code>dat[out_ind, ]\r\n\r\n## # A tibble: 3 x 11\r\n##   manufacturer model   displ  year   cyl trans   drv     cty   hwy fl    class  \r\n##   &lt;chr&gt;        &lt;chr&gt;   &lt;dbl&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt;   &lt;chr&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt; &lt;chr&gt;  \r\n## 1 volkswagen   jetta     1.9  1999     4 manual… f        33    44 d     compact\r\n## 2 volkswagen   new be…   1.9  1999     4 manual… f        35    44 d     subcom…\r\n## 3 volkswagen   new be…   1.9  1999     4 auto(l… f        29    41 d     subcom…<\/code><\/pre><p>Ещё можно напечатать выбросы прямо на диаграмме размаха с помощью функции mtext():<\/p>\n<pre class=\"e2-text-code\"><code>boxplot(dat$hwy,\r\n  ylab = &quot;hwy&quot;,\r\n  main = &quot;Boxplot of highway miles per gallon&quot;\r\n)\r\nmtext(paste(&quot;Outliers: &quot;, paste(out, collapse = &quot;, &quot;)))<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-10-1@2x.png\" width=\"672\" height=\"480\" alt=\"\" \/>\n<\/div>\n<h2>Процентили<\/h2>\n<p>Этот метод обнаружения посторонних значений основан на <a href=\"https:\/\/statsandr.com\/blog\/descriptive-statistics-by-hand\/#a-note-on-deciles-and-percentiles\">процентилях<\/a>. При использовании метода процентилей все наблюдения, выходящие за пределы интервала, образованного 2,5 и 97,5 процентилями будут рассматриваться как потенциальные выбросы. Другие процентили, такие как 1 и 99 или 5 и 95 процентили, тоже могут быть рассмотрены для построения интервала.<\/p>\n<p>Значения нижнего и верхнего процентилей можно вычислить с помощью функции quantile():<\/p>\n<pre class=\"e2-text-code\"><code>lower_bound &lt;- quantile(dat$hwy, 0.025)\r\nlower_bound\r\n\r\n## 2.5% \r\n##   14\r\n\r\nupper_bound &lt;- quantile(dat$hwy, 0.975)\r\nupper_bound\r\n\r\n##  97.5% \r\n## 35.175<\/code><\/pre><p>В соответствии с этим методом, все наблюдения ниже 14 и выше 35,175 будут рассматриваться как потенциальные выбросы. Номера рядов наблюдений за пределами интервала затем могут быть извлечены с помощью функции which():<\/p>\n<pre class=\"e2-text-code\"><code>outlier_ind &lt;- which(dat$hwy &lt; lower_bound | dat$hwy &gt; upper_bound)\r\noutlier_ind\r\n\r\n##  [1]  55  60  66  70 106 107 127 197 213 222 223<\/code><\/pre><p>Можно вывести значение пробега в милях на галлон израсходованного топлива для таких значений:<\/p>\n<pre class=\"e2-text-code\"><code>dat[outlier_ind, &quot;hwy&quot;]\r\n\r\n\r\n## # A tibble: 11 x 1\r\n##      hwy\r\n##    &lt;int&gt;\r\n##  1    12\r\n##  2    12\r\n##  3    12\r\n##  4    12\r\n##  5    36\r\n##  6    36\r\n##  7    12\r\n##  8    37\r\n##  9    44\r\n## 10    44\r\n## 11    41<\/code><\/pre><p>В качестве альтернативы можно вывести все переменные для этих выбросов:<\/p>\n<pre class=\"e2-text-code\"><code>dat[outlier_ind, ]\r\n\r\n## # A tibble: 11 x 11\r\n##    manufacturer model    displ  year   cyl trans  drv     cty   hwy fl    class \r\n##    &lt;chr&gt;        &lt;chr&gt;    &lt;dbl&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt;  &lt;chr&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt; &lt;chr&gt; \r\n##  1 dodge        dakota …   4.7  2008     8 auto(… 4         9    12 e     pickup\r\n##  2 dodge        durango…   4.7  2008     8 auto(… 4         9    12 e     suv   \r\n##  3 dodge        ram 150…   4.7  2008     8 auto(… 4         9    12 e     pickup\r\n##  4 dodge        ram 150…   4.7  2008     8 manua… 4         9    12 e     pickup\r\n##  5 honda        civic      1.8  2008     4 auto(… f        25    36 r     subco…\r\n##  6 honda        civic      1.8  2008     4 auto(… f        24    36 c     subco…\r\n##  7 jeep         grand c…   4.7  2008     8 auto(… 4         9    12 e     suv   \r\n##  8 toyota       corolla    1.8  2008     4 manua… f        28    37 r     compa…\r\n##  9 volkswagen   jetta      1.9  1999     4 manua… f        33    44 d     compa…\r\n## 10 volkswagen   new bee…   1.9  1999     4 manua… f        35    44 d     subco…\r\n## 11 volkswagen   new bee…   1.9  1999     4 auto(… f        29    41 d     subco…<\/code><\/pre><p>Согласно методу процентилей, существует 11 потенциальных выбросов. Чтобы уменьшить это число, вы можете установить процентили от 1 до 99:<\/p>\n<pre class=\"e2-text-code\"><code>lower_bound &lt;- quantile(dat$hwy, 0.01)\r\nupper_bound &lt;- quantile(dat$hwy, 0.99)\r\n\r\noutlier_ind &lt;- which(dat$hwy &lt; lower_bound | dat$hwy &gt; upper_bound)\r\n\r\ndat[outlier_ind, ]\r\n\r\n## # A tibble: 3 x 11\r\n##   manufacturer model   displ  year   cyl trans   drv     cty   hwy fl    class  \r\n##   &lt;chr&gt;        &lt;chr&gt;   &lt;dbl&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt;   &lt;chr&gt; &lt;int&gt; &lt;int&gt; &lt;chr&gt; &lt;chr&gt;  \r\n## 1 volkswagen   jetta     1.9  1999     4 manual… f        33    44 d     compact\r\n## 2 volkswagen   new be…   1.9  1999     4 manual… f        35    44 d     subcom…\r\n## 3 volkswagen   new be…   1.9  1999     4 auto(l… f        29    41 d     subcom…<\/code><\/pre><p>Установка процентилей на 1 и 99 дает те же потенциальные выбросы, что и для критерия IQR.<\/p>\n<h2>Фильтр Хэмпеля<\/h2>\n<p>Другой метод, известный как фильтр Хэмпеля, заключается в том, чтобы рассматривать как выбросы значения вне интервала, которые формируются медианным значением плюс-минус 3 медианы абсолютных отклонений (MAD):<\/p>\n<pre class=\"e2-text-code\"><code>I = [median - 3 * MAD; median + 3 * MAD]<\/code><\/pre><p>в которых MAD — это медианное абсолютное отклонение и определяется как медиана абсолютных отклонений от медианы данных:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/--2021-03-24--14.09.11.png\" width=\"265\" height=\"50\" alt=\"\" \/>\n<\/div>\n<p>Для этого метода мы сначала устанавливаем пределы интервала с помощью функций median() и mad():<\/p>\n<pre class=\"e2-text-code\"><code>lower_bound &lt;- median(dat$hwy) - 3 * mad(dat$hwy, constant=1)\r\nlower_bound\r\n\r\n## [1] 9\r\n\r\nupper_bound &lt;- median(dat$hwy) + 3 * mad(dat$hwy, constant=1)\r\nupper_bound\r\n\r\n## [1] 39<\/code><\/pre><p>Все наблюдения меньше 9 и больше 39 будут рассматриваться как потенциальные выбросы. Номера строк наблюдений за пределами интервала затем могут быть извлечены с помощью функции which():<\/p>\n<pre class=\"e2-text-code\"><code>outlier_ind &lt;- which(dat$hwy &lt; lower_bound | dat$hwy &gt; upper_bound)\r\noutlier_ind\r\n\r\n## 213 222 223<\/code><\/pre><p>Согласно фильтру Хэмпеля, для переменной hwy есть 3 потенциальных выброса.<\/p>\n<h2>Статистические тесты<\/h2>\n<p>В этом разделе мы представим еще 3 формальных метода обнаружения отклонений:<\/p>\n<ol start=\"1\">\n<li>Тест Граббса (Grubbs’s test)<\/li>\n<li>Тест Диксона (Dixon’s test)<\/li>\n<li>Тест Рознера (Rosner’s test)<\/li>\n<\/ol>\n<p>Эти статистические тесты являются частью формальных методов обнаружения выбросов, поскольку все они включают вычисление с помощью тестовой статистики, которая сравнивается с табличными критическими значениями.<\/p>\n<p>Обратите внимание, что эти тесты подходят только тогда, когда данные распределены нормально. Таким образом, предположение о соответствии нормальности должно быть проверено перед применением этих тестов для выбросов (Как проверить предположение о соответствии нормальному распределению в R).<\/p>\n<h2>Тест Граббса (Grubbs’s test)<\/h2>\n<p>Тест Граббса позволяет определить, является ли наибольшее или наименьшее значение в наборе данных выбросом. Он обнаруживает по одному выбросу за раз (максимальное или минимальное значение), поэтому нулевая и альтернативная гипотезы проверки максимального значения выглядит так:<\/p>\n<ul>\n<li>H₀: Наивысшее значение не является выбросом<\/li>\n<li>H₁: Наивысшее значение является выбросом<\/li>\n<\/ul>\n<p>А минимального — так:<\/p>\n<ul>\n<li>H₀: Наименьшее значение не является выбросом<\/li>\n<li>H₁: Наименьшее значение является выбросом<\/li>\n<\/ul>\n<p>Как и в любом статистическом тесте, если значение P меньше порогового уровня статистической значимости (обычно α = 0.05), то нулевая гипотеза отвергается, и мы приходим к выводу, что наименьшее\/наибольшее значение является отклонением. Напротив, если значение P больше или равно пороговому уровню значимости, нулевая гипотеза не отвергается, и мы делаем вывод, что на основе данных о том, что наименьшее \/ наибольшее значение не является выбросом. Обратите внимание на то, что тест Граббса не подходит для выборки объемом 6 или меньше (n <= 6).<\/p>\n<p>Чтобы выполнить тест Граббса в R, используем функцию grubbs.test() из пакетов outliers:<\/p>\n<pre class=\"e2-text-code\"><code># install.packages(&quot;outliers&quot;)\r\nlibrary(outliers)\r\ntest &lt;- grubbs.test(dat$hwy)\r\ntest \r\n\r\n## \r\n##  Grubbs test for one outlier\r\n## \r\n## data:  dat$hwy\r\n## G = 3.45274, U = 0.94862, p-value = 0.05555\r\n## alternative hypothesis: highest value 44 is an outlier<\/code><\/pre><p>Значение P равняется 0,056. На уровне значимости 5% мы не отвергаем гипотезу о том, что наибольшее значение 44 не является выбросом.<\/p>\n<p>По умолчанию тест выполняется на наибольшем значении (как показано в выходных данных R: alternative hypothesis: highest value). Если вы хотите провести тест для наименьшего значения, просто добавьте аргумент opposite = TRUE в функцию grubbs.test():<\/p>\n<pre class=\"e2-text-code\"><code>test &lt;- grubbs.test(dat$hwy, opposite = TRUE)\r\ntest\r\n\r\n## \r\n##  Grubbs test for one outlier\r\n## \r\n## data:  dat$hwy\r\n## G = 1.92122, U = 0.98409, p-value = 1\r\n## alternative hypothesis: lowest value 12 is an outlier<\/code><\/pre><p>Вывод указывает на то, что тест теперь выполняется при наименьшем значении<\/p>\n<p>Значение P равно 1. На уровне значимости 5% мы не отвергаем гипотезу о том, что наименьшее значение 12 не является выбросом.<\/p>\n<p>Для иллюстрации этого заменим наблюдения более экстремальным значением и выполним тест Граббса для нового набора данных. Давайте заменим 34-ую строку со значением 212:<\/p>\n<pre class=\"e2-text-code\"><code>dat[34, &quot;hwy&quot;] &lt;- 212<\/code><\/pre><p>Применяем тест Граббса, чтобы проверить, является ли наибольшее значение выбросом:<\/p>\n<pre class=\"e2-text-code\"><code>test &lt;- grubbs.test(dat$hwy)\r\ntest\r\n\r\n## \r\n##  Grubbs test for one outlier\r\n## \r\n## data:  dat$hwy\r\n## G = 13.72240, U = 0.18836, p-value &lt; 2.2e-16\r\n## alternative hypothesis: highest value 212 is an outlier<\/code><\/pre><p>Значение p < 0,001. На уровне значимости 5% мы делаем вывод, что наивысшее значение 212 является выбросом.<\/p>\n<h2>Тест Диксона (Dixon’s test)<\/h2>\n<p>Подобно тесту Граббса, тест Диксона используется для того, чтобы проверить, является ли самое высокое или самое низкое значение выбросом. Таким образом, если под сомнением находятся более одного выброса, тест необходимо проводить индивидуально для этих предполагаемых значений.<\/p>\n<p>Обратите внимание на то, что тест Диксона наиболее полезен для выборки небольшого объема (обычно когда n <= 25).<\/p>\n<p>Чтобы выполнить тест Диксона в R, мы используем функцию dixon.test() из пакета outliers. Однако мы ограничиваем наш набор данных 20 первыми наблюдениями, поскольку тест Диксона может быть выполнен только на небольшом размере выборки:<\/p>\n<pre class=\"e2-text-code\"><code>subdat &lt;- dat[1:20, ]\r\ntest &lt;- dixon.test(subdat$hwy)\r\ntest\r\n\r\n## \r\n##  Dixon test for outliers\r\n## \r\n## data:  subdat$hwy\r\n## Q = 0.57143, p-value = 0.006508\r\n## alternative hypothesis: lowest value 15 is an outlier<\/code><\/pre><p>Результаты показывают, что самое наименьшее значение 15 является выбросом (p-значение = 0,007).<\/p>\n<p>Чтобы проверить максимальное значение, просто добавьте аргумент opposite = TRUE к функции dixon.test():<\/p>\n<pre class=\"e2-text-code\"><code>test &lt;- dixon.test(subdat$hwy,\r\n  opposite = TRUE\r\n)\r\ntest\r\n\r\n## \r\n##  Dixon test for outliers\r\n## \r\n## data:  subdat$hwy\r\n## Q = 0.25, p-value = 0.8582\r\n## alternative hypothesis: highest value 31 is an outlier<\/code><\/pre><p>Результаты показывают, что максимальное значение 31 не является выбросом (p-значение = 0,858).<\/p>\n<p>Рекомендуется всегда сверять результаты статистического теста на выбросы с диаграммой, чтобы убедиться, что мы проверили все потенциальные выбросы:<\/p>\n<pre class=\"e2-text-code\"><code>out &lt;- boxplot.stats(subdat$hwy)$out\r\nboxplot(subdat$hwy,\r\n  ylab = &quot;hwy&quot;\r\n)\r\nmtext(paste(&quot;Outliers: &quot;, paste(out, collapse = &quot;, &quot;)))<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-24-1@2x.png\" width=\"672\" height=\"480\" alt=\"\" \/>\n<\/div>\n<p>По box plot заметно, что мы можем применить тест Диксона к значению 20 в дополнение к значению 15, выполненному ранее. Это можно сделать, найдя номер строки минимального значения, исключив этот номер строки из набора данных, а затем применив тест Диксона к этому новому набору данных:<\/p>\n<pre class=\"e2-text-code\"><code># find and exclude lowest value\r\nremove_ind &lt;- which.min(subdat$hwy)\r\nsubsubdat &lt;- subdat[-remove_ind, ]\r\n\r\n# Dixon test on dataset without the minimum\r\ntest &lt;- dixon.test(subsubdat$hwy)\r\ntest\r\n\r\n## \r\n##  Dixon test for outliers\r\n## \r\n## data:  subsubdat$hwy\r\n## Q = 0.44444, p-value = 0.1297\r\n## alternative hypothesis: lowest value 20 is an outlier<\/code><\/pre><p>Результаты показывают, что второе наименьшее значение 20 не является выбросом (p-значение = 0,13).<\/p>\n<h2>Тест Рознера (Rosner’s test)<\/h2>\n<ol start=\"1\">\n<li>Тест Рознера на выбросы имеет следующие преимущества:<\/li>\n<li>Он используется для одновременного обнаружения нескольких выбросов (в отличие от теста Граббса и Диксона, которые должны выполняться итеративно для выявления нескольких выбросов)<\/li>\n<li>Он разработан чтобы избежать проблемы, когда выброс, близкий по значению к другому выбросу, может остаться незамеченным.<\/li>\n<\/ol>\n<p>Обратите внимание, что в отличие от теста Диксона, тест Рознера подходит к большому объему выборки (n≥20). Поэтому мы снова используем исходный набор данных dat, который включает 234 наблюдения.<\/p>\n<p>Для выполнения теста Рознера мы используем функцию rosnerTest() из пакета EnvStats. Для этой функции требуется как минимум 2 аргумента: данные и количество предполагаемых выбросов k.<\/p>\n<pre class=\"e2-text-code\"><code>library(EnvStats)\r\ntest &lt;- rosnerTest(dat$hwy,\r\n  k = 3\r\n)\r\ntest\r\n\r\n## $distribution\r\n## [1] &quot;Normal&quot;\r\n## \r\n## $statistic\r\n##       R.1       R.2       R.3 \r\n## 13.722399  3.459098  3.559936 \r\n## \r\n## $sample.size\r\n## [1] 234\r\n## \r\n## $parameters\r\n## k \r\n## 3 \r\n## \r\n## $alpha\r\n## [1] 0.05\r\n## \r\n## $crit.value\r\n## lambda.1 lambda.2 lambda.3 \r\n## 3.652091 3.650836 3.649575 \r\n## \r\n## $n.outliers\r\n## [1] 1\r\n## \r\n## $alternative\r\n## [1] &quot;Up to 3 observations are not\\n                                 from the same Distribution.&quot;\r\n## \r\n## $method\r\n## [1] &quot;Rosner's Test for Outliers&quot;\r\n## \r\n## $data\r\n##   [1]  29  29  31  30  26  26  27  26  25  28  27  25  25  25  25  24  25  23\r\n##  [19]  20  15  20  17  17  26  23  26  25  24  19  14  15  17  27 212  26  29\r\n##  [37]  26  24  24  22  22  24  24  17  22  21  23  23  19  18  17  17  19  19\r\n##  [55]  12  17  15  17  17  12  17  16  18  15  16  12  17  17  16  12  15  16\r\n##  [73]  17  15  17  17  18  17  19  17  19  19  17  17  17  16  16  17  15  17\r\n##  [91]  26  25  26  24  21  22  23  22  20  33  32  32  29  32  34  36  36  29\r\n## [109]  26  27  30  31  26  26  28  26  29  28  27  24  24  24  22  19  20  17\r\n## [127]  12  19  18  14  15  18  18  15  17  16  18  17  19  19  17  29  27  31\r\n## [145]  32  27  26  26  25  25  17  17  20  18  26  26  27  28  25  25  24  27\r\n## [163]  25  26  23  26  26  26  26  25  27  25  27  20  20  19  17  20  17  29\r\n## [181]  27  31  31  26  26  28  27  29  31  31  26  26  27  30  33  35  37  35\r\n## [199]  15  18  20  20  22  17  19  18  20  29  26  29  29  24  44  29  26  29\r\n## [217]  29  29  29  23  24  44  41  29  26  28  29  29  29  28  29  26  26  26\r\n## \r\n## $data.name\r\n## [1] &quot;dat$hwy&quot;\r\n## \r\n## $bad.obs\r\n## [1] 0\r\n## \r\n## $all.stats\r\n##   i   Mean.i      SD.i Value Obs.Num     R.i+1 lambda.i+1 Outlier\r\n## 1 0 24.21795 13.684345   212      34 13.722399   3.652091    TRUE\r\n## 2 1 23.41202  5.951835    44     213  3.459098   3.650836   FALSE\r\n## 3 2 23.32328  5.808172    44     222  3.559936   3.649575   FALSE\r\n## \r\n## attr(,&quot;class&quot;)\r\n## [1] &quot;gofOutlier&quot;<\/code><\/pre><p>Результаты представлены в таблице $all.stats:<\/p>\n<pre class=\"e2-text-code\"><code>test$all.stats\r\n\r\n##   i   Mean.i      SD.i Value Obs.Num     R.i+1 lambda.i+1 Outlier\r\n## 1 0 24.21795 13.684345   212      34 13.722399   3.652091    TRUE\r\n## 2 1 23.41202  5.951835    44     213  3.459098   3.650836   FALSE\r\n## 3 2 23.32328  5.808172    44     222  3.559936   3.649575   FALSE<\/code><\/pre><p>Основываясь на тесте Рознера, мы видим, что существует только один выброс (см. Столбец Outlier), и что это наблюдение 34 (см. Obs.Num) со значением 212 (см. Value).<\/p>\n<h2>Итоги<\/h2>\n<p>Обратите внимание, что некоторые преобразования могут «естественным образом» устранить выбросы. Например, если взять натуральный логарифм или квадратный корень из значения, отклонение станет меньше. Я надеюсь, статья помогла вам обнаружить выбросы в R с помощью нескольких методов <a href=\"https:\/\/statsandr.com\/blog\/descriptive-statistics-in-r\/\">описательной статистики<\/a> (включая минимум, максимум, гистограмму, диаграмму размаха и процентили) или благодаря более формальным методам обнаружения выбросов (включая фильтр Хампеля, тест Граббса, Диксона и Рознера). Следующим этапом проверьте эти значения, и если они действительно являются выбросами — решите, как с ними поступить (сохранить, удалить или изменить), прежде чем проводить анализ.<\/p>\n",
            "date_published": "2021-03-24T14:32:17+03:00",
            "date_modified": "2021-04-02T13:57:17+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-3-1.png",
            "_date_published_rfc2822": "Wed, 24 Mar 2021 14:32:17 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "103",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-3-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-4-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-5-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-10-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/--2021-03-24--14.08.12.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-24-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/outliers-detection-in-R.jpeg",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-3-1@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-4-1@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-5-1@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-10-1@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/--2021-03-24--14.09.11.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/unnamed-chunk-24-1@2x.png"
                ]
            }
        },
        {
            "id": "68",
            "url": "http:\/\/test.leftjoin.ru\/all\/animiruem-tennisnye-myachiki-v-tableau\/",
            "title": "Анимируем теннисные мячики в Tableau",
            "content_html": "<p>В <a href=\"http:\/\/test.leftjoin.ru\/all\/tableau-swing-vision\/\">прошлом видео<\/a> мы научились визуализировать теннисные мячики из Swing Vision на графике в Tableau с кастомным фоном и кастомными фигурами. Сегодня будем анимировать дашборд, чтобы посмотреть, как менялись удары и получим видео с результатами игры, которое можно экспортировать и кому-нибудь показать.<\/p>\n<div class=\"e2-text-video\">\n<iframe src=\"https:\/\/www.youtube.com\/embed\/G33K5bmcJWQ\" frameborder=\"0\" allowfullscreen><\/iframe><\/div>\n<p>Краткое резюме:<\/p>\n<ol start=\"1\">\n<li>Создаём элемент Pages. Он позволяет управлять движением анимации: нажимая на кнопку Play, Title страницы будет меняться.<\/li>\n<li>Добавим историю: ставим галочку на Show History ниже и выберем длину в 7 ударов.<\/li>\n<li>Вернёмся на дашборд. Переходим во вкладку Worksheet — Show Cards и выбираем Current Page.<\/li>\n<li>Для захвата видео с экрана добавим новый контейнер и перенесём в него панель с ударами.<\/li>\n<li>Утилитой записи экрана выбираем область с дашбордом и жмём на Play. Для macOS можно воспользоваться встроенной: достаточно нажать комбинацию клавиш ⌘ + Shift + 5.<\/li>\n<\/ol>\n",
            "date_published": "2020-10-20T13:13:18+03:00",
            "date_modified": "2020-10-20T13:00:12+03:00",
            "_date_published_rfc2822": "Tue, 20 Oct 2020 13:13:18 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "68",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "60",
            "url": "http:\/\/test.leftjoin.ru\/all\/pandas-profiling-2\/",
            "title": "Обзор библиотеки pandas-profiling на примере датасета Superstore Sales",
            "content_html": "<p>Перед тем как работать с данными, необходимо составить представление, с чем мы имеем дело. В материале будем рассматривать датасет SuperStore Sales, а именно его лист <i>Orders<\/i>. В нём собраны данные о покупках клиентов канадского интернет-супермаркета: идентификаторы заказа, товаров, клиента, тип доставки, цены, категории и названия продуктов и прочее. Подробнее с датасетом можно ознакомиться на <a href=\"https:\/\/github.com\/PacktPublishing\/Tableau-10-Best-Practices\/blob\/master\/Chapter%205\/Sample%20-%20Superstore%20Sales%20(Excel).xls\">GitHub<\/a>. Например, если мы создадим из датасета DataFrame, можем воспользоваться стандартным методом <span class=\"inline-code\">describe()<\/span> библиотеки pandas для описания данных:<\/p>\n<pre class=\"e2-text-code\"><code>import pandas as pd\r\n\r\ndf = pd.read_csv('superstore_sales_orders.csv', decimal=',')\r\ndf.describe(include='all')<\/code><\/pre><p>И во многих случаях получим такую кашу:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/1-13.png\" width=\"984\" height=\"427\" alt=\"\" \/>\n<\/div>\n<p class=\"note\">Код библиотеки доступен на <a href=\"https:\/\/github.com\/pandas-profiling\/pandas-profiling\">GitHub<\/a><\/p>\n<p>Если постараться и потратить время, можно извлечь полезную информацию. Например, можем узнать, что люди чаще выбирают «Regular air» в качестве доставки или что большинство заказов поступило из провинции Онтарио. Тем не менее, есть и другое решение, которое подробнее и качественнее описывает датасет — библиотека pandas-profiling. Вы отдаёте ей DataFrame, а она генерирует html-страницу с подробным описанием сета данных:<\/p>\n<pre class=\"e2-text-code\"><code>import pandas_profiling\r\nprofile = pandas_profiling.ProfileReport(df)\r\nprofile.to_file(&quot;output.html&quot;)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/2-15.png\" width=\"973\" height=\"621\" alt=\"\" \/>\n<\/div>\n<p>Всего Pandas Profiling возвращает 6 разделов: обзор датасета, переменные, отношения и корреляцию между ними, количество пропущенных значений и примеры из датасета.<\/p>\n<p class=\"note\">Web-версия отчёта доступна <a href=\"http:\/\/test.leftjoin.ru\/files\/superstore.html\">по ссылке<\/a><\/p>\n<h2>Обзор данных<\/h2>\n<p>Рассмотрим первый подраздел — «Overview». Библиотека собрала следующую статистику: количество переменных, наблюдений, пропущенных ячеек, дубликатов и общий вес файла. В колонке <span class=\"inline-code\">Variable types<\/span> описаны типы переменных: здесь 12 качественных и 9 числовых.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/3-13.png\" width=\"737\" height=\"356\" alt=\"\" \/>\n<\/div>\n<p>В подразделе «Reproduction» собрана техническая информация библиотеки: сколько времени занял анализ сета данных, версия библиотеки и прочее.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/4-8.png\" width=\"725\" height=\"293\" alt=\"\" \/>\n<\/div>\n<p>А подраздел «Warnings» сообщает о возможных проблемах в структуре датасета: сейчас он, например, предупреждает, что у поля «Order Date» — слишком большое количество уникальных значений.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/5-9.png\" width=\"712\" height=\"485\" alt=\"\" \/>\n<\/div>\n<h2>Переменные<\/h2>\n<p>Двигаемся ниже. В этом разделе содержится подробное описание каждой переменной: сколько возможных уникальных значений она принимает, сколько значений пропущено, сколько памяти занимает поле. Справа от статистики присутствует гистограмма с распределением значений поля.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/6-8.png\" width=\"722\" height=\"282\" alt=\"\" \/>\n<\/div>\n<p>При нажатии на <span class=\"inline-code\">Toggle details<\/span> откроется расширенная информация: квартили, медиана и прочая полезная описательная статистика. В остальных вкладках находятся гистограмма из основного экрана, топ-10 значений по частоте и экстремальные значения.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/7-4.png\" width=\"737\" height=\"404\" alt=\"\" \/>\n<\/div>\n<h2>Отношения переменных<\/h2>\n<p>В этом разделе визуализированы отношения переменных при помощи hexbin plot: выглядит это не очень очевидно и понятно. Особенно усугубляет положение отсутствие легенды к графику.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/Screenshot-2020-09-04-at-14.56.01.png\" width=\"716\" height=\"548\" alt=\"\" \/>\n<\/div>\n<h2>Корреляция переменных<\/h2>\n<p>В этом разделе представлена по-разному посчитананя корреляция переменных: например, первым указано r-value Пирсона. Заметно, что переменная Profit положительно коррелирует с переменной <span class=\"inline-code\">Sales<\/span>. При нажатии на <span class=\"inline-code\">Toggle correlation descriptions<\/span> открывается подробное пояснение к каждому коэффициенту.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/8-5.png\" width=\"739\" height=\"554\" alt=\"\" \/>\n<\/div>\n<h2>Пропущенные значения<\/h2>\n<p>Тут всё просто — bar chart, матрица и дендрограмма с количеством заполненных полей в каждой переменной. Заметно, что в колонке <span class=\"inline-code\">Product Base Margin<\/span> отсутствуют три значения.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/9-5.png\" width=\"739\" height=\"411\" alt=\"\" \/>\n<\/div>\n<h2>Примеры<\/h2>\n<p>И, наконец, последний раздел представляет первые и последние 10 значений в качестве примера кусков сета данных — аналог метода <span class=\"inline-code\">head()<\/span> из pandas.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/10-4.png\" width=\"661\" height=\"469\" alt=\"\" \/>\n<\/div>\n<h2>Что в итоге?<\/h2>\n<p>Библиотека уделяет больше внимания статистике, чем pandas: можно получить подробную описательную статистику по каждой переменной, посмотреть, как коррелируют между собой столбцы датасета. В совокупности с генерацией простого и удобного интерфейса библиотека строит полноценный отчёт по датасету, уже на основании которого можно делать выводы и сформировать представление о данных.<br \/>\nИ всё же, у библиотеки есть и минусы. На генерацию отчётов к громадным датасетам может уйти много времени вплоть до нескольких часов. Это безусловно хороший инструмент для автоматического проектирования, но он не может сделать полноценный анализ за вас и добавить больше деталей в графики. Кроме того, если вы только начали практиковаться с анализом данных лучше будет начать с pandas — это закрепит ваши навыки и придаст уверенности при работе с данными.<\/p>\n",
            "date_published": "2020-09-04T17:55:08+03:00",
            "date_modified": "2020-09-04T18:01:13+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/2-14.png",
            "_date_published_rfc2822": "Fri, 04 Sep 2020 17:55:08 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "60",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/2-14.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/2@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/2@1.5x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/1-13.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/2-15.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/3-13.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/4-8.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/5-9.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/6-8.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/7-4.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/Screenshot-2020-09-04-at-14.56.01.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/8-5.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/9-5.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/10-4.png"
                ]
            }
        },
        {
            "id": "48",
            "url": "http:\/\/test.leftjoin.ru\/all\/choosing-font-for-data-vis\/",
            "title": "Выбор шрифтов для визуализации данных",
            "content_html": "<p class=\"note\">Данная статья — перевод оригинала: <a href=\"https:\/\/medium.com\/nightingale\/choosing-a-font-for-your-data-visualization-2ed37afea637\" class=\"nu\">«<u>Choosing Fonts for Your Data Visualization<\/u>»<\/a><\/p>\n<p>Цель визуализации данных — сделать макет, который быстро передаст большое количество информации. Хорошая визуализация помогает пользователю понять сложные данные. Любой текст, сопровождающий график, должен читаться настолько легко, будто он отсутствует совсем.<br \/>\nВ этой статье я расскажу, как выбрать читаемый и эстетичный шрифт для вашего проекта. Я сосредоточусь прежде всего на небольшом тексте, предназначенном для пояснения графика, включая метки, сноски и источники. Чтобы угодить более широкой аудитории, я буду рекомендовать только бесплатные шрифты Google, хотя есть и <a href=\"https:\/\/www.typography.com\/fonts\/gotham\/overview\">множество<\/a> <a href=\"https:\/\/fonts.adobe.com\/fonts\/soleil\">хороших<\/a> <a href=\"https:\/\/klim.co.nz\/retail-fonts\/calibre\/\">платных<\/a> <a href=\"https:\/\/fonts.adobe.com\/fonts\/interstate\">вариантов<\/a>.<\/p>\n<h2>Хорошо читаемые шрифты<\/h2>\n<p>Хорошо читаемые шрифты требуют меньше напряжения для понимания. Визуализации с читаемыми шрифтами имеют постоянный ритм и выглядят как единое целое. Хорошая типографика не является показной — она объясняет содержание, не мешая работе пользователя.<br \/>\nЧтобы понять, что это за тип «хорошо читаемых» шрифтов, давайте рассмотрим несколько ключевых элементов.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/02-1.png\" width=\"1400\" height=\"230\" alt=\"\" \/>\n<\/div>\n<h2>X-высота<\/h2>\n<p>X-высота — это высота строчных букв. Так как строчные буквы иногда имеют разную высоту, этот параметр измеряется с помощью буквы «х».<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/03-1.png\" width=\"1400\" height=\"378\" alt=\"\" \/>\n<\/div>\n<p>Х-высота напрямую влияет на то, насколько шрифт читаем при небольших размерах. Взгляните на изображение выше. Все эти шрифты имеют размер в 10 пунктов. Какой шрифт вы считаете наиболее читабельным? Gill Sans и Athelas имеют меньшую Х-высоту, что затрудняет чтение текста. Open Sans, Noto Sans и Lato имеют большую Х-высоту, что обеспечивает удобство чтения при небольших размерах. При выборе шрифта для визуализации данных выберите шрифт с большой Х-высотой.<\/p>\n<p>Обратите внимание: у Lato хорошая высота по оси Х, но укорочена длина линии.<br \/>\nЕсли ваша визуализация имеет ось Х с ограниченным пространством, вы можете рассмотреть более сжатую гарнитуру, как, например, Lato.<\/p>\n<h2>Апертура<\/h2>\n<p>Апертура — это пустая область в буквах, как «р» и «о».<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/04-1.png\" width=\"1400\" height=\"346\" alt=\"\" \/>\n<\/div>\n<p>Форма апертуры напрямую влияет на читаемость при небольших размерах. Глаза должны легко идти по буквам, человек не должен тратить время на выяснение, буква «о» это или «е». Шрифты с искаженной апертурой плохо отображаются при небольших размерах, поэтому следует избегать причудливых шрифтов вроде Marker Felt. Обратите внимание, как трудно читать плотные шрифты, такие как League Gothic и Futura Condensed при небольших размерах: это связано с тем, что при сжатии шрифта апертура удлиняется. При выборе шрифта для визуализации данных используйте шрифт со стабильной открытой апертурой.<\/p>\n<h2>Засечки<\/h2>\n<p>Засечки — это маленькие галочки вокруг буквы.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/05-1.png\" width=\"1400\" height=\"350\" alt=\"\" \/>\n<\/div>\n<p>При маркировке данных часто избегают засечек, потому что дополнительные элементы иногда запутывают представление о форме буквы. Первые три строки выше — шрифты с засечками. Сравните их с двумя последними без засечек: в этом примере слова без засечек легче расшифровать. И хотя некоторые шрифты с засечками можно использовать при меньших размерах, если вы не уверены, выберите что-нибудь без засечек.<\/p>\n<h2>Использование чисел<\/h2>\n<p>Числа гарнитуры бывают либо пропорциональными, либо табличными.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/06.png\" width=\"1400\" height=\"472\" alt=\"\" \/>\n<\/div>\n<p>Пропорциональные числа выглядят хорошо при использовании в тексте. Табличные числа предназначены для работы с данными и занимают одинаковое количество ширины на символ. Если вы выстроите цифры в линию, увидите, что они попадают в одинаково расположенные столбцы. Табличные числа легче читать в визуализациях.<br \/>\nOpen Sans — табличный шрифт, центрирующий числа в пространстве столбца. А Lato, например, немного смещает число в таком пространстве. Почему это важно?<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/07.png\" width=\"1400\" height=\"291\" alt=\"\" \/>\n<\/div>\n<p>Потому что это маленькое пространство требует от вашего мозга работы для осознания того, что это непрерывная цепочка цифр. Мы можем исключить эту работу, выбрав гарнитуру с табличными числами или даже с пробелами.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/08.png\" width=\"1400\" height=\"314\" alt=\"\" \/>\n<\/div>\n<p>Посмотрите на старомодные числа. Raleway — красивый шрифт, который обычно используется для текстов и логотипов, однако он плохо работает для маркировки и визуализации данных. Это потому, что он использует старомодные числа вместо ровных чисел, идущих в одну линию. Взгляните, как 3, 5 и 4 опускаются ниже базовой линии. Такие числа трудно воспринимать не только в визуализации, но и в обычном тексте.<\/p>\n<h2>Системы типографского дизайна<\/h2>\n<p>Системы типографского дизайна представляют собой набор из размеров шрифта, веса и высоты строк, которые будут использоваться в макете. Системы помогают обеспечить визуальную согласованность проекта. Ниже представлено пять систем, которые можно опробовать в своем дизайне. Все они используют бесплатные шрифты Google с открытым исходным кодом.<\/p>\n<h2>Система 1: один шрифт \/ один размер<\/h2>\n<p>Такая система использует одну гарнитуру с одним заданным размером, что приводит к универсальной визуальной текстуре. Такой тонкий подход хорош для дашбордов или для добавления графиков в бизнес-отчёт. Важная информация может быть отмечена при использовании более тяжелого веса шрифта или оттенков серого.<br \/>\n<i>Пример из жизни<\/i>: примером этой техники является панель инструментов Google Trends. Этот дашборд использует шрифт Roboto как для заголовка, так и для надписей. Панель инструментов разделяет контент с помощью макета на основе карты. Эта карта использует одну гарнитуру и один размер шрифта по всей композиции.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/09.png\" width=\"1400\" height=\"505\" alt=\"\" \/>\n<\/div>\n<p>В следующей таблице используется техника «Один шрифт \/ один размер» с Lato в 14 пунктов и высотой строки в 18 пунктов:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/10-2.png\" width=\"1400\" height=\"784\" alt=\"\" \/>\n<\/div>\n<p><i>Используемый шрифт<\/i>: Lato — хороший шрифт для визуализации данных, потому что хорошо читается при небольших размерах. Шрифт имеет чистую апертуру, умеренную X-высоту и узкий, но неискаженный интервал между буквами. Для чисел он использует табличные значения, они равномерно распределены для удобства чтения. Жирный шрифт легко отличается от обычного, однако полужирный не имеет достаточных отличий от обычного или жирного, так что его следует избегать. Lato был выпущен в Google Fonts в 2015 году и в настоящее время является третьим по популярности шрифтом на их сайте.<\/p>\n<h2>Система 2: Один шрифт \/ большой заголовок<\/h2>\n<p>Типографская система «один шрифт \/ большой заголовок» использует один шрифт с заголовком, который больше и жирнее прочего контента. Больший заголовок выделяется и позволяет пользователю быстро понять, о чём идёт речь.<\/p>\n<p class=\"note\">В визуализации The Washington Post использован шрифт <a href=\"https:\/\/www.myfonts.com\/fonts\/itc\/franklin\/\">ITC Franklin Pro<\/a><\/p>\n<p><i>Пример из жизни<\/i>: The Washington Post использует такую систему в приведённой ниже визуализации. Дизайнер сохраняет семейство и размер шрифта в соответствии с тем, чтобы навигация ощущалась как часть макета.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/11-2.png\" width=\"1400\" height=\"999\" alt=\"\" \/>\n<\/div>\n<p>Ниже приведён пример использования такой системы. В нём используется шрифт Assistant размером в 24 пункта для заголовка и в 14 пунктов для прочего содержимого.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/12.png\" width=\"1400\" height=\"716\" alt=\"\" \/>\n<\/div>\n<p><i>Используемый шрифт<\/i>: Assistant — чёткий современный шрифт, который хорошо работает в дизайне благодаря простому, открытому эстетичному размеру букв. Числа хорошо сбалансированы и не имеют пробелов. Assistant — семейство шрифтов с открытым исходным кодом, основанное на Source Sans Pro. Проект открыт для сотрудничество и принимает участие в репозитории GitHub.<\/p>\n<h2>Система 3. Два шрифта \/ тяжёлый & лёгкий<\/h2>\n<p>Такая система использует два шрифта без засечек с дополнительной X-высотой и межбуквенными интервалами. Заголовок имеет большой вес, содержание — лёгкий.<br \/>\n<i>Пример из жизни<\/i>: для визуализации данных Reuter в материале «The Rohingya Crisis: Life in the camps» используется комбинация тяжелых и легких шрифтов. Дизайнер использовал фирменную гарнитуру Reuter «Knowledge» для заголовка. Прочая информация использует Source Sans Pro, доступный в библиотеке Google Fonts. Обратите внимание, как «Negative for E. Coli» выделено серым цветом, чтобы привлечь внимание к первым трём показателям.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/13.png\" width=\"1316\" height=\"452\" alt=\"\" \/>\n<\/div>\n<p>В следующей таблице используется эта система. Заголовки большие и полужирные, остальное содержимое имеет небольшой вес. Сочетание тяжелых и тонких шрифтов обеспечивает контрастное разделение, помогая пользователю определить важные разделы композиции.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/14.png\" width=\"1400\" height=\"736\" alt=\"\" \/>\n<\/div>\n<p><i>Используемые шрифты<\/i>: PT Sans Bold — сильный округлый шрифт с большой X-высотой. PT Sans и PT Serif были разработаны для русского алфавита как всеобъемлющий шрифт для поощрения печати и чтения на национальной языке. В дополнение к кириллице, PT Sans поддерживает латиницу, греческий, арабский и другие формы. Семейство также включает стиль PT Sans Caption, который был разработан специально для мелкого шрифта и хорошо подходит для длинных заметок или разделов источников.<\/p>\n<div class=\"e2-text-picture\">\n<div class=\"fotorama\" data-width=\"552\" data-ratio=\"3.7808219178082\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/15.png\" width=\"552\" height=\"146\" alt=\"\" \/>\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/15.5.png\" width=\"552\" height=\"147\" alt=\"\" \/>\n<\/div>\n<\/div>\n<p>Содержимое в примере использует шрифт Noto Sans: это чистый шрифт со слегка сжатым межбуквенным интервалом, что компенсируется чёткой круглой апертурой и большой X-высотой. Табличные числа включают в себя прочное число «1» с широкой ножкой, придающую структуре чисел чёткий вид. Noto Sans является частью большого семейства шрифтов Noto. В группе более 100 различных шрифтов с целью обеспечения «визуальной гармонии» на нескольких языках. Это делает Noto Sans особенно хорошим вариантом для многоязычных визуализаций.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/16.png\" width=\"1400\" height=\"804\" alt=\"\" \/>\n<\/div>\n<h2>Система 4: Один с засечками \/ один без засечек.<\/h2>\n<p>Такая система использует два шрифта, один из которых с засечками, а другой — без.<br \/>\n<i>Пример из жизни<\/i>: ниже приведён скриншот статьи New York Times «Coronavirus in US». Название использует шрифт NYT Cheltenham, а метки данных — NYT Franklin, оба шрифта созданы специально для New York Times. Метки имеют два компонента: заметное название штата и менее заметное число. Использование этих двух стилей создает шаблон, который помогает зрителю быстро декодировать информацию. Что бросается в глаза? Название штата или номер?<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/17.png\" width=\"1396\" height=\"1086\" alt=\"\" \/>\n<\/div>\n<p>В следующем примере такой системы используется шрифт Merriweather в 22 пункта с высотой строки в 26 пунктов для заголовка. Source Sans Pro используется для остального контента.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/18.png\" width=\"1400\" height=\"702\" alt=\"\" \/>\n<\/div>\n<p><i>Используемые шрифты<\/i>: Merriweather — слегка округлый шрифт с толстым и тонким штрихом средней контрастности, что делает его подходящим для заголовков, но не для мелкого текста. Вес шрифта, использованный выше, самый тяжелый. Source Sans Pro используется для содержания. Он был разработан специально для пользовательских интерфейсов.<br \/>\nСерая шкала текста может использоваться для создания шаблонов (как в приведённом выше фрагменте NYT), для снижения визуальной значимости элемента (как это было во фрагменте Reuter) или для выцветания больших фрагментов менее заметной информации, таких как источники или раздел заметок.<\/p>\n<h2>Система 5: С засечками для чтения \/ Без засечек для маркировки<\/h2>\n<p>В такой системе заголовок, подзаголовок и прочая информация имеет шрифт с засечками. Эта идея основана на давней дискуссии в теории дизайна о том, что шрифты с засечками для длинных отрывков текста, возможно, позволяют быстрее усваивать информацию. Для максимальной читабельности оставьте длину строки не более 60 символов.<\/p>\n<p><i>Пример из жизни<\/i>: New York Times особенно хорошо смешивает шрифт NYT Cheltenham с засечками для чтения и шрифт NYT Franklin без засечек для марикровки данных. Несмотря на то, что это два совершенно разных шрифта, они работаю вместе, потому что буквы имеют дополнительную апертуру, большую X-высоту и одинаковую ширину штриха при соответствующих весах.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/19.png\" width=\"1400\" height=\"1034\" alt=\"\" \/>\n<\/div>\n<p>На следующем графике отображена такая система с использованием шрифта Lora для заголовка и подзаголовка и шрифта Libre Franklin для содержимого маркировки.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/20.png\" width=\"1400\" height=\"743\" alt=\"\" \/>\n<\/div>\n<p><i>Используемые шрифты<\/i>: Lora — шрифт Google, оптимизированный для экрана, но также хорошо подходящий и для печатных проектов. Текст диаграммы — Libre Franklin, это шрифт на основе Franklin. Он относится к группе шрифтов, вдохновленных оригинальным шрифтом Franklin Gothic, созданным примерно в 1910 году.<\/p>\n<p>Указанные в тексте шрифты и системы являются взаимозаменяемыми. Ниже приведён список упомянутых гарнитур:<\/p>\n<ol start=\"1\">\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Lato\">Lato<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Assistant\">Assistant<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Noto+Sans\">Noto Sans<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Source+Sans+Pro\">Source Sans Pro<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Libre+Franklin\">Libre Franklin<\/a><\/li>\n<\/ol>\n<p>Это шрифты, подходящие для заголовков:<\/p>\n<ol start=\"1\">\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/PT+Sans\">PT Sans<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Merriweather\">Merriweather<\/a><\/li>\n<li><a href=\"https:\/\/fonts.google.com\/specimen\/Lora\">Lora<\/a><\/li>\n<\/ol>\n<p>Хороший выбор шрифтов для системы начинается с понимания того, как элементы форм букв обеспечивают высокую читаемость. Я надеюсь, что этот подход поможет вам найти свою систему дизайна шрифтов. Какие ваши любимые шрифты для визуализации данных? Дайте мне знать ниже и спасибо за чтение!<\/p>\n",
            "date_published": "2020-06-26T12:15:51+03:00",
            "date_modified": "2020-06-25T14:31:26+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/02-1.png",
            "_date_published_rfc2822": "Fri, 26 Jun 2020 12:15:51 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "48",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/fotorama\/fotorama.css",
                    "system\/library\/fotorama\/fotorama.js"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/02-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/03-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/04-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/05-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/06.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/07.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/08.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/09.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/10-2.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/11-2.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/12.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/13.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/14.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/15.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/15.5.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/16.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/17.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/18.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/19.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/20.png"
                ]
            }
        },
        {
            "id": "47",
            "url": "http:\/\/test.leftjoin.ru\/all\/waterfall-chart\/",
            "title": "Как построить красивый waterfall chart в Python?",
            "content_html": "<p>Когда-то давно в 2014ом году для одной из презентаций о рынке e-commerce в Юлмарте мы строили широко известную во всем мире консалтинга диаграмму Waterfall, средствами Excel. В этом материале построим Waterfall chart средствами Python — она наглядно демонстрирует изменения с появлением нового положительного или отрицательного фактора. Для построения диаграммы будем использовать <a href=\"https:\/\/plotly.com\">библиотеку plotly<\/a>.<br \/>\nДля тех, кто пропустил: в цикле материалов о визуализации данных на Python мы уже <a href=\"http:\/\/test.leftjoin.ru\/all\/postroenie-diagrammy-gradusnik\/\">пробовали строить диаграмму Градусник<\/a> — она полезна, когда мы хотим сравнить, как соотносятся ожидаемые и реальные данные.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/ecommerce-wf@2x.png\" width=\"592.5\" height=\"440.5\" alt=\"\" \/>\n<\/div>\n<p>В качестве данных используем сведенную в Юлмарте информацию об изменении объёма рынка e-commerce с 2013 по 2014 год. Данные по оси X — подписи к каждому столбцу, по Y — начальные, итоговые значения и их изменения. Функцией sum() посчитаем итог и добавим его в конец списка. Тег  &lt;br&gt; в списке <span class=\"inline-code\">x_list<\/span> означает перенос строки.<\/p>\n<pre class=\"e2-text-code\"><code>import plotly.graph_objects as go\r\n\r\nx_list = ['2013','Макроэкономика РФ','Сокращение численности&lt;br&gt;трудоспобного населения','Проникновение интернета','Развитие трансграничной&lt;br&gt;торговли', 'Федеральные компании', '2014']\r\ny_list = [738.5, 48.7, -7.4, 68.7, 99.7, 48.0]\r\ntotal = round(sum(y_list))\r\ny_list.append(total)<\/code><\/pre><p>Создадим список <span class=\"inline-code\">text_list<\/span> — это те самые значения столбцов. Они берутся из списка <span class=\"inline-code\">y_list<\/span>, но сперва их нужно немного обработать: переведём все числа в строки и если это столбец с изменением, то есть любой столбец, кроме первого и последнего, добавим к строке знак «плюс» для наглядности. А ещё в случае положительного изменения поменяем цвет на зелёный и на красный в обратном случае. Первому и последнему значению прибавим жирности к шрифту тегом &lt;b&gt;.<\/p>\n<pre class=\"e2-text-code\"><code>text_list = []\r\nfor index, item in enumerate(y_list):\r\n    if item &gt; 0 and index != 0 and index != len(y_list) - 1:\r\n        text_list.append(f'+{str(y_list[index])}')\r\n    else:\r\n        text_list.append(str(y_list[index]))\r\nfor index, item in enumerate(text_list):\r\n    if item[0] == '+' and index != 0 and index != len(text_list) - 1:\r\n        text_list[index] = '&lt;span style=&quot;color:#2ca02c&quot;&gt;' + text_list[index] + '&lt;\/span&gt;'\r\n    elif item[0] == '-' and index != 0 and index != len(text_list) - 1:\r\n        text_list[index] = '&lt;span style=&quot;color:#d62728&quot;&gt;' + text_list[index] + '&lt;\/span&gt;'\r\n    if index == 0 or index == len(text_list) - 1:\r\n        text_list[index] = '&lt;b&gt;' + text_list[index] + '&lt;\/b&gt;'<\/code><\/pre><p>Для того, чтобы поместить на фон пунктирные линии, необходимо задать их параметры. Сделаем список словарей и положим в него пунктирные линии светло-серого цвета с положением по Y от 0 до 1000 с шагом в 200.<\/p>\n<pre class=\"e2-text-code\"><code>dict_list = []\r\nfor i in range(0, 1200, 200):\r\n    dict_list.append(dict(\r\n            type=&quot;line&quot;,\r\n            line=dict(\r\n                 color=&quot;#666666&quot;,\r\n                 dash=&quot;dot&quot;\r\n            ),\r\n            x0=-0.5,\r\n            y0=i,\r\n            x1=6,\r\n            y1=i,\r\n            line_width=1,\r\n            layer=&quot;below&quot;))<\/code><\/pre><p>Теперь зададим диаграмму — она лежит в методе <span class=\"inline-code\">Waterfall()<\/span>. У каждого столбца есть тип — <span class=\"inline-code\">total<\/span>, <span class=\"inline-code\">absolute<\/span> или <span class=\"inline-code\">relative<\/span>. Колонки с итоговыми значениями получают тип <span class=\"inline-code\">total<\/span> или <span class=\"inline-code\">absolute<\/span>, с промежуточными — <span class=\"inline-code\">relative<\/span>. Кроме того, задаём цвета: делаем соединяющую линию прозрачной, положительные изменения — зелёными, отрицательные — красными, а итоговые колонки — фиолетовыми. Для текста выберем шрифт Open Sans.<\/p>\n<p class=\"note\">О том, как подобрать хорошие шрифты для своей визуализации данных, можно узнать в материале <a href=\"https:\/\/medium.com\/nightingale\/choosing-a-font-for-your-data-visualization-2ed37afea637\" class=\"nu\">«<u>Choosing Fonts for Your Data Visualization<\/u>»<\/a><\/p>\n<pre class=\"e2-text-code\"><code>fig = go.Figure(go.Waterfall(\r\n    name = &quot;e-commerce&quot;, orientation = &quot;v&quot;,\r\n    measure = [&quot;absolute&quot;, &quot;relative&quot;, &quot;relative&quot;, &quot;relative&quot;, &quot;relative&quot;, &quot;relative&quot;, &quot;total&quot;],\r\n    x = x_list,\r\n    y = y_list,\r\n    text = text_list,\r\n    textposition = &quot;outside&quot;,\r\n    connector = {&quot;line&quot;:{&quot;color&quot;:'rgba(0,0,0,0)'}},\r\n    increasing = {&quot;marker&quot;:{&quot;color&quot;:&quot;#2ca02c&quot;}},\r\n    decreasing = {&quot;marker&quot;:{&quot;color&quot;:&quot;#d62728&quot;}},\r\n    totals={'marker':{&quot;color&quot;:&quot;#9467bd&quot;}},\r\n    textfont={&quot;family&quot;:&quot;Open Sans, light&quot;,\r\n              &quot;color&quot;:&quot;black&quot;\r\n             }\r\n))<\/code><\/pre><p>Наконец, добавим заголовок и описание графика, уберём легенду, подпишем ось Y и внесём пунктирные линии на график.<\/p>\n<pre class=\"e2-text-code\"><code>fig.update_layout(\r\n    title = \r\n        {'text':'&lt;b&gt;Waterfall chart&lt;\/b&gt;&lt;br&gt;&lt;span style=&quot;color:#666666&quot;&gt;Изменение объема рынка e-commerce с 2013 по 2014 год&lt;\/span&gt;'},\r\n    showlegend = False,\r\n    height=650,\r\n    font={\r\n        'family':'Open Sans, light',\r\n        'color':'black',\r\n        'size':14\r\n    },\r\n    plot_bgcolor='rgba(0,0,0,0)',\r\n    yaxis_title=&quot;млрд руб.&quot;,\r\n    shapes=dict_list\r\n)\r\nfig.update_xaxes(tickangle=-45, tickfont=dict(family='Open Sans, light', color='black', size=14))\r\nfig.update_yaxes(tickangle=0, tickfont=dict(family='Open Sans, light', color='black', size=14))\r\n\r\nfig.show()<\/code><\/pre><p>Получим такую диаграмму:<\/p>\n<iframe id=\"igraph\" scrolling=\"no\" style=\"border:none;\" seamless=\"seamless\" src=\"https:\/\/plotly.com\/~Elisejj\/7.embed?showlink=false\" height=\"650\" width=\"100%\"><\/iframe>\n",
            "date_published": "2020-06-24T15:28:04+03:00",
            "date_modified": "2021-04-23T10:34:10+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/ecommerce-wf@2x.png",
            "_date_published_rfc2822": "Wed, 24 Jun 2020 15:28:04 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "47",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/ecommerce-wf@2x.png"
                ]
            }
        },
        {
            "id": "18",
            "url": "http:\/\/test.leftjoin.ru\/all\/postroenie-diagrammy-gradusnik\/",
            "title": "Красивая визуализация в Python. Диаграмма Градусник",
            "content_html": "<p>Очень часто диаграммы, построенные стандартными средствами Matplotlib, выглядят некрасиво и неинформативно. В 2011ом году для целей одного из отчетов телеком-компании в Excel мы построили полезную симпатичную диаграмму «Градусник», рецепт которой стал известен из популярного в тот момент <a href=\"https:\/\/chandoo.org\">блога Chandoo про приемы визуализации в Excel<\/a>.<br \/>\nВот как она выглядела в Excel:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/nu0tNuAMO4A.jpg\" width=\"1296\" height=\"586\" alt=\"\" \/>\n<\/div>\n<p>Времена меняются, и мы попробуем восстановить знание о построении этой полезной диаграммы, используя штатные средства библиотеки <span class=\"inline-code\">matplotlib<span> в Python.<\/p>\n<p><b>Для каких случаев подойдет диаграмма «Градусник»?<\/b><br \/>\nЛучше всего использовать данный тип для сравнения плановых и фактических значений, таким образом наглядно можно увидеть недовыполнение и перевыполнение показателей. При этом план \/ факт может быть как в процентах, так и в фактических значениях. Мы рассмотрим пример с фактическими значениями в условных единицах.<\/p>\n<p>В этот раз возьмем данные из excel-файла. Используем типичный состав библиотек для работы с данными (и соответствующие им типичные alias):<\/p>\n<pre class=\"e2-text-code\"><code>import numpy as np\r\nimport pandas as pd\r\nimport matplotlib.pyplot as plt<\/code><\/pre><p>И считываем в DataFrame таблицу:<\/p>\n<pre class=\"e2-text-code\"><code>df = pd.read_excel('data.xlsx')<\/code><\/pre><p>Посмотрим, как она выглядит:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/0-1.png\" width=\"190\" height=\"302\" alt=\"\" \/>\n<\/div>\n<p>Начнем извлекать из таблицы колонки. Первый столбец «Продажи» будет вертикальной подписью к каждому столбцу на графике. «План» — статичный столбец, относительно которого измеряется «Факт». Некоторые данные могут приходить в виде вещественных чисел — такие столбцы будут считаны как тип <span class=\"inline-code\">str<\/span>, если в данных будет встречена запятая. Чтобы работать с такими числами, будем сначала менять в них запятую на точку, а затем переводить в тип <span class=\"inline-code\">float<\/span>.<\/p>\n<pre class=\"e2-text-code\"><code>xticks = df.iloc[:,0]\r\ntry:\r\n    bars2 = df.iloc[:,1].str.replace(',','.').astype('float')\r\nexcept AttributeError:\r\n    bars2 = df.iloc[:,1].astype('float')\r\ntry:\r\n    bars1 = df.iloc[:,2].str.replace(',','.').astype('float')\r\nexcept AttributeError:\r\n    bars1 = df.iloc[:,2].astype('float')<\/code><\/pre><p>Так как мы не знаем наверняка, будут ли в данных такие числа, можем словить <span class=\"inline-code\">AttributeError<\/span> в случае их отсутствия, ведь будем обращаться к методу <span class=\"inline-code\">str<\/span>, который есть только у строк. Поэтому напишем обработчик исключений <span class=\"inline-code\">try — except<\/span>, который будет на всякий случай переводить данные в тип <span class=\"inline-code\">float<\/span>.<\/p>\n<p>Построим из этого классический barchart — график со столбцами. Зададим массив положения на оси Х для <span class=\"inline-code\">bars1<\/span> функцией <span class=\"inline-code\">np.arange<\/span> и <span class=\"inline-code\">bars2<\/span>, смещённый на ширину столбца:<\/p>\n<pre class=\"e2-text-code\"><code>barWidth = 0.2\r\nr1 = np.arange(len(bars1))\r\nr2 = [x + barWidth for x in r1]\r\n \r\nplt.bar(r1, bars1, width=barWidth)\r\nplt.bar(r2, bars2, width=barWidth)<\/code><\/pre><p>И посмотрим, что получилось:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/1-19.47.23.png\" width=\"1007\" height=\"293\" alt=\"\" \/>\n<\/div>\n<p>Очевидно, это не совсем то, чего мы ожидали. Зададим разную ширину для графиков, ведь один будет наложен на другой. Массив с расположением по оси X тоже теперь возьмём единый, ведь оба столбца будут идти из одних точек.<\/p>\n<pre class=\"e2-text-code\"><code>barWidth1 = 0.065\r\nbarWidth2 = 0.032\r\nx_range = np.arange(len(bars1) \/ 8, step=0.125)<\/code><\/pre><p>А теперь отобразим столбцы на графике, задав им положение, ширину, значения, цвет, легенду и подписи к диаграммам <span class=\"inline-code\">bars2<\/span>:<\/p>\n<pre class=\"e2-text-code\"><code>plt.bar(x_range, bars1, color='#dce6f2', width=barWidth1\/2, edgecolor='#c3d5e8', label='План')\r\nplt.bar(x_range, bars2, color='#ffc001', width=barWidth2\/2, edgecolor='#c3d5e8', label='Факт')\r\nfor i, bar in enumerate(bars2):\r\n    plt.text(i \/ 8 - 0.015, bar + 1, bar, fontsize=14)<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/2-3.png\" width=\"1009\" height=\"294\" alt=\"\" \/>\n<\/div>\n<p>Наконец, сделаем несколько визуальных штрихов — уберём лишние рамки, чёрточки, добавим серую линию под столбцами, поправим размер и шрифт легенде, сделаем диаграмму шире, выведем её на экран и сохраним как plt.png в директории скрипта:<\/p>\n<pre class=\"e2-text-code\"><code>plt.xticks(x_range, xticks)\r\nplt.tick_params(\r\n    bottom=False,\r\n    left=False,\r\n    labelsize=15\r\n)\r\nplt.rcParams['figure.figsize'] = [25, 7]\r\nplt.axhline(y=0, color='gray')\r\nplt.legend(frameon=False, loc='lower center', bbox_to_anchor=(0.25, -0.3, 0.5, 0.5), prop={'size':20})\r\nplt.box(False)\r\nplt.savefig('plt', bbox_inches = &quot;tight&quot;)\r\nplt.show()<\/code><\/pre><p>Получили такую диаграмму:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/plt.png\" width=\"1444\" height=\"499\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2020-05-27T11:00:17+03:00",
            "date_modified": "2020-09-21T13:24:04+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/nu0tNuAMO4A.jpg",
            "_date_published_rfc2822": "Wed, 27 May 2020 11:00:17 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "18",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/nu0tNuAMO4A.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/0-1.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/1-19.47.23.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/2-3.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/plt.png"
                ]
            }
        },
        {
            "id": "35",
            "url": "http:\/\/test.leftjoin.ru\/all\/cohort-analysis-in-redash\/",
            "title": "Когортный анализ в Redash",
            "content_html": "<p>В одной из прошлых заметок мы рассматривали <a href=\"all\/retention-rate\/\">построение Retention-отчета<\/a> и в нем частично затрагивали понятие <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9A%D0%BE%D0%B3%D0%BE%D1%80%D1%82%D0%B0_(%D0%B4%D0%B5%D0%BC%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F)\">когорт<\/a>.<br \/>\nПод когортой обычно подразумевают группу пользователей продукта или компании. Чаще всего группы выделяют на основе времени установки приложения \/ появления пользователя в системе.<br \/>\nВыходит, что используя когортный анализ, можно отследить как повлияли изменения в продукте на поведение пользователей (например, на старых и новых пользователей).<\/p>\n<p>Вместе с этим когорты можно определить исходя и из других параметров: география пользователя, источник трафика, платформа устройства и другие важные параметры вашего продукта.<\/p>\n<p>Мы разберемся с тем, как сравнить Retention пользователей недельных когорт в Redash, поскольку у Redash имеется специальный тип визуализации для построения такого отчета.<br \/>\nОпределимся для начала c SQL-запросом. У нас как и прежде две таблицы — <i>user<\/i> (id пользователя и время установки приложения) и <i>client_session<\/i> — таймстемпы (<i>created_at<\/i>) активности каждого пользователя (<i>user_id<\/i>). Будем считать Retention первых семи дней для недельных когорт за последние 60 дней.<br \/>\nЗапрос написан в Cloudera Impala, рассмотрим его.<\/p>\n<p>Для начала построим общую численность когорт:<\/p>\n<pre class=\"e2-text-code\"><code>select trunc(from_unixtime(user.installed_at), &quot;WW&quot;) AS cohort_week, \r\n\tndv(distinct user.id) as cohort_size \/\/считаем количество пользователей в когорте\r\n\tfrom user \r\n\twhere from_unixtime(user.installed_at) between date_add(now(), -60) and now() \/\/берем зарегистрированных за последние 60 дней\r\ngroup by trunc(from_unixtime(user.installed_at), &quot;WW&quot;)<\/code><\/pre><p>Вторая часть запроса поможет посчитать количество активных пользователей на каждый день в течение первых тридцати:<\/p>\n<pre class=\"e2-text-code\"><code>select trunc(from_unixtime(user.installed_at), &quot;WW&quot;) AS cohort_week, \r\n        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,\r\n\tndv(distinct user.id) as value  \/\/считаем количество активных пользователей на каждый день\r\n\t\tfrom user \r\n\t\tleft join client_session cs on user.id=cs.user_id\r\nwhere from_unixtime(user.installed_at) between date_add(now(), -60) and now()\r\nand from_unixtime(cs.created_at) &gt;= date_add(now(), -60) \/\/берем сессии за последние 60 дней\r\nand datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30 \/\/отрезаем только первые 30 дней активности\r\ngroup by 1,2<\/code><\/pre><p>Итого запрос целиком:<\/p>\n<pre class=\"e2-text-code\"><code>select size.cohort_week, size.cohort_size, ret.days, ret.value from\r\n(select trunc(from_unixtime(user.installed_at), &quot;WW&quot;) AS cohort_week, \r\n\t\tndv(distinct user.id) as cohort_size \r\n\tfrom user \r\n\twhere from_unixtime(user.installed_at) between date_add(now(), -60) and now()\r\ngroup by trunc(from_unixtime(user.installed_at), &quot;WW&quot;)) size\r\nleft join (select trunc(from_unixtime(user.installed_at), &quot;WW&quot;) AS cohort_week, \r\n        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,\r\n\t\tndv(distinct user.id) as value \r\n\t\tfrom user \r\n\t\tleft join client_session cs on user.id=cs.user_id\r\nwhere from_unixtime(user.installed_at) between date_add(now(), -60) and now()\r\nand from_unixtime(cs.created_at) &gt;= date_add(now(), -60)\r\nand datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30\r\ngroup by 1,2) ret on size.cohort_week=ret.cohort_week<\/code><\/pre><p>Отлично, теперь нам доступны правильно посчитанные данные.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/cohort-data@2x.png\" width=\"326\" height=\"180\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Данные когорт в табличном виде<\/div>\n<\/div>\n<p>Создадим новую визуализацию в Redash и правильно укажем параметры:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/cohort-viz@2x.png\" width=\"589\" height=\"471\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Важно правильно указать параметры — им соответствуют колонки результирующего запроса<\/div>\n<\/div>\n<p>Обязательно отметим, что у нас недельные когорты:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/cohort-options@2x.png\" width=\"580\" height=\"187\" alt=\"\" \/>\n<\/div>\n<p>Вуа-ля, наша визуализация когорт готова:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/cohort-graph@2x.png\" width=\"807\" height=\"366\" alt=\"\" \/>\n<div class=\"e2-text-caption\">К ней можно добавить фильтры и параметры и использовать в дашборде<\/div>\n<\/div>\n<p>Материалы по теме<\/p>\n<ul>\n<li><a href=\"https:\/\/gopractice.ru\/cohort_analysis\/\">Когортный анализ. Метрики продукта vs метрики роста<\/a><\/li>\n<li><a href=\"https:\/\/medium.com\/analytics-for-humans\/a-beginners-guide-to-cohort-analysis-the-most-actionable-and-underrated-report-on-google-c0797d826bf4\">A beginners guide to cohort analysis<\/a><\/li>\n<\/ul>\n",
            "date_published": "2020-03-03T14:17:54+03:00",
            "date_modified": "2020-05-10T17:38:53+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/cohort-data.png",
            "_date_published_rfc2822": "Tue, 03 Mar 2020 14:17:54 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "35",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/cohort-data.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/cohort-data@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/cohort-viz@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/cohort-options@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/cohort-graph@2x.png"
                ]
            }
        },
        {
            "id": "21",
            "url": "http:\/\/test.leftjoin.ru\/all\/new-in-redash-v8\/",
            "title": "Нововведения в Redash v8",
            "content_html": "<p>Две недели назад произошел <a href=\"https:\/\/discuss.redash.io\/t\/final-release-of-redash-v8\/4803\">финальный релиз Redash версии 8<\/a>. Полный список усовершенствований представлен на <a href=\"https:\/\/discuss.redash.io\/t\/v8-beta-is-here\/4532\">странице релиза beta версии v8<\/a>.<\/p>\n<p>В своем обзоре новшеств остановлюсь только на тех, которые максимально значимо меняют пользовательский опыт использования Redash, то есть наиболее наглядны для нас с вами:<\/p>\n<ul>\n<li>Support for multi-select in dropdown (and query dropdown) parameters.<\/li>\n<li>Support for dynamic values in date and date-range parameters.<\/li>\n<li>Search dropdown parameter values.<\/li>\n<li>Pivot Table: support hiding totals.<\/li>\n<li>New Visualization: Details View.<\/li>\n<\/ul>\n<h2>Support for multi-select in dropdown (and query dropdown) parameters.<\/h2>\n<p>В 8-ой версии Redash появилась долгожданная поддержка выбора нескольких значений в параметре типа «выпадающий список» или «выпадающий список на основе запроса»:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/dropdown-multiple@2x.png\" width=\"497\" height=\"306\" alt=\"\" \/>\n<\/div>\n<p>При выборе нескольких разных значений, эти значения преобразуются в список, разделенный запятой, при этом есть возможность обернуть значения такого списка в одинарные или двойные кавычки.<\/p>\n<div class=\"e2-text-picture\">\n<div class=\"fotorama\" data-width=\"434\" data-ratio=\"3.8407079646018\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/multiple-quotation@2x.png\" width=\"434\" height=\"113\" alt=\"\" \/>\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/single-double-quotation@2x.png\" width=\"476\" height=\"143\" alt=\"\" \/>\n<\/div>\n<\/div>\n<p>Для нас это означает возможность использования в запросах условия типа IN с параметром:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/select-in@2x.png\" width=\"573\" height=\"215\" alt=\"\" \/>\n<\/div>\n<p>При использовании таких значений параметра, мы получим следующий URL в строке браузера:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/url-parameter@2x.png\" width=\"383\" height=\"33\" alt=\"\" \/>\n<\/div>\n<h2>Support for dynamic values in date and date-range parameters.<\/h2>\n<p>Максимально удобная и полезная фича для выбора дат, которая часто используется в Tableau, но до сих пор не была реализована в Redash: использование динамических значений для дат:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/dynamic-dates@2x.png\" width=\"785\" height=\"414\" alt=\"\" \/>\n<\/div>\n<p>При нажатии на молнию, можно выбрать произвольный период и Redash самостоятельно подставит нужные значения в запрос. Так, например, можно быстро посмотреть статистику за последнюю неделю или последний месяц.<\/p>\n<h2>Search dropdown parameter values.<\/h2>\n<p>Допустим, мы используем параметр типа «выпадающий список на основе запроса», и в выбранном запросе более 300 результирующих строчек, по которым нам надо найти интересующее нас значение. В 8-ой версии эта проблема решена автокомплитом и поиском по значениям параметра. В примере ниже я ввожу строку «<i>Orga<\/i>» и получаю все значения, в которых данная строка встречается, удобно.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/search-autocomplete-v8@2x.png\" width=\"450\" height=\"270\" alt=\"\" \/>\n<\/div>\n<h2>Pivot Table: support hiding totals.<\/h2>\n<p>Достаточно долгожданная фича, позволяющая убрать итоги по строкам \/ столбцам.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/totals@2x.png\" width=\"260\" height=\"286\" alt=\"\" \/>\n<\/div>\n<h2>New Visualization: Details View<\/h2>\n<p>Новое представление результатов данных: просмотр деталей по каждой строке. Предполагаю, что может быть удобно при использовании в дашборде при фильтрации по какому-то конкретному пользователю \/ партнеру.<br \/>\nВизуализирует в табличной форме названия колонок и результаты конкретной строки.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/details-view@2x.png\" width=\"490\" height=\"251\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2019-11-25T16:03:18+03:00",
            "date_modified": "2020-05-12T17:31:47+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/dropdown-multiple@2x.png",
            "_date_published_rfc2822": "Mon, 25 Nov 2019 16:03:18 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "21",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/fotorama\/fotorama.css",
                    "system\/library\/fotorama\/fotorama.js"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/dropdown-multiple@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/multiple-quotation@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/single-double-quotation@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/select-in@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/url-parameter@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/dynamic-dates@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/search-autocomplete-v8@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/totals@2x.png",
                    "http:\/\/test.leftjoin.ru\/pictures\/details-view@2x.png"
                ]
            }
        }
    ],
    "_e2_version": 3365,
    "_e2_ua_string": "E2 (v3365; Aegea)"
}