8000 Piecewise fix for 0d by astrojuanlu · Pull Request #4792 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

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

Merged
merged 1 commit into from
Jun 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
BUG: Fixed piecewise function for 0d input
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
astrojuanlu committed Jun 8, 2014
commit 292b9ff538ea4950c7380c76cf65d1a5b108b75c
32 changes: 12 additions & 20 deletions numpy/lib/function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

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 things

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 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.

Copy link
Contributor

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

Copy link
Contributor

Choose a reason for hiding this comment

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

not fixed yet

Copy link
Contributor Author

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.

Copy link
Contributor

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



See Also
Expand Down Expand Up @@ -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):
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author
< 8000 div class="ml-5">

Choose a reason for hiding this comment

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

Actually I tend to use select myself and avoid piecewise because of these little annoyances. I can quickly try it or leave it for the next version.

Copy link
Contributor

Choose a reason for hiding this comment

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

lets leave that to the next version

Expand Down
10 changes: 10 additions & 0 deletions numpy/lib/tests/test_function_base.py
8000
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand All @@ -1505,6 +1506,15 @@ def test_0d(self):
assert_(y.ndim == 0)
assert_(y == 0)

x = 5
y = piecewise(x, [[True], [False]], [1, 0])
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't make any sense. If x is 0d, the conditions should be 0d too, as [True, False]

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):
Expand Down
0