Skip to content
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

DM-38386: Add utility function to calculate safe plotting limits #151

Merged
merged 8 commits into from Mar 30, 2023

Conversation

mfisherlevine
Copy link
Contributor

@mfisherlevine mfisherlevine commented Mar 29, 2023

Checklist

  • ran Jenkins
  • added a release note for user-visible changes to doc/changes

@codecov
Copy link

codecov bot commented Mar 29, 2023

Codecov Report

Patch coverage: 100.00% and project coverage change: +1.19 🎉

Comparison is base (76ffbdc) 92.42% compared to head (876f023) 93.61%.

❗ Current head 876f023 differs from pull request most recent head 48ffb93. Consider uploading reports for the commit 48ffb93 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #151      +/-   ##
==========================================
+ Coverage   92.42%   93.61%   +1.19%     
==========================================
  Files          41       43       +2     
  Lines        2654     2680      +26     
==========================================
+ Hits         2453     2509      +56     
+ Misses        201      171      -30     
Impacted Files Coverage Δ
python/lsst/utils/plotting/limits.py 100.00% <100.00%> (ø)
tests/test_plotting_limits.py 100.00% <100.00%> (ø)

... and 20 files with indirect coverage changes

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

import numpy as np


def calculateSafePlottingLimits(dataSeries, percentile=99.9, constantExtra=None, symmetricAroundZero=False):
Copy link
Member

@timj timj Mar 29, 2023

Choose a reason for hiding this comment

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

Suggested change
def calculateSafePlottingLimits(dataSeries, percentile=99.9, constantExtra=None, symmetricAroundZero=False):
def calculateSafePlottingLimits(dataSeries, percentile: float = 99.9, constantExtra: Optional[float] = None, symmetricAroundZero: bool = False) -> tuple[float, float]:

Copy link
Member

Choose a reason for hiding this comment

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

You may also need from __future__ import annotations at the top since we still use python 3.8.

Copy link
Member

Choose a reason for hiding this comment

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

Also will need to import Optional from typing up top. I'm not really sure what dataSeries is an iterable of. Floats? So maybe Union[Iterable[float], Iterable[Iterable[float]] ?

Copy link
Member

@TallJimbo TallJimbo Mar 29, 2023

Choose a reason for hiding this comment

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

Instead of Optional, use float | None and avoid that import. And if dataSeries is effectively "numpy array-like", then typing.Any may be our best bet. Others may have had better (and more extensive) experiences, but I'm pretty skeptical of the value of typing anything numpy-related.

Edit: ah, if we're still trying to support Python 3.8, then float | None is out. I'm guessing we're only stil trying to on this ticket so Merlin doesn't have to be the one to edit all the package metadata right now?

Copy link
Member

Choose a reason for hiding this comment

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

except we still support python 3.8 and I don't know if that works there.

@timj
Copy link
Member

timj commented Mar 29, 2023

This PR is going to need a test file. I note that this is not a plotting function at all but a statistics function, so is plotting the right namespace for it? Also this is a new function in utils so should use snake case.

Copy link
Member

@timj timj left a comment

Choose a reason for hiding this comment

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

Looks good.

The value to set the ylim maximum to.
"""
if not isinstance(data_series, Iterable):
raise ValueError("data_series must be either an iterable, or an iterable of iterables")
Copy link
Member

Choose a reason for hiding this comment

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

Please add a quick test that this ValueError happens if you pass in something like a single float.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, and changed it to a TypeError too, because, well, it is 🙂

# now we're sure we have an iterable, if it's just one make it a list of it
# lsst.utils.ensure_iterable is not suitable here as we already have one,
# we would need ensure_iterable_of_iterables here
if not isinstance(data_series[0], Iterable): # np.array are Iterable but not Sequence so isinstance that
Copy link
Member

Choose a reason for hiding this comment

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

I am concerned that we are being a bit inconsistent. Remind me what the type error was if Iterable is used? numpy not being a Sequence is a bit odd.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I hate hate hate this. Getting this to work with a list, a numpy array, and a column from a pandas.dataframe, and an iterable of all of these was not super fun. This was the best solution I found. It is quite likely someone who knows what they're doing with mypy could do better, but I could not.

Copy link
Member

Choose a reason for hiding this comment

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

I am absolutely not suggesting a change to this particular code (especially now that it's working and merged), but in general when you run into trouble making a whole host of differently-behaving argument types work in single function, it might be time to make it into multiple functions that share code. Python was not designed to support function overloading, and while we often want it to avoid having to come up with or export multiple names, sometimes that's the better price to pay.

python/lsst/utils/plotting/limits.py Outdated Show resolved Hide resolved
python/lsst/utils/plotting/limits.py Outdated Show resolved Hide resolved
tests/test_plotting_limits.py Outdated Show resolved Hide resolved
@mfisherlevine mfisherlevine merged commit 8963c40 into main Mar 30, 2023
11 checks passed
@mfisherlevine mfisherlevine deleted the tickets/DM-38386 branch March 30, 2023 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants