def test_setitem(self): tree = Tree() tree['a'] = {'b': 1, 'c': 2} self.assertIsInstance(tree, Tree) self.assertIsInstance(tree['a'], Tree) self.assertEqual(tree.getval(['a', 'b']), 1) self.assertEqual(tree.getval(['a', 'c']), 2)
def filter(self, condition): """Return subset of results matching specific conditions Parameters ---------- condition : dict Dictionary listing all parameters and values to be matched in the results set. Each parameter, i.e., each key of the dictionary must be an iterable object containing the path in the parameters tree to the required parameter metrics : dict, optional List of metrics to be reported Returns ------- filtered_results : ResultSet List of 2-tuples of filtered results, where the first element is a tree of all experiment parameters and the second value is a tree with experiment results. """ filtered_resultset = ResultSet() for parameters, results in self._results: parameters = Tree(parameters) if parameters.match(condition): filtered_resultset.add(parameters, results) return filtered_resultset
def test_paths(self): tree = Tree() tree['b']['c']['e'] = 4 tree['b']['v']['d'] = 3 tree['a'] = 1 expected = {('b', 'c', 'e'): 4, ('b', 'v', 'd'): 3, ('a', ): 1} assert expected == tree.paths()
def test_paths(self): tree = Tree() tree['b']['c']['e'] = 4 tree['b']['v']['d'] = 3 tree['a'] = 1 expected = {('b', 'c', 'e'): 4, ('b', 'v', 'd'): 3, ('a',): 1} self.assertDictEqual(expected, tree.paths())
def test_update_new_brach(self): tree = Tree() tree['a'].update({'b': 1, 'c': 2}) self.assertIsInstance(tree, Tree) self.assertIsInstance(tree['a'], Tree) self.assertEqual(tree.getval(['a', 'b']), 1) self.assertEqual(tree.getval(['a', 'c']), 2)
def test_update_new_brach(self): tree = Tree() tree['a'].update({'b': 1, 'c': 2}) assert isinstance(tree, Tree) assert isinstance(tree['a'], Tree) assert tree.getval(['a', 'b']) == 1 assert tree.getval(['a', 'c']) == 2
def test_paths(self): tree = Tree() tree['b']['c']['e'] = 4 tree['b']['v']['d'] = 3 tree['a'] = 1 expected = {('b', 'c', 'e'): 4, ('b', 'v', 'd'): 3, ('a', ): 1} self.assertDictEqual(expected, tree.paths())
def test_update_new_brach(self): tree = Tree() tree["a"].update({"b": 1, "c": 2}) assert isinstance(tree, Tree) assert isinstance(tree["a"], Tree) assert tree.getval(["a", "b"]) == 1 assert tree.getval(["a", "c"]) == 2
def test_setitem(self): tree = Tree() tree["a"] = {"b": 1, "c": 2} assert isinstance(tree, Tree) assert isinstance(tree["a"], Tree) assert tree.getval(["a", "b"]) == 1 assert tree.getval(["a", "c"]) == 2
def test_paths(self): tree = Tree() tree["b"]["c"]["e"] = 4 tree["b"]["v"]["d"] = 3 tree["a"] = 1 expected = {("b", "c", "e"): 4, ("b", "v", "d"): 3, ("a", ): 1} assert expected == tree.paths()
def test_getval_empty(self): tree = Tree() _ = tree[1][2][3] self.assertIsNotNone(tree.getval([1])) self.assertIsNotNone(tree.getval([1, 2])) self.assertIsNone(tree.getval([1, 2, 3])) self.assertIsNone(tree.getval([1, 2, 3, 4]))
def test_setitem(self): tree = Tree() tree['a'] = {'b': 1, 'c': 2} assert isinstance(tree, Tree) assert isinstance(tree['a'], Tree) assert tree.getval(['a', 'b']) == 1 assert tree.getval(['a', 'c']) == 2
def test_nested_update(self): tree = Tree() tree["a"].update({"b": {"c": 1}, "d": 2}) assert isinstance(tree, Tree) assert isinstance(tree["a"], Tree) assert isinstance(tree["a"]["b"], Tree) assert tree.getval(["a", "b", "c"]) == 1 assert tree.getval(["a", "d"]) == 2
def test_nested_update(self): tree = Tree() tree['a'].update({'b': {'c': 1}, 'd': 2}) self.assertIsInstance(tree, Tree) self.assertIsInstance(tree['a'], Tree) self.assertIsInstance(tree['a']['b'], Tree) self.assertEqual(tree.getval(['a', 'b', 'c']), 1) self.assertEqual(tree.getval(['a', 'd']), 2)
def test_nested_update(self): tree = Tree() tree['a'].update({'b': {'c': 1}, 'd': 2}) assert isinstance(tree, Tree) assert isinstance(tree['a'], Tree) assert isinstance(tree['a']['b'], Tree) assert tree.getval(['a', 'b', 'c']) == 1 assert tree.getval(['a', 'd']) == 2
def test_init_from_nested_dict(self): tree = Tree({'a': {'c': {'e': 1}}, 'b': {'d': 2}}) self.assertEqual(tree.getval(['a', 'c', 'e']), 1) self.assertEqual(tree.getval(['b', 'd']), 2) self.assertIsInstance(tree, Tree) self.assertIsInstance(tree['a'], Tree) self.assertIsInstance(tree['a']['c'], Tree) self.assertIsInstance(tree.getval(['a', 'c']), Tree) self.assertIsInstance(tree['b'], Tree)
def test_init_from_nested_dict(self): tree = Tree({"a": {"c": {"e": 1}}, "b": {"d": 2}}) assert tree.getval(["a", "c", "e"]) == 1 assert tree.getval(["b", "d"]) == 2 assert isinstance(tree, Tree) assert isinstance(tree["a"], Tree) assert isinstance(tree["a"]["c"], Tree) assert isinstance(tree.getval(["a", "c"]), Tree) assert isinstance(tree["b"], Tree)
def test_init_from_nested_dict(self): tree = Tree({'a': {'c': {'e': 1}}, 'b': {'d': 2}}) assert tree.getval(['a', 'c', 'e']) == 1 assert tree.getval(['b', 'd']) == 2 assert isinstance(tree, Tree) assert isinstance(tree['a'], Tree) assert isinstance(tree['a']['c'], Tree) assert isinstance(tree.getval(['a', 'c']), Tree) assert isinstance(tree['b'], Tree)
def test_match(self): t = {'a': {'b': 1}, 'c': 2, 'd': {'e': 3}} pos_match_equal = {'a': {'b': 1}, 'c': 2, 'd': {'e': 3}} pos_match_subset = {'a': {'b': 1}, 'd': {'e': 3}} neg_match_diff = {'a': {'b': 2}, 'c': 2, 'd': {'e': 3}} neg_match_superset = {'a': {'b': 1}, 'c': 2, 'd': {'e': 3}, 'f': 3} tree = Tree(t) self.assertTrue(tree.match(pos_match_equal)) self.assertTrue(tree.match(pos_match_subset)) self.assertFalse(tree.match(neg_match_diff)) self.assertFalse(tree.match(neg_match_superset))
def results(self): n_sess = self.cache_hits + self.serv_hits hit_ratio = self.cache_hits / n_sess results = Tree(**{'MEAN': hit_ratio}) if self.off_path_hits: results['MEAN_OFF_PATH'] = self.off_path_hit_count / n_sess results[ 'MEAN_ON_PATH'] = results['MEAN'] - results['MEAN_OFF_PATH'] if self.cont_hits: cont_set = set( list(self.cont_cache_hits.keys()) + list(self.cont_serv_hits.keys())) cont_hits = dict( (i, (self.cont_cache_hits[i] / (self.cont_cache_hits[i] + self.cont_serv_hits[i]))) for i in cont_set) results['PER_CONTENT'] = cont_hits if self.per_node: for v in self.per_node_cache_hits: self.per_node_cache_hits[v] /= n_sess for v in self.per_node_server_hits: self.per_node_server_hits[v] /= n_sess results['PER_NODE_CACHE_HIT_RATIO'] = self.per_node_cache_hits results['PER_NODE_SERVER_HIT_RATIO'] = self.per_node_server_hits return results
def results(self): if self.cdf: results['CDF'] = cdf(self.latency_data) results = Tree( {'SATISFACTION': 1.0 * self.n_satisfied / self.sess_count}) per_service_sats = {} for service in self.service_requests.keys(): per_service_sats[service] = 1.0 * self.service_satisfied[ service] / self.service_requests[service] results['PER_SERVICE_SATISFACTION'] = per_service_sats results['PER_SERVICE_REQUESTS'] = self.service_requests results['PER_SERVICE_SAT_REQUESTS'] = self.service_satisfied results['SAT_TIMES'] = self.satrate_times results['IDLE_TIMES'] = self.idle_times results['NODE_IDLE_TIMES'] = self.node_idle_times results['LATENCY'] = self.latency_times results['DEADLINE_METRIC'] = self.deadline_metric_times results['CLOUD_SAT_TIMES'] = self.cloud_sat_times results['INSTANTIATION_OVERHEAD'] = self.instantiations_times #print "Printing Sat. rate times:" #for key in sorted(self.satrate_times): # print (repr(key) + " " + repr(self.satrate_times[key])) #print "Printing Idle times:" #for key in sorted(self.idle_times): # print (repr(key) + " " + repr(self.idle_times[key])) #results['VMS_PER_SERVICE'] = self.vms_per_service return results
def results(self): duration = self.t_end - self.t_start used_links = set(self.req_count.keys()).union( set(self.cont_count.keys())) link_loads = { link: (self.req_size * self.req_count[link] + self.content_size * self.cont_count[link]) / duration for link in used_links } link_loads_int = { link: load for link, load in link_loads.items() if self.view.link_type(*link) == "internal" } link_loads_ext = { link: load for link, load in link_loads.items() if self.view.link_type(*link) == "external" } mean_load_int = (sum(link_loads_int.values()) / len(link_loads_int) if len(link_loads_int) > 0 else 0) mean_load_ext = (sum(link_loads_ext.values()) / len(link_loads_ext) if len(link_loads_ext) > 0 else 0) return Tree({ "MEAN_INTERNAL": mean_load_int, "MEAN_EXTERNAL": mean_load_ext, "PER_LINK_INTERNAL": link_loads_int, "PER_LINK_EXTERNAL": link_loads_ext, })
def results(self): result_dict = {} for node in self.window_sizes: if self.view.model.cache[node].__class__.__name__ in \ ['DataStreamCachingAlgorithmWithAdaptiveWindowSizeCache']: result_dict['node %d: window sizes' % node] = self.window_sizes[node] return Tree(result_dict)
def results(self): results = Tree({'MEAN': self.mean_stretch/self.sess_count, 'MEAN_REQUEST': self.mean_req_stretch/self.sess_count, 'MEAN_CONTENT': self.mean_cont_stretch/self.sess_count}) if self.cdf: results['CDF'] = cdf(self.stretch_data) results['CDF_REQUEST'] = cdf(self.req_stretch_data) results['CDF_CONTENT'] = cdf(self.cont_stretch_data) return results
def results(self): duration = self.t_end - self.t_start return Tree({ 'CONTENT_HOP_COUNT_PER_EDGE': self.cont_count, 'REQUESTS_COUNT_PER_EDGE': self.req_count, 'ALL_REQUESTS': self.req_timeline, 'ALL_SOURCES': self.all_sources_dic })
def test_pickle_empty(self): tree = Tree() f = BytesIO() pickle.dump(tree, f) f.seek(0) tree_2 = pickle.load(f) assert type(tree) == type(tree_2) assert tree == tree_2 assert isinstance(tree, Tree) assert isinstance(tree_2, Tree)
def results(self): results = Tree({ 'MEAN_RSN_ZERO_HOP': np.mean(self.rsn_hit_ratio[0]), 'MEAN_RSN_ONE_HOP': np.mean(self.rsn_hit_ratio[1]), 'MEAN_RSN_TWO_HOP': np.mean(self.rsn_hit_ratio[2]), 'MEAN_RSN_THREE_HOP': np.mean(self.rsn_hit_ratio[3]), }) results['MEAN_RSN_ALL'] = results['MEAN_RSN_ZERO_HOP'] + \ results['MEAN_RSN_ONE_HOP'] + \ results['MEAN_RSN_TWO_HOP'] + \ results['MEAN_RSN_THREE_HOP'] if self.cdf: results.update({ 'CDF_RSN_ZERO_HOP': cdf(self.rsn_hit_ratio[0]), 'CDF_RSN_ONE_HOP': cdf(self.rsn_hit_ratio[1]), 'CDF_RSN_TWO_HOP': cdf(self.rsn_hit_ratio[2]), 'CDF_RSN_THREE_HOP': cdf(self.rsn_hit_ratio[3]), }) return results
def test_pickle_empty(self): tree = Tree() f = BytesIO() pickle.dump(tree, f) f.seek(0) tree_2 = pickle.load(f) self.assertEquals(type(tree), type(tree_2)) self.assertEquals(tree, tree_2) self.assertIsInstance(tree, Tree) self.assertIsInstance(tree_2, Tree)
def test_getval_empty(self): tree = Tree() _ = tree[1][2][3] assert tree.getval([1]) is not None assert tree.getval([1, 2]) is not None assert tree.getval([1, 2, 3]) is None assert tree.getval([1, 2, 3, 4]) is None
def add(self, parameters, results): """Add a result to the result set. Parameters ---------- parameters : Tree Tree of experiment parameters results : Tree Tree of experiment results Notes ----- If parameters and results are dictionaries, this method will attempt to convert them to trees and storing them anyway. It is necessary that parameters and results are saved as trees so that plotting functions can search correctly in them. """ if not isinstance(parameters, Tree): parameters = Tree(parameters) if not isinstance(results, Tree): results = Tree(results) self._results.append((parameters, results))
def results(self): results = Tree({ "MEAN": self.mean_stretch / self.sess_count, "MEAN_REQUEST": self.mean_req_stretch / self.sess_count, "MEAN_CONTENT": self.mean_cont_stretch / self.sess_count, }) if self.cdf: results["CDF"] = cdf(self.stretch_data) results["CDF_REQUEST"] = cdf(self.req_stretch_data) results["CDF_CONTENT"] = cdf(self.cont_stretch_data) return results
def test_pickle(self): tree = Tree() tree[1][2][3] = '123' tree[1][2][4] = '124' f = BytesIO() pickle.dump(tree, f) f.seek(0) tree_2 = pickle.load(f) self.assertEquals(type(tree), type(tree_2)) self.assertEquals(tree, tree_2) self.assertEquals(tree[1][2][3], '123') self.assertEquals(tree_2[1][2][3], '123') self.assertIsInstance(tree, Tree) self.assertIsInstance(tree_2, Tree)
def test_pickle(self): tree = Tree() tree[1][2][3] = '123' tree[1][2][4] = '124' f = BytesIO() pickle.dump(tree, f) f.seek(0) tree_2 = pickle.load(f) assert type(tree) == type(tree_2) assert tree == tree_2 assert tree[1][2][3] == '123' assert tree_2[1][2][3] == '123' assert isinstance(tree, Tree) assert isinstance(tree_2, Tree)
def plot_cdf(resultset, desc, filename, plotdir): """Plot a CDF with characteristics described in the plot descriptor out of the data contained in the resultset and save the plot in given directory. Parameters ---------- rs : ResultSet Result set desc : dict The plot descriptor (more info below) filename : str The name used to save the file. The file format is determined by the extension of the file. For example, if this filename is 'foo.pdf', the file will be saved in pdf format. plotdir : str The directory in which the plot will be saved. Notes ----- The plot descriptor is a dictionary with a set of values that describe how to make the plot. The dictionary can contain the following keys: * title : str, optional. The title of the graph * xlabel : str, optional The x label * ylabel : str, optional The y label. The default value is 'Cumulative probability' * confidence : float, optional The confidence used to plot error bars. Default value is 0.95 * metric : list A list of values representing the metric to plot. These values are the path to identify a specific metric into an entry of a result set. Normally, it is a 2-value list where the first value is the name of the collector which measured the metric and the second value is the metric name. The metric must be a CDF. Example values could be ['LATENCY', 'CDF']. * filter : dict, optional A dictionary of values to filter in the resultset. Example: {'network_cache': 0.004, 'topology_name': 'GEANT'} If not specified or None, no filtering is executed on the results and possibly heterogeneous results may be plotted together * ymetrics : list of tuples List of metrics to be shown on the graph. The i-th metric of the list is the metric that the i-th line on the graph will represent. If all lines are for the same metric, then all elements of the list are equal. Each single metric (i.e. each element of the list) is a tuple modeling the path to identify a specific metric into an entry of a result set. Normally, it is a 2-value list where the first value is the name of the collector which measured the metric and the second value is the metric name. Example values could be ('CACHE_HIT_RATIO', 'MEAN'), ('LINK_LOAD', 'MEAN_INTERNAL') or ('LATENCY', 'MEAN'). For example, if in a graph of N lines all lines of the graph show mean latency, then ymetrics = [('LATENCY', 'MEAN')]*5. * ycondnames : list of tuples, optional List of condition names specific to each line of the graph. Different from the conditions expressed in the filter parameter, which are global, these conditions are specific to one bar. Ech condition name, different from the filter parameter is a path to a condition to be checked, e.g. ('topology', 'name'). Values to be matched for this conditions are specified in ycondvals. This list must be as long as the number of lines to plot. If not specified, all lines are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * ycondvals : list of tuples, optional List of values that the conditions of ycondnames must meet. This list must be as long as the number of lines to plot. If not specified, all lines are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * xscale : str, optional The scale of x axis. Options allowed are 'linear' and 'log'. Default value is 'linear' * yscale : str, optional The scale of y axis. Options allowed are 'linear' and 'log'. Default value is 'linear' * step : bool, optional If *True* draws the CDF with steps. Default value is *True* * line_style : dict, optional Dictionary mapping each value of yvals with a line style * legend : dict, optional Dictionary mapping each value of yvals with a legend label. If not specified, it is not plotted. If you wish to plot it with the name of the line, set it to put yvals or ymetrics, depending on which one is used * legend_loc : str, optional Legend location, e.g. 'upper left' * legend_args : dict, optional Optional legend arguments, such as ncol * plotempty : bool, optional If *True*, plot and save graph even if empty. Default is *True* """ fig = plt.figure() if 'title' in desc: plt.title(desc['title']) if 'xlabel' in desc: plt.xlabel(desc['xlabel']) plt.ylabel(desc['ylabel'] if 'ylabel' in desc else 'Cumulative probability') if 'xscale' in desc: plt.xscale(desc['xscale']) if 'yscale' in desc: plt.yscale(desc['yscale']) if 'filter' not in desc or desc['filter'] is None: desc['filter'] = {} step = desc['step'] if 'step' in desc else True plot_empty = desc['plotempty'] if 'plotempty' in desc else True ymetrics = desc['ymetrics'] ycondnames = desc['ycondnames'] if 'ycondnames' in desc else None ycondvals = desc['ycondvals'] if 'ycondvals' in desc else None if ycondnames is not None and ycondvals is not None: if not len(ymetrics) == len(ycondnames) == len(ycondvals): raise ValueError('ymetrics, ycondnames and ycondvals must have the same length') # yvals is basically the list of values that differentiate each line # it is used for legends and styles mainly yvals = ycondvals if len(set(ymetrics)) == 1 else zip(ymetrics, ycondvals) else: yvals = ymetrics x_min = np.infty x_max = - np.infty empty = True for i in range(len(yvals)): condition = Tree(desc['filter']) if ycondnames is not None: condition.setval(ycondnames[i], ycondvals[i]) data = [v.getval(ymetrics[i]) for _, v in resultset.filter(condition) if v.getval(ymetrics[i]) is not None] # If there are more than 1 CDFs in the resultset, take the first one if data: x_cdf, y_cdf = data[0] if step: x_cdf, y_cdf = step_cdf(x_cdf, y_cdf) else: x_cdf, y_cdf = [], [] fmt = desc['line_style'][yvals[i]] if 'line_style' in desc \ and yvals[i] in desc['line_style'] else '-' # This check is to prevent crashing when trying to plot arrays of nan # values with axes log scale if all(np.isnan(x) for x in x_cdf) or all(np.isnan(y) for y in y_cdf): plt.plot([], [], fmt) else: plt.plot(x_cdf, y_cdf, fmt) empty = False x_min = min(x_min, x_cdf[0]) x_max = max(x_max, x_cdf[-1]) if empty and not plot_empty: return plt.xlim(x_min, x_max) if 'legend' in desc: legend = [desc['legend'][l] for l in desc['yvals']] legend_args = desc['legend_args'] if 'legend_args' in desc else {} if 'legend_loc' in desc: legend_args['loc'] = desc['legend_loc'] plt.legend(legend, prop={'size': LEGEND_SIZE}, **legend_args) plt.legend(legend, prop={'size': LEGEND_SIZE}, loc=desc['legend_loc']) plt.savefig(os.path.join(plotdir, filename), bbox_inches='tight') plt.close(fig)
def plot_lines(resultset, desc, filename, plotdir): """Plot a graph with characteristics described in the plot descriptor out of the data contained in the resultset and save the plot in given directory. Parameters ---------- rs : ResultSet Result set desc : dict The plot descriptor (more info below) filename : str The name used to save the file. The file format is determined by the extension of the file. For example, if this filename is 'foo.pdf', the file will be saved in pdf format. plotdir : str The directory in which the plot will be saved. Notes ----- The plot descriptor is a dictionary with a set of values that describe how to make the plot. The dictionary can contain the following keys: * title : str, optional. The title of the graph * xlabel : str, optional The x label * ylabel : str, optional The y label * errorbar : bool, optional If *True* error bars will be plotted. Default value is *True* * confidence : float, optional The confidence used to plot error bars. Default value is 0.95 * xparam : iterable Path to the value of the x axis metric, e.g. ['workload', 'alpha'] * xvals : list Range of x values, e.g. [0.6, 0.7, 0.8, 0.9] * filter : dict, optional A dictionary of values to filter in the resultset. Example: {'network_cache': 0.004, 'topology_name': 'GEANT'} If not specified or None, no filtering is executed on the results and possibly heterogeneous results may be plotted together * ymetrics : list of tuples List of metrics to be shown on the graph. The i-th metric of the list is the metric that the i-th line on the graph will represent. If all lines are for the same metric, then all elements of the list are equal. Each single metric (i.e. each element of the list) is a tuple modeling the path to identify a specific metric into an entry of a result set. Normally, it is a 2-value list where the first value is the name of the collector which measured the metric and the second value is the metric name. Example values could be ('CACHE_HIT_RATIO', 'MEAN'), ('LINK_LOAD', 'MEAN_INTERNAL') or ('LATENCY', 'MEAN'). For example, if in a graph of N lines all lines of the graph show mean latency, then ymetrics = [('LATENCY', 'MEAN')]*5. * ycondnames : list of tuples, optional List of condition names specific to each line of the graph. Different from the conditions expressed in the filter parameter, which are global, these conditions are specific to one bar. Ech condition name, different from the filter parameter is a path to a condition to be checked, e.g. ('topology', 'name'). Values to be matched for this conditions are specified in ycondvals. This list must be as long as the number of lines to plot. If not specified, all lines are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * ycondvals : list of tuples, optional List of values that the conditions of ycondnames must meet. This list must be as long as the number of lines to plot. If not specified, all lines are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * xscale : ('linear' | 'log'), optional The scale of x axis. Default value is 'linear' * yscale : ('linear' | 'log'), optional The scale of y axis. Default value is 'linear' * xticks : list, optional Values to display as x-axis ticks. * yticks : list, optional Values to display as y-axis ticks. * line_style : dict, optional Dictionary mapping each value of yvals with a line style * plot_args : dict, optional Additional args to be provided to the Pyplot errorbar function. Example parameters that can be specified here are *linewidth* and *elinewidth* * legend : dict, optional Dictionary mapping each value of yvals with a legend label. If not specified, it is not plotted. If you wish to plot it with the name of the line, set it to put yvals or ymetrics, depending on which one is used * legend_loc : str, optional Legend location, e.g. 'upper left' * legend_args : dict, optional Optional legend arguments, such as ncol * plotempty : bool, optional If *True*, plot and save graph even if empty. Default is *True* * xmin, xmax: float, optional The limits of the x axis. If not specified, they're set to the min and max values of xvals * ymin, ymax: float, optional The limits of the y axis. If not specified, they're automatically selected by Matplotlib """ fig = plt.figure() _, ax1 = plt.subplots() if 'title' in desc: plt.title(desc['title']) if 'xlabel' in desc: plt.xlabel(desc['xlabel']) if 'ylabel' in desc: plt.ylabel(desc['ylabel']) if 'xscale' in desc: plt.xscale(desc['xscale']) if 'yscale' in desc: plt.yscale(desc['yscale']) if 'filter' not in desc or desc['filter'] is None: desc['filter'] = {} xvals = sorted(desc['xvals']) if 'xticks' in desc: ax1.set_xticks(desc['xticks']) ax1.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax1.set_xticklabels([str(xtick) for xtick in desc['xticks']]) if 'yticks' in desc: ax1.set_yticks(desc['yticks']) ax1.get_yaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax1.set_yticklabels([str(ytick) for ytick in desc['yticks']]) ymetrics = desc['ymetrics'] ycondnames = desc['ycondnames'] if 'ycondnames' in desc else None ycondvals = desc['ycondvals'] if 'ycondvals' in desc else None if ycondnames is not None and ycondvals is not None: if not len(ymetrics) == len(ycondnames) == len(ycondvals): raise ValueError('ymetrics, ycondnames and ycondvals must have the same length') # yvals is basically the list of values that differentiate each line # it is used for legends and styles mainly yvals = ycondvals if len(set(ymetrics)) == 1 else zip(ymetrics, ycondvals) else: yvals = ymetrics plot_args = desc['plot_args'] if 'plot_args' in desc else {} plot_empty = desc['plotempty'] if 'plotempty' in desc else True empty = True for i in range(len(yvals)): means = np.zeros(len(xvals)) err = np.zeros(len(xvals)) for j in range(len(xvals)): condition = Tree(desc['filter']) condition.setval(desc['xparam'], xvals[j]) if ycondnames is not None: condition.setval(ycondnames[i], ycondvals[i]) data = [v.getval(ymetrics[i]) for _, v in resultset.filter(condition) if v.getval(ymetrics[i]) is not None] confidence = desc['confidence'] if 'confidence' in desc else 0.95 means[j], err[j] = means_confidence_interval(data, confidence) yerr = None if 'errorbar' in desc and not desc['errorbar'] or all(err == 0) else err fmt = desc['line_style'][yvals[i]] if 'line_style' in desc \ and yvals[i] in desc['line_style'] else '-' # This check is to prevent crashing when trying to plot arrays of nan # values with axes log scale if all(np.isnan(x) for x in xvals) or all(np.isnan(y) for y in means): plt.errorbar([], [], fmt=fmt) else: plt.errorbar(xvals, means, yerr=yerr, fmt=fmt, **plot_args) empty = False if empty and not plot_empty: return x_min = desc['xmin'] if 'xmin' in desc else min(xvals) x_max = desc['xmax'] if 'xmax' in desc else max(xvals) plt.xlim(x_min, x_max) if 'ymin' in desc: plt.ylim(ymin=desc['ymin']) if 'ymax' in desc: plt.ylim(ymax=desc['ymax']) if 'legend' in desc: legend = [desc['legend'][l] for l in yvals] legend_args = desc['legend_args'] if 'legend_args' in desc else {} if 'legend_loc' in desc: legend_args['loc'] = desc['legend_loc'] plt.legend(legend, prop={'size': LEGEND_SIZE}, **legend_args) plt.savefig(os.path.join(plotdir, filename), bbox_inches='tight') plt.close(fig)
def plot_bar_chart(resultset, desc, filename, plotdir): """Plot a bar chart with characteristics described in the plot descriptor out of the data contained in the resultset and save the plot in given directory. Parameters ---------- rs : ResultSet Result set desc : dict The plot descriptor (more info below) filename : str The name used to save the file. The file format is determined by the extension of the file. For example, if this filename is 'foo.pdf', the file will be saved in pdf format. plotdir : str The directory in which the plot will be saved. Notes ----- The plot descriptor is a dictionary with a set of values that describe how to make the plot. The dictionary can contain the following keys: * title : str, optional. The title of the graph * xlabel : str, optional The x label * ylabel : str, optional The y label * errorbar : bool, optional If *True* error bars will be plotted. Default value is *True* * confidence : float, optional The confidence used to plot error bars. Default value is 0.95 * filter : tree or dict of dicts, optional A tree or nested dictionary of values to include from the resultset. Example: {'cache_placement': {'network_cache': 0.004}, 'topology': {'name', 'GEANT'}}. If not specified or None, no filtering is executed on the results and possibly heterogeneous results may be plotted together. * xparam : tuple The path of the x axis metric, e.g. ('workload', 'alpha') * xvals : list Range of x values, e.g. [0.6, 0.7, 0.8, 0.9] * xticks : list, optional Names to display as ticks. If not specified, xvals is used instead * ymetrics : list of tuples List of metrics to be shown on the graph. The i-th metric of the list is the metric that the i-th bar on the graph will represent. If all bars are for the same metric, then all elements of the list are equal. Each single metric (i.e. each element of the list) is a tuple modeling the path to identify a specific metric into an entry of a result set. Normally, it is a 2-value list where the first value is the name of the collector which measured the metric and the second value is the metric name. Example values could be ('CACHE_HIT_RATIO', 'MEAN'), ('LINK_LOAD', 'MEAN_INTERNAL') or ('LATENCY', 'MEAN'). For example, if in a graph of N bars all bar of the graph show mean latency, then ymetrics = [('LATENCY', 'MEAN')]*5. * ycondnames : list of tuples, optional List of condition names specific to each bar of the graph. Different from the conditions expressed in the filter parameter, which are global, these conditions are specific to one bar. Ech condition name, different from the filter parameter is a path to a condition to be checked, e.g. ('topology', 'name'). Values to be matched for this conditions are specified in ycondvals. This list must be as long as the number of bars to plot. If not specified, all bars are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * ycondvals : list of tuples, optional List of values that the conditions of ycondnames must meet. This list must be as long as the number of bars to plot. If not specified, all bars are filtered by the conditions of filter parameter only, but in this case all ymetrics should be different. * placement : (grouped | stacked | [x, y, ...]) Defines how to place bars in the plot. If grouped, defaults, all bars for a specific xval are grouped next to each other, if stacked, they are plot on top of each other. It is also possible to specify a custom grouped+stacked placement with a list of integers, in which the number of items is the number of columns and the actual value of an items is the number of metrics stacked on the column. For example [4,2,3] means plotting 4 + 2 + 3 = 9 metrics: 4 stacked in the first column, 2 stacked on the second and 3 stacked on the third If *True*, draw all bars of a group stacked on top of each other. Default value is *False*. * group_width : float, default: 0.4 Width of a group of bars * bar_color : dict, optional Dictionary mapping each value of yvals with a bar color * bar_hatch : dict, optional Dictionary mapping each value of yvals with a bar hatch. If set to None all bars will be plotted without hatch. If not set, hatches will be plotted randomly * legend : dict, optional Dictionary mapping each value of yvals with a legend label. If not specified, it is not plotted. If you wish to plot it with the name of the line, set it to put yvals or ymetrics, depending on which one is used * legend_loc : str, optional Legend location, e.g. 'upper left' * legend_args : dict, optional Optional legend arguments, such as ncol * plotempty : bool, optional If *True*, plot and save graph even if empty. Default is *True* * ymax: float, optional The upper limit of the y axis. If not specified, it is automatically selected by Matplotlib """ fig = plt.figure() if 'title' in desc: plt.title(desc['title']) plt.subplot(111) plt.grid(b=True, which='major', color='k', axis='y', linestyle='--') if 'xlabel' in desc: plt.xlabel(desc['xlabel']) if 'ylabel' in desc: plt.ylabel(desc['ylabel']) if 'filter' not in desc or desc['filter'] is None: desc['filter'] = {} plot_empty = desc['plotempty'] if 'plotempty' in desc else True ymetrics = desc['ymetrics'] ycondnames = desc['ycondnames'] if 'ycondnames' in desc else None ycondvals = desc['ycondvals'] if 'ycondvals' in desc else None if ycondnames is not None and ycondvals is not None: if not len(ymetrics) == len(ycondnames) == len(ycondvals): raise ValueError('ymetrics, ycondnames and ycondvals must have the same length') # yvals is basically the list of values that differentiate each bar # it is used for legends and styles mainly yvals = ycondvals if len(set(ymetrics)) == 1 else zip(ymetrics, ycondvals) else: yvals = ymetrics placement = desc['placement'] if 'placement' in desc else 'grouped' if placement == 'grouped': placement = [1 for _ in range(len(yvals))] elif placement == 'stacked': placement = [len(yvals)] else: if sum(placement) != len(yvals): raise ValueError('Placement definition incorrect. ' 'The sum of values of the list must be equal to ' 'the number of y values') xticks = desc['xticks'] if 'xticks' in desc else desc['xvals'] empty = True # Spacing attributes # width of a group of bars group_width = desc['group_width'] if 'group_width' in desc else 0.4 width = group_width/len(placement) # width of a single bar separation = width/2 # space between adjacent groups border = 0.6 * separation # left and right borders elem = collections.defaultdict(int) # bar objects (for legend) # Select colors and hatches if 'bar_color' in desc and all(y in desc['bar_color'] for y in yvals): color = desc['bar_color'] elif len(yvals) <= len(BW_COLOR_CATALOGUE): color = dict((y, BW_COLOR_CATALOGUE[yvals.index(y)]) for y in yvals) else: color = collections.defaultdict(lambda: None) if 'bar_hatch' in desc and desc['bar_hatch'] is None: hatch = collections.defaultdict(lambda: None) elif 'bar_hatch' in desc and all(y in desc['bar_hatch'] for y in yvals): hatch = desc['bar_hatch'] elif len(yvals) <= len(BW_COLOR_CATALOGUE): hatch = dict((y, HATCH_CATALOGUE[yvals.index(y)]) for y in yvals) else: hatch = collections.defaultdict(lambda: None) # Plot bars left = border # left-most point of the bar about to draw for i in range(len(desc['xvals'])): l = 0 for x in placement: bottom = 0 # Bottom point of a bar. It is alway 0 if stacked is False for y in range(x): condition = Tree(desc['filter']) condition.setval(desc['xparam'], desc['xvals'][i]) if ycondnames is not None: condition.setval(ycondnames[l], ycondvals[l]) data = [v.getval(ymetrics[i]) for _, v in resultset.filter(condition) if v.getval(ymetrics[i]) is not None] confidence = desc['confidence'] if 'confidence' in desc else 0.95 meanval, err = means_confidence_interval(data, confidence) yerr = None if 'errorbar' in desc and not desc['errorbar'] else err if not np.isnan(meanval): empty = False elem[yvals[l]] = plt.bar(left, meanval, width, color=color[yvals[l]], yerr=yerr, bottom=bottom, ecolor='k', hatch=hatch[yvals[l]], label=yvals[l]) bottom += meanval l += 1 left += width left += separation if empty and not plot_empty: return n_bars = len(placement) plt.xticks(border + 0.5*(n_bars*width) + (separation + n_bars*width)*np.arange(len(xticks)), xticks) if 'legend' in desc: legend = [desc['legend'][l] for l in yvals] if 'legend'in desc else yvals legend_args = desc['legend_args'] if 'legend_args' in desc else {} if 'legend_loc' in desc: legend_args['loc'] = desc['legend_loc'] plt.legend([elem[x] for x in yvals], legend, prop={'size': LEGEND_SIZE}, **legend_args) xmin, _ = plt.xlim() plt.xlim(xmin, left - separation + border) if 'ymax' in desc: plt.ylim(ymax=desc['ymax']) plt.savefig(os.path.join(plotdir, filename), bbox_inches='tight') plt.close(fig)
def test_dict_2(self): d = {'a': {'b': 'a'}, 'b': 'c', 'd': {'b': 'c'}} tree = Tree(d) self.assertEqual(d, tree.dict())
def test_dict_3(self): d = {'a': {'b': [1, 2, 'v']}, 'b': 'c', 'd': {'b': 4}} tree = Tree(d) self.assertEqual(d, tree.dict())
def test_init_from_nested_kwargs(self): tree = Tree(a=1, b=dict(c=2)) self.assertEqual(tree.getval(['a']), 1) self.assertEqual(tree.getval(['b', 'c']), 2) self.assertIsInstance(tree, Tree) self.assertIsInstance(tree['b'], Tree)
def test_init_from_dict(self): tree = Tree({'a': 1, 'b': 2}) self.assertEqual(tree.getval(['a']), 1) self.assertEqual(tree.getval(['b']), 2) self.assertIsInstance(tree, Tree)
def test_dict_1(self): d = {'a': 1, 'b': 2} tree = Tree(d) self.assertEqual(d, tree.dict())
def test_update_base(self): tree = Tree() tree.update({'b': 1, 'c': 2}) self.assertIsInstance(tree, Tree) self.assertEqual(tree.getval(['b']), 1) self.assertEqual(tree.getval(['c']), 2)
def test_getval(self): tree = Tree() tree[1][2][3] = 4 self.assertEqual(tree.getval([1, 2, 3]), 4) self.assertEqual(tree.getval([1, 2])[3], 4) self.assertEqual(tree.getval([1])[2][3], 4)
def test_getval_none(self): tree = Tree() self.assertIsNone(tree.getval([1])) self.assertIsNone(tree.getval([1, 2])) self.assertIsNone(tree.getval([3, 4, 5]))
def test_init_from_dict_kwargs(self): tree = Tree({'c': 3}, a=1, b=2) self.assertEqual(tree.getval(['a']), 1) self.assertEqual(tree.getval(['b']), 2) self.assertEqual(tree.getval(['c']), 3) self.assertIsInstance(tree, Tree)
def test_getset(self): tree = Tree() tree.setval([1, 2, 3, 4], 5) self.assertEqual(tree.getval([1, 2, 3, 4]), 5)
def test_match_empty_tree(self): tree = Tree() self.assertFalse(tree.match({'a': 1}))