Can someone help me understand and correct my short code for the error in the title?
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 1, 100)
a = 2
def f(x):
if x<0.5:
return a*x
elif x>=0.5:
return a*(1-x)
plt.plot(x, f(x))
plt.show()
6
7 def f(x):
----> 8 if x<0.5:
9 return a*x
10 elif x>=0.5:
Edit:
The plot should look like an upside-down V. Trying np.piecewise():
x = np.linspace(0, 1, 100)
a = 2
y = np.piecewise(x, [x < 0.5, x >= 0.5], [a*x, a*(1-x)])
plt.plot(x, y)
plt.show()
I get the error: NumPy boolean array indexing assignment cannot assign 100 input values to the 50 output values where the mask is true.
CodePudding user response:
The x
and the 2 derived arrays:
In [249]: x = np.linspace(0,1,11)
In [250]: x
Out[250]: array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])
In [251]: 2*x
Out[251]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. ])
In [252]: 2*(1-x)
Out[252]: array([2. , 1.8, 1.6, 1.4, 1.2, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
One way to make a composite from the two, is to start with one array, and replace a subset of the values with other:
In [253]: res = 2*(1-x)
In [254]: res[x<.5] = 2*x[x<.5]
In [255]: res
Out[255]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
np.where
can be used to do the same:
In [256]: np.where(x<.5, 2*x, 2*(1-x))
Out[256]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
We can use your f
, but with scalar arguments:
In [261]: np.array([f(i) for i in x])
Out[261]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
To use piecewise
we have to provide functions, not whole arrays:
In [266]: np.piecewise(x, [x < 0.5, x >= 0.5], [lambda i:a*i, lambda i:a*(1-i)])
Out[266]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
As for your original error:
In [267]: x<.5
Out[267]:
array([ True, True, True, True, True, False, False, False, False,
False, False])
In [268]: if (x<.5): print(1)
Traceback (most recent call last):
File "<ipython-input-268-f1475f5112fc>", line 1, in <module>
if (x<.5): print(1)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
if
is a plain python expression. There's no implied iteration of the elements of x
. if (x<.5).all():...
runs, but only tests if all the elements of x
satisfy the condition. That's not what you want. Similarly for any
. The kind of x<.5
masking that I did at the start is, perhaps a more common solution to this ambiguity
.
A more direct translation of the f(x)
switch, would be to calculate the mask
, and apply it, and its complement to create the two parts of the result:
In [269]: mask = x<.5
In [270]: res = np.zeros_like(x)
In [271]: res
Out[271]: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [272]: res[mask] = 2*x[mask]
In [273]: res[~mask] = 2*(1-x[~mask])
In [274]: res
Out[274]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])
or
In [275]: np.hstack((2*x[mask], 2*(1-x[~mask])))
Out[275]: array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 0.8, 0.6, 0.4, 0.2, 0. ])