Search code examples
pythonarraysmatplotliberrorbarscatter3d

Error bars on 3D scatter plots, with array of points


I am trying to get error bars on a 3D scatter plot I have. I have seen this topic on here 'Plotting columns of an list or array with scatter3D in python' However mine is an array of z values with a corresponding array of error values.

Here is the code

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits import mplot3d

df = pd.    read_csv(r'C:\Users\brown\Dropbox\Thesis\8. Pharm Method development 5 - IDR zone\Excel docs\test.csv')
print (df)


#data
fx = (0.5,1.5,2,6)
fy = (25,20.5,16)
z = [37.8   38.61   42.29   43.8
     32.47  33.34   38.39   39.19
     24.68  26.32   28.49   30.58]


x, y = np.meshgrid(fx, fy)


#error data
zerror = [3.1   3.7 3.7  4.36
          1.44  1.47    2.85    2.2
          0.7   0.49    2.03    1.47]



#plot points
fig = plt.figure(figsize = (10,10))
ax = fig.gca(projection='3d')
surf=ax.scatter3D(x, y, z,  c=z, cmap="jet", s=200, edgecolors="black", linewidth=1, marker= "^", antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=10)

ax.set_xlabel('Measurement zone area (mm²)')
ax.set_ylabel('Distance from compact (mm)')
ax.set_zlabel('IDR (µg/min/cm²)')
ax.set_title('Ibuprofen non-plate')
ax.view_init(30, 300)
plt.rcParams["axes.edgecolor"] = "yellow"
plt.rcParams["axes.linewidth"]  = 2


#plot errorbars
for i in np.arange(0, len(fx)):
    ax.plot([x[i], x[i]], [y[i], y[i]], [z[i]+zerror[i], z[i]-zerror[i]], marker="_")
    
**Data frame values** 
   a  a sd      b  b sd      c  c sd      d  d sd
0  37.80  3.10  38.61  3.70  42.29  3.70  43.80  4.36
1  32.47  1.44  33.34  1.47  38.39  2.85  39.19  2.20
2  24.68  0.70  26.32  0.49  28.49  2.03  30.58  1.47

I get a 3D scatter graph with all the points on, but no error bars.

I get the error message 'input operand has more dimensions than allowed by the axis remapping'

I would really appreciate any help,

Many thanks


Solution

  • The issue was with formatting of your z and zerror variables (in your original code, you were wrapping the pandas operations in lists, so you need to remove the external brackets [ ] from around them).

    Once that is done, we can plot the lines by fixing the xy positions, and drawing a line from z+zerror, z-zerror.

    The snippet below does what you want:

    import matplotlib.pyplot as plt
    import numpy as np
    
    # Move these here to set these values before plotting
    plt.rcParams["axes.edgecolor"] = "yellow"
    plt.rcParams["axes.linewidth"]  = 2
    
    #data
    fx = (0.5,1.5,2,6)
    fy = (25,20.5,16)
    z = [[37.8,   38.61,   42.29,   43.8],  # Pay attention to the formatting here!
         [32.47,  33.34,   38.39,   39.19],
         [24.68,  26.32,   28.49,   30.58]]
    
    
    x, y = np.meshgrid(fx, fy)
    
    
    #error data
    zerror = [[3.1,   3.7,      3.7,  4.36],  # Pay attention to the formatting here!
              [1.44,  1.47,    2.85,  2.20],
              [0.7,   0.49,    2.03,  1.47]]
    
    
    
    #plot points
    fig = plt.figure(figsize = (10,10))
    ax = fig.gca(projection='3d')
    surf=ax.scatter3D(x, y, z,  c=z, cmap="jet", s=200, edgecolors="black", linewidth=1, marker= "^", antialiased=False)
    fig.colorbar(surf, shrink=0.5, aspect=10)
    
    ax.set_xlabel('Measurement zone area (mm²)')
    ax.set_ylabel('Distance from compact (mm)')
    ax.set_zlabel('IDR (µg/min/cm²)')
    ax.set_title('Ibuprofen non-plate')
    ax.view_init(30, 300)
    
    #plot errorbars
    for i in np.arange(0, len(fx)-1):  # Lines were modified here!
        for xval, yval, zval, zerr in zip(x[i], y[i], z[i], zerror[i]):
            ax.plot([xval, xval], [yval, yval], [zval+zerr, zval-zerr], marker="_", color='k')
    plt.show()
    

    output image:enter image description here