aboutsummaryrefslogtreecommitdiff
path: root/trappy/plotter/StaticPlot.py
diff options
context:
space:
mode:
Diffstat (limited to 'trappy/plotter/StaticPlot.py')
-rw-r--r--trappy/plotter/StaticPlot.py289
1 files changed, 289 insertions, 0 deletions
diff --git a/trappy/plotter/StaticPlot.py b/trappy/plotter/StaticPlot.py
new file mode 100644
index 0000000..9b58108
--- /dev/null
+++ b/trappy/plotter/StaticPlot.py
@@ -0,0 +1,289 @@
+# Copyright 2016-2017 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.
+#
+"""Base matplotlib plotter module"""
+from abc import abstractmethod, ABCMeta
+from collections import defaultdict as ddict
+import matplotlib.pyplot as plt
+from trappy.plotter import AttrConf
+from trappy.plotter.Constraint import ConstraintManager
+from trappy.plotter.PlotLayout import PlotLayout
+from trappy.plotter.AbstractDataPlotter import AbstractDataPlotter
+from trappy.plotter.ColorMap import ColorMap
+
+
+
+class StaticPlot(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 drawstyle: This argument is forwarded to the matplotlib
+ corresponding :func:`matplotlib.pyplot.plot` call
+
+ drawing style.
+
+ .. note::
+
+ step plots are not currently supported for filled
+ graphs
+
+ :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 title: A title describing all the generated plots
+ :type title: str
+
+ :param style: Created pre-styled graphs loaded from
+ :mod:`trappy.plotter.AttrConf.MPL_STYLE`
+ :type style: bool
+
+ :param signals: A string of the type event_name:column
+ to indicate the value that needs to be plotted
+
+ .. 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
+
+ :param legend_ncol: A positive integer that represents the
+ number of columns in the legend
+ :type legend_ncol: int
+ """
+ __metaclass__ = ABCMeta
+
+ def __init__(self, traces, templates, **kwargs):
+ self._fig = None
+ self._layout = None
+ super(StaticPlot, self).__init__(traces=traces,
+ templates=templates)
+
+ self.set_defaults()
+
+ for key in kwargs:
+ if key in AttrConf.ARGS_TO_FORWARD:
+ self._attr["args_to_forward"][key] = kwargs[key]
+ else:
+ 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")
+
+ zip_constraints = not self._attr["permute"]
+ self.c_mgr = ConstraintManager(traces, self._attr["column"],
+ self.templates, self._attr["pivot"],
+ self._attr["filters"],
+ zip_constraints=zip_constraints)
+
+ def savefig(self, *args, **kwargs):
+ """Save the plot as a PNG fill. This calls into
+ :mod:`matplotlib.figure.savefig`
+ """
+
+ if self._fig is None:
+ self.view()
+ self._fig.savefig(*args, **kwargs)
+
+ @abstractmethod
+ def set_defaults(self):
+ """Sets the default attrs"""
+ self._attr["width"] = AttrConf.WIDTH
+ self._attr["length"] = AttrConf.LENGTH
+ self._attr["per_line"] = AttrConf.PER_LINE
+ self._attr["concat"] = AttrConf.CONCAT
+ self._attr["filters"] = {}
+ self._attr["style"] = True
+ self._attr["permute"] = False
+ self._attr["pivot"] = AttrConf.PIVOT
+ self._attr["xlim"] = AttrConf.XLIM
+ self._attr["ylim"] = AttrConf.YLIM
+ self._attr["title"] = AttrConf.TITLE
+ self._attr["args_to_forward"] = {}
+ self._attr["map_label"] = {}
+ self._attr["_legend_handles"] = []
+ self._attr["_legend_labels"] = []
+ self._attr["legend_ncol"] = AttrConf.LEGEND_NCOL
+
+ def view(self, test=False):
+ """Displays the graph"""
+
+ if test:
+ self._attr["style"] = True
+ AttrConf.MPL_STYLE["interactive"] = False
+
+ permute = self._attr["permute"] and not self._attr["concat"]
+ if self._attr["style"]:
+ with plt.rc_context(AttrConf.MPL_STYLE):
+ self._resolve(permute, self._attr["concat"])
+ else:
+ self._resolve(permute, self._attr["concat"])
+
+ def make_title(self, constraint, pivot, permute, concat):
+ """Generates a title string for an axis"""
+ if concat:
+ return str(constraint)
+
+ if permute:
+ return constraint.get_data_name()
+ elif pivot != AttrConf.PIVOT_VAL:
+ return "{0}: {1}".format(self._attr["pivot"], self._attr["map_label"].get(pivot, pivot))
+ else:
+ return ""
+
+ def add_to_legend(self, series_index, handle, constraint, pivot, concat, permute):
+ """
+ Add series handles and names to the legend
+ A handle is returned from a plot on an axis
+ e.g. Line2D from axis.plot()
+ """
+ self._attr["_legend_handles"][series_index] = handle
+ legend_labels = self._attr["_legend_labels"]
+
+ if concat and pivot == AttrConf.PIVOT_VAL:
+ legend_labels[series_index] = self._attr["column"]
+ elif concat:
+ legend_labels[series_index] = "{0}: {1}".format(
+ self._attr["pivot"],
+ self._attr["map_label"].get(pivot, pivot)
+ )
+ elif permute:
+ legend_labels[series_index] = constraint._template.name + ":" + constraint.column
+ else:
+ legend_labels[series_index] = str(constraint)
+
+ def _resolve(self, permute, concat):
+ """Determine what data to plot on which axis"""
+ pivot_vals, len_pivots = self.c_mgr.generate_pivots(permute)
+ pivot_vals = list(pivot_vals)
+
+ num_of_axes = len(self.c_mgr) if concat else len_pivots
+
+ # Create a 2D Layout
+ self._layout = PlotLayout(
+ self._attr["per_line"],
+ num_of_axes,
+ width=self._attr["width"],
+ length=self._attr["length"],
+ title=self._attr['title'])
+
+ self._fig = self._layout.get_fig()
+
+ # Determine what constraint to plot and the corresponding pivot value
+ if permute:
+ legend_len = self.c_mgr._max_len
+ pivots = [y for _, y in pivot_vals]
+ c_dict = {c : str(c) for c in self.c_mgr}
+ c_list = sorted(c_dict.items(), key=lambda x: (x[1].split(":")[-1], x[1].split(":")[0]))
+ constraints = [c[0] for c in c_list]
+ cp_pairs = [(c, p) for c in constraints for p in sorted(set(pivots))]
+ else:
+ legend_len = len_pivots if concat else len(self.c_mgr)
+ pivots = pivot_vals
+ cp_pairs = [(c, p) for c in self.c_mgr for p in pivots if p in c.result]
+
+ # Initialise legend data and colormap
+ self._attr["_legend_handles"] = [None] * legend_len
+ self._attr["_legend_labels"] = [None] * legend_len
+
+ if "colors" in self._attr:
+ self._cmap = ColorMap.rgb_cmap(self._attr["colors"])
+ else:
+ self._cmap = ColorMap(legend_len)
+
+ # Group constraints/series with the axis they are to be plotted on
+ figure_data = ddict(list)
+ for i, (constraint, pivot) in enumerate(cp_pairs):
+ axis = self._layout.get_axis(constraint.trace_index if concat else i)
+ figure_data[axis].append((constraint, pivot))
+
+ # Plot each axis
+ for axis, series_list in figure_data.iteritems():
+ self.plot_axis(
+ axis,
+ series_list,
+ permute,
+ self._attr["concat"],
+ self._attr["args_to_forward"]
+ )
+ if self._attr["xlim"]:
+ axis.set_xlim(self._attr["xlim"])
+ if self._attr["ylim"]:
+ axis.set_ylim(self._attr["ylim"])
+
+ # Show legend
+ legend = self._fig.legend(self._attr["_legend_handles"],
+ self._attr["_legend_labels"],
+ loc='lower center',
+ ncol=self._attr["legend_ncol"],
+ borderaxespad=0.)
+ legend.get_frame().set_facecolor('#F4F4F4')
+
+ self._layout.finish(num_of_axes)
+
+ def plot_axis(self, axis, series_list, permute, concat, args_to_forward):
+ """Internal Method called to plot data (series_list) on a given axis"""
+ raise NotImplementedError("Method Not Implemented")