From 68f91d560ec7062918744a05dda8975beea11de3 Mon Sep 17 00:00:00 2001 From: Charles Date: Sun, 27 Dec 2020 22:38:07 -0800 Subject: [PATCH 01/12] Basic changes --- lib/matplotlib/sankey.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index 58eb69a053a9..fa21db65d479 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -739,7 +739,14 @@ def _get_angle(a, r): if label is None or angle is None: label = '' elif self.unit is not None: - quantity = self.format % abs(number) + self.unit + if self.format is None: + self.format = '%G' + if isinstance(self.format, str): + quantity = self.format % abs(number) + self.unit + elif callable(self.format): + quantity = self.format(100. * abs(number)) + else: + raise TypeError('format must be callable or a format string') if label != '': label += "\n" label += quantity From dcd55aadbbf36e2d06114cf385108b607b808d30 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 28 Dec 2020 14:32:00 -0800 Subject: [PATCH 02/12] Basic changes --- lib/matplotlib/sankey.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index fa21db65d479..b5e667ca2dcf 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -83,10 +83,10 @@ def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, unit : str The physical unit associated with the flow quantities. If *unit* is None, then none of the quantities are labeled. - format : str - A Python number formatting string to be used in labeling the flow - as a quantity (i.e., a number times a unit, where the unit is - given). + format : str or callable + A Python number formatting string or callable used to label the flows + with their quantities. If a format string is given, the label will be + ``format % quantity``. If a callable is given, it will be called. gap : float Space between paths that break in/break away to/from the top or bottom. @@ -739,8 +739,7 @@ def _get_angle(a, r): if label is None or angle is None: label = '' elif self.unit is not None: - if self.format is None: - self.format = '%G' + if isinstance(self.format, str): quantity = self.format % abs(number) + self.unit elif callable(self.format): From c6118771d3189d61f54c50d54363f8841f752e74 Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 29 Dec 2020 10:23:04 -0800 Subject: [PATCH 03/12] Basic changes --- lib/matplotlib/sankey.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index b5e667ca2dcf..f75894149dce 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -84,9 +84,10 @@ def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, The physical unit associated with the flow quantities. If *unit* is None, then none of the quantities are labeled. format : str or callable - A Python number formatting string or callable used to label the flows - with their quantities. If a format string is given, the label will be - ``format % quantity``. If a callable is given, it will be called. + A Python number formatting string or callable used to label the + flows with their quantities. If a format string is given, the + label will be `format % quantity``. If a callable is given, it + will be called. gap : float Space between paths that break in/break away to/from the top or bottom. @@ -739,13 +740,13 @@ def _get_angle(a, r): if label is None or angle is None: label = '' elif self.unit is not None: - if isinstance(self.format, str): quantity = self.format % abs(number) + self.unit elif callable(self.format): - quantity = self.format(100. * abs(number)) + quantity = self.format(abs(number)) else: - raise TypeError('format must be callable or a format string') + raise TypeError( + 'format must be callable or a format string') if label != '': label += "\n" label += quantity From a87ffda935322e7b0545b65e4c15838d185d04a3 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 30 Dec 2020 13:23:54 -0800 Subject: [PATCH 04/12] Removed abs value on parameter --- lib/matplotlib/sankey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index f75894149dce..040de6f66cf7 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -743,7 +743,7 @@ def _get_angle(a, r): if isinstance(self.format, str): quantity = self.format % abs(number) + self.unit elif callable(self.format): - quantity = self.format(abs(number)) + quantity = self.format(number) else: raise TypeError( 'format must be callable or a format string') From 049e6e62288d07610837e8f2f4d500823e57a0d6 Mon Sep 17 00:00:00 2001 From: CharlesHe16 Date: Wed, 30 Dec 2020 13:27:30 -0800 Subject: [PATCH 05/12] Update lib/matplotlib/sankey.py Co-authored-by: Jody Klymak --- lib/matplotlib/sankey.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index 040de6f66cf7..ef6fe4d99ae7 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -86,8 +86,8 @@ def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, format : str or callable A Python number formatting string or callable used to label the flows with their quantities. If a format string is given, the - label will be `format % quantity``. If a callable is given, it - will be called. + label will be ``format % quantity``. If a callable is given, it + will be called with ``quantity`` as an argument. gap : float Space between paths that break in/break away to/from the top or bottom. From 4fa79f18207f1709a2c4fb1adb2635fecdbe3f43 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 30 Dec 2020 13:51:27 -0800 Subject: [PATCH 06/12] Add test for callable --- lib/matplotlib/tests/test_sankey.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/matplotlib/tests/test_sankey.py b/lib/matplotlib/tests/test_sankey.py index 62c4fce662cb..7fb5e3e21211 100644 --- a/lib/matplotlib/tests/test_sankey.py +++ b/lib/matplotlib/tests/test_sankey.py @@ -10,3 +10,13 @@ def test_sankey(): def test_label(): s = Sankey(flows=[0.25], labels=['First'], orientations=[-1]) assert s.diagrams[0].texts[0].get_text() == 'First\n0.25' + + +def test_format_using_callable(): + # test using callable by slightly incrementing above label example + + def show_three_decimal_places(value): + return f'{value:.3f}' + + s = Sankey(flows=[0.25], labels=['First'], orientations=[-1], format=show_three_decimal_places) + assert s.diagrams[0].texts[0].get_text() == 'First\n0.250' \ No newline at end of file From 58d8f5ce4b3ca1a2aa5154c192bec1ebbe1e3c84 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 30 Dec 2020 13:54:00 -0800 Subject: [PATCH 07/12] Reduced size of long line --- lib/matplotlib/tests/test_sankey.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_sankey.py b/lib/matplotlib/tests/test_sankey.py index 7fb5e3e21211..53bd1368e45e 100644 --- a/lib/matplotlib/tests/test_sankey.py +++ b/lib/matplotlib/tests/test_sankey.py @@ -18,5 +18,7 @@ def test_format_using_callable(): def show_three_decimal_places(value): return f'{value:.3f}' - s = Sankey(flows=[0.25], labels=['First'], orientations=[-1], format=show_three_decimal_places) + s = Sankey(flows=[0.25], labels=['First'], orientations=[-1], + format=show_three_decimal_places) + assert s.diagrams[0].texts[0].get_text() == 'First\n0.250' \ No newline at end of file From 585297498c62699eafe6596cea854413bf82383b Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 30 Dec 2020 13:57:57 -0800 Subject: [PATCH 08/12] Add new line to EOF as per linting message. --- lib/matplotlib/tests/test_sankey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_sankey.py b/lib/matplotlib/tests/test_sankey.py index 53bd1368e45e..1851525bd4e2 100644 --- a/lib/matplotlib/tests/test_sankey.py +++ b/lib/matplotlib/tests/test_sankey.py @@ -21,4 +21,4 @@ def show_three_decimal_places(value): s = Sankey(flows=[0.25], labels=['First'], orientations=[-1], format=show_three_decimal_places) - assert s.diagrams[0].texts[0].get_text() == 'First\n0.250' \ No newline at end of file + assert s.diagrams[0].texts[0].get_text() == 'First\n0.250' From c908a4f289642be6da0951709bd9b0be1fa249cc Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 1 Jan 2021 16:33:57 -0800 Subject: [PATCH 09/12] Add new line to EOF as per linting message. --- lib/matplotlib/sankey.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index ef6fe4d99ae7..8032001561bb 100644 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -85,9 +85,10 @@ def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, is None, then none of the quantities are labeled. format : str or callable A Python number formatting string or callable used to label the - flows with their quantities. If a format string is given, the - label will be ``format % quantity``. If a callable is given, it - will be called with ``quantity`` as an argument. + flows with their quantities (i.e., a number times a unit, where the + unit is given). If a format string is given, the label will be + ``format % quantity``. If a callable is given, it will be called + with ``quantity`` as an argument. gap : float Space between paths that break in/break away to/from the top or bottom. From ba42251b0788563bfacdfd2ec0b8471e2fe3599f Mon Sep 17 00:00:00 2001 From: Charles Date: Sun, 3 Jan 2021 20:57:04 -0800 Subject: [PATCH 10/12] Added entry in doc/users/next_whats_new/, as per checklist. --- ...callables_for_formatting_sankey_labels.rst | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst diff --git a/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst new file mode 100644 index 000000000000..d3ccdd1ca397 --- /dev/null +++ b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst @@ -0,0 +1,46 @@ +Support callable for formatting of Sankey labels +------------------------------------------------ + +The `format` parameter of `matplotlib.sankey.Sankey` can now accept callables. + +This allows the use of an arbitrary function to label flows, for example allowing +the mapping of numbers to emoji. + +.. plot:: + + import matplotlib.pyplot as plt + from matplotlib.sankey import Sankey + import math + + # on Windows, this font may be necessary to display emojis + plt.rcParams['font.family'] = "Segoe UI Emoji" + + def display_in_cats(values, min_cats, max_cats): + + def display_in_cat_scale(value): + max_value = max(values, key=abs) + number_cats_to_show = max(min_cats, math.floor(abs(value) / max_value * max_cats)) + return str(number_cats_to_show * '🐈') + + return display_in_cat_scale + + flows = [35, 15, 40, -20, -15, -5, -40, -10] + orientations = [-1, 1, 0, 1, 1, 1, -1, -1] + + # Cats are good, we want a strictly positive number of them + min_cats = 1 + # More than four cats might be too much for some people + max_cats = 4 + + cats_format = display_in_cats(flows, min_cats, max_cats) + + sankey = Sankey(flows=flows, orientations=orientations, format=cats_format, + offset=.1, head_angle=180, shoulder=0, scale=.010) + + diagrams = sankey.finish() + + diagrams[0].texts[2].set_text('') + + plt.title(f'Sankey flows measured in cats \n🐈 = {max(flows, key=abs) / max_cats}') + + plt.show() From ba2d7f8f1719a58d3edb19f2d2327109c8e781b0 Mon Sep 17 00:00:00 2001 From: Charles Date: Sun, 3 Jan 2021 21:02:12 -0800 Subject: [PATCH 11/12] Added entry in doc/users/next_whats_new/, as per checklist. --- .../callables_for_formatting_sankey_labels.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst index d3ccdd1ca397..4709b2c8d953 100644 --- a/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst +++ b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst @@ -15,15 +15,17 @@ the mapping of numbers to emoji. # on Windows, this font may be necessary to display emojis plt.rcParams['font.family'] = "Segoe UI Emoji" - def display_in_cats(values, min_cats, max_cats): + def display_in_cats(values, min_cats, max_cats): def display_in_cat_scale(value): max_value = max(values, key=abs) - number_cats_to_show = max(min_cats, math.floor(abs(value) / max_value * max_cats)) + number_cats_to_show = \ + max(min_cats, math.floor(abs(value) / max_value * max_cats)) return str(number_cats_to_show * '🐈') return display_in_cat_scale + flows = [35, 15, 40, -20, -15, -5, -40, -10] orientations = [-1, 1, 0, 1, 1, 1, -1, -1] @@ -41,6 +43,7 @@ the mapping of numbers to emoji. diagrams[0].texts[2].set_text('') - plt.title(f'Sankey flows measured in cats \n🐈 = {max(flows, key=abs) / max_cats}') + plt.title(f'Sankey flows measured in cats \n' + f'🐈 = {max(flows, key=abs) / max_cats}') plt.show() From 35954a07dce1fbb7eb22cbb6cdc8421074604012 Mon Sep 17 00:00:00 2001 From: Charles Date: Sun, 3 Jan 2021 22:10:31 -0800 Subject: [PATCH 12/12] =?UTF-8?q?Cat=20emoji=20in=20example=20changed=20to?= =?UTF-8?q?=20'=F0=9F=90=B1',=20as=20previous=20Glyph=20'=F0=9F=90=88'=20n?= =?UTF-8?q?ot=20included=20in=20Matplotlib's=20standard=20font=20DejaVu=20?= =?UTF-8?q?Sans,=20breaking=20doc=20building.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../callables_for_formatting_sankey_labels.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst index 4709b2c8d953..38aa766a38e4 100644 --- a/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst +++ b/doc/users/next_whats_new/callables_for_formatting_sankey_labels.rst @@ -12,16 +12,13 @@ the mapping of numbers to emoji. from matplotlib.sankey import Sankey import math - # on Windows, this font may be necessary to display emojis - plt.rcParams['font.family'] = "Segoe UI Emoji" - def display_in_cats(values, min_cats, max_cats): def display_in_cat_scale(value): max_value = max(values, key=abs) number_cats_to_show = \ max(min_cats, math.floor(abs(value) / max_value * max_cats)) - return str(number_cats_to_show * '🐈') + return str(number_cats_to_show * '🐱') return display_in_cat_scale @@ -44,6 +41,6 @@ the mapping of numbers to emoji. diagrams[0].texts[2].set_text('') plt.title(f'Sankey flows measured in cats \n' - f'🐈 = {max(flows, key=abs) / max_cats}') + f'🐱 = {max(flows, key=abs) / max_cats}') plt.show()