Problem with 3D wireframe visualization in matplotlib

Ken Grimes :

I need to plot 3D data of the form z_i as function of (x_i, y_i) using a wireframe. I wrote the code below:

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import scipy.interpolate as spint

## Data to plot
sim_data = np.array([[ 20,   1,   8],
   [ 20,   2,   7],
   [ 20,   4,   7],
   [ 20,   6,   6],
   [ 20,  10,   6],
   [ 50,   0.4,  15],
   [ 50,   0.8,  11],
   [ 50,   1,  10],
   [ 50,   2,   8],
   [ 50,   4,   7],
   [ 50,   6,   7],
   [ 50,  10,   7],
   [100,   0.4,  22],
   [100,   0.8,  15],
   [100,   1,  13],
   [100,   2,  10],
   [100,   4,   8],
   [100,   6,   7],
   [100,  10,   7]])

x = sim_data[:, 0]
y = sim_data[:, 1]
z = sim_data[:, 2]

# Do trisurf plot
fig = plt.figure()
ax  = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(x, y, z)
ax.set_xlabel('Air flow')
ax.set_ylabel('Fuel rate')
ax.set_zlabel('Temp.')
ax.text2D(0.05, 0.95, "Trisurf plot", transform=ax.transAxes)

# Transform from vector to grid
X, Y = np.meshgrid(x, y)
xi   = (X, Y)

Z    = spint.griddata((x,y), z, xi)

# Do wireframe plot
fig = plt.figure()
ax  = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, Y, Z)
ax.set_xlabel('Air flow')
ax.set_ylabel('Fuel rate')
ax.set_zlabel('Temp.')
ax.text2D(0.05, 0.95, "Wireframe plot", transform=ax.transAxes)

# Do surface plot
fig = plt.figure()
ax  = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z)
ax.set_xlabel('Air flow')
ax.set_ylabel('Fuel rate')
ax.set_zlabel('Temp.')
ax.text2D(0.05, 0.95, "Surface plot", transform=ax.transAxes)

But I get some annoying extra wires (marked with a red arrow):

enter image description here

How can I get rid of this arrow? I have the same problem while trying a surface plot by the way:

enter image description here

My goal is to have a plot similar to a trisurf plot like the one below, but with a wireframe visualization.

enter image description here

Many thanks in advance.

trsvchn :

What's wrong with wireframe?

I am not sure, but I think the problem is in your data. It's small and if you look carefully you will see that it looks like a stack of three different lines (observations). Look at this plot:

img0

it's definitely there are three parallel lines over there. I suppose, that's might cause confusion with plot_wireframe, as well as with plot from the previous image. I see 3 possible solutions:

Solution 1: Use plot

Ok, so the first solution is not to use plot_wireframe at all. Let's use good old one plot to build our own wires. But first, let's break our data into 3 lines data:

line1 = sim_data[0:5][::-1]  # NOTE: the first line is shorter
line2 = sim_data[5:12][::-1]
line3 = sim_data[12:][::-1]

Plot them all!

# a helper function
def prepare_fig(fw=7, fh=7, view = (25, 30)):
    fig = plt.figure(figsize=(fw, fh))
    ax = fig.add_subplot(111, projection='3d')
    ax.view_init(view[0], view[-1])
    return ax

ax = prepare_fig()
ax.title.set_text('3 Lines')
for line in [line1, line2, line3]:
    x, y, z = line[:, 0], line[:, 1], line[:, 2]
    ax.plot(x, y, z, c='tab:blue', linewidth=3)

img1

Ok, we fixed undesired links, now let's add parallel links (lines) to connect our main lines:

ax = prepare_fig()
ax.title.set_text('Paralel links')
for i in range(len(line3)):
    x, y, z = [], [], []
    if i < len(line1):
        x.append(line1[:, 0][i])
        y.append(line1[:, 1][i])
        z.append(line1[:, 2][i])
    else:
        # line1 is shorter so we will put nan here (for now)
        x.append(np.nan)
        y.append(np.nan)
        z.append(np.nan)
    x.extend([line2[:, 0][i], line3[:, 0][i]])
    y.extend([line2[:, 1][i], line3[:, 1][i]])
    z.extend([line2[:, 2][i], line3[:, 2][i]]) 
    ax.plot(x, y, z, c='tab:blue', linewidth=3)

img2

Now all in one:

Final Code:

ax = prepare_fig()
ax.title.set_text('Handmade Wireframe (enclosed)')

line1 = sim_data[0:5][::-1]
line2 = sim_data[5:12][::-1]
line3 = sim_data[12:][::-1]

for line in [line1, line2, line3]:
    x, y, z = line[:, 0], line[:, 1], line[:, 2]
    ax.plot(x, y, z, c='tab:blue', linewidth=3)

for i in range(len(line3)):
    x, y, z = [], [], []
    if i < len(line1):
        x.append(line1[:, 0][i])
        y.append(line1[:, 1][i])
        z.append(line1[:, 2][i])
    else:
        # put nan because line1 is shorter
#         x.append(np.nan)
#         y.append(np.nan)
#         z.append(np.nan)
        # Or you can just replace it with last line1 value
        x.append(line1[:, 0][-1])
        y.append(line1[:, 1][-1])
        z.append(line1[:, 2][-1])
    x.extend([line2[:, 0][i], line3[:, 0][i]])
    y.extend([line2[:, 1][i], line3[:, 1][i]])
    z.extend([line2[:, 2][i], line3[:, 2][i]])
    ax.plot(x, y, z, c='tab:blue', linewidth=3)

img3

Solution 2: Use plot_trisurf.

If triangles are acceptable, another solution is to transform trisurf to wireframe-like by some tweaking.

x = sim_data[:, 0]
y = sim_data[:, 1]
z = sim_data[:, 2]

ax = prepare_fig()
ax.title.set_text('Trisurf Wireframe')

trisurf = ax.plot_trisurf(x, y, z)
# turn of surface color, you can control it with alpha here:
trisurf.set_facecolor(mpl.colors.colorConverter.to_rgba('w', alpha=0.0))
# setting wire color
trisurf.set_edgecolor('tab:blue')
#setting wire width
trisurf.set_linewidth(3)

img4

Solution 3: Use plot_wireframe and interpolation at linspace grid.

This might be solution if you want good looking smooth surface. You just need to generate new grid and then using scipy's spint.griddata to perform interpolation:

import scipy.interpolate as spint

x = sim_data[:, 0]
y = sim_data[:, 1]
z = sim_data[:, 2]

# generate new linear grid based on previous
X, Y = np.meshgrid(np.linspace(min(x), max(x), len(x)),
                   np.linspace(min(y), max(y), len(y)))

Z = spint.griddata((x, y), z, (X, Y))

ax = prepare_fig()
ax.title.set_text('Interpotation on Linspace Grid')

# ax.plot_wireframe(X, Y, Z, rstride=3, cstride=3)
ax.plot_surface(X, Y, Z, rstride=3, cstride=3)

And you will get something like this:

img5

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=12111&siteId=1