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: Fix interpolate #5624

Merged
merged 1 commit into from Jan 7, 2016
Merged

FIX: Fix interpolate #5624

merged 1 commit into from Jan 7, 2016

Conversation

larsoner
Copy link
Member

@ev-br this fixes a cryptic error I was getting:

  File "/home/larsoner/.local/lib/python2.7/site-packages/scipy/interpolate/interpolate.py", line 410, in __init__
    self._fill_value_below, self._fill_value_above = _duplicate(fill_value)
  File "/home/larsoner/.local/lib/python2.7/site-packages/scipy/interpolate/interpolate.py", line 311, in _duplicate
    a, b = ab
ValueError: too many values to unpack

Turns out that doing e.g. a, b = np.array([1.]) raises ValueError, not TypeError like a, b = 1. would.

@larsoner
Copy link
Member Author

Although thinking about it a bit, this probably isn't going to be a safe check case for some fill values that start with dimension 2, right...? I guess it would be ambiguous at that point if the two elements were start and end fills, or if they were the 2 values that should be used for both the start and the end. So maybe an error should be raised in that specific case?

@@ -309,7 +309,7 @@ def _duplicate(ab):
# transform to a pair (a, a)
try:
a, b = ab
except TypeError:
except (TypeError, ValueError): # ndarray can rais ValueError here
Copy link
Member Author

Choose a reason for hiding this comment

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

I can fix this typo and open the fix to master if people agree it's the right move

@ev-br ev-br added defect A clear bug or issue that prevents SciPy from being installed or used as expected scipy.interpolate labels Dec 24, 2015
@ev-br ev-br added this to the 0.17.0 milestone Dec 24, 2015
@ev-br
Copy link
Member

ev-br commented Dec 24, 2015

Somehow I did not test array-valued fill_values, thanks for catching it!

Sadly, just catching ValueError does not do the right thing e.g., here:

>>> interp1d(x, y, fill_value=(1, 2, 3))
Traceback (most recent call last):
...
ValueError: too many values to unpack

Maybe the right thing is to do np.atleast_1d(fill_value) and explicitly check that the length of the result is 1 or 2. (or np.asarray, but then np.asarray(1).size == 1 while len(np.asarray(1)) raises).

One more issue with arrays as fill_values is if fill_value == "extrapolate" which raises a FutureWarning: interpolate.py:398: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison.

You can fix this now or I'll do it separately a bit later.

@larsoner
Copy link
Member Author

One more issue with arrays as fill_values is if fill_value ==
"extrapolate" which raises a FutureWarning: interpolate.py:398:
FutureWarning: elementwise comparison failed; returning scalar instead, but
in the future will perform elementwise comparison.

This could be fixed by checking isinstance(fill_value, string_types) and fill_value=='extrapolate'), which is easy enough.

@larsoner
Copy link
Member Author

Hmmm... this is a bit more complex than I originally thought. I have some fixes in the works. But I realized that our use case actually made use of the fact that the fill_value could be broadcast. In other words, it didn't need to be a float at all, but anything that could be broadcast up to the proper shape. I realize from looking at the docstring that this wasn't actually a supported use case, but it seems like it should be. I guess that would have to be an enhancement, then?

@larsoner
Copy link
Member Author

@ev-br pushed a commit that fixes the use cases you mentioned. It also makes the code more DRY by only doing fill_value setting in one place. Instead of having a bit of duplicated logic in the constructor and the @fill_value.setter, now the constructor calls the setter, so the logic can live in one place.

@larsoner
Copy link
Member Author

Sadly this is going to actually break my use case, but I can fix it for my library. I hope it doesn't also silently break other people's code who had been using the broadcastability before, even if it wasn't documented. I could build the broadcastability check instead, if that's a worthwhile enhancement.

@ev-br
Copy link
Member

ev-br commented Dec 24, 2015

Ugh. Yes, broadcasting fill_value is undocumented and untested. But. Assuming it's useful (and I trust you on that!), this is likely more important use case than having two fill_values: there's no need to break good code by small convenience enhancements.

Therefore, I guess it makes sense to i) write a bunch of tests ignoring the two fill_values first, ii) write the definitive statements in the docstring. Then, when it's tested and documented, the question is if we can add two fill_values on top of that in a way that is unambiguous and not too brittle :-). If we can, great. Otherwise, we take two fill_values off until it's too late and it's frozen by being released.

How does this sound? Since you have a use case, how much is it to ask you to work on it? I'm willing to help but I don't know your library and its uses.

@larsoner
Copy link
Member Author

larsoner commented Dec 24, 2015 via email

@larsoner
Copy link
Member Author

@ev-br do you think it's sufficient to check the type as tuple, list for the case where the lower and upper bounds are set separately? That might take care of the iffy cases.

@larsoner
Copy link
Member Author

Okay @ev-br I made it work with broadcasting, modified docstring (should explain the changes properly) and tests, got rid of the warnings during tests about fill_value, and this code works with the case I have been using in my other repo. Ready for review/merge from my end. When it's good to go, I can also squash to a single commit for easier inclusing in master.

# at least one check failed
raise ValueError('%s argument must be able to broadcast up '
'to shape %s but had shape %s'
% (name, shape_to, shape_from))
Copy link
Member Author

Choose a reason for hiding this comment

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

It seems like something like this must already exist in numpy or scipy, but I couldn't find it with a little bit of googling so I implemented it myself

Copy link
Member

Choose a reason for hiding this comment

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

It could likely work with broacast_to (which is new in numpy 1.10 IIRC) in a try-except block?
Anyway, this version seems to work so it's fine IMO.

Copy link
Member Author

Choose a reason for hiding this comment

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

@ev-br ev-br added the backport-candidate This fix should be ported by a maintainer to previous SciPy versions. label Dec 28, 2015
@larsoner
Copy link
Member Author

@ev-br regarding backport -- the checks I added just make the failures come earlier, with a bit clearer error messages. Previously, broadcasting worked if the array was the right size. If the fill_value was the wrong size, it would bomb mentioning a size mismatch only once the fill_value was actually used. But the error message was reasonable, mentioning broadcasting problems. So the gain for backporting the size checks is fairly small. I probably wouldn't bother backporting unless others have reported failures previously, especially since the broadcasting was an implicit, unsupported / undocumented feature.

@ev-br
Copy link
Member

ev-br commented Dec 30, 2015

@Eric89GXL I'm confused now. Is the external code of yours still broken with this PR?

@larsoner
Copy link
Member Author

larsoner commented Dec 30, 2015 via email

@ev-br
Copy link
Member

ev-br commented Dec 31, 2015

And it's broken with master/rc? Why then it should not be backported to 0.17.x?

@larsoner
Copy link
Member Author

larsoner commented Dec 31, 2015 via email

@ev-br
Copy link
Member

ev-br commented Dec 31, 2015

Uh, I see now. We're on the same page, great! Thanks for clarifying it.

The fix looks pretty good. I think we need some more tests checking that an array-valued fill_value is actually used, similar to https://github.com/scipy/scipy/blob/master/scipy/interpolate/tests/test_interpolate.py#L305, for potentially confusing cases: a fill_value being a tuple of length 2, a list of length 2, a 1d array of two elements, a list of two lists (is this a single fill_value?), a tuple of two lists (this should be a pair of fill_values), a 2 by 2 array.

@larsoner
Copy link
Member Author

@ev-br good call on the tests, I found a bug with the broadcasting. Ready to go from my end if you're happy with the latest commit.

@ev-br
Copy link
Member

ev-br commented Dec 31, 2015

Will have a careful look a bit later, ran out of time this year :-). Happy New Year!

@larsoner
Copy link
Member Author

Sounds good -- happy new year to you, too!

@larsoner larsoner force-pushed the minor-fix branch 4 times, most recently from 00d0d9e to 163c638 Compare December 31, 2015 19:09
y = self._reshape_yi(y)
self._y = self._reshape_yi(self.y)
self.x = x
del y, x
Copy link
Member

Choose a reason for hiding this comment

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

For my education: What does this do here?

Copy link
Member Author

@larsoner larsoner Jan 4, 2016 via email

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Up to you, I'm fine either way.

@ev-br
Copy link
Member

ev-br commented Jan 4, 2016

This had grown a bit hairier than anticipated, huh.
The patch looks good, the tests are extensive, but nonetheless this warrants an rc2, I'm afraid. Eric, you mentioned you could squash the commits --- now would be good time. Also this should be discussed in the release notes, would you be able to add a sentence or two please.

@larsoner
Copy link
Member Author

larsoner commented Jan 4, 2016

This had grown a bit hairier than anticipated, huh.

Yeah, testing and covering all the possible cases took more lines than I thought. But I think the result should be pretty stable.

Eric, you mentioned you could squash the commits --- now would be good time. Also this should be discussed in the release notes, would you be able to add a sentence or two please.

Done and done. The only things changed since you last looked at the code is adding an inline comment to the del statement to explain it, plus changes to the release notes. Let me know if there's anything else.

@ev-br
Copy link
Member

ev-br commented Jan 4, 2016

Great, thanks! I'll keep this open for a short while in case someone wants to have a look. Otherwise, I'll merge it and forward port to master.

ev-br added a commit that referenced this pull request Jan 7, 2016
@ev-br ev-br merged commit bf97bc7 into scipy:maintenance/0.17.x Jan 7, 2016
@ev-br
Copy link
Member

ev-br commented Jan 7, 2016

Merged, thank you Eric.

@ev-br ev-br removed the backport-candidate This fix should be ported by a maintainer to previous SciPy versions. label Jan 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
defect A clear bug or issue that prevents SciPy from being installed or used as expected scipy.interpolate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants