Source code for trappy.plotter.ILinePlotGen

#    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 is helper module for :mod:`trappy.plotter.ILinePlot`
for adding HTML and javascript necessary for interactive
plotting. The Linear to 2-D co-ordination transformations
are done by using the functionality in
:mod:`trappy.plotter.PlotLayout`
"""

from trappy.plotter import AttrConf
import uuid
from collections import OrderedDict
import json
import os
from trappy.plotter import IPythonConf
from trappy.plotter.ColorMap import to_dygraph_colors


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

from IPython.display import display, HTML

[docs]def df_to_dygraph(data_frame): """Helper function to convert a :mod:`pandas.DataFrame` to dygraph data :param data_frame: The DataFrame to be converted :type data_frame: :mod:`pandas.DataFrame` """ values = data_frame.values.tolist() data = [[x] for x in data_frame.index.tolist()] for idx, (_, val) in enumerate(zip(data, values)): data[idx] += val return { "data": data, "labels": ["index"] + data_frame.columns.tolist(), }
[docs]class ILinePlotGen(object): """ :param num_plots: The total number of plots :type num_plots: int The linear co-ordinate system :math:`[0, N_{plots}]` is mapped to a 2-D coordinate system with :math:`N_{rows}` and :math:`N_{cols}` such that: .. math:: N_{rows} = \\frac{N_{cols}}{N_{plots}} """ def _add_graph_cell(self, fig_name, color_map): """Add a HTML table cell to hold the plot""" colors_opt_arg = ", " + to_dygraph_colors(color_map) if color_map else "" graph_js = '' lib_urls = [IPythonConf.DYGRAPH_COMBINED_URL, IPythonConf.DYGRAPH_SYNC_URL, IPythonConf.UNDERSCORE_URL] for url in lib_urls: graph_js += '<!-- TRAPPY_PUBLISH_SOURCE_LIB = "{}" -->\n'.format(url) graph_js += """ <script> /* TRAPPY_PUBLISH_IMPORT = "plotter/js/ILinePlot.js" */ /* TRAPPY_PUBLISH_REMOVE_START */ var ilp_req = require.config( { paths: { "dygraph-sync": '""" + IPythonConf.add_web_base("plotter_scripts/ILinePlot/synchronizer") + """', "dygraph": '""" + IPythonConf.add_web_base("plotter_scripts/ILinePlot/dygraph-combined") + """', "ILinePlot": '""" + IPythonConf.add_web_base("plotter_scripts/ILinePlot/ILinePlot") + """', "underscore": '""" + IPythonConf.add_web_base("plotter_scripts/ILinePlot/underscore-min") + """', }, shim: { "dygraph-sync": ["dygraph"], "ILinePlot": { "deps": ["dygraph-sync", "dygraph", "underscore"], "exports": "ILinePlot" } } }); /* TRAPPY_PUBLISH_REMOVE_STOP */ ilp_req(["require", "ILinePlot"], function() { /* TRAPPY_PUBLISH_REMOVE_LINE */ ILinePlot.generate(""" + fig_name + "_data" + colors_opt_arg + """); }); /* TRAPPY_PUBLISH_REMOVE_LINE */ </script> """ cell = '<td style="border-style: hidden;"><div class="ilineplot" id="{}"></div></td>'.format(fig_name) self._html.append(cell) self._js.append(graph_js) def _add_legend_cell(self, fig_name): """Add HTML table cell for the legend""" legend_div_name = fig_name + "_legend" cell = '<td style="border-style: hidden;"><div style="text-align:center" id="{}"></div></td>'.format(legend_div_name) self._html.append(cell) def _begin_row(self): """Add the opening tag for HTML row""" self._html.append("<tr>") def _end_row(self): """Add the closing tag for the HTML row""" self._html.append("</tr>") def _end_table(self): """Add the closing tag for the HTML table""" self._html.append("</table>") def _generate_fig_name(self): """Generate a unique figure name""" fig_name = "fig_" + uuid.uuid4().hex self._fig_map[self._fig_index] = fig_name self._fig_index += 1 return fig_name def _init_html(self, color_map): """Initialize HTML code for the plots""" table = '<table style="border-style: hidden;">' self._html.append(table) if self._attr["title"]: cell = '<caption style="text-align:center; font: 24px sans-serif bold; color: black">{}</caption>'.format(self._attr["title"]) self._html.append(cell) for _ in range(self._rows): self._begin_row() legend_figs = [] for _ in range(self._attr["per_line"]): fig_name = self._generate_fig_name() legend_figs.append(fig_name) self._add_graph_cell(fig_name, color_map) self._end_row() self._begin_row() for l_fig in legend_figs: self._add_legend_cell(l_fig) self._end_row() self._end_table() def __init__(self, num_plots, **kwargs): self._attr = kwargs self._html = [] self._js = [] self._js_plot_data = [] self.num_plots = num_plots self._fig_map = {} self._fig_index = 0 self._single_plot = False if self.num_plots == 0: raise RuntimeError("No plots for the given constraints") if self.num_plots < self._attr["per_line"]: self._attr["per_line"] = self.num_plots self._rows = (self.num_plots / self._attr["per_line"]) if self.num_plots % self._attr["per_line"] != 0: self._rows += 1 self._attr["height"] = AttrConf.HTML_HEIGHT self._init_html(kwargs.pop("colors", None)) def _check_add_scatter(self, fig_params): """Check if a scatter plot is needed and augment the fig_params accordingly""" if self._attr["scatter"]: fig_params["drawPoints"] = True fig_params["strokeWidth"] = 0.0 else: fig_params["drawPoints"] = False fig_params["strokeWidth"] = AttrConf.LINE_WIDTH fig_params["pointSize"] = self._attr["point_size"]
[docs] def add_plot(self, plot_num, data_frame, title="", test=False): """Add a plot for the corresponding index :param plot_num: The linear index of the plot :type plot_num: int :param data_frame: The data for the plot :type data_frame: :mod:`pandas.DataFrame` :param title: The title for the plot :type title: str """ datapoints = sum(len(v) for _, v in data_frame.iteritems()) if datapoints > self._attr["max_datapoints"]: msg = "This plot is too big and will probably make your browser unresponsive. If you are happy to wait, pass max_datapoints={} to view()".\ format(datapoints + 1) raise ValueError(msg) fig_name = self._fig_map[plot_num] fig_params = {} fig_params["data"] = df_to_dygraph(data_frame) fig_params["name"] = fig_name fig_params["rangesel"] = False fig_params["logscale"] = False fig_params["title"] = title fig_params["step_plot"] = self._attr["step_plot"] fig_params["fill_graph"] = self._attr["fill"] fig_params["per_line"] = self._attr["per_line"] fig_params["height"] = self._attr["height"] self._check_add_scatter(fig_params) if "group" in self._attr: fig_params["syncGroup"] = self._attr["group"] if "sync_zoom" in self._attr: fig_params["syncZoom"] = self._attr["sync_zoom"] else: fig_params["syncZoom"] = AttrConf.DEFAULT_SYNC_ZOOM if "ylim" in self._attr: fig_params["valueRange"] = self._attr["ylim"] if "xlim" in self._attr: fig_params["dateWindow"] = self._attr["xlim"] fig_data = "var {}_data = {};".format(fig_name, json.dumps(fig_params)) self._js_plot_data.append("<script>") self._js_plot_data.append(fig_data) self._js_plot_data.append("</script>")
[docs] def finish(self): """Called when the Plotting is finished""" display(HTML(self.html()))
[docs] def html(self): """Return the raw HTML text""" return "\n".join(self._html + self._js_plot_data + self._js)