Creating 3D plot with pyplot - ValueError: shape mismatch: objects cannot be broadcast to a single shape

217 Views Asked by At

I have a problem generating the chart. I want to create a 3d chart. I don't know where I made a mistake adding variables.

This is my code:

d = 0.2
i_min = 1 / d
i_max = pipe.max_slope(d=d) # float value
slope = ctrl.Antecedent(np.arange(i_min, i_max + 1, 1), 'slope')

v_min = 0
v_max = 5
velocity = ctrl.Antecedent(np.arange(v_min, v_max, 0.1), 'velocity')
diameter = ctrl.Consequent(np.arange(1, 4, 1), 'diameter')

# Populate slope with membership functions.
slope['low'] = fuzz.trapmf(slope.universe, [i_min, i_min, i_min + 20, 50])
slope['medium'] = fuzz.trapmf(slope.universe, [25, 40, 120, 150])
slope['high'] = fuzz.trapmf(slope.universe, [100, 150, i_max, i_max])

# Populate velocity with membership functions.
velocity['low'] = fuzz.trimf(velocity.universe, [v_min, v_min, 0.5 * v_max])
velocity['medium'] = fuzz.trimf(velocity.universe, [v_min, 0.5 * v_max, v_max])
velocity['high'] = fuzz.trimf(velocity.universe, [0.5 * v_max, v_max, v_max])

# Populate dimension 
diameter['reduction'] = fuzz.trimf(diameter.universe, [1, 1, 1])
diameter['optimal'] = fuzz.trimf(diameter.universe, [1, 2, 3])
diameter['increase'] = fuzz.trimf(diameter.universe, [2, 3, 3])

# Define rules
r1 = ctrl.Rule(slope['low'] & velocity['low'] | velocity['medium'], diameter['reduction'])
r2 = ctrl.Rule(slope['low'] & velocity['high'] | velocity['medium'], diameter['optimal'])
r3 = ctrl.Rule(slope['medium'] & velocity['medium'], diameter['optimal'])
r4 = ctrl.Rule(slope['high'] & velocity['high'] | slope['medium'], diameter['increase'])

# Create model
diameter_ctrl = ctrl.ControlSystem([r1, r2, r3, r4])
diameters = ctrl.ControlSystemSimulation(diameter_ctrl)

# Model using
diameters.input['slope'] = 150
diameters.input['velocity'] = 5

diameters.compute()
print(diameters.output['diameter'])


# Create 3D chart
s = np.arange(i_min, i_max + 1, 1)
v = np.arange(v_min, v_max + 0.1, 0.1)
s, v = np.meshgrid(s, v)

pred_val: np.ndarray = np.zeros(shape=(len(v), len(s)))
for i in range(len(s)):
    for j in range(len(v)):
        diameters.input['slope'] = i_min + i
        diameters.input['velocity'] = v_min + j
        diameters.compute()
        pred_val[i][j] = diameters.output['diameter']

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(s, v, pred_val, cmap=cm.coolwarm, linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()

Here is my traceback

ValueError                                Traceback (most recent call last)
<ipython-input-79-bb38fe77253e> in <module>()
     13 fig = plt.figure()
     14 ax = fig.gca(projection='3d')
---> 15 surf = ax.plot_surface(s, v, pred_val, cmap=cm.coolwarm, linewidth=0, antialiased=False)
     16 fig.colorbar(surf, shrink=0.5, aspect=5)
     17 plt.show()

2 frames
/usr/local/lib/python3.7/dist-packages/mpl_toolkits/mplot3d/axes3d.py in plot_surface(self, X, Y, Z, norm, vmin, vmax, lightsource, *args, **kwargs)
   1502 
   1503         # TODO: Support masked arrays
-> 1504         X, Y, Z = np.broadcast_arrays(X, Y, Z)
   1505         rows, cols = Z.shape
   1506 

<__array_function__ internals> in broadcast_arrays(*args, **kwargs)

/usr/local/lib/python3.7/dist-packages/numpy/lib/stride_tricks.py in broadcast_arrays(subok, *args)
    536     args = [np.array(_m, copy=False, subok=subok) for _m in args]
    537 
--> 538     shape = _broadcast_shape(*args)
    539 
    540     if all(array.shape == shape for array in args):

/usr/local/lib/python3.7/dist-packages/numpy/lib/stride_tricks.py in _broadcast_shape(*args)
    418     # use the old-iterator because np.nditer does not handle size 0 arrays
    419     # consistently
--> 420     b = np.broadcast(*args[:32])
    421     # unfortunately, it cannot handle 32 or more arguments directly
    422     for pos in range(32, len(args), 31):

ValueError: shape mismatch: objects cannot be broadcast to a single shape

I want the graph to look like the one in the: example The viewport is generated correctly, the problem is probably in the data I want to add.

1

There are 1 best solutions below

1
On

It's trying to turn the 3 input arrays into compatible ones, s, v, pred_val.

They come from these lines:

s = np.arange(i_min, i_max + 1, 1)
v = np.arange(v_min, v_max + 0.1, 0.1)
s, v = np.meshgrid(s, v)

pred_val: np.ndarray = np.zeros(shape=(len(v), len(s)))

As a sample let's try:

In [145]: s=np.arange(5); v=np.arange(4)
In [146]: s,v=np.meshgrid(s,v)
In [147]: s.shape
Out[147]: (4, 5)
In [148]: v.shape
Out[148]: (4, 5)
In [149]: pred_val: np.ndarray = np.zeros(shape=(len(v), len(s)))
In [150]: pred_val
Out[150]: 
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
In [151]: _.shape
Out[151]: (4, 4)

pred_val should be (4,5), like s and v. len(s) is the first dimension of s. You want its second dimenions.

np.zeros(v.shape)

would produce the correct shape.

You might want to add indexing='ij' to meshgrid, if you want (in this case) (5,4) shape.