#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This docstring was part of Matplotlib. All rights reserved.
# Full Text:
# https://matplotlib.org/stable/users/project/license.html
#
# This file is part of the
# Flask-Plots Project https://github.com/juniors90/Flask-Plots/
#
# Copyright (c) 2021, Ferreira Juan David
# License: MIT
# Full Text:
# https://github.com/juniors90/Flask-Plots/blob/master/LICENSE
#
# =============================================================================
# DOCS
# =============================================================================
"""Flask-Plots.
Implementation of Matplotlib in Flask.
"""
# =============================================================================
# IMPORTS
# =============================================================================
import base64
import io
from flask import Blueprint, current_app
[docs]def raise_helper(message): # pragma: no cover
"""Handle for raise in jinja templates."""
raise RuntimeError(message)
[docs]class Plots(object):
"""Base extension class for different Plots versions.
.. versionadded:: 0.0.1
"""
static_folder = "plots"
# Generate the figure **without using pyplot**.
def __init__(self, app=None):
if app is not None:
self.init_app(app)
[docs] def init_app(self, app):
"""Sample factory function for initialize the extension."""
app.config.setdefault("PLOTS_CMAP", "Greys")
app.config.setdefault("STATIC_FOLDER", "plots")
app.config.setdefault("BAR_HEIGHT", 50)
if not hasattr(app, "extensions"): # pragma: no cover
app.extensions = {}
app.extensions["plots"] = self
blueprint = Blueprint(
"plots",
__name__,
static_folder=f"static/{self.static_folder}",
static_url_path=f"{app.static_url_path}",
template_folder="templates",
)
app.register_blueprint(blueprint)
app.jinja_env.globals["plots"] = self
app.jinja_env.globals["raise"] = raise_helper
app.jinja_env.add_extension("jinja2.ext.do")
[docs] def get_data(self, fig, fmt="png", decode="ascii"):
"""
Create a data for embed the result in the html output.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
format : str, default: "png"
A extension type for the images.
decode : str, default: "ascii"
A buffer decode.
"""
buf = io.BytesIO()
fig.savefig(buf, format=fmt)
data = base64.b64encode(buf.getbuffer()).decode(decode)
return data
# Statistics plots: Plots for statistical analysis.
[docs] def hist(self, fig, x, ax=None, hist_kws=None):
"""
Plot a histogram using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x : (n,) array or sequence of (n,) arrays
Input values, this takes either a single array or a sequence of
arrays which are not required to be of the same length.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
hist_kwargs : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
hist_kws = {} if hist_kws is None else hist_kws
ax.hist(x, **hist_kws)
return ax
[docs] def errorbar(self, fig, x, y, ax=None, errorbar_kws=None):
"""
Plot y versus x as lines and/or markers with attached errorbars.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : float or array-like
The data positions.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
errorbar_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
errorbar_kws = {} if errorbar_kws is None else errorbar_kws
ax.errorbar(x, y, **errorbar_kws)
return ax
[docs] def violinplot(
self, fig, dataset, positions, ax=None, violinplot_kws=None
):
"""
Make a violin plot using Matlotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
dataset : Array or a sequence of vectors.
The input data.
positions : array-like, default: [1, 2, ..., n]
The positions of the violins. The ticks and limits are
automatically set to match the positions.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
violinplot_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
violinplot_kws = {} if violinplot_kws is None else violinplot_kws
vp = ax.violinplot(dataset, positions, **violinplot_kws)
return vp
[docs] def eventplot(self, fig, positions, ax=None, eventplot_kws=None):
"""
Plot identical parallel lines at the given positions.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
positions : array-like or list of array-like
A 1D array-like defines the positions of one sequence of events.
Multiple groups of events may be passed as a list of array-likes.
Each group can be styled independently by passing lists of values
to *lineoffsets*, *linelengths*, *linewidths*, *colors* and
*linestyles*.
Note that *positions* can be a 2D array, but in practice different
event groups usually have different counts so that one will use a
list of different-length arrays rather than a 2D array.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
eventplot_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
eventplot_kws = {} if eventplot_kws is None else eventplot_kws
ax.eventplot(positions, **eventplot_kws)
return ax
[docs] def hist2d(self, fig, x, y, ax=None, hist2d_kws=None):
"""
Make a 2D histogram plot using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : array-like, shape (n, )
Input values
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
hist2d_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
hist2d_kws = {} if hist2d_kws is None else hist2d_kws
ax.hist2d(x, y, **hist2d_kws)
return ax
[docs] def hexbin(self, fig, x, y, ax=None, hexbin_kws=None):
"""
Make a 2D hexagonal binning plot of points *x*, *y* using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : array-like
The data positions. *x* and *y* must be of the same length.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
hexbin_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
hexbin_kws = {} if hexbin_kws is None else hexbin_kws
ax.hexbin(x, y, **hexbin_kws)
return ax
[docs] def scatter_hist2d(
self, fig, x, y, ax=None, hist2d_kws=None, scatter_kws=None
):
"""
Make a 2D histogram plot using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : array-like, shape (n, )
Input values
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
hist2d_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
scatter_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot in term scatter method.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
hist2d_kws = {} if hist2d_kws is None else hist2d_kws
scatter_kws = {} if scatter_kws is None else scatter_kws
hist2d_kws.setdefault("cmap", current_app.config["PLOTS_CMAP"])
ax.hist2d(x, y, **hist2d_kws)
ax.scatter(x, y, **scatter_kws)
return ax
[docs] def scatter_hexbin(
self, fig, x, y, ax=None, hexbin_kws=None, scatter_kws=None
):
"""
Make a 2D scatter-hexagonal binning plot of points *x*, *y*.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : array-like
The data positions. *x* and *y* must be of the same length.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
hexbin_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot in term hexbin method.
scatter_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot in term scatter method.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
hexbin_kws = {} if hexbin_kws is None else hexbin_kws
scatter_kws = {} if scatter_kws is None else scatter_kws
hexbin_kws.setdefault("cmap", current_app.config["PLOTS_CMAP"])
ax.hexbin(x, y, **hexbin_kws)
ax.scatter(x, y, **scatter_kws)
return ax
[docs] def bar(self, fig, x, bar_height=None, ax=None, bar_kws=None):
"""
Make a bar plot using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x : float or array-like
The x coordinates of the bars. See also *align* for the
alignment of the bars to the coordinates.
bar_height : float or array-like,
The height(s) of the bars. You can config this value
using ``app.config["BAR_HEIGHT"]``.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
bar_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
bar_kws = {} if bar_kws is None else bar_kws
bar_height = (
current_app.config["BAR_HEIGHT"]
if bar_height is None
else bar_height
)
ax.bar(x, bar_height, **bar_kws)
return ax
[docs] def pie(self, fig, x, ax=None, pie_kws=None):
"""
Make a pie plot using Matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x : 1D array-like
The wedge sizes.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
bar_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
pie_kws = {} if pie_kws is None else pie_kws
ax.pie(x, **pie_kws)
return ax
[docs] def boxplot(self, fig, x, ax=None, boxplot_kws=None):
"""
Draw a box and whisker plot using MAtplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x : Array or a sequence of vectors.
The input data. If a 2D array, a boxplot is drawn for each column
in *x*. If a sequence of 1D arrays, a boxplot is drawn for each
array in *x*.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
boxplot_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
boxplot_kws = {} if boxplot_kws is None else boxplot_kws
ax.boxplot(x, **boxplot_kws)
return ax
[docs] def quiver(self, fig, x, y, u, v, ax=None, quiver_kws=None):
"""
Plot a 2D field of arrows using matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : 1D or 2D array-like, optional
The x and y coordinates of the arrow locations.
If not given, they will be generated as a uniform integer meshgrid
based on the dimensions of *u* and *v*.
If *x* and *y* are 1D but *u*, *v* are 2D, *x*, *y* are expanded
to 2D using ``x, y = np.meshgrid(x, y)``. In this case ``len(x)``
and ``len(y)`` must match the column and row dimensions of
*u* and *v*.
u, v : 1D or 2D array-like
The x and y direction components of the arrow vectors.
They must have the same number of elements, matching the
number of arrow locations. *u* and *v* may be masked. Only
locations unmasked in *u*, *v*, and *C* will be drawn.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
quiver_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
quiver_kws = {} if quiver_kws is None else quiver_kws
ax.quiver(x, y, u, v, **quiver_kws)
return ax
[docs] def streamplot(self, fig, x, y, u, v, ax=None, streamplot_kws=None):
"""
Draw streamlines of a vector flow using matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : 1D/2D arrays
Evenly spaced strictly increasing arrays to make a grid.
If 2D, all rows of *x* must be equal and all columns of
*y* must be equal; i.e., they must be as if generated
by ``np.meshgrid(x_1d, y_1d)``.
u, v : 2D arrays
*x* and *y*-velocities. The number of rows and columns
must match the length of *y* and *x*, respectively.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
streamplot_kws : ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
"""
ax = fig.gca() if ax is None else ax
streamplot_kws = {} if streamplot_kws is None else streamplot_kws
ax.streamplot(x, y, u, v, **streamplot_kws)
return ax
[docs] def contourf(self, fig, x, y, z, levels, ax=None, contourf_kws=None):
"""
Plot contour lines using matplotlib.
Parameters
----------
fig : matplotlib.Figure
A instance of Figure Object.
x, y : array-like, optional
The coordinates of the values in *z*.
*x* and *y* must both be 2D with the same shape as *z* (e.g.
created via `numpy.meshgrid`), or they must both be 1-D such
that ``len(x) == N`` is the number of columns in *z* and
``len(y) == M`` is the number of rows in *z*.
*X* and *Y* must both be ordered monotonically.
If not given, they are assumed to be integer indices, i.e.
``x = range(N)``, ``y = range(M)``.
levels : int or array-like, optional
Determines the number and positions of the contour lines / regions.
If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries
to automatically choose no more than *n+1* "nice" contour levels
between *vmin* and *vmax*.
If array-like, draw contour lines at the specified levels.
The values must be in increasing order.
ax : matplotlib.Figure.Axis, (optional)
A matplotlib axis.
contourf_kws: ``dict`` or ``None`` (optional)
The parameters to send to the data plot.
Returns
-------
ax : matplotlib.Figure.Axis
A matplotlib axis.
"""
ax = fig.gca() if ax is None else ax
contourf_kws = {} if contourf_kws is None else contourf_kws
ax.contourf(x, y, z, levels, **contourf_kws)
return ax