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

Fix NaN handling in cube fitting #3191

Merged
merged 12 commits into from
Sep 13, 2024

Conversation

rosteen
Copy link
Collaborator

@rosteen rosteen commented Sep 12, 2024

I split this out from #3190 so it can be backported to 3.10. Still need to add a test and move the changelog.

Edit: ready for review. Note that simply adding filter_non_finite to the specutils.fit_lines call like in the 1D case didn't seem to work, so I masked out the NaN locations instead. Why that didn't work might be worth further investigation in the future.

Remove debugging prints, add comment for context

Codestyle, changelog

Remove unit conversion stuff to just fix NaN handling
@rosteen rosteen added bug Something isn't working cubeviz plugin Label for plugins common to multiple configurations 💤backport-v3.10.x on-merge: backport to v3.10.x labels Sep 12, 2024
@rosteen rosteen added this to the 3.10.4 milestone Sep 12, 2024
Copy link

codecov bot commented Sep 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 88.46%. Comparing base (88ff8da) to head (f1e7898).
Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3191      +/-   ##
==========================================
+ Coverage   88.45%   88.46%   +0.01%     
==========================================
  Files         124      124              
  Lines       18389    18394       +5     
==========================================
+ Hits        16266    16273       +7     
+ Misses       2123     2121       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -190,7 +190,7 @@ def initialize(self, instance, x, y):

# width can be estimated by the weighted
# 2nd moment of the X coordinate.
dx = x - np.mean(x)
dx = x - np.nanmean(x)
Copy link
Contributor

Choose a reason for hiding this comment

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

But np.nanmean would still return NaN if x is all NaNs. Is this something we need to handle?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Honestly this probably didn't need to change to nanmean since there shouldn't be NaN x (spectral axis) values. The y_mean case above is the important one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Same question for y also.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't know why someone would try to fit a cube of all NaNs but I think it's reasonable for it to not work if they do 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

But didn't you say multiprocessing would swallow the exception, causing confusion?

Copy link
Contributor

Choose a reason for hiding this comment

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

I could imagine someone did a bad subset that encompass all NaNs in the cube and then try to fit it. It is possible?

@@ -917,6 +917,12 @@ def _fit_model_to_cube(self, add_data):

# Apply masks from selected spectral subset
spec = self._apply_subset_masks(spec, self.spectral_subset)
# Also mask out NaNs for fitting. Simply adding filter_non_finite to the cube fit
# didn't work out of the box, so doing this for now.
if spec.mask is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this new logic tested?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The None case is, I should probably add a case to test the combination of existing mask and Nan-masking.

Copy link
Collaborator Author

@rosteen rosteen Sep 12, 2024

Choose a reason for hiding this comment

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

I added a test that should cover the existing spec.mask case as well (that mask would come from a spectral subset).

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

I have no idea why tests are failing on 3.11 and not 3.10 or 3.12, I'll need to investigate.

Also note that I was initially testing the spec.mask combined with NaN-masking case by loading a Spectrum1D with a mask attribute before realizing that that loading case is not supported right now - I fixed the first couple bugs I ran into with that, but stopped before going to far down the rabbit hole.

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

The tests are passing locally for me on python 3.11, I don't see any package version differences that should affect them...I restarted the test and am crossing my fingers.

@pllim
Copy link
Contributor

pllim commented Sep 12, 2024

Hmm still fails. Could it be floating point differences? Maybe use allclose instead of equality? Also did you run with --remote-data --run-slow locally?

@pllim
Copy link
Contributor

pllim commented Sep 12, 2024

Try change

assert np.all(result.get_component("flux").data == 1)

to

np.testing.assert_allclose(result.get_component("flux").data, 1)

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

Try change

assert np.all(result.get_component("flux").data == 1)

to

np.testing.assert_allclose(result.get_component("flux").data, 1)

No, you can see they're all 0s (as was the case before I fixed it) in that test environment rather than all 1s.

I just confirmed that it also fails on my local WSL Ubuntu install, while still passing with an env set up the same way on my Mac, so at least I can reproduce it...

@pllim
Copy link
Contributor

pllim commented Sep 12, 2024

also fails on my local WSL Ubuntu install, while still passing with an env set up the same way on my Mac

🤯

What do we have that can possibly be so OS specific?

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

Locally the tests pass on Ubuntu if I wrap the fitting call in the test to ignore the AstropyUserWarning about linear fitting, even though that's not what the logs were showing as the error 🤯

@pllim
Copy link
Contributor

pllim commented Sep 12, 2024

Looks like warning is not always emitted.

E       Failed: DID NOT WARN. No warnings of type (<class 'AstropyUserWarning'>,) were emitted.
E        Emitted warnings: [].

Maybe just a blind ignore instead of pytest.warn

with warnings.catch_warnings():
    warnings.filterwarnings(...)

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

Maybe just a blind ignore instead of pytest.warn

with warnings.catch_warnings():
    warnings.filterwarnings(...)

Yup I just realized that's what was in the test above these.

Copy link
Contributor

@pllim pllim left a comment

Choose a reason for hiding this comment

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

Assuming CI is green this time, code changes look reasonable. Thanks!

@rosteen
Copy link
Collaborator Author

rosteen commented Sep 12, 2024

Whew, looks like that did it. Passing locally too.

Copy link
Contributor

@gibsongreen gibsongreen left a comment

Choose a reason for hiding this comment

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

I've been following all the comments, and all the new tests look good and give the result I'd expect in a notebook, great work finishing this one off!

@rosteen rosteen merged commit d26971f into spacetelescope:main Sep 13, 2024
19 checks passed
Copy link

lumberbot-app bot commented Sep 13, 2024

Owee, I'm MrMeeseeks, Look at me.

There seem to be a conflict, please backport manually. Here are approximate instructions:

  1. Checkout backport branch and update it.
git checkout v3.10.x
git pull
  1. Cherry pick the first parent branch of the this PR on top of the older branch:
git cherry-pick -x -m1 d26971f50716fe9210756bf251d28720adcbc21f
  1. You will likely have some merge/cherry-pick conflict here, fix them and commit:
git commit -am 'Backport PR #3191: Fix NaN handling in cube fitting'
  1. Push to a named branch:
git push YOURFORK v3.10.x:auto-backport-of-pr-3191-on-v3.10.x
  1. Create a PR against branch v3.10.x, I would have named this PR:

"Backport PR #3191 on branch v3.10.x (Fix NaN handling in cube fitting)"

And apply the correct labels and milestones.

Congratulations — you did some good work! Hopefully your backport PR will be tested by the continuous integration and merged soon!

Remember to remove the Still Needs Manual Backport label once the PR gets merged.

If these instructions are inaccurate, feel free to suggest an improvement.

@pllim
Copy link
Contributor

pllim commented Sep 13, 2024

@rosteen is this worth backporting manually? If not, please update the milestone. And we also need follow-up PR to move the change log. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working cubeviz plugin Label for plugins common to multiple configurations Still Needs Manual Backport 💤backport-v3.10.x on-merge: backport to v3.10.x
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants