WhiskeyHammer
WhiskeyHammer

Reputation: 226

Scatter points are disappearing when rotating a 3d surface plot

I'm trying to get a feel for how well a surface fits my data points by graphing everything and rotating the surface around to check for any oddities in the surface behavior relative to the scattered point in 3d space.

The problem is that when I rotate the render to do this, the plots disappear. How can I make the plots persist?

enter image description here

You can repro with the below code - mainly taken from the amazing answers at Python 3D polynomial surface fit, order dependent.

import numpy as np
import scipy.linalg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import itertools

def main():
    # Generate Data...
    numdata = 100
    x = np.random.random(numdata)
    y = np.random.random(numdata)
    z = x**2 + y**2 + 3*x**3 + y + np.random.random(numdata)

    # Fit a 3rd order, 2d polynomial
    m = polyfit2d(x,y,z)

    # Evaluate it on a grid...
    nx, ny = 20, 20
    xx, yy = np.meshgrid(np.linspace(x.min(), x.max(), nx), 
                         np.linspace(y.min(), y.max(), ny))
    zz = polyval2d(xx, yy, m)

    # Plot
    #plt.imshow(zz, extent=(x.min(), y.max(), x.max(), y.min()))
    #plt.scatter(x, y, c=z)
    #plt.show()

    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(x, y, z, color='red', zorder=0)
    ax.plot_surface(xx, yy, zz, zorder=10)
    ax.set_xlabel('X data')
    ax.set_ylabel('Y data')
    ax.set_zlabel('Z data')

    plt.show()
    text = "filler"

def polyfit2d(x, y, z, order=4):
    ncols = (order + 1)**2
    G = np.zeros((x.size, ncols))
    #ij = itertools.product(range(order+1), range(order+1))
    ij = xy_powers(order)
    for k, (i,j) in enumerate(ij):
        G[:,k] = x**i * y**j
    m, _, _, _ = np.linalg.lstsq(G, z)
    return m

def polyval2d(x, y, m):
    order = int(np.sqrt(len(m))) - 1
    #ij = itertools.product(range(order+1), range(order+1))
    ij = xy_powers(order)
    z = np.zeros_like(x)
    for a, (i,j) in zip(m, ij):
        z += a * x**i * y**j
    return z

def xy_powers(order):
    powers = itertools.product(range(order + 1), range(order + 1))
    return [tup for tup in powers if sum(tup) <= order]

main()

Upvotes: 2

Views: 1114

Answers (2)

Thinklex
Thinklex

Reputation: 143

TLDR; It is not possible to do it in general, but there is more than (only) setting the transparency value alpha (setting specific z-order).

Hi, although this question is relatively old , I would like to add that this is in general a known issue of Matplotlib-3D (GitHub-Issue). But there is some silver lining, if you don't like setting the transparency value, you can manually specify the z-order (Z-Order-Demo) of your surface and your dots, as detailed in the GitHub issue (see also merged Pull Request, and related issue).

As you already include a z-order argument in your code, you could specify the z-order explicitly, s.t. the points are always seen:

    # Draw scatter with higher zorder than surface
    ax.scatter(x, y, z, color='red', zorder=10) 
    ax.plot_surface(xx, yy, zz, zorder=0)

If you only want to see a fraction of the points, depending on their natural visibility, you would need to calculate manually whether the points are visible or not, and then set the z-order of the visible ones higher. Be aware, this could be quite some work to implement correctly in general, but certain edge cases might be easier to implement.

Upvotes: 0

jylls
jylls

Reputation: 4705

A simple thing you can do is setting the transparency of your surface to a lower value than your scatter plot. See example below where I used a transparency value equal to 0.4 with the line ax.plot_surface(xx, yy, zz, zorder=10,alpha=0.4).

And the output gives:

enter image description here

Upvotes: 2

Related Questions