Source code for trappy.plotter.ILinePlot

#    Copyright 2015-2016 ARM Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""This module contains the class for plotting and customizing
Line/Linear Plots with :mod:`trappy.trace.BareTrace` or derived
classes.  This plot only works when run from an IPython notebook

"""

from collections import OrderedDict
import matplotlib.pyplot as plt
from trappy.plotter import AttrConf
from trappy.plotter import Utils
from trappy.plotter.Constraint import ConstraintManager
from trappy.plotter.ILinePlotGen import ILinePlotGen
from trappy.plotter.AbstractDataPlotter import AbstractDataPlotter
from trappy.plotter.ColorMap import ColorMap
from trappy.plotter import IPythonConf
from trappy.utils import handle_duplicate_index
import pandas as pd

if not IPythonConf.check_ipython():
    raise ImportError("Ipython Environment not Found")

[docs]class ILinePlot(AbstractDataPlotter): """ This class uses :mod:`trappy.plotter.Constraint.Constraint` to represent different permutations of input parameters. These constraints are generated by creating an instance of :mod:`trappy.plotter.Constraint.ConstraintManager`. :param traces: The input data :type traces: a list of :mod:`trappy.trace.FTrace`, :mod:`trappy.trace.SysTrace`, :mod:`trappy.trace.BareTrace` or :mod:`pandas.DataFrame` or a single instance of them. :param column: specifies the name of the column to be plotted. :type column: (str, list(str)) :param templates: TRAPpy events .. note:: This is not required if a :mod:`pandas.DataFrame` is used :type templates: :mod:`trappy.base.Base` :param filters: Filter the column to be plotted as per the specified criteria. For Example: :: filters = { "pid": [ 3338 ], "cpu": [0, 2, 4], } :type filters: dict :param per_line: Used to control the number of graphs in each graph subplot row :type per_line: int :param concat: Draw all the pivots on a single graph :type concat: bool :param permute: Draw one plot for each of the traces specified :type permute: bool :param fill: Fill the area under the plots :type fill: bool :param xlim: A tuple representing the upper and lower xlimits :type xlim: tuple :param ylim: A tuple representing the upper and lower ylimits :type ylim: tuple :param drawstyle: Set the drawstyle to a matplotlib compatible drawing style. .. note:: Only "steps-post" is supported as a valid value for the drawstyle. This creates a step plot. :type drawstyle: str :param sync_zoom: Synchronize the zoom of a group of plots. Zooming in one plot of a group (see below) will zoom in every plot of that group. Defaults to False. :type sync_zoom: boolean :param group: Name given to the plots created by this ILinePlot instance. This name is only used for synchronized zoom. If you zoom on any plot in a group all plots will zoom at the same time. :type group: string :param signals: A string of the type event_name:column to indicate the value that needs to be plotted. You can add an additional parameter to specify the color of the lin in rgb: "event_name:column:color". The color is specified as a comma separated list of rgb values, from 0 to 255 or from 0x0 to 0xff. E.g. 0xff,0x0,0x0 is red and 100,40,32 is brown. .. note:: - Only one of `signals` or both `templates` and `columns` should be specified - Signals format won't work for :mod:`pandas.DataFrame` input :type signals: str """ def __init__(self, traces, templates=None, **kwargs): # Default keys, each can be overridden in kwargs self._layout = None super(ILinePlot, self).__init__(traces=traces, templates=templates) self.set_defaults() for key in kwargs: self._attr[key] = kwargs[key] if "signals" in self._attr: self._describe_signals() self._check_data() if "column" not in self._attr: raise RuntimeError("Value Column not specified") if self._attr["drawstyle"] and self._attr["drawstyle"].startswith("steps"): self._attr["step_plot"] = True zip_constraints = not self._attr["permute"] window = self._attr["xlim"] if "xlim" in self._attr else None self.c_mgr = ConstraintManager(traces, self._attr["column"], self.templates, self._attr["pivot"], self._attr["filters"], window=window, zip_constraints=zip_constraints)
[docs] def savefig(self, *args, **kwargs): raise NotImplementedError("Not Available for ILinePlot")
[docs] def view(self, max_datapoints=75000, test=False): """Displays the graph :param max_datapoints: Maximum number of datapoints to plot. Dygraph can make the browser unresponsive if it tries to plot too many datapoints. Chrome 50 chokes at around 75000 on an i7-4770 @ 3.4GHz, Firefox 47 can handle up to 200000 before becoming too slow in the same machine. You can increase this number if you know what you're doing and are happy to wait for the plot to render. :type max_datapoints: int :param test: For testing purposes. Only set to true if run from the testsuite. :type test: boolean """ # Defer installation of IPython components # to the .view call to avoid any errors at # when importing the module. This facilitates # the importing of the module from outside # an IPython notebook if not test: IPythonConf.iplot_install("ILinePlot") self._attr["max_datapoints"] = max_datapoints if self._attr["concat"]: self._plot_concat() else: self._plot(self._attr["permute"], test)
[docs] def set_defaults(self): """Sets the default attrs""" self._attr["per_line"] = AttrConf.PER_LINE self._attr["concat"] = AttrConf.CONCAT self._attr["filters"] = {} self._attr["pivot"] = AttrConf.PIVOT self._attr["permute"] = False self._attr["drawstyle"] = None self._attr["step_plot"] = False self._attr["fill"] = AttrConf.FILL self._attr["draw_line"] = True self._attr["scatter"] = AttrConf.PLOT_SCATTER self._attr["point_size"] = AttrConf.POINT_SIZE self._attr["map_label"] = {} self._attr["title"] = AttrConf.TITLE
def _plot(self, permute, test): """Internal Method called to draw the plot""" pivot_vals, len_pivots = self.c_mgr.generate_pivots(permute) self._layout = ILinePlotGen(len_pivots, **self._attr) plot_index = 0 for p_val in pivot_vals: data_dict = OrderedDict() for constraint in self.c_mgr: if permute: trace_idx, pivot = p_val if constraint.trace_index != trace_idx: continue legend = constraint._template.name + ":" + constraint.column else: pivot = p_val legend = str(constraint) result = constraint.result if pivot in result: data_dict[legend] = result[pivot] if permute: title = self.traces[plot_index].name elif pivot != AttrConf.PIVOT_VAL: title = "{0}: {1}".format(self._attr["pivot"], self._attr["map_label"].get(pivot, pivot)) else: title = "" if len(data_dict) > 1: data_frame = self._fix_indexes(data_dict) else: data_frame = pd.DataFrame(data_dict) self._layout.add_plot(plot_index, data_frame, title, test=test) plot_index += 1 self._layout.finish() def _plot_concat(self): """Plot all lines on a single figure""" pivot_vals, _ = self.c_mgr.generate_pivots() plot_index = 0 self._layout = ILinePlotGen(len(self.c_mgr), **self._attr) for constraint in self.c_mgr: result = constraint.result title = str(constraint) data_dict = OrderedDict() for pivot in pivot_vals: if pivot in result: if pivot == AttrConf.PIVOT_VAL: key = ",".join(self._attr["column"]) else: key = "{0}: {1}".format(self._attr["pivot"], self._attr["map_label"].get(pivot, pivot)) data_dict[key] = result[pivot] if len(data_dict) > 1: data_frame = self._fix_indexes(data_dict) else: data_frame = pd.DataFrame(data_dict) self._layout.add_plot(plot_index, data_frame, title) plot_index += 1 self._layout.finish() def _fix_indexes(self, data_dict): """ In case of multiple traces with different indexes (i.e. x-axis values), create new ones with same indexes """ # 1) Check if we are processing multiple traces if len(data_dict) <= 1: raise ValueError("Cannot fix indexes for single trace. "\ "Expecting multiple traces!") # 2) Merge the data frames to obtain common indexes df_columns = list(data_dict.keys()) dedup_data = [handle_duplicate_index(s) for s in data_dict.values()] ret = pd.Series(dedup_data, index=df_columns) merged_df = pd.concat(ret.get_values(), axis=1) merged_df.columns = df_columns # 3) Fill NaN values depending on drawstyle if self._attr["drawstyle"] == "steps-post": merged_df = merged_df.ffill() elif self._attr["drawstyle"] == "steps-pre": merged_df = merged_df.bfill() elif self._attr["drawstyle"] == "steps-mid": merged_df = merged_df.ffill() else: # default merged_df = merged_df.interpolate() return merged_df