From: Vladimír Vondruš Date: Sun, 7 Jul 2019 11:18:17 +0000 (+0200) Subject: m.plots: support stacked values. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=2ec8965b2d398c663a4c7f828c78311f68448d37;p=blog.git m.plots: support stacked values. --- diff --git a/doc/plugins/plots-and-graphs.rst b/doc/plugins/plots-and-graphs.rst index 4d3aa6ca..2e6101c3 100644 --- a/doc/plugins/plots-and-graphs.rst +++ b/doc/plugins/plots-and-graphs.rst @@ -129,7 +129,8 @@ height is calculated automatically based on amount of values, you can adjust the bar height using :rst:`:bar_height:`. Default value is :py:`0.4`. It's possible to add an extra line of labels using :rst:`:labels_extra:`. -Again, there should be as many entries as primary labels and values. To omit an extra label for a value, specify it as the :abbr:`reST ` +Again, there should be as many entries as primary labels and values. To omit an +extra label for a value, specify it as the :abbr:`reST ` comment :rst:`..`. .. code-figure:: @@ -172,6 +173,64 @@ comment :rst:`..`. :colors: success info danger dim :bar_height: 0.6 +`Stacked values`_ +----------------- + +It's possible to stack several values on each other by providing a second +(third, ...) like for :rst:`:values:` (and :rst:`:errors:` as well). The values +are added together --- not overlapped --- so e.g. showing values of 20 and 40 +stacked together will result in the bar being 60 units long in total. Hovering +over the stacked values will show magnitude of just given part, not the summed +value. + +The :rst:`:colors:` option works for these as well, either have each line a +single value on each line to color each "slice" differently, or have one color +per value like shown above. + +.. code-figure:: + + .. code:: rst + + .. plot:: Download size (*.js, *.wasm) + :type: barh + :labels: + Sdl2Application + Sdl2Application + EmscriptenApplication + :labels_extra: + -s USE_SDL=2 + -s USE_SDL=1 + .. + :units: kB + :values: + 111.9 74.4 52.1 + 731.2 226.3 226.0 + :colors: + success + info + + .. plot:: Download size (*.js, *.wasm) + :type: barh + :labels: + Sdl2Application + Sdl2Application + EmscriptenApplication + :labels_extra: + -s USE_SDL=2 + -s USE_SDL=1 + .. + :units: kB + :values: + 111.9 74.4 52.1 + 731.2 226.3 226.0 + :colors: + success + info + + .. class:: m-text-center m-text m-dim m-small + + (graph source: https://blog.magnum.graphics/announcements/new-emscripten-application-implementation/) + `Graphs`_ ========= diff --git a/documentation/test_python/page_plugins/index.html b/documentation/test_python/page_plugins/index.html index 8368a6ef..03673af7 100644 --- a/documentation/test_python/page_plugins/index.html +++ b/documentation/test_python/page_plugins/index.html @@ -67,10 +67,10 @@ div.m-plot svg { font-family: DejaVu Sans; } - 15.0 meters, i guess? + 15.0 meters, i guess? - 30.0 meters, i guess? + 30.0 meters, i guess? diff --git a/plugins/m/plots.py b/plugins/m/plots.py index 9c84fd71..94e0b35d 100644 --- a/plugins/m/plots.py +++ b/plugins/m/plots.py @@ -126,9 +126,9 @@ _class_mapping = [ ] # Titles for bars -_bar_titles_src = '' -_bar_titles_dst = '{} {}' -_bar_titles_dst_error = '{} ± {} {}' +_bar_titles_src = '' +_bar_titles_dst = '{} {}' +_bar_titles_dst_error = '{} ± {} {}' class Plot(rst.Directive): required_arguments = 1 @@ -164,20 +164,36 @@ class Plot(rst.Directive): else: labels_extra = None - # Values. Should be one for each label. - values = [float(v) for v in self.options['values'].split()] - assert len(values) == len(labels) + # Values. Should be one for each label, if there are multiple lines + # then the values get stacked. + value_sets = [] + for row in self.options['values'].split('\n'): + values = [float(v) for v in row.split()] + assert len(values) == len(labels) + value_sets += [values] # Optional errors if 'errors' in self.options: - errors = [float(e) for e in self.options['errors'].split()] + error_sets = [] + for row in self.options['errors'].split('\n'): + errors = [float(e) for e in row.split()] + assert len(errors) == len(values) + error_sets += [errors] + assert len(error_sets) == len(value_sets) else: - errors = None + error_sets = [None]*len(value_sets) # Colors. Should be either one for all or one for every value - colors = [style_mapping[c] for c in self.options.get('colors', 'default').split()] - if len(colors) == 1: colors = colors[0] - else: assert len(colors) == len(labels) + if 'colors' in self.options: + color_sets = [] + for row in self.options['colors'].split('\n'): + colors = [style_mapping[c] for c in row.split()] + if len(colors) == 1: colors = colors[0] + else: assert len(colors) == len(labels) + color_sets += [colors] + assert len(color_sets) == len(value_sets) + else: + color_sets = [style_mapping['default']]*len(value_sets) # Bar height bar_height = float(self.options.get('bar_height', '0.4')) @@ -188,12 +204,15 @@ class Plot(rst.Directive): # Setup the graph fig, ax = plt.subplots() # TODO: let matplotlib calculate the height somehow - fig.set_size_inches(8, 0.78 + len(values)*bar_height) + fig.set_size_inches(8, 0.78 + len(labels)*bar_height) yticks = np.arange(len(labels)) - plot = ax.barh(yticks, values, xerr=errors, - align='center', color=colors, ecolor='#cafe0a', capsize=5*bar_height/0.4) - for i, v in enumerate(plot): - v.set_gid('plot{}-value{}'.format(mpl.rcParams['svg.hashsalt'], i)) + left = np.array([0.0]*len(labels)) + for i in range(len(value_sets)): + plot = ax.barh(yticks, value_sets[i], xerr=error_sets[i], + align='center', color=color_sets[i], ecolor='#cafe0a', capsize=5*bar_height/0.4, left=left) + left += np.array(value_sets[i]) + for j, v in enumerate(plot): + v.set_gid('plot{}-value{}-{}'.format(mpl.rcParams['svg.hashsalt'], i, j)) ax.set_yticks(yticks) ax.invert_yaxis() # top-to-bottom ax.set_xlabel(units) @@ -224,13 +243,15 @@ class Plot(rst.Directive): # Replace color codes with CSS classes for src, dst in _class_mapping: imgdata = imgdata.replace(src, dst) # Add titles for bars - for i in range(len(values)): - if errors: imgdata = imgdata.replace( - _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i), - _bar_titles_dst_error.format(mpl.rcParams['svg.hashsalt'], i, values[i], errors[i], units)) - else: imgdata = imgdata.replace( - _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i), - _bar_titles_dst.format(mpl.rcParams['svg.hashsalt'], i, values[i], units)) + for i in range(len(value_sets)): + for j in range(len(labels)): + id = i*len(labels) + j + if error_sets[i]: imgdata = imgdata.replace( + _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i, j), + _bar_titles_dst_error.format(mpl.rcParams['svg.hashsalt'], i, j, value_sets[i][j], error_sets[i][j], units)) + else: imgdata = imgdata.replace( + _bar_titles_src.format(mpl.rcParams['svg.hashsalt'], i, j), + _bar_titles_dst.format(mpl.rcParams['svg.hashsalt'], i, j, value_sets[i][j], units)) container = nodes.container(**self.options) container['classes'] += ['m-plot'] diff --git a/plugins/m/test/plots/page.html b/plugins/m/test/plots/page.html index 896edd1f..e6598208 100644 --- a/plugins/m/test/plots/page.html +++ b/plugins/m/test/plots/page.html @@ -39,10 +39,10 @@ div.m-plot svg { font-family: DejaVu Sans; } - 15.0 meters, i guess? + 15.0 meters, i guess? - 30.0 meters, i guess? + 30.0 meters, i guess? @@ -172,13 +172,13 @@ div.m-plot svg { font-family: DejaVu Sans; } - 3.0 ± 0.1 Mondays + 3.0 ± 0.1 Mondays - 4.0 ± 2.1 Mondays + 4.0 ± 2.1 Mondays - 5.0 ± 1.0 Mondays + 5.0 ± 1.0 Mondays @@ -338,6 +338,345 @@ div.m-plot svg { font-family: DejaVu Sans; } +
+ + + + + + + + + + 111.9 kB + + + 74.4 kB + + + 52.1 kB + + + 731.2 kB + + + 226.3 kB + + + 226.0 kB + + + + + + + + + + + + + + 0 + + + + + + + + + + 100 + + + + + + + + + + 200 + + + + + + + + + + 300 + + + + + + + + + + 400 + + + + + + + + + + 500 + + + + + + + + + + 600 + + + + + + + + + + 700 + + + + + + + + + + 800 + + + + kB + + + + + + + + + + + + + + A + + + + + + + + + + B + + + + + + + + + + C + + + + + Stacked plot + + + + + + + + + +
+
+ + + + + + + + + + 111.9 ± 25.0 kB + + + 74.4 ± 15.3 kB + + + 731.2 ± 200.0 kB + + + 226.3 ± 5.0 kB + + + + + + + + + + + + + + 0 + + + + + + + + + + 200 + + + + + + + + + + 400 + + + + + + + + + + 600 + + + + + + + + + + 800 + + + + + + + + + + 1000 + + + + kB + + + + + + + + + + + + + + A + + + + + + + + + + B + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stacked plot with errors and full colors + + + + + + + + + +
diff --git a/plugins/m/test/plots/page.rst b/plugins/m/test/plots/page.rst index 295429a5..480d070e 100644 --- a/plugins/m/test/plots/page.rst +++ b/plugins/m/test/plots/page.rst @@ -34,3 +34,33 @@ predictable rendering on the CIs. :errors: 0.1 2.1 1.0 :colors: success info danger :bar_height: 0.75 + +.. plot:: Stacked plot + :type: barh + :labels: + A + B + C + :units: kB + :values: + 111.9 74.4 52.1 + 731.2 226.3 226.0 + :colors: + success + info + +.. plot:: Stacked plot with errors and full colors + :type: barh + :labels: + A + B + :units: kB + :values: + 111.9 74.4 + 731.2 226.3 + :errors: + 25.0 15.3 + 200.0 5.0 + :colors: + success danger + info primary