StaticPlot.py revision 5d35796164c77d1350b7dc26329fb0d0abc549b5
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=None, **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 if self._attr["concat"]: 172 if self._attr["style"]: 173 with plt.rc_context(AttrConf.MPL_STYLE): 174 self._resolve_concat() 175 else: 176 self._resolve_concat() 177 else: 178 if self._attr["style"]: 179 with plt.rc_context(AttrConf.MPL_STYLE): 180 self._resolve(self._attr["permute"]) 181 else: 182 self._resolve(self._attr["permute"]) 183 184 def make_title(self, constraint, pivot, permute, concat): 185 """Generates a title string for an axis""" 186 if concat: 187 return str(constraint) 188 title = "" 189 if permute: 190 title += constraint.get_data_name() + ":" 191 192 if pivot == AttrConf.PIVOT_VAL: 193 if isinstance(self._attr["column"], list): 194 title += ", ".join(self._attr["column"]) 195 else: 196 title += self._attr["column"] 197 else: 198 title += "{0}: {1}".format(self._attr["pivot"], 199 self._attr["map_label"].get(pivot, pivot)) 200 return title 201 202 def add_to_legend(self, series_index, handle, constraint, pivot, concat): 203 """ 204 Add series handles and names to the legend 205 A handle is returned from a plot on an axis 206 e.g. Line2D from axis.plot() 207 """ 208 self._attr["_legend_handles"][series_index] = handle 209 legend_labels = self._attr["_legend_labels"] 210 211 if concat and pivot == AttrConf.PIVOT_VAL: 212 legend_labels[series_index] = self._attr["column"] 213 elif concat: 214 legend_labels[series_index] = "{0}: {1}".format( 215 self._attr["pivot"], 216 self._attr["map_label"].get(pivot, pivot) 217 ) 218 else: 219 legend_labels[series_index] = str(constraint) 220 221 def _resolve(self, permute=False): 222 """Determine what data to plot""" 223 pivot_vals, len_pivots = self.c_mgr.generate_pivots(permute) 224 pivot_vals = list(pivot_vals) 225 226 # Create a 2D Layout 227 self._layout = PlotLayout( 228 self._attr["per_line"], 229 len_pivots, 230 width=self._attr["width"], 231 length=self._attr["length"], 232 title=self._attr['title']) 233 234 self._fig = self._layout.get_fig() 235 236 #Determine what constraint to plot and the corresponding pivot value 237 if permute: 238 legend_len = self.c_mgr._max_len 239 pivots = [y for _, y in pivot_vals] 240 cp_pairs = [(c, p) for c in self.c_mgr for p in sorted(set(pivots))] 241 else: 242 legend_len = len(self.c_mgr) 243 pivots = pivot_vals 244 cp_pairs = [(c, p) for c in self.c_mgr for p in pivots if p in c.result] 245 246 #Initialise legend data and colormap 247 self._attr["_legend_handles"] = [None] * legend_len 248 self._attr["_legend_labels"] = [None] * legend_len 249 self._cmap = ColorMap(legend_len) 250 251 #Group constraints/series with the axis they are to be plotted on 252 figure_data = ddict(list) 253 for i, (constraint, pivot) in enumerate(cp_pairs): 254 axis = self._layout.get_axis(i) 255 figure_data[axis].append((constraint, pivot)) 256 257 #Plot each axis 258 for axis, series_list in figure_data.iteritems(): 259 self.plot_axis( 260 axis, 261 series_list, 262 permute, 263 self._attr["concat"], 264 args_to_forward=self._attr["args_to_forward"] 265 ) 266 267 #Add the legend to the figure 268 self._fig.legend(self._attr["_legend_handles"], 269 self._attr["_legend_labels"]) 270 self._layout.finish(len_pivots) 271 272 def _resolve_concat(self): 273 """Plot all lines on a single figure""" 274 pivot_vals, len_pivots = self.c_mgr.generate_pivots(False) 275 pivot_vals = list(pivot_vals) 276 277 # Create a 2D Layout 278 self._layout = PlotLayout( 279 self._attr["per_line"], 280 len(self.c_mgr), 281 width=self._attr["width"], 282 length=self._attr["length"], 283 title=self._attr['title']) 284 285 self._fig = self._layout.get_fig() 286 287 #Determine what constraint to plot and the corresponding pivot value 288 legend_len = len_pivots 289 pivots = pivot_vals 290 cp_pairs = [(c, p) for c in self.c_mgr for p in pivots if p in c.result] 291 292 #Initialise legend data and colormap 293 self._attr["_legend_handles"] = [None] * legend_len 294 self._attr["_legend_labels"] = [None] * legend_len 295 self._cmap = ColorMap(legend_len) 296 297 #Group constraints/series with the axis they are to be plotted on 298 figure_data = ddict(list) 299 for i, (constraint, pivot) in enumerate(cp_pairs): 300 axis = self._layout.get_axis(constraint.trace_index) 301 figure_data[axis].append((constraint, pivot)) 302 303 #Plot each axis 304 for axis, series_list in figure_data.iteritems(): 305 self.plot_axis( 306 axis, 307 series_list, 308 False, 309 self._attr["concat"], 310 self._attr["args_to_forward"] 311 ) 312 313 #Add the legend to the figure 314 self._fig.legend(self._attr["_legend_handles"], 315 self._attr["_legend_labels"]) 316 self._layout.finish(len(self.c_mgr)) 317 318 def plot_axis(self, axis, series_list, permute, concat, args_to_forward): 319 """Internal Method called to plot data (series_list) on a given axis""" 320 raise NotImplementedError("Method Not Implemented") 321