{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: matplotlib",
    "home_page_url": "http:\/\/test.leftjoin.ru\/tags\/matplotlib\/",
    "feed_url": "http:\/\/test.leftjoin.ru\/tags\/matplotlib\/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": "118",
            "url": "http:\/\/test.leftjoin.ru\/all\/mean-vs-median\/",
            "title": "Различия между медианой и средним арифметическим как целевым показателем анализа данных",
            "content_html": "<p>В сегодняшней статье мы бы хотели осветить простую, но в то же время важную тему выбора простой метрики для оценки того или иного датасета. Со средним арифметическим все давным давно знакомы, чуть ли не каждый школьник отлично знает, что нужно просуммировать все имеющиеся значения, поделить на их количество и получить среднее значение. В школьные знания не входят никакие альтернативные варианты, которых, на самом деле, в статистике много — на любой вкус и случай. Однако, в решении исследовательских и маркетинговых задач люди часто берут именно эту метрику за основу. Правомерно ли это или есть более удачный вариант? Давайте разбираться.<\/p>\n<p>Для начала стоит вспомнить определения двух метрик, о которых мы сегодня поговорим.<br \/>\nСреднее  — самый популярный статистический показатель, который используется для измерения центра данных. А что же такое медиана? Медиана — значение, которое разбивает данные, отсортированные по порядку увеличения значений, на две равные части. Это значит, что медиана показывает центральное значение в выборке, если наблюдений нечетное количество и среднее арифметическое двух значений, если количество наблюдений в выборке четно.<\/p>\n<h2>Исследовательские задачи<\/h2>\n<p>Итак, оценка среднего значения выборки — зачастую важна во многих исследовательских вопросах. Например, специалисты, изучающие демографию часто задаются вопросом изменения численности регионов России, чтобы проследить за динамикой и отразить это в отчетностях. Давайте попробуем рассчитать среднюю численность региона России, а также медиану, а затем сравним полученные результаты.<br \/>\nДля начала, нужно найти и загрузить данные, подключив для этого библиотеку pandas.<\/p>\n<pre class=\"e2-text-code\"><code>import pandas as pd\r\ncity = pd.read_csv('city.csv')<\/code><\/pre><p>Затем, нужно посчитать среднее и медиану выборки.<\/p>\n<pre class=\"e2-text-code\"><code>mean_pop = round(city.population_2020.mean(), 0)\r\nmedian_pop = round(city.population_2020.median(), 0)<\/code><\/pre><p>Значения, естественно, получились разными, так как распределение наблюдений в выборке отлично от нормального. Для того, чтобы понять, сильно ли они отличаются, построим график распределения и отметим среднее и медиану.<\/p>\n<pre class=\"e2-text-code\"><code>import matplotlib.pyplot as plt\r\nimport seaborn as sns\r\n\r\nsns.set_palette('rainbow')\r\nfig = plt.figure(figsize = (20, 15))\r\nax = fig.add_subplot(1, 1, 1)\r\ng = sns.histplot(data = city, x= 'population_2020', alpha=0.6, bins = 100, ax=ax)\r\n\r\ng.axvline(mean_pop, linewidth=2, color='r', alpha=0.9, linestyle='--', label = 'Среднее = {:,.0f}'.format(mean_pop).replace(',', ' '))\r\ng.axvline(median_pop, linewidth=2, color='darkgreen', alpha=0.9, linestyle='--', label = 'Медиана = {:,.0f}'.format(median_pop).replace(',', ' '))\r\n\r\nplt.ticklabel_format(axis='x', style='plain')\r\nplt.xlabel(&quot;Численность населения&quot;, fontsize=25)\r\nplt.ylabel(&quot;Количество городов&quot;, fontsize=25)\r\nplt.title(&quot;Распределение численности населения российских городов&quot;, fontsize=25)\r\nplt.legend(fontsize=&quot;xx-large&quot;)\r\nplt.show()<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/avg_p2.jpg\" width=\"1440\" height=\"1080\" alt=\"\" \/>\n<\/div>\n<p>Также, на этих данных стоит построить боксплот для более точной визуализации основных квантилей распределения, медианы, среднего и выбросов.<\/p>\n<pre class=\"e2-text-code\"><code>fig = plt.figure(figsize = (10, 10))\r\nsns.set_theme(style=&quot;whitegrid&quot;)\r\nsns.set_palette(palette=&quot;pastel&quot;)\r\n\r\nsns.boxplot(y = city['population_2020'], showfliers = False)\r\n\r\nplt.scatter(0, 550100, marker='*', s=100, color = 'black', label = 'Выбросы')\r\nplt.scatter(0, 560200, marker='*', s=100, color = 'black')\r\nplt.scatter(0, 570300, marker='*', s=100, color = 'black')\r\nplt.scatter(0, mean_pop, marker='o', s=100, color = 'red', edgecolors = 'black', label = 'Среднее')\r\nplt.legend()\r\n\r\nplt.ylabel(&quot;Численность населения&quot;, fontsize=15)\r\nplt.ticklabel_format(axis='y', style='plain')\r\nplt.title(&quot;Боксплот численности населения&quot;, fontsize=15)\r\nplt.show()<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/bp_city.jpg\" width=\"720\" height=\"720\" alt=\"\" \/>\n<\/div>\n<p>Из графиков следует, что медиана существенно меньше среднего, а также, ясно, что это следствие наличия больших выбросов — Москвы и Санкт-Петербурга. Поскольку среднее арифметическое — метрика крайне чувствительная к выбросам — при их наличии в выборке опираться на выводы относительно среднего не стоит. Рост или снижение численности населения Москвы может сильно смещать среднюю численность по России, однако это не будет влиять на настоящий общерегиональный тренд.<br \/>\nИспользуя среднее арифметическое мы скажем, что численность типичного (среднего) города в РФ — 268 тысяч человек. Однако, это вводит нас в заблуждение, так как среднее значительно превышает медиану исключительно из-за численности населения Москвы и Санкт-Петербурга. На самом деле, численность типичного российского города существенно меньше (аж в 2 раза!) и составляет 104 тысячи жителей.<\/p>\n<h2>Маркетинговые задачи<\/h2>\n<p>В контексте бизнеса разница между средним арифметическим и медианой также важна, так как использование неверной метрики может серьезно сказаться на результатах проведения акции или затруднить достижение цели. Давайте посмотрим на реальном примере, с какими трудностями может столкнуться предприниматель в ритейле, если неверно выберет целевую метрику.<br \/>\nДля начала, как и в предыдущем примере, загрузим датасет о покупках в супермаркете. Выберем необходимые для анализа столбцы датасета и переименуем их, для упрощения кода в дальнейшем. Поскольку эти данные не так хорошо подготовлены, как предыдущие, необходимо сгруппировать все купленные товары по чекам. В этом случае необходима группировка по двум переменным: по id покупателя и по дате покупки (дата и время определяется моментом закрытия чека, поэтому все покупки в рамках одного чека совпадают по дате). Затем, назовем полученный столбец «total_bill», то есть сумма чека и посчитаем среднее и медиану.<\/p>\n<pre class=\"e2-text-code\"><code>df = pd.read_excel('invoice_data.xlsx')\r\ndf_nes = df[['Номер КПП', 'Сумма', 'Дата продажи']]\r\ndf_nes.columns = ['user','total_price', 'date']\r\ngroupped_df = pd.DataFrame(df_nes.groupby(['user', 'date']).total_price.sum())\r\ngroupped_df.columns = ['total_bill']\r\nmean_bill = groupped_df.total_bill.mean()\r\nmedian_bill = groupped_df.total_bill.median()<\/code><\/pre><p>Теперь, как и в предыдущем примере нужно построить график распределения чеков покупателей и боксплот, а также отметить медиану и среднее арифметическое на каждом из них.<\/p>\n<pre class=\"e2-text-code\"><code>sns.set_palette('rainbow')\r\nfig = plt.figure(figsize = (20, 15))\r\nax = fig.add_subplot(1, 1, 1)\r\nsns.histplot(groupped_df, x = 'total_bill', binwidth=200, alpha=0.6, ax=ax)\r\nplt.xlabel(&quot;Покупки&quot;, fontsize=25)\r\nplt.ylabel(&quot;Суммы чеков&quot;, fontsize=25)\r\nplt.title(&quot;Распределение суммы чеков&quot;, fontsize=25)\r\nplt.axvline(mean_bill, linewidth=2, color='r', alpha=1, linestyle='--', label = 'Среднее = {:.0f}'.format(mean_bill))\r\nplt.axvline(median_bill, linewidth=2, color='darkgreen', alpha=1, linestyle='--', label = 'Медиана = {:.0f}'.format(median_bill))\r\nplt.legend(fontsize=&quot;xx-large&quot;)\r\nplt.show()<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/avg_invoice_33.jpg\" width=\"1440\" height=\"1080\" alt=\"\" \/>\n<\/div>\n<pre class=\"e2-text-code\"><code>fig = plt.figure(figsize = (10, 10))\r\nsns.set_theme(style=&quot;whitegrid&quot;)\r\nsns.set_palette(palette=&quot;pastel&quot;)\r\n\r\nsns.boxplot(y = groupped_df['total_bill'], showfliers = False)\r\n\r\nplt.scatter(0, 1800, marker='*', s=100, color = 'black', label = 'Выбросы')\r\nplt.scatter(0, 1850, marker='*', s=100, color = 'black')\r\nplt.scatter(0, 1900, marker='*', s=100, color = 'black')\r\nplt.scatter(0, mean_bill, marker='o', s=100, color = 'red', edgecolors = 'black', label = 'Среднее')\r\nplt.legend()\r\n\r\nplt.ticklabel_format(axis='y', style='plain')\r\nplt.ylabel(&quot;Сумма чека&quot;, fontsize=15)\r\nplt.title(&quot;Боксплот суммы чеков&quot;, fontsize=15)\r\nplt.show()<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/bp_invoice.jpg\" width=\"720\" height=\"720\" alt=\"\" \/>\n<\/div>\n<p>Из графиков следует, что распределение смещено к началу координат (отличное от нормального), а значит медиана и среднее не равны. Медианное значение меньше среднего примерно на 220 рублей.<br \/>\nТеперь представим, что у маркетологов есть задача повысить средний чек покупателя. Маркетолог может решить, что поскольку средний чек равен 601 рублю, то можно предложить следующую акцию: «Всем покупателям, кто совершит покупку на 600 рублей, мы предоставляем скидку 20% на товар за 100 рублей». В целом, резонное предложение, однако, в реальности, средний чек ниже — 378 рублей. То есть большая часть покупателей не заинтересуется в предложении, поскольку их покупка обычно не достигает предложенного порога. Это значит. что они не воспользуются предложением и не получат скидку, а компания не сможет достичь поставленной цели и увеличить прибыль супермаркета. Все дело в том, что исходные предпосылки были ошибочны.<\/p>\n<h2>Выводы<\/h2>\n<p>Как вы уже поняли, среднее арифметическое зачастую показывает более значимый и приятный результат, как для бизнеса, так и для исследовательских задач, ведь руководству всегда выгоднее представить ситуацию со средним чеком или демографической ситуацией в стране лучше, чем она есть на самом деле. Однако, необходимо всегда помнить о недостатках такой метрики, как среднее арифметическое, чтобы уметь грамотно выбрать подходящий аналог для оценки той или иной ситуации.<\/p>\n",
            "date_published": "2021-09-16T21:20:47+03:00",
            "date_modified": "2021-10-27T14:06:01+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/avg_p2.jpg",
            "_date_published_rfc2822": "Thu, 16 Sep 2021 21:20:47 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "118",
            "_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\/avg_p2.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/bp_city.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/avg_invoice_33.jpg",
                    "http:\/\/test.leftjoin.ru\/pictures\/bp_invoice.jpg"
                ]
            }
        },
        {
            "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"
                ]
            }
        }
    ],
    "_e2_version": 3365,
    "_e2_ua_string": "E2 (v3365; Aegea)"
}