Source code for fp_match_plots

"""
Plot motif related graphs
"""
import logging
import os
import traceback
import datetime as dt
import matplotlib
matplotlib.use('Agg')

load_libraries = True
if load_libraries:
    import matplotlib.pyplot as plt
    # import matplotlib.image as mpimg
    from matplotlib.pylab import rcParams
    from matplotlib.dates import DateFormatter
    # @added 20230626 - Task #4962: Build and test skyline v4.0.0
    #                   Task #4778: v4.0.0 - update dependencies
    # As per https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.7.0.html#the-first-parameter-of-axes-grid-and-axis-grid-has-been-renamed-to-visible
    from matplotlib import __version__ as matplotlib_version


# @added 20220317 - Feature #4540: Plot matched timeseries
#                   Feature #4014: Ionosphere - inference
[docs]def plot_fp_match( current_skyline_app, metric, fp_id, fp_values, not_anomalous_timeseries, output_file, strip_prefix=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_fp_match - graph image already exists - %s' % output_file) return (True, output_file) try: current_logger.info('plot_fp_match - creating graph image - %s' % output_file) matched_timeseries_length = len(not_anomalous_timeseries) # Ensure timesereis are same length if len(fp_values) > matched_timeseries_length: fp_values = fp_values[-matched_timeseries_length:] if len(fp_values) < matched_timeseries_length: not_anomalous_timeseries = not_anomalous_timeseries[-len(fp_values):] not_anomalous_motif = [item[1] for item in not_anomalous_timeseries] # Plot match rcParams['figure.figsize'] = 8, 4 fig = plt.figure(frameon=False) ax = fig.add_subplot(111) not_anomalous_timestamp = int(not_anomalous_timeseries[-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_timeseries[0][0]) graph_period_minutes = round(graph_period_seconds / 60) graph_period_minutes_str = '%s minutes' % str(graph_period_minutes) graph_title = '%s\nMATCHED fp id %s at %s' % ( metric, str(fp_id), not_anomalous_date_str) if strip_prefix: metric_elements = metric.split('.') metric = '.'.join(metric_elements[1:]) graph_title = '%s\nMATCHED trained pattern %s at %s' % ( metric, str(fp_id), not_anomalous_date_str) 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_timeseries] 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') if graph_period_seconds > 87000: xfmt = DateFormatter('%m/%d') else: xfmt = DateFormatter('%H:%M:%S') plt.gca().xaxis.set_major_formatter(xfmt) ax.xaxis.set_major_formatter(xfmt) not_anomalous_label = 'potential anomalous timeseries' not_anomalous_line_color = 'green' if len(not_anomalous_timeseries) > 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=4) 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=4) ax.tick_params(axis='both', labelsize='small') matched_label = 'fp_id %s - similar pattern' % (str(fp_id)) if strip_prefix: matched_label = 'trained pattern %s - similar' % (str(fp_id)) ax.plot(datetimes, fp_values, lw=1, label=matched_label, color='blue', ls='--', zorder=3, alpha=0.5) 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='small') plt.rc('lines', lw=1, color='black') plt.grid(True) # @modified 20230626 - Task #4962: Build and test skyline v4.0.0 # Task #4778: v4.0.0 - update dependencies # As per https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.7.0.html#the-first-parameter-of-axes-grid-and-axis-grid-has-been-renamed-to-visible if matplotlib_version < '3.7.0': ax.grid(b=True, which='both', axis='both', color='lightgray', linestyle='solid', alpha=0.5, linewidth=0.6) else: ax.grid(visible=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) text_x = datetimes[0] text_y = max(not_anomalous_motif) - ((max(not_anomalous_motif) / 10) * 1) ax_text = 'features profile spans %s\nwith %s data points' % (graph_period_minutes_str, str(len(not_anomalous_motif))) if strip_prefix: ax_text = 'pattern slice spans %s\nwith %s data points' % (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_fp_match - created graph image - %s' % output_file) except: current_logger.error(traceback.format_exc()) current_logger.error('error :: plot_fp_match :: failed to create %s' % output_file) return (False, None) return (True, output_file)