Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Support callable for formatting of Sankey labels #19187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 4, 2021

Conversation

CharlesHe16
Copy link
Contributor

@CharlesHe16 CharlesHe16 commented Dec 29, 2020

PR Summary

This PR is in regards to #19133:

This slightly extends sankey by allowing the format parameter to accept a callable (more specifically, a callable that accepts a numerical value for each flow, and outputs a string).

Example:

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]

min_cats = 1
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()

image

Comment 1:

This change is modelled after pie autopct, and pretty much copies and pastes this code from that class:

if isinstance(autopct, str):
s = autopct % (100. * frac)
elif callable(autopct):
s = autopct(100. * frac)
else:
raise TypeError(
'autopct must be callable or a format string')

Note that @timhoffm's suggestion in issue 19133 was that there was two templates to model this PR on:

If I understand correctly, in the first, thefmt scheme used for the contour class, the additional functionality seems extremely large. The label seems to have its own class, coming in at ~600 lines. See:

class ContourLabeler:

I choose the simpler autopct because it's far more feasible for me and this seems to match the intention of the PR.

Comment 2:

In the current sankey class, when the format parameter is explicitly set to None, this produces an error.

This is different than the functionality in the two examples previously mentioned:

In these examples, when the respective parameter is set to None, contour seems to revert to a default ScalarFormatter object, and pie removes labels altogether.

I left the current behavior of sankey, instead of changing it to the two choices above.

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • Conforms to Matplotlib style conventions (install flake8-docstrings and run flake8 --docstring-convention=all).
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

Copy link
Member

@jklymak jklymak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely needs a test. Wording of the docstring could be made more explicit.

if isinstance(self.format, str):
quantity = self.format % abs(number) + self.unit
elif callable(self.format):
quantity = self.format(100. * abs(number))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm skeptical you want to multiply by 100 here?

Copy link
Contributor Author

@CharlesHe16 CharlesHe16 Dec 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. First PR and I derped.

I think what happened was that I was confused or missed the difference between committing to local branch and pushing to master branch on my fork. What you saw was an "early version" and not the best indicator of quality, not that the final version is complex or deep.

Thanks for catching the error.

@CharlesHe16
Copy link
Contributor Author

This definitely needs a test.

Can you elaborate or provide an example?

Wording of the docstring could be made more explicit.

Do you have suggestions for the docstring?

For reference, the autopct docstring is:

autopct : None or str or callable, default: None
If not *None*, is a string or function used to label the wedges
with their numeric value. The label will be placed inside the
wedge. If it is a format string, the label will be ``fmt % pct``.
If it is a function, it will be called.

@jklymak
Copy link
Member

jklymak commented Dec 29, 2020

Please start here, and in particular the section under "testing". I assume there are other sankey tests you could likely add to. https://matplotlib.org/devel/index.html

@CharlesHe16
Copy link
Contributor Author

Please start here, and in particular the section under "testing". I assume there are other sankey tests you could likely add to. https://matplotlib.org/devel/index.html

Thanks, I have reviewed the guide and testing in general.

I have searched the indicated directory of tests, and found this 12 line file for testing test_sankey.py

from matplotlib.sankey import Sankey
def test_sankey():
# lets just create a sankey instance and check the code runs
sankey = Sankey()
sankey.add()
def test_label():
s = Sankey(flows=[0.25], labels=['First'], orientations=[-1])
assert s.diagrams[0].texts[0].get_text() == 'First\n0.25'

My current guess for satisfying your testing suggestion, is that I will append code to test_sankey.py to get sankey format to use a callable.

I'll go ahead and do this in a few days or so.

@CharlesHe16
Copy link
Contributor Author

CharlesHe16 commented Dec 30, 2020

Ok, wow. I pressed a push button in my IDE, and the whole testing suite started firing in my browser window.

Seems like magic.

Anyways, I know how to test for new functionality in my local, offline branch, but I don't know how to "test" my test against Matplotlib's Github testing suites.

So I just uploaded my test as I understand it.

@timhoffm
Copy link
Member

timhoffm commented Jan 1, 2021

So I just uploaded my test as I understand it.

That's the correct thing to do. πŸ‘

@timhoffm timhoffm changed the title Kitty cat enabled formatting for Sankey Support callable for formatting of Sankey labels Jan 1, 2021
@CharlesHe16
Copy link
Contributor Author

timhoffm changed the title Kitty cat enabled formatting for Sankey Support callable for formatting of Sankey labels 1 hour ago

RIP kitty cat title.

@timhoffm, I have a few questions:

Finishing the PR Checklist:

There's a number of tasks in the "PR Checklist".

I have completed a few, but others such as documentation changes are incomplete.

Should I go ahead and try to do these? Or is the normal practice to leave these to approved maintainers (perhaps to benefit from batching and writing uniformity in the docs)?

Replicating functionality to other functions:

There's been mention of a need for adding/unifying format functionality in other functions, see @ianhi #19133 (comment):

There are a few other places in matplotlib that have similar string formatting. It would be nice if a change to accept both types of formatting was applied uniformly across them. Most notable (to me at least) is the Slider widgets which also use % style formatting.
...

Should I try to work on these other cases, replicating my work here?

@timhoffm
Copy link
Member

timhoffm commented Jan 3, 2021

PR checklist:

Almost all items are checked (or not relevant in the context of the PR). The only thing that could be added is:

New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).

Replicating functionality to other functions:

I'd first see that this PR gets merged so that we have proven consensus on the concept. The other functions can be fixed in a followup PR. You are welcome to contribute that as well.

@CharlesHe16
Copy link
Contributor Author

CharlesHe16 commented Jan 4, 2021

Almost all items are checked (or not relevant in the context of the PR). The only thing that could be added is:

New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).

Ok, I added an entry as indicated.

The entry includes an example. This particular example uses emojis, which could be difficult to display. If this is inconvenient, the example can be removed.

Emoji fixed, example should work.

…ed in Matplotlib's standard font DejaVu Sans, breaking doc building.
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 * '🐱')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least for me the way GH renders this I don't see the cat emoji, but it is in there. This renders as https://51382-1385122-gh.circle-artifacts.com/0/doc/build/html/users/next_whats_new/callables_for_formatting_sankey_labels.html
image

@tacaswell tacaswell added this to the v3.4.0 milestone Jan 4, 2021
@tacaswell tacaswell merged commit fce534c into matplotlib:master Jan 4, 2021
@tacaswell
Copy link
Member

Thanks @CharlesHe16 and congratulations on your first Matplotlib PR πŸŽ‰ Hopefully we will hear from you again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants