StaticPlot.py revision b42593d6a0cabfc64d415820c682f652b70acb64
1# Copyright 2016-2016 ARM Limited 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# 15"""Base matplotlib plotter module""" 16from abc import abstractmethod, ABCMeta 17from collections import defaultdict as ddict 18import matplotlib.pyplot as plt 19from trappy.plotter import AttrConf 20from trappy.plotter.Constraint import ConstraintManager 21from trappy.plotter.PlotLayout import PlotLayout 22from trappy.plotter.AbstractDataPlotter import AbstractDataPlotter 23from trappy.plotter.ColorMap import ColorMap 24 25 26 27class StaticPlot(AbstractDataPlotter): 28 """ 29 This class uses :mod:`trappy.plotter.Constraint.Constraint` to 30 represent different permutations of input parameters. These 31 constraints are generated by creating an instance of 32 :mod:`trappy.plotter.Constraint.ConstraintManager`. 33 34 :param trace: The input data 35 :type trace: :mod:`trappy.trace.FTrace` or :mod:`pandas.DataFrame`, list or single 36 37 :param column: specifies the name of the column to 38 be plotted. 39 :type column: (str, list(str)) 40 41 :param templates: TRAPpy events 42 43 .. note:: 44 45 This is not required if a :mod:`pandas.DataFrame` is 46 used 47 48 :type templates: :mod:`trappy.base.Base` 49 50 :param filters: Filter the column to be plotted as per the 51 specified criteria. For Example: 52 :: 53 54 filters = 55 { 56 "pid": [ 3338 ], 57 "cpu": [0, 2, 4], 58 } 59 :type filters: dict 60 61 :param per_line: Used to control the number of graphs 62 in each graph subplot row 63 :type per_line: int 64 65 :param concat: Draw all the pivots on a single graph 66 :type concat: bool 67 68 :param permute: Draw one plot for each of the traces specified 69 :type permute: bool 70 71 :param drawstyle: This argument is forwarded to the matplotlib 72 corresponding :func:`matplotlib.pyplot.plot` call 73 74 drawing style. 75 76 .. note:: 77 78 step plots are not currently supported for filled 79 graphs 80 81 :param xlim: A tuple representing the upper and lower xlimits 82 :type xlim: tuple 83 84 :param ylim: A tuple representing the upper and lower ylimits 85 :type ylim: tuple 86 87 :param title: A title describing all the generated plots 88 :type title: str 89 90 :param style: Created pre-styled graphs loaded from 91 :mod:`trappy.plotter.AttrConf.MPL_STYLE` 92 :type style: bool 93 94 :param signals: A string of the type event_name:column 95 to indicate the value that needs to be plotted 96 97 .. note:: 98 99 - Only one of `signals` or both `templates` and 100 `columns` should be specified 101 - Signals format won't work for :mod:`pandas.DataFrame` 102 input 103 104 :type signals: str 105 106 """ 107 __metaclass__ = ABCMeta 108 109 def __init__(self, traces, templates, **kwargs): 110 self._fig = None 111 self._layout = None 112 super(StaticPlot, self).__init__(traces=traces, 113 templates=templates) 114 115 self.set_defaults() 116 117 for key in kwargs: 118 if key in AttrConf.ARGS_TO_FORWARD: 119 self._attr["args_to_forward"][key] = kwargs[key] 120 else: 121 self._attr[key] = kwargs[key] 122 123 if "signals" in self._attr: 124 self._describe_signals() 125 126 self._check_data() 127 128 if "column" not in self._attr: 129 raise RuntimeError("Value Column not specified") 130 131 zip_constraints = not self._attr["permute"] 132 self.c_mgr = ConstraintManager(traces, self._attr["column"], 133 self.templates, self._attr["pivot"], 134 self._attr["filters"], zip_constraints) 135 136 def savefig(self, *args, **kwargs): 137 """Save the plot as a PNG fill. This calls into 138 :mod:`matplotlib.figure.savefig` 139 """ 140 141 if self._fig is None: 142 self.view() 143 self._fig.savefig(*args, **kwargs) 144 145 @abstractmethod 146 def set_defaults(self): 147 """Sets the default attrs""" 148 self._attr["width"] = AttrConf.WIDTH 149 self._attr["length"] = AttrConf.LENGTH 150 self._attr["per_line"] = AttrConf.PER_LINE 151 self._attr["concat"] = AttrConf.CONCAT 152 self._attr["filters"] = {} 153 self._attr["style"] = True 154 self._attr["permute"] = False 155 self._attr["pivot"] = AttrConf.PIVOT 156 self._attr["xlim"] = AttrConf.XLIM 157 self._attr["ylim"] = AttrConf.XLIM 158 self._attr["title"] = AttrConf.TITLE 159 self._attr["args_to_forward"] = {} 160 self._attr["map_label"] = {} 161 self._attr["_legend_handles"] = [] 162 self._attr["_legend_labels"] = [] 163 164 def view(self, test=False): 165 """Displays the graph""" 166 167 if test: 168 self._attr["style"] = True 169 AttrConf.MPL_STYLE["interactive"] = False 170 171 permute = self._attr["permute"] and not self._attr["concat"] 172 if self._attr["style"]: 173 with plt.rc_context(AttrConf.MPL_STYLE): 174 self._resolve(permute, self._attr["concat"]) 175 else: 176 self._resolve(permute, self._attr["concat"]) 177 178 def make_title(self, constraint, pivot, permute, concat): 179 """Generates a title string for an axis""" 180 if concat: 181 return str(constraint) 182 title = "" 183 if permute: 184 title += constraint.get_data_name() + ":" 185 186 if pivot == AttrConf.PIVOT_VAL: 187 if isinstance(self._attr["column"], list): 188 title += ", ".join(self._attr["column"]) 189 else: 190 title += self._attr["column"] 191 else: 192 title += "{0}: {1}".format(self._attr["pivot"], 193 self._attr["map_label"].get(pivot, pivot)) 194 return title 195 196 def add_to_legend(self, series_index, handle, constraint, pivot, concat): 197 """ 198 Add series handles and names to the legend 199 A handle is returned from a plot on an axis 200 e.g. Line2D from axis.plot() 201 """ 202 self._attr["_legend_handles"][series_index] = handle 203 legend_labels = self._attr["_legend_labels"] 204 205 if concat and pivot == AttrConf.PIVOT_VAL: 206 legend_labels[series_index] = self._attr["column"] 207 elif concat: 208 legend_labels[series_index] = "{0}: {1}".format( 209 self._attr["pivot"], 210 self._attr["map_label"].get(pivot, pivot) 211 ) 212 else: 213 legend_labels[series_index] = str(constraint) 214 # Remove trace name if there is only one trace to plot 215 if not isinstance(self.traces, list): 216 legend_labels[series_index] = legend_labels[series_index].replace( 217 constraint.get_data_name()+":", "" 218 ) 219 220 def _resolve(self, permute, concat): 221 """Determine what data to plot on which axis""" 222 pivot_vals, len_pivots = self.c_mgr.generate_pivots(permute) 223 pivot_vals = list(pivot_vals) 224 225 num_of_axes = len(self.c_mgr) if concat else len_pivots 226 227 # Create a 2D Layout 228 self._layout = PlotLayout( 229 self._attr["per_line"], 230 num_of_axes, 231 width=self._attr["width"], 232 length=self._attr["length"], 233 title=self._attr['title']) 234 235 self._fig = self._layout.get_fig() 236 237 #Determine what constraint to plot and the corresponding pivot value 238 if permute: 239 legend_len = self.c_mgr._max_len 240 pivots = [y for _, y in pivot_vals] 241 cp_pairs = [(c, p) for c in self.c_mgr for p in sorted(set(pivots))] 242 else: 243 legend_len = len_pivots if concat else len(self.c_mgr) 244 pivots = pivot_vals 245 cp_pairs = [(c, p) for c in self.c_mgr for p in pivots if p in c.result] 246 247 #Initialise legend data and colormap 248 self._attr["_legend_handles"] = [None] * legend_len 249 self._attr["_legend_labels"] = [None] * legend_len 250 self._cmap = ColorMap(legend_len) 251 252 #Group constraints/series with the axis they are to be plotted on 253 figure_data = ddict(list) 254 for i, (constraint, pivot) in enumerate(cp_pairs): 255 axis = self._layout.get_axis(constraint.trace_index if concat else i) 256 figure_data[axis].append((constraint, pivot)) 257 258 #Plot each axis 259 for axis, series_list in figure_data.iteritems(): 260 self.plot_axis( 261 axis, 262 series_list, 263 permute, 264 self._attr["concat"], 265 self._attr["args_to_forward"] 266 ) 267 268 #Add the legend to the figure if more than one signal is plotted 269 if legend_len > 1: 270 self._fig.legend(self._attr["_legend_handles"], 271 self._attr["_legend_labels"]) 272 273 self._layout.finish(num_of_axes) 274 275 def plot_axis(self, axis, series_list, permute, concat, args_to_forward): 276 """Internal Method called to plot data (series_list) on a given axis""" 277 raise NotImplementedError("Method Not Implemented") 278