-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Piecewise fix for 0d #4792
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
Piecewise fix for 0d #4792
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
When `x` has more than one element the condlist `[True, False]` is being made equivalent to `[[True, False]]`, which is correct. However, when `x` is zero dimensional the expected condlist is `[[True], [False]]`: this commit addresses the issue. Besides, the documentation stated that there could be undefined values but actually these are 0 by default: using `nan` would be desirable, but for the moment the docs were corrected. Closes #331.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -651,7 +651,7 @@ def piecewise(x, condlist, funclist, *args, **kw): | |
The output is the same shape and type as x and is found by | ||
calling the functions in `funclist` on the appropriate portions of `x`, | ||
as defined by the boolean arrays in `condlist`. Portions not covered | ||
by any condition have undefined values. | ||
by any condition have a default value of 0. | ||
|
||
|
||
See Also | ||
|
@@ -693,32 +693,24 @@ def piecewise(x, condlist, funclist, *args, **kw): | |
if (isscalar(condlist) or not (isinstance(condlist[0], list) or | ||
isinstance(condlist[0], ndarray))): | ||
condlist = [condlist] | ||
condlist = [asarray(c, dtype=bool) for c in condlist] | ||
condlist = array(condlist, dtype=bool) | ||
n = len(condlist) | ||
if n == n2 - 1: # compute the "otherwise" condition. | ||
totlist = condlist[0] | ||
for k in range(1, n): | ||
totlist |= condlist[k] | ||
condlist.append(~totlist) | ||
n += 1 | ||
if (n != n2): | ||
raise ValueError( | ||
"function list and condition list must be the same") | ||
zerod = False | ||
# This is a hack to work around problems with NumPy's | ||
# handling of 0-d arrays and boolean indexing with | ||
# numpy.bool_ scalars | ||
zerod = False | ||
if x.ndim == 0: | ||
x = x[None] | ||
zerod = True | ||
newcondlist = [] | ||
for k in range(n): | ||
if condlist[k].ndim == 0: | ||
condition = condlist[k][None] | ||
else: | ||
condition = condlist[k] | ||
newcondlist.append(condition) | ||
condlist = newcondlist | ||
if condlist.shape[-1] != 1: | ||
condlist = condlist.T | ||
if n == n2 - 1: # compute the "otherwise" condition. | ||
totlist = np.logical_or.reduce(condlist, axis=0) | ||
condlist = np.vstack([condlist, ~totlist]) | ||
n += 1 | ||
if (n != n2): | ||
raise ValueError( | ||
"function list and condition list must be the same") | ||
|
||
y = zeros(x.shape, x.dtype) | ||
for k in range(n): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not something for this PR but the following part could maybe use np.select somehow, just to reduce the optimization targets, e.g. select uses copyto instead of indexing as its a little faster.
<
8000
div class="ml-5">
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I tend to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets leave that to the next version |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1487,6 +1487,7 @@ def test_simple(self): | |
x = piecewise([0, 0], [[False, True]], [lambda x:-1]) | ||
assert_array_equal(x, [0, -1]) | ||
|
||
def test_two_conditions(self): | ||
x = piecewise([1, 2], [[True, False], [False, True]], [3, 4]) | ||
assert_array_equal(x, [3, 4]) | ||
|
||
|
@@ -1505,6 +1506,15 @@ def test_0d(self): | |
assert_(y.ndim == 0) | ||
assert_(y == 0) | ||
|
||
x = 5 | ||
y = piecewise(x, [[True], [False]], [1, 0]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't make any sense. If |
||
assert_(y.ndim == 0) | ||
assert_(y == 1) | ||
|
||
def test_0d_comparison(self): | ||
x = 3 | ||
y = piecewise(x, [x <= 3, x > 3], [4, 0]) | ||
|
||
|
||
class TestBincount(TestCase): | ||
def test_simple(self): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to define the value?
if not I think it would be better to leave it undefined for now. Just to keep options open to use the ufunc
where=
argument in future for some thingsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think it would be better to leave it
nan
but that changes the behaviour of the function. I have no problem in addressing that if you devs agree.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes nan would make more sense but we want to release 1.9 soon and I don't want to rush in a change that might bite us later
leaving the status quo of undefined is safer, we can revisit for 1.10
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not fixed yet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excuse me, the status quo now is not undefined, but 0 as a default: that's what I kept in these changes. In fact most of the tests rely on this, see https://github.com/Juanlu001/numpy/blob/292b9ff538ea4950c7380c76cf65d1a5b108b75c/numpy/lib/tests/test_function_base.py#L1466. Either I set this to
nan
or leave it as it is now, but in this case the docs were wrong.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in principle the documentation defines the interface but if our tets rely on it maybe also third party software
using 0 has the advantages that it is defined for all types (including integers) and the memory can be sparsely allocated on capable operating systems, so I guess it is fine to keep it.
thanks, merging