Каждый, кто хоть раз работал с Matplotlib, знает, насколько неэстетичными могут быть его графики по умолчанию. Давайте изучим несколько хитростей, которые помогут вашим визуализациям выделяться на фоне стандартных.
import pandas as pd
import numpy as np
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib import rcParams
from matplotlib.path import Path
from matplotlib.patches import PathPatch
np.random.seed(38) # Для воспроизводимости результатов
dates = pd.date_range(start='2024-02-01', periods=100, freq='D')
initial_rate = 75
drift = 0.003
volatility = 0.1
returns = np.random.normal(drift, volatility, len(dates))
rates = initial_rate * np.cumprod(1 + returns)
x, y = dates, rates
fig, ax = plt.subplots()
ax.plot(x, y)
ax.xaxis.set_major_locator(mdates.DayLocator(interval=30))
plt.show()
fig, ax = plt.subplots(figsize=(10, 6)) # Устанавливаем размер графика
plt.title("Ежедневное количество посетителей", fontsize=18, color="black") # Добавляем заголовок
rcParams['font.family'] = 'DejaVu Sans' # Меняем шрифт
rcParams['font.size'] = 14 # Меняем размер шрифтв
ax.yaxis.tick_right() # Переносим метки оси Y на правую сторону
ax.yaxis.set_label_position("right") # Перемещаем подпись оси Y вправо
ax.plot(x, y, color='#268358', linewidth=2) # Изменяем цвет, стиль и толщину линии
x_interval = 30
ax.xaxis.set_major_locator(mdates.DayLocator(interval=x_interval))
plt.show()
ax.grid(color="gray", linestyle=(0, (10, 10)), linewidth=0.5, alpha=0.6) # Минималистичная сетка
ax.tick_params(axis="x", colors="black") # Установка цвета меток оси X
ax.tick_params(axis="y", left=False, labelleft=False) # Скрытие делений и меток слева
ax.spines["top"].set_visible(False) # Убираем верхнюю границу
ax.spines["right"].set_visible(False) # Убираем правую границу
ax.spines["bottom"].set_color("black") # Цвет нижней границы
ax.spines["left"].set_color("white") # Скрываем левую границу
ax.spines["left"].set_linewidth(1) # Толщина левой границы
ax.tick_params(axis="y", length=0) # Убираем деления с оси Y
def custom_date_formatter(val_num, pos, dates_ref, interval):
"""
Функция для форматирования меток оси X.
Получает числовое представление даты 'val_num' и позицию метки 'pos'.
Если это первая метка (pos=0), добавляем год, иначе только дату.
"""
try:
date = mdates.num2date(val_num) # Преобразуем число в дату
# Проверяем, первая ли это метка по индексу pos
if pos == 0:
return date.strftime('%d %b \'%y') # Формат: "01 Feb '24"
else:
return date.strftime('%d %b') # Формат: "01 Feb"
except (ValueError, IndexError):
# В случае ошибки вернуть пустую строку
return ""
# Применяем пользовательский форматтер к оси X
ax.xaxis.set_major_formatter(ticker.FuncFormatter(
lambda val, pos: custom_date_formatter(val, pos, dates_ref=x, interval=x_interval)
))
# Преобразуем даты в числовой формат Matplotlib для Path и extent
x_num = mdates.date2num(x)
# Создаем координаты для Path, используя числовое представление
# Добавляем точки на оси X по краям для замыкания полигона
x_num_patch = np.concatenate(([x_num[0]], x_num, [x_num[-1]]))
# Добавляем нули по краям для Y, чтобы замкнуть полигон по оси X
y_patch = np.concatenate(([0], y, [0]))
# Создаем путь (Path) на основе числовых координат дат и значений Y
path_coords = np.array([x_num_patch, y_patch]).transpose()
path = Path(path_coords)
# Создаем PathPatch - невидимую область (facecolor='none'), которая будет маской для обрезки
patch = PathPatch(path, facecolor='none', lw=0)
ax.add_patch(patch) # Добавляем патч на оси
# Создаем градиентное изображение (здесь просто вертикальный градиент)
gradient_img = np.linspace(0, 1, 100).reshape(-1, 1)
# Отображаем градиентное изображение с помощью imshow
im = ax.imshow(
gradient_img,
interpolation="bicubic", # Сглаживание градиента
cmap=plt.cm.Greens, # Зеленая цветовая карта
origin='lower', # Начало координат внизу
alpha=0.3, # Прозрачность
aspect="auto", # Автоматическое соотношение сторон
# extent задает границы изображения в координатах данных (числовые даты!)
extent=[
x_num[0], # min x (первая дата в числовом формате)
x_num[-1], # max x (последняя дата в числовом формате)
min(y_patch), # min y (0)
max(y) * 1.2 # max y (немного выше максимума данных)
],
clip_path=patch, # Обрезать изображение по этому пути (патчу)
clip_on=True, # Включить обрезку
zorder=1 # Слой градиента (ниже линии, выше сетки)
)
# Рисуем линию поверх градиента (zorder=2 по умолчанию)
ax.plot(x, y, color='#268358', linewidth=2, zorder=2)
# Убедимся, что пределы оси X соответствуют диапазону наших дат
ax.set_xlim(x[0], x[-1])
fig, ax = plt.subplots(figsize=(10, 6)) # Устанавливаем размер графика
plt.title("Ежедневное количество посетителей", fontsize=18, color="black") # Добавляем заголовок
rcParams['font.family'] = 'DejaVu Sans' # Меняем шрифт
rcParams['font.size'] = 14 # Меняем размер шрифтв
ax.yaxis.tick_right() # Переносим метки оси Y на правую сторону
ax.yaxis.set_label_position("right") # Перемещаем подпись оси Y вправо
ax.plot(x, y, color='#268358', linewidth=2) # Изменяем цвет, стиль и толщину линии
# Определяем интервал для меток оси X (в днях)
x_interval = 30
# Устанавливаем локатор оси X (где будут стоять метки)
ax.xaxis.set_major_locator(mdates.DayLocator(interval=x_interval))
ax.grid(color="gray", linestyle=(0, (10, 10)), linewidth=0.5, alpha=0.6) # Минималистичная сетка
ax.tick_params(axis="x", colors="black") # Установка цвета меток оси X
ax.tick_params(axis="y", left=False, labelleft=False) # Скрытие делений и меток слева
ax.spines["top"].set_visible(False) # Убираем верхнюю границу
ax.spines["right"].set_visible(False) # Убираем правую границу
ax.spines["bottom"].set_color("black") # Цвет нижней границы
ax.spines["left"].set_color("white") # Скрываем левую границу
ax.spines["left"].set_linewidth(1) # Толщина левой границы
ax.tick_params(axis="y", length=0) # Убираем деления с оси Y
def custom_date_formatter(val_num, pos, dates_ref, interval):
"""
Функция для форматирования меток оси X.
Получает числовое представление даты 'val_num' и позицию метки 'pos'.
Если это первая метка (pos=0), добавляем год, иначе только дату.
"""
try:
date = mdates.num2date(val_num) # Преобразуем число в дату
# Проверяем, первая ли это метка по индексу pos
if pos == 0:
return date.strftime('%d %b \'%y') # Формат: "01 Feb '24"
else:
return date.strftime('%d %b') # Формат: "01 Feb"
except (ValueError, IndexError):
# В случае ошибки вернуть пустую строку
return ""
# Применяем пользовательский форматтер к оси X
ax.xaxis.set_major_formatter(ticker.FuncFormatter(
lambda val, pos: custom_date_formatter(val, pos, dates_ref=x, interval=x_interval)
))
# Преобразуем даты в числовой формат Matplotlib для Path и extent
x_num = mdates.date2num(x)
# Создаем координаты для Path, используя числовое представление дат
# Добавляем точки на оси X по краям для замыкания полигона
x_num_patch = np.concatenate(([x_num[0]], x_num, [x_num[-1]]))
# Добавляем нули по краям для Y, чтобы замкнуть полигон по оси X
y_patch = np.concatenate(([0], y, [0]))
# Создаем путь (Path) на основе числовых координат дат и значений Y
path_coords = np.array([x_num_patch, y_patch]).transpose()
path = Path(path_coords)
# Создаем PathPatch - невидимую область (facecolor='none'), которая будет маской для обрезки
patch = PathPatch(path, facecolor='none', lw=0)
ax.add_patch(patch) # Добавляем патч на оси
# Создаем градиентное изображение (здесь просто вертикальный градиент)
gradient_img = np.linspace(0, 1, 100).reshape(-1, 1)
# Отображаем градиентное изображение с помощью imshow
im = ax.imshow(
gradient_img,
interpolation="bicubic", # Сглаживание градиента
cmap=plt.cm.Greens, # Зеленая цветовая карта
origin='lower', # Начало координат внизу
alpha=0.3, # Прозрачность
aspect="auto", # Автоматическое соотношение сторон
# extent задает границы изображения в координатах данных (числовые даты!)
extent=[
x_num[0], # min x (первая дата в числовом формате)
x_num[-1], # max x (последняя дата в числовом формате)
min(y_patch), # min y (0)
max(y) * 1.2 # max y (немного выше максимума данных)
],
clip_path=patch, # Обрезать изображение по этому пути (патчу)
clip_on=True, # Включить обрезку
zorder=1 # Слой градиента (ниже линии, выше сетки)
)
# Рисуем линию поверх градиента (zorder=2 по умолчанию)
ax.plot(x, y, color='#268358', linewidth=2, zorder=2)
# Убедимся, что пределы оси X соответствуют диапазону наших дат
ax.set_xlim(x[0], x[-1])
plt.show()
# Опционально: Сохраняем график
# plt.savefig('high_quality_plot_final_v3.png', dpi=300, bbox_inches='tight')
Источник: Medium