Module progbg.graphing
Graphing Primitives Module
Expand source code
"""Graphing Primitives Module"""
from ._bargraph import BarGraph, Bar, BarFactory, BarGroup
from ._custom import CustomGraph
from ._linegraph import Line, LineGraph, ConstLine
__all__ = [
"BarGraph",
"Bar",
"BarFactory",
"BarGroup",
"CustomGraph",
"Line",
"LineGraph",
"ConstLine",
]
Classes
class Bar (wl, composed_of, label=None)
-
Bar object used within
BarGraph
This represent a bar within a bar graph. Its construction used an execution object. Once an execution is done, metrics objects are pulled and summarized into means and standard deviations.
The keys within the
core.Metrics
are used to compose bars. You may select just one. But optionally you may compose bars of many metrics (See matplotlibs stacked bar).Arguments
wl (Execution, List): Execution object or list for constant values composed_of (List): A key for the data to use, or optionally a list of keys label (str): Label of the bar
Example
>>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> b1 = Bar(e1, ["data1", "data2"]) >>> >>> # Below Bar: data1 = 10, data2 = 22 >>> b1 = Bar([10, 22], ["data1", "data2"])
Expand source code
class Bar(GraphObject): """Bar object used within `BarGraph` This represent a bar within a bar graph. Its construction used an execution object. Once an execution is done, metrics objects are pulled and summarized into means and standard deviations. The keys within the `core.Metrics` are used to compose bars. You may select just one. But optionally you may compose bars of many metrics (See matplotlibs stacked bar). Arguments: wl (Execution, List): Execution object or list for constant values composed_of (List): A key for the data to use, or optionally a list of keys label (str): Label of the bar Example: >>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> b1 = Bar(e1, ["data1", "data2"]) >>> >>> # Below Bar: data1 = 10, data2 = 22 >>> b1 = Bar([10, 22], ["data1", "data2"]) """ def __init__(self, wl, composed_of, label=None): self.composed = composed_of if isinstance(wl, list): d = dict() for i, x in enumerate(self.composed): d[x] = wl[i] wl = ExecutionStub(**d) self.workload = wl self.label = label if isinstance(label, str): self.label = [label] if label is None: self.label = [""] def get_data(self, restrict_on): d = filter(self.workload._cached, restrict_on)[0].get_stats() composed = [] for c in self.composed: composed.append(c) composed.append(c + "_std") label = self.label return pd.DataFrame({c: d[c] for c in composed}, index=self.label).T
Ancestors
- progbg.graphing._graph.GraphObject
- abc.ABC
Methods
def get_data(self, restrict_on)
-
Expand source code
def get_data(self, restrict_on): d = filter(self.workload._cached, restrict_on)[0].get_stats() composed = [] for c in self.composed: composed.append(c) composed.append(c + "_std") label = self.label return pd.DataFrame({c: d[c] for c in composed}, index=self.label).T
class BarFactory (wl)
-
Ease of use Factory Class
Used to quickly be able to make many bars from one Execution object
Expand source code
class BarFactory: """Ease of use Factory Class Used to quickly be able to make many bars from one Execution object """ def __init__(self, wl): self.workload = wl def __call__(self, composed_of, label=None): if not label: label = self.workload.name return Bar(self.workload, composed_of, label)
class BarGraph (workloads: List, **kwargs)
-
Bar Graph
Args
workloads
:List
- A list of list of bars. Each list is a grouping of bars to be graphs.
group_labels
:List
- Labels associated to each grouped list in workloads.
formatter
:Function
, optional- Function object for post customization of graphs.
width
:float
- Width of each bar
out
:Path
- Output file for this single graph to be saved to
style
:str
- Style of the graph (default: color_a)
kwargs
:optional
- Passed to matplotlib
Axes.plot
function or optional named params below.
Progbg optional kwargs: title (str): Title of the graph or figure
Examples
Suppose we have some previously defined execution called
exec
.>>> exec = plan_execution(...) >>> bar1 = Bar(exec, "stat-one", label="Custom Stat") >>> bar2 = Bar(exec, "stat-two", label="Custom Stat Two") >>> plan_graph( >>> BarGraph([[bar1, bar2]], >>> group_labels=["These a grouped!"], >>> out="custom.svg" >>> )
The above example would create a graph grouping both bar1, and bar2 next to each other. The below example would seperate bar1 and bar2. "stat-one", and "stat-two", are both values that would have been added to the associated
core.Metrics
object which is passed through the parser functions provided by the user.>>> plan_graph( >>> BarGraph([[bar1], [bar2]], >>> group_labels=["Group 1!", "Group 2!"], >>> out="custom.svg" >>> )
Expand source code
class BarGraph(Graph): """Bar Graph Args: workloads (List): A list of list of bars. Each list is a grouping of bars to be graphs. group_labels (List): Labels associated to each grouped list in workloads. formatter (Function, optional): Function object for post customization of graphs. width (float): Width of each bar out (Path): Output file for this single graph to be saved to style (str): Style of the graph (default: color_a) kwargs (optional): Passed to matplotlib `Axes.plot` function or optional named params below. Progbg optional kwargs: title (str): Title of the graph or figure Examples: Suppose we have some previously defined execution called `exec`. >>> exec = plan_execution(...) >>> bar1 = Bar(exec, "stat-one", label="Custom Stat") >>> bar2 = Bar(exec, "stat-two", label="Custom Stat Two") >>> plan_graph( >>> BarGraph([[bar1, bar2]], >>> group_labels=["These a grouped!"], >>> out="custom.svg" >>> ) The above example would create a graph grouping both bar1, and bar2 next to each other. The below example would seperate bar1 and bar2. "stat-one", and "stat-two", are both values that would have been added to the associated `core.Metrics` object which is passed through the parser functions provided by the user. >>> plan_graph( >>> BarGraph([[bar1], [bar2]], >>> group_labels=["Group 1!", "Group 2!"], >>> out="custom.svg" >>> ) """ def __init__(self, workloads: List, **kwargs): super().__init__(**kwargs) default_options = dict( std=True, group_labels=[], log=False, width=0.5, ) for prop, default in default_options.items(): setattr(self, prop, kwargs.get(prop, default)) self.workloads = workloads self.html_out = ".".join(self.out.split(".")[:-1]) + ".svg" def _graph(self, ax, data): if isinstance(self.workloads[0], BarGroup): # Retrieve top level labels data = [pd.concat(x) for x in data] data = pd.concat(data, axis=1) cols = [c for c in data.columns if c[-4:] != "_std"] cols_std = [c for c in data.columns if len(c) > 4 and c[-4:] == "_std"] df = data[cols].T std = data[cols_std] std.columns = [x[:-4] for x in std.columns] std = std.T df.plot.bar(rot=-90, ax=ax, yerr=std, capsize=4, width=self.width) if self.log: ax.set_yscale("log") else: data = pd.concat(data, axis=1).T cols = [c for c in data.columns if c[-4:] != "_std"] cols_std = [c for c in data.columns if len(c) > 4 and c[-4:] == "_std"] df = data[cols] std = data[cols_std].T df.plot(rot=-90, kind="bar", stacked=True, ax=ax)
Ancestors
- progbg.graphing._graph.Graph
- abc.ABC
class BarGroup (executions, cat, label)
-
Groups Data as bars on single value
Arguments
executions (List): List of executions to include in group cat (str): category to compare the workloads label (List[str]): Labels for the elements in the group
Examples
>>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> b1 = BarGroup([e1, e2, e3], "data1", ["exec1", "exec2", "exec3"]) >>> b2 = BarGroup([e1, 20, e3], "data1", ["exec1", "normal", "exec3"])
Expand source code
class BarGroup(GraphObject): """Groups Data as bars on single value Arguments: executions (List): List of executions to include in group cat (str): category to compare the workloads label (List[str]): Labels for the elements in the group Examples: >>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> b1 = BarGroup([e1, e2, e3], "data1", ["exec1", "exec2", "exec3"]) >>> b2 = BarGroup([e1, 20, e3], "data1", ["exec1", "normal", "exec3"]) """ def __init__(self, executions, cat, label): self.wls = executions self.cat = cat self.label = label def get_data(self, restrict_on): bars = [] for i, w in enumerate(self.wls): bars.append(Bar(w, [self.cat], [self.label[i]])) dfs = [b.get_data(restrict_on).T for b in bars] return dfs def bars(self): bars = [] half = (len(self.wls) - 1 / 2) - 1 for i, w in enumerate(self.wls): if i == half: bars.append(Bar(w, self.cat, self.label[i])) else: bars.append(Bar(w, self.cat, None)) return bars
Ancestors
- progbg.graphing._graph.GraphObject
- abc.ABC
Methods
def bars(self)
-
Expand source code
def bars(self): bars = [] half = (len(self.wls) - 1 / 2) - 1 for i, w in enumerate(self.wls): if i == half: bars.append(Bar(w, self.cat, self.label[i])) else: bars.append(Bar(w, self.cat, None)) return bars
def get_data(self, restrict_on)
-
Expand source code
def get_data(self, restrict_on): bars = [] for i, w in enumerate(self.wls): bars.append(Bar(w, [self.cat], [self.label[i]])) dfs = [b.get_data(restrict_on).T for b in bars] return dfs
class ConstLine (value, label, index, style=':')
-
Const Line Object Will plot a horizontal line
Arguments
value: Value for constant line, either a workload or straight number label (str): Label for the line index: Label used for the value's index. Used when comparing against other lines
Expand source code
class ConstLine(GraphObject): """Const Line Object Will plot a horizontal line Arguments: value: Value for constant line, either a workload or straight number label (str): Label for the line index: Label used for the value's index. Used when comparing against other lines """ def __init__(self, value, label, index, style=":"): self.label = label self.value = value self.index = index self.style = style def get_data(self, restrict_on): val = self.value._cached[0].get_stats()[self.index] return (self.label, val)
Ancestors
- progbg.graphing._graph.GraphObject
- abc.ABC
Methods
def get_data(self, restrict_on)
-
Expand source code
def get_data(self, restrict_on): val = self.value._cached[0].get_stats()[self.index] return (self.label, val)
class CustomGraph (workloads, func, out, formatter=[], style='color_a')
-
Helper class that provides a standard way to create an ABC using inheritance.
Expand source code
class CustomGraph(Graph): def __init__(self, workloads, func, out, formatter=[], style="color_a"): self.workloads = workloads self.formatter = formatter self.formatters = formatter self.style = style self.out = out self._opts = dict( std=True, ) self.html_out = ".".join(out.split(".")[:-1]) + ".svg" self._restrict_on = {} self.func = func def _graph(self, ax, data): self.func(ax, data)
Ancestors
- progbg.graphing._graph.Graph
- abc.ABC
class Line (execution, value: str, x=None, label: str = None, style='--')
-
Line Object Used to specify a line within a graph.
Arguments
workload (Execution, list[Execution]): Specifies an Execution or list to produce a series. value (str): Data label to capture in the series x (str, list): When one execution is specified, represent string label to specify x axis.
Optional
label (str): Label for the line style (str): Style for the line
Examples
>>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> l1 = Line(e1, "data1", x="x_axis_data") >>> # In below line object, line at point 0 will specify e1["data1"], >>> # 1 will specify e2["data1"], and 2 will specify e3["data1"] >>> l2 = Line([e1, e2, e3], "data1", x=[0, 1, 2])
Expand source code
class Line(GraphObject): """Line Object Used to specify a line within a graph. Arguments: workload (Execution, list[Execution]): Specifies an Execution or list to produce a series. value (str): Data label to capture in the series x (str, list): When one execution is specified, represent string label to specify x axis. Optional: label (str): Label for the line style (str): Style for the line Examples: >>> e1 = plan_execution(...) >>> e2 = plan_execution(...) >>> e3 = plan_execution(...) >>> >>> l1 = Line(e1, "data1", x="x_axis_data") >>> # In below line object, line at point 0 will specify e1["data1"], >>> # 1 will specify e2["data1"], and 2 will specify e3["data1"] >>> l2 = Line([e1, e2, e3], "data1", x=[0, 1, 2]) """ def __init__(self, execution, value: str, x=None, label: str = None, style="--"): if label: self.label = label else: self.label = value self.value = value self.workload = execution self.x = x self.style = style assert x is not None, "x must be specified" if isinstance(x, str): assert not isinstance( x, list ), "When x is data ID, only one execution can be specified" if isinstance(x, list): assert len(x) == len( workload ), "When x is a list, workload list length must equal x list length" def get_data(self, restrict_on, iter=None): d = {self.label: [], self.label + "_std": []} if isinstance(self.workload, list): for x in self.workload: d[self.label].append(x._cached[0].get_stats()[self.value]) d[self.label + "_std"].append( x._cached[0].get_stats()[self.value + "_std"] ) return pd.DataFrame(d, index=self.x) else: metrics = filter(self.workload._cached, restrict_on) dicts = [d.get_stats() for d in metrics] df = pd.DataFrame(dicts) df = df.groupby([self.x]) return pd.DataFrame(df.mean()[[self.value, self.value + "_std"]])
Ancestors
- progbg.graphing._graph.GraphObject
- abc.ABC
Methods
def get_data(self, restrict_on, iter=None)
-
Expand source code
def get_data(self, restrict_on, iter=None): d = {self.label: [], self.label + "_std": []} if isinstance(self.workload, list): for x in self.workload: d[self.label].append(x._cached[0].get_stats()[self.value]) d[self.label + "_std"].append( x._cached[0].get_stats()[self.value + "_std"] ) return pd.DataFrame(d, index=self.x) else: metrics = filter(self.workload._cached, restrict_on) dicts = [d.get_stats() for d in metrics] df = pd.DataFrame(dicts) df = df.groupby([self.x]) return pd.DataFrame(df.mean()[[self.value, self.value + "_std"]])
class LineGraph (lines, **kwargs)
-
progbg Line Graph
Args
lines
:List[Line]
- Workloads that the line graph will use in the WRK:BCK1/BCK2 format
- type (str) (Function, optional): Type of line graph (default, cdf)
style
:str, cycler
- Style string for progbg styles or a cycler object to dictate custom style of lines
formatter
:Function
, optional- Formatter to be used on the graph once the graph is complete
out
:str
, optional- Optional name for file the user wishes to save the graph too.
kwargs
:optional
- Passed to matplotlib
Axes.plot
function or optional named params below.
Types of Line Graphs: default: This is just the standard line graph cdf: Creates a CDF line graph
Examples
Suppose we have some previously defined backend
composed_backend
and workloadsWrk
:>>> exec = sb.plan_execution( >>> Wrk({}, [("x", range(0, 5))], iterations = 5), >>> out = "out", >>> backends = [composed_backend({}, >>> [("pass_me_in", range(0, 10, 2))])], >>> parser = file_func, >>> )
Note: We are executing the benchmark over a ranging value called "x". Say we want to see how our stat changes over this value using a line graph. The following would be done:
>>> line1 = Line(exec, "stat-one", x="x", label="Custom Stat") >>> line2 = Line(exec, "stat-two", x="x", label="Custom Stat Two") >>> plan_graph( >>> LineGraph([line1, line2], >>> restrict_on = { >>> "pass_me_in", 0, >>> }, >>> out="custom.svg" >>> )
We restrict on
pass_me_in = 0
as in the above execution we are executing over this as well so we need to isolate on one changing value for the line graph.Expand source code
class LineGraph(Graph): """progbg Line Graph Args: lines (List[Line]): Workloads that the line graph will use in the WRK:BCK1/BCK2 format type (str) (Function, optional): Type of line graph (default, cdf) style (str, cycler): Style string for progbg styles or a cycler object to dictate custom style of lines formatter (Function, optional): Formatter to be used on the graph once the graph is complete out (str, optional): Optional name for file the user wishes to save the graph too. kwargs (optional): Passed to matplotlib `Axes.plot` function or optional named params below. Types of Line Graphs: default: This is just the standard line graph cdf: Creates a CDF line graph Examples: Suppose we have some previously defined backend `composed_backend` and workloads `Wrk`: >>> exec = sb.plan_execution( >>> Wrk({}, [("x", range(0, 5))], iterations = 5), >>> out = "out", >>> backends = [composed_backend({}, >>> [("pass_me_in", range(0, 10, 2))])], >>> parser = file_func, >>> ) Note: We are executing the benchmark over a ranging value called "x". Say we want to see how our stat changes over this value using a line graph. The following would be done: >>> line1 = Line(exec, "stat-one", x="x", label="Custom Stat") >>> line2 = Line(exec, "stat-two", x="x", label="Custom Stat Two") >>> plan_graph( >>> LineGraph([line1, line2], >>> restrict_on = { >>> "pass_me_in", 0, >>> }, >>> out="custom.svg" >>> ) We restrict on `pass_me_in = 0` as in the above execution we are executing over this as well so we need to isolate on one changing value for the line graph. """ def __init__(self, lines, **kwargs): super().__init__(**kwargs) default_options = dict( std=False, group_labels=[], type="default", log=False, width=0.5, ) for prop, default in default_options.items(): setattr(self, prop, kwargs.get(prop, default)) self.consts = [] self.workloads = [] for c in lines: if isinstance(c, ConstLine): self.consts.append(c) else: self.workloads.append(c) self.html_out = ".".join(self.out.split(".")[:-1]) + ".svg" def _graph(self, ax, data): # Hack for dealing with const lines. consts = [x.get_data(self._restrict_on) for x in self.consts] vals = [x for x in data[0].T.columns] styles = [x.style for x in self.workloads] styles_consts = [x.style for x in self.consts] # Combine data data = pd.concat(data, axis=1) consts = [pd.DataFrame({c[0]: [c[1]] * len(vals)}, index=vals) for c in consts] if len(consts): consts = pd.concat(consts, axis=1) # Pull out the standard deviation and such cols = [c for c in data.columns if c[-4:] != "_std"] cols_std = [c for c in data.columns if len(c) > 4 and c[-4:] == "_std"] d = data[cols] std = data[cols_std] std.columns = [x[:-4] for x in std.columns] y = [x for x in d.T.columns] # It seems like styles is not respected setting them so we will manually do them style = iter(get_style_cycler()) for i, x in enumerate(d.columns): tmp = next(style) if self.std: ax.errorbar(y, d[x].tolist(), yerr=std[x], **tmp) else: ax.plot(y, d[x].tolist(), styles[i], **tmp) if len(consts): for i, x in enumerate(consts.columns): tmp = next(style) ax.plot(y, consts[x].tolist(), **tmp)
Ancestors
- progbg.graphing._graph.Graph
- abc.ABC