"""
Plot motif related graphs
"""
import logging
import os
import traceback
import datetime as dt
import matplotlib
matplotlib.use('Agg')
if True:
import matplotlib.pyplot as plt
# import matplotlib.image as mpimg
from matplotlib.pylab import rcParams
from matplotlib.dates import DateFormatter
# @added 20200512 - Bug #2534: Ionosphere - fluid approximation - IONOSPHERE_MINMAX_SCALING_RANGE_TOLERANCE on low ranges
# Feature #2404: Ionosphere - fluid approximation
[docs]def plot_motif_match(
current_skyline_app, metric, metric_timestamp, fp_id, full_duration,
generation_str, motif_id, index, size, distance, type_id, fp_motif,
not_anomalous_motif_sequence, output_file,
on_demand_motif_analysis=False):
"""
Creates a png graph image using the features profile time seires data and
the training data data, if it existing otherwise grab it from Graphite.
:param current_skyline_app: the Skyline app name calling the function
:param output_file: full path and filename to output where the png image is
to be saved to
:param graph_title: the graph image title
:param timeseries: the time series
:type current_skyline_app: str
:type output_file: str
:type graph_title: str
:type timeseries: list
:return: (status, file)
:rtype: (boolean, str)
"""
current_skyline_app_logger = current_skyline_app + 'Log'
current_logger = logging.getLogger(current_skyline_app_logger)
if os.path.isfile(output_file):
current_logger.info('plot_motif_match - graph image already exists - %s' % output_file)
return (True, output_file)
try:
current_logger.info('plot_motif_match - creating graph image - %s' % output_file)
not_anomalous_motif = [item[1] for item in not_anomalous_motif_sequence]
# Plot match
rcParams['figure.figsize'] = 8, 4
fig = plt.figure(frameon=False)
ax = fig.add_subplot(111)
not_anomalous_timestamp = int(not_anomalous_motif_sequence[-1][0])
not_anomalous_date_str = dt.datetime.fromtimestamp(not_anomalous_timestamp).isoformat()
# @added 20210415 - Feature #4014: Ionosphere - inference
# I realised looking through the graphs with Sab that users of Skyline are
# normally used to 7 days graps mostly, 24hour graphs and 30d graphs.
# They are not used to minutes.
# Make the user aware of the specific resolution they are viewing, a new
# UI resolution for Skyline (LAST X MINUTES). Thank you my love.
# For everything.
graph_period_seconds = not_anomalous_timestamp - int(not_anomalous_motif_sequence[0][0])
graph_period_minutes = round(graph_period_seconds / 60)
graph_period_minutes_str = '%s minutes' % str(graph_period_minutes)
if type_id == 1:
graph_title = '%s\nMATCHED fp id %s - EXACT MATCH - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
elif type_id == 2:
graph_title = '%s\nMATCHED fp id %s - ALL IN RANGE - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
elif type_id == 4:
graph_title = '%s\nNOT SIMILAR ENOUGH fp id %s - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
elif type_id == 5:
graph_title = '%s\nINVALIDATED fp id %s - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
elif type_id == 6:
graph_title = '%s\nMATCHED fp id %s - ON DISTANCE - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
else:
graph_title = '%s\nMATCHED fp id %s - in_range - %s (LAST %s MINUTES)' % (
metric, str(fp_id), not_anomalous_date_str, graph_period_minutes)
ax.set_title(graph_title, fontsize='medium')
if hasattr(ax, 'set_facecolor'):
ax.set_facecolor('white')
else:
ax.set_axis_bgcolor('white')
datetimes = [dt.datetime.utcfromtimestamp(int(item[0])) for item in not_anomalous_motif_sequence]
plt.xticks(rotation=0, horizontalalignment='center')
# if full_duration == FULL_DURATION:
# xfmt = DateFormatter('%H:%M:%S')
# else:
# xfmt = DateFormatter('%H:%M')
xfmt = DateFormatter('%H:%M:%S')
plt.gca().xaxis.set_major_formatter(xfmt)
ax.xaxis.set_major_formatter(xfmt)
not_anomalous_label = 'potentially anomalous motif'
# @modified 20210415 - Feature #4014: Ionosphere - inference
# ax.plot(datetimes, not_anomalous_motif, label=not_anomalous_label, color='red', lw=1 zorder=3)
# ax.plot(datetimes, not_anomalous_motif, label=not_anomalous_label, color='red', lw=1, markevery=not_anomalous_motif, zorder=3)
# ax.plot(datetimes, not_anomalous_motif, 'ro', label=not_anomalous_label, color='red', lw=1, zorder=3)
# ax.plot(datetimes, not_anomalous_motif, 'ro', label=not_anomalous_label, color='red', lw=1, marker='o', linestyle='solid', markersize=4, zorder=3)
not_anomalous_line_color = 'red'
if on_demand_motif_analysis:
if type_id == 1:
not_anomalous_line_color = 'green'
if type_id == 2:
not_anomalous_line_color = 'green'
if type_id == 3:
not_anomalous_line_color = 'green'
if type_id == 4:
not_anomalous_line_color = 'red'
if type_id == 5:
not_anomalous_line_color = 'red'
if type_id == 6:
not_anomalous_line_color = 'green'
if len(not_anomalous_motif) > 60:
# Do not plot loads of markers
ax.plot(
datetimes, not_anomalous_motif, label=not_anomalous_label,
color=not_anomalous_line_color, lw=1, linestyle='solid',
zorder=3)
else:
ax.plot(
datetimes, not_anomalous_motif, 'ro', label=not_anomalous_label,
color=not_anomalous_line_color, lw=1, marker='o', linestyle='solid',
markersize=4, zorder=3)
ax.tick_params(axis='both', labelsize='small')
matched_label = 'fp_id %s (%s as normal) - similar motif found' % (str(fp_id), generation_str)
ax.plot(datetimes, fp_motif, lw=1, label=matched_label, color='blue', ls='--', zorder=4)
ax.get_yaxis().get_major_formatter().set_useOffset(False)
ax.get_yaxis().get_major_formatter().set_scientific(False)
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.1,
box.width, box.height * 0.9])
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.1),
fancybox=True, shadow=True, ncol=2, fontsize='x-small')
plt.rc('lines', lw=1, color='black')
plt.grid(True)
ax.grid(b=True, which='both', axis='both', color='lightgray',
linestyle='solid', alpha=0.5, linewidth=0.6)
if hasattr(ax, 'set_facecolor'):
ax.set_facecolor('white')
else:
ax.set_axis_bgcolor('white')
rcParams['xtick.direction'] = 'out'
rcParams['ytick.direction'] = 'out'
ax.margins(y=.02, x=.03)
# @added 20210415 - Feature #4014: Ionosphere - inference
# bbox = dict(boxstyle='square', lw=3, ec='gray',
# fc=(0.9, 0.9, .9, .5), alpha=0.5)
# plt.text(0.5, 0.5, graph_period_minutes_str, ha='center', va='center',
# rotation=0, fontsize=18, color='gray', alpha=0.5, bbox=bbox)
# plt.text(0.6, 0.7, graph_period_minutes_str, size=18, rotation=0,
# ha="center", va="center", color='black', alpha=0.5,
# bbox=dict(boxstyle="round",
# ec=(1., 0.5, 0.5),
# fc=(1., 0.8, 0.8),
# )
# )
text_x = datetimes[0]
text_y = max(not_anomalous_motif) - ((max(not_anomalous_motif) / 10) * 1)
if type_id == 4:
ax_text = 'motif spans %s\nwith %s data points\nNOT SIMILAR ENOUGH TO MATCH' % (graph_period_minutes_str, str(len(not_anomalous_motif)))
ax_text_color = 'red'
elif type_id == 5:
ax_text = 'motif spans %s\nwith %s data points\nINVALIDATED' % (graph_period_minutes_str, str(len(not_anomalous_motif)))
ax_text_color = 'red'
else:
ax_text = 'motif spans %s\nwith %s data points' % (graph_period_minutes_str, str(len(not_anomalous_motif)))
ax_text_color = 'black'
if on_demand_motif_analysis:
ax_text = 'motif spans %s\nwith %s data points\nMATCH' % (graph_period_minutes_str, str(len(not_anomalous_motif)))
ax_text_color = 'green'
ax.text(text_x, text_y, ax_text, size=8, color=ax_text_color, alpha=0.7)
plt.savefig(output_file, format='png')
fig.clf()
plt.close(fig)
current_logger.info('plot_motif_match - created graph image - %s' % output_file)
except:
current_logger.error(traceback.format_exc())
current_logger.error('error :: plot_motif_match :: failed to create %s' % output_file)
return (False, None)
return (True, output_file)