Вопрос или проблема
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. Этот метод позволяет легко стилизовать график, добавлять значения и управлять отображаемыми данными. Вы можете также изменить цвета, форму и формат отображаемых значений, чтобы настроить график по своему усмотрению.