Въведение: Защо визуализацията на данни има толкова голямо значение
Можете да имате перфектно почистен набор от данни и прецизно настроен модел за машинно обучение, но ако не успеете да покажете резултатите визуално — голяма част от работата ви просто остава невидима. Визуализацията не е просто „красиви графики". Честно казано, тя е може би най-важният инструмент за разбиране на закономерностите в данните, за представяне на резултати и за вземане на информирани решения.
В Python екосистемата две библиотеки доминират пейзажа: Matplotlib (версия 3.10.x към март 2026 г.) и Seaborn (версия 0.13.2). Първата дава пълен контрол върху всеки пиксел, а втората е елегантна надстройка за бърза статистическа визуализация с минимум код.
И така, нека се потопим. В това ръководство ще минем през всичко — от основните типове графики до по-напреднали техники. Ще работим с реални примери и код, който можете директно да копирате и адаптирате.
Инсталация и първоначална настройка
Инсталиране на библиотеките
Ако вече имате Python 3.10 или по-нова версия, инсталацията е елементарна:
pip install matplotlib seaborn pandas numpy
Seaborn автоматично инсталира Matplotlib като зависимост, но аз лично предпочитам да инсталирам двете изрично — така контролирам версиите по-добре.
Основен импорт и конфигурация
Ето стандартната настройка, която ще използваме навсякъде:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# Настройка на Seaborn темата
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
# Настройка на размера на фигурите по подразбиране
plt.rcParams["figure.figsize"] = (10, 6)
plt.rcParams["figure.dpi"] = 100
Функцията sns.set_theme() задава глобален стил за всички графики — включително тези, направени с чист Matplotlib. Параметърът style приема стойности като "whitegrid", "darkgrid", "white", "dark" и "ticks".
Matplotlib: Фундаментът на визуализацията
Анатомия на фигурата в Matplotlib
Преди да скочите в кода, трябва да разберете йерархията на обектите. Това е нещо, което много начинаещи пропускат:
- Figure — цялото „платно", което съдържа всичко
- Axes — отделна графика (една фигура може да съдържа няколко Axes)
- Axis — оста X или Y на конкретна графика
- Artist — всеки визуален елемент (линия, текст, точка и т.н.)
Най-гъвкавият начин за създаване на графики е чрез обектно-ориентирания интерфейс:
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [10, 20, 25, 30])
ax.set_xlabel("Ос X")
ax.set_ylabel("Ос Y")
ax.set_title("Моята първа графика")
plt.show()
Линейни графики
Линейните графики са идеални за показване на тенденции във времето. Ето един конкретен пример с данни за продажби:
# Генериране на примерни данни за продажби
months = np.arange(1, 13)
sales_2025 = [45, 52, 48, 61, 55, 67, 72, 68, 75, 82, 79, 91]
sales_2026 = [50, 58, 53, 68, 62, 74, 80, 76, 83, 90, 87, 98]
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(months, sales_2025, marker="o", linewidth=2, label="2025")
ax.plot(months, sales_2026, marker="s", linewidth=2, label="2026")
ax.set_xlabel("Месец")
ax.set_ylabel("Продажби (хил. лв.)")
ax.set_title("Месечни продажби: 2025 срещу 2026")
ax.set_xticks(months)
ax.set_xticklabels(["Яну", "Фев", "Мар", "Апр", "Май", "Юни",
"Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"])
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Стълбовидни диаграми
Когато трябва да сравните категории, стълбовидните диаграми са естественият избор:
categories = ["Python", "R", "SQL", "Julia", "Scala"]
usage = [77, 42, 65, 8, 12]
fig, ax = plt.subplots(figsize=(8, 5))
bars = ax.bar(categories, usage, color=sns.color_palette("muted", len(categories)))
# Добавяне на стойности върху стълбовете
for bar, val in zip(bars, usage):
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 1,
f"{val}%", ha="center", va="bottom", fontweight="bold")
ax.set_ylabel("Процент на използване (%)")
ax.set_title("Използване на езици за Data Science (2026)")
ax.set_ylim(0, 95)
plt.tight_layout()
plt.show()
Точкови диаграми (Scatter Plots)
Scatter plot-овете показват връзката между две числови променливи. Доста полезни са, когато подозирате корелация:
np.random.seed(42)
x = np.random.normal(50, 15, 200)
y = 2.5 * x + np.random.normal(0, 20, 200)
colors = np.random.choice(["A", "B", "C"], 200)
fig, ax = plt.subplots(figsize=(8, 6))
for label, color in zip(["A", "B", "C"], ["#4C72B0", "#DD8452", "#55A868"]):
mask = colors == label
ax.scatter(x[mask], y[mask], c=color, label=f"Група {label}",
alpha=0.6, edgecolors="white", s=60)
ax.set_xlabel("Променлива X")
ax.set_ylabel("Променлива Y")
ax.set_title("Scatter Plot с групиране")
ax.legend()
plt.tight_layout()
plt.show()
Хистограми
Хистограмите показват разпределението на една числова променлива. Ето пример с бимодално разпределение (две „гърбици"):
np.random.seed(42)
data = np.concatenate([
np.random.normal(50, 10, 500),
np.random.normal(80, 5, 300)
])
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(data, bins=40, color="#4C72B0", edgecolor="white",
alpha=0.7, density=True)
ax.set_xlabel("Стойност")
ax.set_ylabel("Плътност")
ax.set_title("Хистограма на бимодално разпределение")
plt.tight_layout()
plt.show()
Подграфики (Subplots)
Често се налага да показвате няколко графики наведнъж. Matplotlib прави това сравнително лесно:
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# Горе ляво — линейна графика
axes[0, 0].plot(months, sales_2026, marker="o", color="#4C72B0")
axes[0, 0].set_title("Линейна графика")
# Горе дясно — стълбовидна
axes[0, 1].bar(categories, usage, color="#DD8452")
axes[0, 1].set_title("Стълбовидна диаграма")
# Долу ляво — scatter
axes[1, 0].scatter(x[:50], y[:50], color="#55A868", alpha=0.6)
axes[1, 0].set_title("Scatter Plot")
# Долу дясно — хистограма
axes[1, 1].hist(data, bins=30, color="#C44E52", edgecolor="white")
axes[1, 1].set_title("Хистограма")
for ax in axes.flat:
ax.grid(True, alpha=0.3)
plt.suptitle("Четири типа графики с Matplotlib", fontsize=14, fontweight="bold")
plt.tight_layout()
plt.show()
Seaborn: Статистическа визуализация с минимум код
Защо Seaborn е толкова популярен
Seaborn е изграден върху Matplotlib, но предлага три основни предимства: работи директно с Pandas DataFrames, има вградени статистически изчисления и визуално е много по-привлекателен по подразбиране. Вместо ръчно да извличате колони и да ги подавате, просто давате целия DataFrame и задавате имената на колоните. Просто е и бързо.
Графики за разпределения
Нека заредим реален набор от данни и да видим какво можем:
# Зареждане на вграден набор от данни
tips = sns.load_dataset("tips")
# Хистограма с оценка на плътност на ядрото (KDE)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
sns.histplot(data=tips, x="total_bill", kde=True, ax=axes[0],
color="#4C72B0")
axes[0].set_title("Разпределение на сметките")
sns.histplot(data=tips, x="total_bill", hue="time", kde=True,
ax=axes[1], palette="muted")
axes[1].set_title("Разпределение по време на деня")
plt.tight_layout()
plt.show()
Параметърът kde=True добавя крива на KDE (Kernel Density Estimation) върху хистограмата — много удобно за визуализиране формата на разпределението.
Box Plot и Violin Plot
Тези два типа графики са незаменими за сравняване на разпределения между категории. Лично аз обичам да ги показвам едно до друго:
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# Box Plot
sns.boxplot(data=tips, x="day", y="total_bill", hue="sex",
ax=axes[0], palette="Set2")
axes[0].set_title("Box Plot: Сметки по дни и пол")
# Violin Plot
sns.violinplot(data=tips, x="day", y="total_bill", hue="sex",
split=True, ax=axes[1], palette="Set2",
inner="quart")
axes[1].set_title("Violin Plot: Сметки по дни и пол")
plt.tight_layout()
plt.show()
Violin Plot е по-информативен от Box Plot, защото показва цялото разпределение, а не само квартилите. Параметърът split=True позволява директно сравняване на две групи в едно поле — доста елегантно решение.
Корелационна топлинна карта (Heatmap)
Топлинните карти са един от любимите ми инструменти за бързо идентифициране на връзки между променливи:
# Зареждане на набор от данни с повече числови колони
penguins = sns.load_dataset("penguins").dropna()
# Изчисляване на корелационна матрица
corr_matrix = penguins.select_dtypes(include="number").corr()
# Маска за горния триъгълник (за избягване на дублиране)
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
fig, ax = plt.subplots(figsize=(8, 6))
sns.heatmap(corr_matrix, mask=mask, annot=True, fmt=".2f",
cmap="coolwarm", center=0, square=True,
linewidths=1, cbar_kws={"shrink": 0.8},
vmin=-1, vmax=1, ax=ax)
ax.set_title("Корелационна матрица — Palmer Penguins")
plt.tight_layout()
plt.show()
Ключови параметри на sns.heatmap():
annot=True— показва числовите стойности върху клеткитеmask— скрива горния триъгълник (той е огледален)cmap="coolwarm"— цветова схема с различни цветове за положителна и отрицателна корелацияcenter=0— центрира цветовата скала около нулата
Pair Plot за бърз преглед
Когато искате бързо да прегледате връзките между всички числови променливи, sns.pairplot() е незаменим. Буквално един ред код ви дава матрица от графики:
sns.pairplot(penguins, hue="species", palette="muted",
diag_kind="kde",
plot_kws={"alpha": 0.6, "s": 40, "edgecolor": "white"})
plt.suptitle("Pair Plot — Palmer Penguins", y=1.02, fontsize=14)
plt.show()
Pair Plot генерира матрица от scatter plots за всяка двойка числови променливи, а по диагонала — разпределението на всяка от тях. Параметърът hue оцветява точките по категория и веднага разкрива клъстери.
Регресионни графики
Seaborn може автоматично да добавя линия на регресия заедно с доверителен интервал, което е изключително полезно при бърз анализ:
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# Линейна регресия
sns.regplot(data=tips, x="total_bill", y="tip", ax=axes[0],
scatter_kws={"alpha": 0.5}, color="#4C72B0")
axes[0].set_title("Линейна регресия: Сметка vs Бакшиш")
# Регресия по категории
sns.lmplot(data=tips, x="total_bill", y="tip", hue="smoker",
palette="Set1", height=6, aspect=1.2,
scatter_kws={"alpha": 0.5, "s": 40})
plt.title("Регресия по групи: Пушачи vs Непушачи")
plt.show()
Комбиниране на Matplotlib и Seaborn
Финна настройка на Seaborn графики с Matplotlib
Тъй като всяка Seaborn графика е Matplotlib обект отдолу, можете свободно да ги комбинирате. Това е нещо, което аз правя постоянно:
fig, ax = plt.subplots(figsize=(10, 6))
# Seaborn за основната графика
sns.boxplot(data=tips, x="day", y="total_bill", palette="pastel", ax=ax)
# Matplotlib за допълнителни елементи
ax.axhline(y=tips["total_bill"].mean(), color="red", linestyle="--",
linewidth=1.5, label=f"Средна стойност: {tips['total_bill'].mean():.2f}")
ax.legend(fontsize=11)
# Добавяне на анотация
ax.annotate("Най-високи сметки",
xy=(2, 50), xytext=(0.5, 48),
arrowprops=dict(arrowstyle="->", color="gray"),
fontsize=11, color="gray")
ax.set_title("Разпределение на сметките по ден от седмицата",
fontsize=13, fontweight="bold")
ax.set_xlabel("Ден")
ax.set_ylabel("Обща сметка ($)")
plt.tight_layout()
plt.show()
Дашборд с множество графики
А ето и нещо по-сериозно — пълен дашборд, комбиниращ различни типове визуализации. GridSpec на Matplotlib позволява доста гъвкав layout:
fig = plt.figure(figsize=(16, 12))
# Дефиниране на layout с GridSpec
gs = fig.add_gridspec(3, 3, hspace=0.35, wspace=0.3)
# Голяма графика отляво (scatter + регресия)
ax1 = fig.add_subplot(gs[0:2, 0:2])
sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time",
style="smoker", palette="muted", s=60, ax=ax1)
ax1.set_title("Сметка vs Бакшиш", fontsize=13, fontweight="bold")
# Малка графика горе дясно (box plot)
ax2 = fig.add_subplot(gs[0, 2])
sns.boxplot(data=tips, y="tip", x="time", palette="pastel", ax=ax2)
ax2.set_title("Бакшиш по време")
# Малка графика средата дясно (histogram)
ax3 = fig.add_subplot(gs[1, 2])
sns.histplot(data=tips, x="tip", kde=True, color="#55A868", ax=ax3)
ax3.set_title("Разпределение на бакшиша")
# Долен ред — три bar plots
ax4 = fig.add_subplot(gs[2, 0])
sns.countplot(data=tips, x="day", palette="muted", ax=ax4)
ax4.set_title("Брой записи по ден")
ax5 = fig.add_subplot(gs[2, 1])
sns.barplot(data=tips, x="day", y="total_bill", estimator=np.mean,
palette="muted", ax=ax5)
ax5.set_title("Средна сметка по ден")
ax6 = fig.add_subplot(gs[2, 2])
sns.barplot(data=tips, x="day", y="tip", estimator=np.mean,
palette="muted", ax=ax6)
ax6.set_title("Среден бакшиш по ден")
plt.suptitle("Дашборд за анализ на ресторантски данни",
fontsize=16, fontweight="bold", y=1.01)
plt.show()
Практически проект: EDA визуализация стъпка по стъпка
Нека приложим наученото върху реален сценарий — пълен визуален Exploratory Data Analysis на набора от данни „Palmer Penguins". Тук е, където нещата стават наистина интересни.
Стъпка 1: Зареждане и първоначален преглед
penguins = sns.load_dataset("penguins")
print(f"Размер: {penguins.shape}")
print(f"\nЛипсващи стойности:\n{penguins.isnull().sum()}")
print(f"\nОписателна статистика:\n{penguins.describe()}")
# Почистване на липсващите стойности
penguins_clean = penguins.dropna()
Стъпка 2: Разпределение на целевата променлива
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
# Брой по вид
sns.countplot(data=penguins_clean, x="species", palette="muted",
ax=axes[0], order=penguins_clean["species"].value_counts().index)
axes[0].set_title("Брой пингвини по вид")
for container in axes[0].containers:
axes[0].bar_label(container, fontweight="bold")
# По остров
sns.countplot(data=penguins_clean, x="island", hue="species",
palette="muted", ax=axes[1])
axes[1].set_title("Разпределение по остров")
# По пол
sns.countplot(data=penguins_clean, x="sex", hue="species",
palette="muted", ax=axes[2])
axes[2].set_title("Разпределение по пол")
plt.tight_layout()
plt.show()
Стъпка 3: Числови разпределения
numeric_cols = ["bill_length_mm", "bill_depth_mm",
"flipper_length_mm", "body_mass_g"]
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
for ax, col in zip(axes.flat, numeric_cols):
sns.histplot(data=penguins_clean, x=col, hue="species",
kde=True, palette="muted", alpha=0.5, ax=ax)
ax.set_title(f"Разпределение: {col}")
ax.legend(title="Вид")
plt.suptitle("Числови разпределения по вид пингвин",
fontsize=14, fontweight="bold")
plt.tight_layout()
plt.show()
Стъпка 4: Корелации и взаимовръзки
# Pair plot
g = sns.pairplot(penguins_clean, hue="species", palette="muted",
diag_kind="kde",
plot_kws={"alpha": 0.6, "s": 30, "edgecolor": "white"},
corner=True)
g.fig.suptitle("Pair Plot — Всички числови променливи",
y=1.02, fontsize=14)
plt.show()
# Корелационна матрица по видове
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
species_list = penguins_clean["species"].unique()
for ax, species in zip(axes, species_list):
subset = penguins_clean[penguins_clean["species"] == species]
corr = subset[numeric_cols].corr()
sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm",
center=0, vmin=-1, vmax=1, ax=ax, square=True)
ax.set_title(f"Корелации — {species}")
plt.suptitle("Корелационни матрици по вид",
fontsize=14, fontweight="bold")
plt.tight_layout()
plt.show()
Стъпка 5: Ключов инсайт
fig, ax = plt.subplots(figsize=(10, 7))
sns.scatterplot(data=penguins_clean, x="bill_length_mm",
y="bill_depth_mm", hue="species", style="sex",
palette="muted", s=80, alpha=0.7, ax=ax)
ax.set_xlabel("Дължина на клюна (mm)", fontsize=12)
ax.set_ylabel("Дълбочина на клюна (mm)", fontsize=12)
ax.set_title("Парадокс на Симпсън: Дължина vs Дълбочина на клюна",
fontsize=13, fontweight="bold")
ax.legend(title="Вид / Пол", bbox_to_anchor=(1.05, 1), loc="upper left")
plt.tight_layout()
plt.show()
Тази графика илюстрира класически пример за парадокса на Симпсън. Без разделяне по видове, изглежда че дължината и дълбочината на клюна са отрицателно корелирани. Но когато разделим данните по видове — изненада! — виждаме положителна корелация във всяка отделна група. Това е чудесно напомняне винаги да сегментирате данните си.
Matplotlib срещу Seaborn: Кога кое да използвате
Изборът между двете не е „кое е по-добро", а „кое е по-подходящо за конкретния случай".
Използвайте Matplotlib когато:
- Имате нужда от пълен контрол над всеки визуален елемент
- Създавате нестандартни или сложни layout-и с GridSpec
- Работите с 3D визуализации
- Правите анимации с
matplotlib.animation - Подготвяте графики за научни публикации с точни изисквания
Използвайте Seaborn когато:
- Работите с Pandas DataFrames и искате бърза визуализация
- Имате нужда от статистически графики (box plot, violin plot, pair plot)
- Правите Exploratory Data Analysis (EDA)
- Искате привлекателни графики с минимум код
- Работите с категориални данни
Комбинирайте двете когато:
- Започвате с Seaborn за бърз прототип и после финно настройвате с Matplotlib
- Създавате дашборд с различни типове графики
- Искате Seaborn естетика с Matplotlib анотации и допълнителни елементи
Често допускани грешки при визуализация
1. Прекалено много информация в една графика
Добавянето на твърде много серии, цветове и анотации прави графиката нечетима. Имам едно просто правило: ако не можете да обясните графиката за 10 секунди, тя е прекалено сложна. Разделете я на няколко по-прости.
2. Неподходящ тип графика
Кръговите диаграми (pie charts) рядко са правилният избор. Човешкото око просто не е добро в сравняването на ъгли. Предпочитайте стълбовидни диаграми за сравнение на категории — много по-лесно се четат.
3. Липсващи надписи и заглавия
Всяка графика трябва да има ясно заглавие, надписи на осите и легенда (ако показвате повече от една серия). Без тях графиката губи целия си контекст.
4. Неподходяща цветова палитра
Избягвайте палитри с твърде близки цветове. И помислете за достъпност — около 8% от мъжете имат далтонизъм. Seaborn предлага специална палитра за целта:
sns.set_palette("colorblind")
5. Липса на plt.tight_layout()
Без plt.tight_layout() елементите често се припокриват. Винаги слагайте този ред преди plt.show(). Сериозно, спасява толкова главоболия.
Запазване на графики за публикация
Когато сте готови да експортирате графиките си:
# PNG с висока резолюция
fig.savefig("grafika.png", dpi=300, bbox_inches="tight",
facecolor="white", edgecolor="none")
# SVG за уеб
fig.savefig("grafika.svg", format="svg", bbox_inches="tight")
# PDF за печат
fig.savefig("grafika.pdf", format="pdf", bbox_inches="tight")
Параметърът bbox_inches="tight" гарантира, че нищо не се отрязва. А dpi=300 е стандартът за графики, предназначени за печат или презентации.
Често задавани въпроси
Каква е разликата между Matplotlib и Seaborn?
Matplotlib е нисконивова библиотека с пълен контрол върху всеки елемент, но изисква повече код. Seaborn е високонивова надстройка, която опростява статистическите графики и изглежда по-добре по подразбиране. Тъй като всяка Seaborn графика е Matplotlib обект отдолу, двете се комбинират безпроблемно.
Мога ли да използвам Seaborn без Matplotlib?
Не точно — Seaborn зависи от Matplotlib за рендериране на графиките. Инсталира се автоматично като зависимост. За прости визуализации обаче можете да работите предимно със Seaborn функции и да използвате Matplotlib само за plt.show() и plt.savefig().
Кой инструмент е по-добър за начинаещи?
Seaborn определено. Изисква по-малко код и резултатите изглеждат добре от самото начало. Но всъщност си заслужава да научите и основите на Matplotlib — това ви дава разбиране какво се случва „под капака" и ви позволява да правите финни настройки, когато Seaborn не стига.
Как да избера правилния тип графика за моите данни?
Зависи от типа данни и целта: за тенденции във времето — линейни графики; за сравнение на категории — стълбовидни диаграми; за връзки между две числови променливи — scatter plot; за разпределения — хистограми или violin plot; за корелации между множество променливи — heatmap.
Как да направя графиките достъпни за хора с далтонизъм?
Използвайте палитрата "colorblind" в Seaborn (sns.set_palette("colorblind")), избягвайте комбинации от червено и зелено, добавяйте различни маркери и стилове линии (не разчитайте само на цвета) и включвайте текстови анотации за ключовите стойности.