""" utils.py
Contains a useful plotting function that is used in the coding examples.
The function was built using Plotly instead of Matplotlib due to its
interactive graphs.
Author: Eduardo Nigro
rev 0.0.5
2021-12-26
"""
import numpy as np
import plotly.io as pio
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Setting plotting modified template as default
mytemplate = pio.templates["plotly_white"]
mytemplate.layout["paper_bgcolor"] = "rgb(250, 250, 250)"
pio.templates.default = mytemplate
[docs]def plot_line(
x, y, xname="Time (s)", yname=None, axes="single", marker=False, legend=None):
"""
Plot lines using plotly.
:param x: x values for plotting.
:type x: list(float)
:param y: y values for plotting.
:type y: list(float)
:param xname: The x axis title. Default value is ``'Time (s)'``.
:type xname: str
:param yname: The y axis title.
A string or list of strings containing the names of the y axis titles.
If ``None``, the y axis titles will be ``'y0'``, ``'y1'``, etc.
:type yname: str, list(str)
:param axes: The configuration of axis on the plot.
If ``'single'``, multiple curves are plotted on the same axis.
If ``'multi'``, each curve is plotted on its own axis.
:type axes: str
:param marker: Displays markers on the curves if ``True``.
:type marker: bool
:param legend: List of legend names for multiple curves.
Length of `legend` must be the same as length of `y`.
:type legend: list(str)
"""
# Adjusting inputs
if type(x) != list:
x = [x]
if type(y) != list:
y = [y]
if not yname:
yname = ["y" + str(i) for i in range(len(y))]
elif type(yname) != list:
yname = [yname]
if (len(yname) == 1) and (len(y) > 1):
yname = yname * len(y)
# Setting legend display option
if legend is not None:
if len(legend) == len(y):
showlegend = True
else:
raise Exception("'y' and 'legend' must have the same length.")
else:
showlegend = False
legend = [None] * len(y)
# Checking for single (with multiple curves)
# or multiple axes with one curve per axes
if axes == "single":
naxes = 1
iaxes = [0] * len(y)
colors = [
"#1F77B4",
"#FF7F0E",
"#2CA02C",
"#D62728",
"#9467BD",
"#8C564B",
"#E377C2",
"#7F7F7F",
"#BCBD22",
"#17BECF",
]
elif axes == "multi":
naxes = len(y)
iaxes = range(0, len(y))
colors = ["rgb(50, 100, 150)"] * len(y)
else:
raise Exception("Valid axes options are: 'single' or 'multi'.")
# Checking for marker options
if marker:
mode = "lines+markers"
else:
mode = "lines"
# Setting figure parameters
m0 = 10
margin = dict(l=6 * m0, r=3 * m0, t=3 * m0, b=3 * m0)
wfig = 750
hfig = 100 + 150 * naxes
# Plotting results
fig = make_subplots(rows=naxes, cols=1)
for i, xi, yi, ynamei, legendi, color in zip(iaxes, x, y, yname, legend, colors):
fig.add_trace(
go.Scatter(
x=xi,
y=yi,
name=legendi,
mode=mode,
line=dict(width=1, color=color),
marker=dict(size=2, color=color),
),
row=i + 1,
col=1,
)
fig.update_yaxes(title_text=ynamei, row=i + 1, col=1)
if xname.lower().find("angle") < 0:
fig.update_xaxes(matches="x", row=i + 1, col=1)
else:
fig.update_xaxes(
tickmode="array",
tickvals=np.arange(0, np.round(xi[-1]) + 360, 360),
matches="x",
row=i + 1,
col=1,
)
fig.update_xaxes(title_text=xname, row=i + 1, col=1)
fig.update_layout(margin=margin, width=wfig, height=hfig, showlegend=showlegend)
fig.show()