I have a function F def F(a, b): c = a * b Initially, a is a scalar, b[240,3000]. No problem. Later I want to use F, where a[240] is a vector. I want to allow both the scalar and vector cases. So I write: def F(a,b): a = np.atleast_1d(a) c = a[:,None] * b This now works for scalar a or vector a. But this solutions seems inelegant, and somewhat fragile. Suppose later we want to allow a[240,3000], a 2d array matching b. Certainly don't want to write code like: if a.ndim == 0:... Is there a more elegant/robust approach? Thanks, Neal
On Wed, Mar 22, 2023 at 9:34 AM Neal Becker <ndbecker2@gmail.com> wrote:
I have a function F def F(a, b): c = a * b
Initially, a is a scalar, b[240,3000]. No problem. Later I want to use F, where a[240] is a vector. I want to allow both the scalar and vector cases. So I write:
def F(a,b): a = np.atleast_1d(a) c = a[:,None] * b
This now works for scalar a or vector a. But this solutions seems inelegant, and somewhat fragile. Suppose later we want to allow a[240,3000], a 2d array matching b.
Certainly don't want to write code like: if a.ndim == 0:...
Is there a more elegant/robust approach?
I would leave it as `c = a * b` and simply record in the docstring that `a` and `b` should be broadcastable. Yes, that means that the user will have to write `F(a[:, np.newaxis], b)` for that one case, and that looks a little ugly, but overall it's less cognitive load on the user to just reuse the common convention of broadcasting than to record the special case. -- Robert Kern
On Wed, 2023-03-22 at 12:00 -0400, Robert Kern wrote:
On Wed, Mar 22, 2023 at 9:34 AM Neal Becker <ndbecker2@gmail.com> wrote:
I have a function F def F(a, b): c = a * b
Initially, a is a scalar, b[240,3000]. No problem. Later I want to use F, where a[240] is a vector. I want to allow both the scalar and vector cases. So I write:
def F(a,b): a = np.atleast_1d(a) c = a[:,None] * b
This now works for scalar a or vector a. But this solutions seems inelegant, and somewhat fragile. Suppose later we want to allow a[240,3000], a 2d array matching b.
Certainly don't want to write code like: if a.ndim == 0:...
Is there a more elegant/robust approach?
I would leave it as `c = a * b` and simply record in the docstring that `a` and `b` should be broadcastable. Yes, that means that the user will have to write `F(a[:, np.newaxis], b)` for that one case, and that looks a little ugly, but overall it's less cognitive load on the user to just reuse the common convention of broadcasting than to record the special case.
I will note that it is not hard to insert the new axes. `np.expand_dims` may be convenient. many functions (ufuncs) also have the `outer` version which does this: `np.add.outer()`, etc. However, I agree. Unless the use-case exceedingly clear about requiring "outer" behavior. "outer" behavior is uncommon for functions in the NumPy world and broadcasting is what users will generally expect (and that includes future self). - Sebastian
_______________________________________________ NumPy-Discussion mailing list -- numpy-discussion@python.org To unsubscribe send an email to numpy-discussion-leave@python.org https://mail.python.org/mailman3/lists/numpy-discussion.python.org/ Member address: sebastian@sipsolutions.net
On Thu, Mar 23, 2023 at 5:21 AM Sebastian Berg <sebastian@sipsolutions.net> wrote:
On Wed, 2023-03-22 at 12:00 -0400, Robert Kern wrote:
On Wed, Mar 22, 2023 at 9:34 AM Neal Becker <ndbecker2@gmail.com> wrote:
I have a function F def F(a, b): c = a * b
Initially, a is a scalar, b[240,3000]. No problem. Later I want to use F, where a[240] is a vector. I want to allow both the scalar and vector cases. So I write:
def F(a,b): a = np.atleast_1d(a) c = a[:,None] * b
This now works for scalar a or vector a. But this solutions seems inelegant, and somewhat fragile. Suppose later we want to allow a[240,3000], a 2d array matching b.
Certainly don't want to write code like: if a.ndim == 0:...
Is there a more elegant/robust approach?
I would leave it as `c = a * b` and simply record in the docstring that `a` and `b` should be broadcastable. Yes, that means that the user will have to write `F(a[:, np.newaxis], b)` for that one case, and that looks a little ugly, but overall it's less cognitive load on the user to just reuse the common convention of broadcasting than to record the special case.
I will note that it is not hard to insert the new axes. `np.expand_dims` may be convenient. many functions (ufuncs) also have the `outer` version which does this: `np.add.outer()`, etc.
However, I agree. Unless the use-case exceedingly clear about requiring "outer" behavior. "outer" behavior is uncommon for functions in the NumPy world and broadcasting is what users will generally expect (and that includes future self).
- Sebastian
Thanks for the advice! On reflection, I agree.
participants (3)
-
Neal Becker
-
Robert Kern
-
Sebastian Berg