Search code examples
pythonmatplotlibmplot3d

mplot3D fill_between extends over axis limits


I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.

Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.

I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.

This screen shot shows the plot generated with filled area extending beyond the set axis limits. Generated plot with filled area extending beyond the set axis limits.

  1. How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
  2. How do I add a legend for those plots onto the figure?

Code as follows:

from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D

x,y = genfromtxt("data.dat",unpack=True)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')

ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()

ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)

plt.show()

Solution

  • I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:

    from numpy import *
    import matplotlib.pylab as plt
    from mpl_toolkits.mplot3d import Axes3D
    from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
    
    #x,y = genfromtxt("data.dat",unpack=True)
    # Generated some random data
    w = 3
    x,y = np.arange(100), np.random.randint(0,100+w,100)
    y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
    z = np.zeros(x.shape)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    #ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
    verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
    ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
    
    ax.plot(x,z,y,label="line plot")
    ax.legend()
    ax.set_ylim(-1,1)
    plt.show()
    

    The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:

    Polygon 3D mimicking fill_between in matplotlib