import matplotlib.pyplot as plt
from matplotlib.figure import Figure
[документация]class NothingDrawError(Exception):
"""
Исключение, возбуждаемое, когда невозможно ничего нарисовать на графике.
"""
def __init__(self, text):
self.txt = text
def _make_tuples_from_filler(*tuples, length=1, filler=None):
"""
Делает из filler кортеж длиной length, если передан не None, то оставляет его, как есть
"""
result = []
for tuple_ in tuples:
if tuple_ is None:
result.append(tuple([filler] * length))
else:
result.append(tuple_)
return result
def _set_ticks_fontsize(axes, xfontsize, yfontsize):
"""
Устанавливает размер шрифта подписям по осям
:param axes: объект графика
:param xfontsize: размер шрифта по оси x
:param yfontsize: размер шрифта по оси y
:return: None
"""
for item in axes.get_xticklabels():
item.set_fontsize(xfontsize)
for item in axes.get_yticklabels():
item.set_fontsize(yfontsize)
def _get_default_params():
"""
Генерирует значения размеров шрифта по умолчанию
:return:
"""
params = dict()
params['figsize'] = (10, 8)
params['dpi'] = 100
params['legend_loc'] = 'best'
params['xticks_fontsize'] = 16
params['yticks_fontsize'] = 16
params['xlabel_fontsize'] = 22
params['ylabel_fontsize'] = 22
params['legend_fontsize'] = 20
params['title_fontsize'] = 26
return params
def _update_params(params, kwargs):
"""
Обновляет значения параметров построения графика с учётом переданных дополнитеьных параметров
:param params:
:param kwargs:
:return:
"""
targets = ('figsize', 'dpi', 'legend_loc',
'xticks_fontsize', 'yticks_fontsize',
'xlabel_fontsize', 'ylabel_fontsize',
'legend_fontsize', 'title_fontsize',
'LEGEND_FROM_APPROXIMATOR', 'xvar', 'yvar')
for target in targets:
if target in kwargs:
params[target] = kwargs.pop(target)
if kwargs:
print(f'Лишние аргументы', kwargs)
def _get_default_color(i=0):
"""
:return: первый цвет в цикле цветов matplotlib, точнее тускло-синий
"""
return plt.rcParams['axes.prop_cycle'].by_key()['color'][i % 10]
def _enable_minor_ticks(axes):
"""
Включает побочную сетку на графике
:param axes:
:return:
"""
axes.minorticks_on()
axes.grid(which='major', color='k', linewidth=0.8)
axes.grid(which='minor', color='k', linestyle=':')
def _set_font_params(axes, params, xlabel, ylabel, title):
# Устанавливает размер шрифта для подписей по осям
_set_ticks_fontsize(axes, params['xticks_fontsize'], params['yticks_fontsize'])
# Устанавливает размер шрифта для разных элементов
axes.set_xlabel(xlabel, fontsize=params['xlabel_fontsize'])
axes.set_ylabel(ylabel, fontsize=params['ylabel_fontsize'])
axes.set_title(title, fontsize=params['title_fontsize'])
def _get_axes(axes, params, minot_ticks):
"""
Создаёт новые объект графика и фигуры, если axes=None
Возвращает текущие оси, если они не None
"""
if axes is None:
figure, axes = plt.subplots(figsize=params['figsize'], dpi=params['dpi'])
if minot_ticks:
_enable_minor_ticks(axes)
return axes
def _prepare(axes, xlabel, ylabel, title, minor_ticks, points, line, xerr, yerr, approximator, kwargs):
_check_correct_input(points, line, xerr, yerr, approximator)
# Инициализирует параметры
params = _get_default_params()
# Обновляет параметры
_update_params(params, kwargs)
# Создаёт новые объект графика и фигуры, если не переданы существующие
axes = _get_axes(axes, params, minor_ticks)
# ----------------Шрифты----------------
_set_font_params(axes, params, xlabel, ylabel, title)
return axes, params
def _check_correct_input(points, line, xerr, yerr, approximator):
"""
Проверяет, может ли что-то нарисоваться.
Если нет, то выбрасывает исключение NothingDrawError
:param points:
:param line:
:param xerr:
:param yerr:
:return:
"""
if not points and not line and not xerr and not yerr and not approximator:
raise NothingDrawError("Хотя бы один из параметров points, line, xerr, yerr должен быть True,"
" чтобы что-то нарисовалось")
def _draw(axes, x, y, xerr, yerr, color, legend, points, line, approximator, params):
"""
Сама функция рисования
"""
# Устанавливает стандартный цвет, если не передан другой
if color is None:
color = _get_default_color()
# Нужно для костыля, чтобы легенда отрисовыволась лишь один раз
label = legend
if approximator:
x_, y_ = approximator.approximate(x, y, xerr, yerr)
if params.get("LEGEND_FROM_APPROXIMATOR"):
xvar = 'x'
yvar = 'y'
if 'xvar' in params:
xvar = params['xvar']
if 'yvar' in params:
yvar = params['yvar']
app_label = approximator.label(xvar, yvar)
else:
app_label = None
axes.plot(x_, y_, c=color, label=app_label)
# Рисует точки, если нужно
if points:
axes.scatter(x, y, c=color, label=label)
label = None
# Рисует кресты погрешностей, если нужно
if xerr is not None or yerr is not None:
axes.errorbar(x, y, fmt='none', xerr=xerr, yerr=yerr, c=color, label=label)
label = None
# Соединяет точки ломаной, если нужно
if line:
axes.plot(x, y, c=color, label=label)
label = None
return axes
[документация]def pretty_plot(x, y, xerr=0, yerr=0,
xlabel=None, ylabel=None, title=None, legend=None,
minor_ticks=True, color=None, points=True, line=False,
axes=None, approximator=None, **kwargs):
"""
Рисует график, с требованиями лабников.
По умолчания не соединяет точки
Без доп настроек можно рассчитывать на это
.. figure:: _static/images/base.png
:scale: 50 %
:align: center
:alt: Простой пример
TODO: пока не умеет выносить степень. в будущем это появится.
TODO: может поменяться интерфейс
**Для отображения можно использовать:**
- matplotlib.pyplot.show()
- pretty_plot(...).figure.show()
- show() (определён ниже)
**Для сохранения графика в картинку можно использовать:**
- matplotlib.pyplot.savefig(filename)
- pretty_plot(...).figure.savefig(filename)
- savefig(fig, filename) (определён ниже)
:param x: координаты по оси x
:param y: координаты по оси y
:param xerr: погрешности по оси x.
Либо одно число (применится к все точкам), либо список (применится к соответсвующей точке).
:param yerr: погрешности по оси y.
Либо одно число (применится к все точкам), либо список (применится к соответсвующей точке).
:param xlabel: подпись по оси x
:param ylabel: подпись по оси y
:param title: название графика
:param legend: легенда
:param minor_ticks: нужна ли вспомогательная сетка
:param color: цвет графика. Если None, то будет синий
:param points: нужно ли рисовать точки
:param line: нужно ли соединить ломаной все точки
:param axes: объект графика. Можно передать, чтобы дорисовать всё на существующем графике
:param approximator: аппроксиматор для точек, должен быть экземпляром класса Approximator
:param kwargs: дополнительные аргументы
:Дополнительные параметры:
* *figsize* (``tuple[int]``) --
размер графика в дюймах, по умолчанию 10 на 8
* *dpi* (``int``) --
количество пикселей на дюйм, по умолчанию 100
* *legend_loc* (``str``) --
положение легенды (см. документацию matplotlib), по умолчанию best
* *xticks_fontsize* (``int``) --
размер шрифта подписей оси x, по умолчанию 16
* *yticks_fontsize* (``int``) --
размер шрифта подписей оси y, по умолчанию 16
* *xlabel_fontsize* (``int``) --
размер шрифта обозначения оси x, по умолчанию 22
* *ylabel_fontsize* (``int``) --
размер шрифта обозначения оси y, по умолчанию 22
* *title_fontsize* (``int``) --
размер шрифта заголовка, по умолчанию 26
* *legend_fontsize* (``int``) --
размер шрифта легенды, по умолчанию 22
* *LEGEND_FROM_APPROXIMATOR* (``int``) --
если передано True, то рисует легенду ещё из аппроксиматора
* *xvar* (``str``) --
величина по оси x, если легенда рисуется из аппроксиматора
* *yvar* (``str``) --
величина по оси y, если легенда рисуется из аппроксиматора
:return: экземпляр класса matplotlib.Axes - объект только что нарисованного графика
"""
# ----------------Инициализация----------------
axes, params = _prepare(
axes=axes,
xlabel=xlabel,
ylabel=ylabel,
title=title,
minor_ticks=minor_ticks,
points=points,
line=line,
xerr=xerr,
yerr=yerr,
approximator=approximator,
kwargs=kwargs
)
# ----------------Рисование----------------
_draw(axes, x, y, xerr, yerr, color, legend, points, line, approximator, params)
# Рисует легенду, если нужно
if legend or params.get("LEGEND_FROM_APPROXIMATOR"):
axes.legend(loc=params['legend_loc'], fontsize=params['legend_fontsize'])
return axes
[документация]def pretty_plot_many(xs, ys, xerrs=None, yerrs=None,
xlabel=None, ylabel=None, title=None, legends=None,
minor_ticks=True, colors=None, points=True, line=False,
axes=None, approximator=None, **kwargs):
r"""
Рисует график так же, как и pretty_plot,
только вместо x, y передаётся два списка с наборами координат,
что позволяет сразу отрисовать несколько графиков
:param xs: наборы координат по оси x
:param ys: наборы координат по оси y
:param xerrs: наборы погрешностей по оси x.
Либо одно число (применится к все точкам), либо список (применится к соответсвующей точке).
:param yerrs: наборы погрешностей по оси y.
Либо одно число (применится к все точкам), либо список (применится к соответсвующей точке).
:param xlabel: наборы подписей по оси x
:param ylabel: наборы подписей по оси y
:param title: название графика
:param legends: легенды
:param minor_ticks: нужна ли впсомогательная сетка
:param colors: наборы цветов графика. Если None, то будет синий
:param points: нужно ли рисвоать точки
:param line: нужно ли соединить ломаной все точки
:param axes: объект графика. Можно передать, чтобы дорисовать всё на существующем графике
:param approximator: аппроксиматор для точек, должен быть экземпляром класса Approximator
:param kwargs: дополнительные агрменты:
:Дополнительные параметры:
* *figsize* (``tuple[int]``) --
размер графика в дюймах, по умолчанию 10 на 8
* *dpi* (``int``) --
количество пикселей на дюйм, по умолчанию 100
* *legend_loc* (``str``) --
положение легенды (см. документацию matplotlib), по умолчанию best
* *xticks_fontsize* (``int``) --
размер шрифта подписей оси x, по умолчанию 16
* *yticks_fontsize* (``int``) --
размер шрифта подписей оси y, по умолчанию 16
* *xlabel_fontsize* (``int``) --
размер шрифта обозначения оси x, по умолчанию 22
* *ylabel_fontsize* (``int``) --
размер шрифта обозначения оси y, по умолчанию 22
* *title_fontsize* (``int``) --
размер шрифта заголовка, по умолчанию 26
* *legend_fontsize* (``int``) --
размер шрифта легенды, по умолчанию 22
* *LEGEND_FROM_APPROXIMATOR* (``bool``) --
если передано True, то рисует легенду ещё из аппроксиматора
* *xvar* (``str``) --
величина по оси x, если легенда рисуется из аппроксиматора
* *yvar* (``str``) --
величина по оси y, если легенда рисуется из аппроксиматора
:return: экземпляр класса matplotlib.Axes - объект только что нарисованного графика
"""
# ----------------Инициализация----------------
axes, params = _prepare(
axes=axes,
xlabel=xlabel,
ylabel=ylabel,
title=title,
minor_ticks=minor_ticks,
points=points,
line=line,
xerr=xerrs,
yerr=yerrs,
approximator=approximator,
kwargs=kwargs
)
# Создаём кортежы из None или нулей длины = количеству графиков
legends, colors = _make_tuples_from_filler(legends, colors)
xerrs, yerrs = _make_tuples_from_filler(xerrs, yerrs, filler=0)
# ----------------Рисование----------------
for num, (x, y, xerr, yerr, legend, color) in enumerate(zip(xs, ys, xerrs, yerrs, legends, colors)):
_draw(axes, x, y, xerr, yerr, color, legend, points, line, approximator, params)
# Рисует легенду, если нужно
if legends or params.get("LEGEND_FROM_APPROXIMATOR"):
axes.legend(loc=params['legend_loc'], fontsize=params['legend_fontsize'])
return axes
[документация]def show(*args, **kwargs):
"""
Рисует все сгенерированные графики. По-сути обертка над matplotlib.pyplot.show
Подробнее в `документации matplotlib`_
.. _`документации matplotlib`: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.show.html
"""
return plt.show(*args, **kwargs)
[документация]def savefig(obj, filename, *, transparent=None, **kwargs):
"""
Сохраняет фигуру. По-сути обертка над встроенной функцие сохранения
:param obj: экземпляр класса Axes или Figure из matplotlib. Его же возвращают функции pretty_plot и pretty_plot_many
:param filename: имя файла, в который нужно сохранить. Если передан без расширения, то сохраниться в png.
:param transparent: If *True*, the axes patches will all be transparent; the
figure patch will also be transparent unless facecolor
and/or edgecolor are specified via kwargs.
This is useful, for example, for displaying
a plot on top of a colored background on a web page. The
transparency of these patches will be restored to their
original values upon exit of this function.
:param kwargs: дополнительные параметры. Смотри `документацию matplotlib`_
.. _`документацию matplotlib`: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.savefig.html
"""
figure = obj if isinstance(obj, Figure) else obj.figure
figure.savefig(filename, transparent=transparent, **kwargs)