Как построить сегментированную столбчатую диаграмму (насыщенный столбчатый график) с помощью Python?

Вопрос или проблема

cat = {'A':1, 'B':2, 'C':3}
dog = {'A':2, 'B':2, 'C':4}
owl = {'A':3, 'B':3, 'C':3}

Предположим, у меня есть 3 словаря, каждый из которых содержит пары (подкатегория, количество). Как я могу построить сегментированную гистограмму (т.е. накопительную гистограмму) с использованием Python, где x представляет 3 категории (кот, собака, сова), а y — это доля (каждой подкатегории)? То, что я имею в виду, выглядит так:

пример

Используя Matplotlib:

import numpy as np
import matplotlib.pyplot as plt

def plot_stacked_bar(data, series_labels, category_labels=None, 
                     show_values=False, value_format="{}", y_label=None, 
                     colors=None, grid=False, reverse=False, 
                     savefig=False, save_figname="Stacked_Bar.png"):
    
    """
    Строит накопительную гистограмму с предоставленными данными и подписями.

    Ключевые аргументы:
    data            -- 2-мерный массив numpy или вложенный список,
                       содержащий данные для каждой серии в строках
    series_labels   -- список меток серии (они отображаются в 
                       легенде)
    category_labels -- список меток категорий (они отображаются 
                       на оси x)
    show_values     -- Если True, то числовые метки значений будут 
                       показаны на каждой гистограмме
    value_format    -- Формат строки для числовых меток значений
                       (по умолчанию "{}")
    y_label         -- Подпись для оси y (str)
    colors          -- Список меток цветов
    grid            -- Если True, отображать сетку
    reverse         -- Если True, менять порядок отображения
                       серий (слева направо или справа налево)
    savefig         -- Если True, экспортировать график в png
    save_figname    -- Имя файла для сохранения изображения
    """

    ny = len(data[0])
    ind = list(range(ny))

    axes = []
    cum_size = np.zeros(ny)

    data = np.array(data)

    if reverse:
        data = np.flip(data, axis=1)
        category_labels = reversed(category_labels)

    for i, row_data in enumerate(data):
        color = colors[i] if colors is not None else None
        axes.append(plt.bar(ind, row_data, bottom=cum_size, 
                            label=series_labels[i], color=color))
        cum_size += row_data

    if category_labels:
        plt.xticks(ind, category_labels)

    if y_label:
        plt.ylabel(y_label)

    plt.legend()

    if grid:
        plt.grid()

    if show_values:
        for axis in axes:
            for bar in axis:
                w, h = bar.get_width(), bar.get_height()
                plt.text(bar.get_x() + w/2, bar.get_y() + h/2, 
                         value_format.format(h), ha="center", 
                         va="center")
    
    if savefig:
        plt.savefig(save_figname)

Подготовка данных

cat = {'A':1, 'B':2, 'C':3}
dog = {'A':2, 'B':2, 'C':4}
owl = {'A':3, 'B':3, 'C':3}

from collections import defaultdict

dd = defaultdict(list)

# вы можете перечислить столько входных словарей, сколько хотите
for d in (cat, dog, owl): 
    for key, value in d.items():
        dd[key].append(value)

# print(list(dd.values()))

data = list(dd.values())
series_labels  = list(dd.keys())

category_labels = ["Кот", "Собака", "Сова"]

Построение графика

plt.figure(figsize=(5, 8))

plot_stacked_bar(
    data, 
    series_labels, 
    category_labels=category_labels, 
    show_values=True, 
    value_format="{:.1f}",
    colors=['tab:blue', 'tab:orange', 'tab:green'],
    y_label="Количество",
    savefig=True
)
plt.show()

Рисунок

введите описание изображения здесь

Ответ или решение

Чтобы построить сегментированную столбчатую диаграмму (или stacked bar chart) в Python, можно воспользоваться библиотекой Matplotlib. В данной инструкции мы рассмотрим, как создать такую визуализацию, используя три словаря, содержащие данные по подкатегориям и количествам.

Шаг 1: Установка необходимых библиотек

Если у вас ещё не установлена библиотека Matplotlib, вы можете установить её с помощью pip:

pip install matplotlib

Шаг 2: Подготовка данных

Начнём с создания трёх словарей с данными для каждого животного:

cat = {'A': 1, 'B': 2, 'C': 3}
dog = {'A': 2, 'B': 2, 'C': 4}
owl = {'A': 3, 'B': 3, 'C': 3}

Теперь объединим данные из этих словарей в удобном формате для построения графика:

from collections import defaultdict

# Используем defaultdict для группировки данных
dd = defaultdict(list)

# Обходим каждый словарь и добавляем данные в общую структуру
for d in (cat, dog, owl):
    for key, value in d.items():
        dd[key].append(value)

# Преобразуем собранные данные в список
data = list(dd.values())
series_labels = list(dd.keys())
category_labels = ["Cat", "Dog", "Owl"]

Шаг 3: Определение функции для построения сегментированной столбчатой диаграммы

Теперь создадим функцию для построения графика:

import numpy as np
import matplotlib.pyplot as plt

def plot_stacked_bar(data, series_labels, category_labels=None, 
                     show_values=False, value_format="{}", y_label=None, 
                     colors=None, grid=False, reverse=False, 
                     savefig=False, save_figname="Stacked_Bar.png"):

    ny = len(data[0])  # Количество категорий
    ind = list(range(ny))  # Индексы для оси x

    axes = []
    cum_size = np.zeros(ny)  # Начальное значение для накопления высоты столбцов

    data = np.array(data)  # Преобразуем данные в numpy массив

    if reverse:
        data = np.flip(data, axis=1)
        category_labels = reversed(category_labels)

    for i, row_data in enumerate(data):
        color = colors[i] if colors is not None else None
        axes.append(plt.bar(ind, row_data, bottom=cum_size, 
                            label=series_labels[i], color=color))
        cum_size += row_data  # Обновляем накопленное значение

    if category_labels:
        plt.xticks(ind, category_labels)  # Подписываем метки по оси x

    if y_label:
        plt.ylabel(y_label)

    plt.legend()  # Показываем легенду

    if grid:
        plt.grid()

    if show_values:
        for axis in axes:
            for bar in axis:
                w, h = bar.get_width(), bar.get_height()
                plt.text(bar.get_x() + w/2, bar.get_y() + h/2, 
                         value_format.format(h), ha="center", 
                         va="center")

    if savefig:
        plt.savefig(save_figname)  # Сохраняем график

Шаг 4: Построение графика

Теперь мы можем вызвать нашу функцию для построения графика:

plt.figure(figsize=(5, 8))  # Указываем размер графика

plot_stacked_bar(
    data, 
    series_labels, 
    category_labels=category_labels, 
    show_values=True, 
    value_format="{:.1f}",  # Формат отображаемых значений
    colors=['tab:blue', 'tab:orange', 'tab:green'],  # Цвета для баров
    y_label="Count",  # Подпись для оси y
    savefig=True  # Опционально сохраняем график
)

plt.show()  # Показываем график

Заключение

Таким образом, вы создали сегментированную столбчатую диаграмму с использованием библиотеки Matplotlib в Python. Этот метод позволяет легко стилизовать график, добавлять значения и управлять отображаемыми данными. Вы можете также изменить цвета, форму и формат отображаемых значений, чтобы настроить график по своему усмотрению.

Оцените материал
Добавить комментарий

Капча загружается...