"""
Timeseries plots.
:githublink:`%|py|5`
"""
import calendar
import datetime
import matplotlib.pyplot as plt
import matplotlib.patches as patches
[docs]def plot_week_timeseries(time, value, normalise=True,
label=None, h=0.85, value2=None,
label2=None, daynames=None,
xfmt="%1.0f", ax=None):
"""
Shows a timeseries dispatched by days as bars.
:param time: dates
:param value: values to display as bars.
:param normalise: normalise data before showing it
:param label: label of the series
:param values2: second series to show as a line
:param label2: label of the second series
:param daynames: names to use for week day names (default is English)
:param xfmt: format number of the X axis
:param ax: existing axis
:return: axis
.. plot::
import datetime
import matplotlib.pyplot as plt
from mlinsights.timeseries.datasets import artificial_data
from mlinsights.timeseries.agg import aggregate_timeseries
from mlinsights.timeseries.plotting import plot_week_timeseries
dt1 = datetime.datetime(2019, 8, 1)
dt2 = datetime.datetime(2019, 9, 1)
data = artificial_data(dt1, dt2, minutes=15)
print(data.head())
agg = aggregate_timeseries(data, per='week')
plot_week_timeseries(
agg['weektime'], agg['y'], label="y",
value2=agg['y']/2, label2="y/2", normalise=False)
plt.show()
:githublink:`%|py|47`
"""
if time.shape[0] != value.shape[0]:
raise AssertionError("Dimension mismatch") # pragma: no cover
def coor(ti):
days = ti.days
x = days
y = ti.seconds
return x, y
max_value = value.max()
if value2 is not None:
max_value = max(max_value, value2.max())
value2 = value2 / max_value
value = value / max_value
input_maxy = 1.
if ax is None:
ax = plt.gca()
# bars
delta = None
maxx, maxy = None, None
first = True
for i in range(time.shape[0]):
ti = time[i]
if i < time.shape[0] - 1:
ti1 = time[i + 1]
delta = (ti1 - ti) if delta is None else min(delta, ti1 - ti)
if delta == 0:
raise RuntimeError( # pragma: no cover
"The timeseries contains duplicated time values.")
else:
ti1 = ti + delta
x1, y1 = coor(ti)
x2, y2 = coor(ti1)
if y2 < y1:
x2, y2 = coor(ti + delta)
y2 = y1 + (y2 - y1) * h
if first and label:
ax.plot([x1, x1 + value[i] * 0.8], [y1, y1],
'b', alpha=0.5, label=label)
first = False
if maxx is None:
maxx = (x1, x1 + input_maxy)
maxy = (y1, y2)
else:
maxx = (min(x1, maxx[0]), max(x1 + input_maxy, maxx[1]))
maxy = (min(y1, maxy[0]), max(y2, maxy[1]))
rect = patches.Rectangle((x1, y1), value[i] * h, y2 - y1,
linewidth=1, edgecolor=None,
facecolor='b', fill=True,
alpha=0.5)
ax.add_patch(rect)
# days border
xticks = []
if daynames is None:
daynames = list(calendar.day_name)
maxx = [(maxx[0] // 7) * 7, maxx[1]]
new_ymin = maxy[0] - (maxy[1] * 0.025 + maxy[0] * 0.975 - maxy[0])
for i in range(int(maxx[0]), int(maxx[1] + 0.1)):
x1i = maxx[0] + input_maxy * i
x2i = x1i + input_maxy
xticks.append(x1i)
ax.plot([x1i, x1i + input_maxy], [new_ymin, new_ymin], 'k', alpha=0.5)
ax.plot([x1i, x1i + input_maxy], [maxy[1], maxy[1]], 'k', alpha=0.5)
ax.plot([x1i, x1i], [maxy[0], maxy[1]], 'k', alpha=0.5)
ax.plot([x2i, x2i], [maxy[0], maxy[1]], 'k', alpha=0.5)
ax.text(x1i, new_ymin, daynames[i])
# invert y axis
ax.invert_yaxis()
# change y labels
nby = len(ax.get_yticklabels())
ys = ax.get_yticks()
ylabels = []
for i in range(nby):
dh = ys[i]
dt = datetime.timedelta(seconds=dh)
tx = "%dh%02d" % (dt.seconds // 3600,
60 * (dt.seconds / 3600 - dt.seconds // 3600))
ylabels.append(tx)
ax.set_yticklabels(ylabels)
# change x labels
xs = ax.get_xticks()
xticks = []
xlabels = []
for i in range(0, len(xs) - 1):
if xs[i] < 0:
continue
dx = xs[i] - int(xs[i] / input_maxy) * input_maxy
xlabels.append(dx if normalise else (dx * max_value))
xticks.append(xs[i])
dx = (xs[i] + xs[i + 1]) / 2
dx = dx - int(dx / input_maxy) * input_maxy
xlabels.append(dx if normalise else (dx * max_value))
xticks.append((xs[i] + xs[i + 1]) / 2)
if len(xticks) < len(xlabels):
xticks.append(xs[-1])
ax.set_xticks(xticks)
ax.set_xticklabels(
[xfmt % x for x in xlabels] if xfmt else xlabels)
ax.tick_params(axis='x', rotation=30)
# value2
if value2 is not None:
value = value2.copy()
if normalise:
value = value / max_value
first = True
xs = []
ys = []
for i in range(time.shape[0]):
ti = time[i]
if i < time.shape[0] - 1:
ti1 = time[i + 1]
else:
ti1 = ti + delta
x1, y1 = coor(ti)
x2, y2 = coor(ti1)
if y2 < y1:
x2, y2 = coor(ti + delta)
y2 = y1 + (y2 - y1) * h
x2 = x1 + value[i] * h
if len(ys) > 0 and y2 < ys[-1]:
if first and label2 is not None:
ax.plot(xs, ys, color='orange', linewidth=2, label=label2)
first = False
else:
ax.plot(xs, ys, color='orange', linewidth=2)
xs, ys = [], []
xs.append(x2)
ys.append((y1 + y2) / 2)
if len(xs) > 0:
ax.plot(xs, ys, color='orange', linewidth=2)
return ax