Home > Blockchain >  Trying to compare array but getting "ValueError: The truth value of an array with more than one
Trying to compare array but getting "ValueError: The truth value of an array with more than one

Time:09-30

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. ])
  • Related