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

add fit_non_finite flag to model_fitting 1D #2437

Merged
merged 1 commit into from
Sep 28, 2023

Conversation

cshanahan1
Copy link
Contributor

@cshanahan1 cshanahan1 commented Sep 11, 2023

This PR passes the filter_non_finite flag (set to True), to specutils.fitting.fit_lines. This allows non-finite values in the uncertainty/weights array to be filtered out before fitting, allowing the fit to proceed without NonFiniteValueError.

Closes #2328

@codecov
Copy link

codecov bot commented Sep 11, 2023

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Files Coverage Δ
...s/default/plugins/model_fitting/fitting_backend.py 84.00% <100.00%> (ø)
...igs/default/plugins/model_fitting/model_fitting.py 86.57% <100.00%> (ø)
...default/plugins/model_fitting/tests/test_plugin.py 99.60% <100.00%> (+0.03%) ⬆️
jdaviz/core/template_mixin.py 90.92% <96.00%> (+0.09%) ⬆️
jdaviz/configs/specviz/plugins/parsers.py 90.56% <94.44%> (-0.24%) ⬇️

... and 3 files with indirect coverage changes

📢 Thoughts on this report? Let us know!.

@kecnry
Copy link
Member

kecnry commented Sep 11, 2023

Do we still want an in-plugin warning when there are detected non-finite values that this would affect?

@cshanahan1
Copy link
Contributor Author

Do we still want an in-plugin warning when there are detected non-finite values that this would affect?

an AstropyUserWarning will be raised, indicating that non-finite values have been removed from the data before fitting. I was considering adding some additional warnings and if you think that would be useful I can

@kecnry
Copy link
Member

kecnry commented Sep 11, 2023

that would get buried in the standalone mode, so I think we should try to detect in advance (if it isn't too difficult or add too much computation expense) that there are non-finite values in the selected data/subset and just show some text in the plugin saying they will be ignored.

@cshanahan1
Copy link
Contributor Author

that would get buried in the standalone mode, so I think we should try to detect in advance (if it isn't too difficult or add too much computation expense) that there are non-finite values in the selected data/subset and just show some text in the plugin saying they will be ignored.

ok in that case I have that working in #2429, so I can apply those changes here too. I wasn't sure what the syntax was for displaying a snackbar message here though, who would the 'sender' be in this case? i couldn't find an example in the package of this being done within a function and not a class

@kecnry
Copy link
Member

kecnry commented Sep 11, 2023

I don't think we want a snackbar unless we can't detect the values in advance and can only catch the warning and pass it on to the user instead.

Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

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

Just a few comments from a quick skim. Are there any test cases we can add without introducing large data sets to the tests?

@@ -41,8 +44,9 @@ def specviz_spectrum1d_parser(app, data, data_label=None, format=None, show_in_v
raise TypeError("SpectrumCollection detected."
" Please provide a Spectrum1D or SpectrumList")
elif isinstance(data, Spectrum1D):
data = [data]
# should we make a copy if Spectrum1D is passed in to avoid modifying it when uncerts are reset to None?
Copy link
Member

Choose a reason for hiding this comment

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

did you test this to see if a copy is needed?

Copy link
Contributor Author

@cshanahan1 cshanahan1 Sep 22, 2023

Choose a reason for hiding this comment

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

If you pass in a Spectrum1D with uncertainty=[nan, nan, nan...], the input will be modified without a copy, with uncertainty now being None. What do you think about that, is it worth making a copy to avoid modifying input? Im not sure how it works elsewhere in the package if any modifications are made to the input spectrum

jdaviz/configs/specviz/plugins/parsers.py Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
@kecnry kecnry added this to the 3.8 milestone Sep 22, 2023
@cshanahan1
Copy link
Contributor Author

I think i responded to all of your comments @kecnry. The only open question is should we be making copies so that the input Spectrum1D is not modified? I added some basic tests (which you can use to demo this, as well as the original dataset when you reported this issue @javerbukh )

Comment on lines 292 to 297
<v-row v-if="non_finite_uncertainty_mismatch">
<span class="v-messages v-messages__message text--secondary" style="color: red !important">
Finite values in the selected data will be omitted in the fit due to corresponding non-finite uncertanties.
</span>
</v-row>

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not seeing this in the plugin. How can I make it appear?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

uncertainty=StdDevUncertainty([1, 1, np.nan, np.nan, np.nan, np.nan]*u.Jy) 
spec = Spectrum1D(flux=[1, 2, 3, 4, 5, 6]*u.Jy, uncertainty=uncertainty)
specviz = Specviz()
specviz.load_data(spec)
specviz.show()

that should trigger it. it will appear just above the 'fit model' button.

Copy link
Contributor

Choose a reason for hiding this comment

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

I tried following the steps of the original ticket and I see the snackbar message "All uncertanties are nonfinite, setting to None". So if no finite values are detected, the uncertainty is set to None. If some non-finite values are detected, this message should show up in the plugin. Do I understand correctly?

I tried and that does in fact make the message appear, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep, if the whole array is none then the first snackbar message appears. if its not and there are some non finite uncertainties, then the message in the plugin is displayed

Comment on lines 1704 to 1708
# note! the 'n/a' traitlet doesn't exist. we are watching it here because
# DatasetSpectralSubsetValidMixin is also watching the exact set
# of real traitlets, which creates a bug where only the last inhereted method
# will run, so we need to spoof it like this.
@observe("n/a", "dataset_selected", "spectral_subset_selected")
Copy link
Contributor

@javerbukh javerbukh Sep 25, 2023

Choose a reason for hiding this comment

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

Are you sure observing "n/a" is necessary? I don't understand what you mean by "watching the exact set of real traitlets", maybe someone can explain it to me if I'm missing something? I see what you mean by real traitlets, dataset_selected and spectral_subset_selected. Still not sure about "n/a".

Copy link
Member

Choose a reason for hiding this comment

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

Just observing "dataset_selected" and "spectral_subset_selected" would conflict with another observe and only the last one added would actually be called. @cshanahan1 - is there an issue for this yet that we can reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will post the issue in a sec, i was getting an example code snippet working to create the issue

Copy link
Contributor

Choose a reason for hiding this comment

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

Has this been resolved? I also don't understand what "n/a" is for.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think, somehow miraculously, im not seeing this anymore so i've removed the dummy traitlet.

Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

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

Sorry for a ton of comments, but they're all quite minor - overall this seems to work well and the code is quite clean, thanks!

CHANGES.rst Outdated
@@ -58,6 +58,14 @@ Specviz2d
Other Changes and Additions
---------------------------

- Better handling of non-finite uncertainties in model fitting. The
'filter_non_finite' flag (for the LevMarLSQFitter) now filters datapoints with
non-finite weights. In Specviz, if a fully-finite spectrum with non-finite
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if this would ever happen for a cube in cubeviz and if its worth generalizing to apply to that case as well (perhaps as part of an eventual parser refactor - not necessarily in this PR)... thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i think so, we should open a follow up ticket. we would need to move some things (like get_spectrum_1d) to somewhere more general to make this work

jdaviz/configs/specviz/plugins/parsers.py Outdated Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Show resolved Hide resolved
Comment on lines 1704 to 1708
# note! the 'n/a' traitlet doesn't exist. we are watching it here because
# DatasetSpectralSubsetValidMixin is also watching the exact set
# of real traitlets, which creates a bug where only the last inhereted method
# will run, so we need to spoof it like this.
@observe("n/a", "dataset_selected", "spectral_subset_selected")
Copy link
Member

Choose a reason for hiding this comment

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

Just observing "dataset_selected" and "spectral_subset_selected" would conflict with another observe and only the last one added would actually be called. @cshanahan1 - is there an issue for this yet that we can reference?

jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Outdated Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Outdated Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

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

LGTM (definitely qualifies for squashing though)!

@cshanahan1
Copy link
Contributor Author

LGTM (definitely qualifies for squashing though)!

will do!

Comment on lines 113 to 116
if uncerts is not None:
if not np.any(uncerts):
uncerts = None
set_nans_to_none = True
Copy link
Contributor

Choose a reason for hiding this comment

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

Can combine nested if?

Suggested change
if uncerts is not None:
if not np.any(uncerts):
uncerts = None
set_nans_to_none = True
if uncerts is not None and not np.any(uncerts):
uncerts = None
set_nans_to_none = True

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

# otherwise, data should be a list containing a single [Spectrum1D]
# check just to be safe.
m = 'Only supported for one data entry, if multiple spectra use SpectrumList.'
raise NotImplementedError(m)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be checked right at the beginning before all the extra processing is done?

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, moved this to the top

Comment on lines 132 to 134
msg = 'All uncertainties are nonfinite, replacing with uncertainty=None.'
info_msg = SnackbarMessage(msg, color="warning", sender=app)
app.hub.broadcast(info_msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

I have been shortening old calls like this one to one line. No reason to assign 2 variables to this one snackbar call, as they will never be used again.

Suggested change
msg = 'All uncertainties are nonfinite, replacing with uncertainty=None.'
info_msg = SnackbarMessage(msg, color="warning", sender=app)
app.hub.broadcast(info_msg)
msg = 'All uncertainties are nonfinite, replacing with uncertainty=None.'
info_msg = SnackbarMessage(msg, color="warning", sender=app)
app.hub.broadcast(info_msg)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i shortened this to two lines, i thought it was nicer looking than splitting the string over two lines

CHANGES.rst Outdated Show resolved Hide resolved
jdaviz/configs/specviz/plugins/parsers.py Outdated Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
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.

Missed some doc indentation.

Otherwise I think in general the diff LGTM, so @javerbukh can do his review now. Thanks for your patience!

jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
jdaviz/core/template_mixin.py Outdated Show resolved Hide resolved
@pllim
Copy link
Contributor

pllim commented Sep 28, 2023

p.s. Open a new #2487 for general clean-up of the parser, if we ever get to it in the future.

Copy link
Contributor

@javerbukh javerbukh left a comment

Choose a reason for hiding this comment

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

LGTM!

@cshanahan1
Copy link
Contributor Author

ok thanks everyone! im going to merge this once CI passes

@cshanahan1 cshanahan1 merged commit 8bb2bd4 into spacetelescope:main Sep 28, 2023
15 checks passed
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.

Traceback when model fitting: encountered a non-finite value
5 participants