2014-12-19 17 views
21

Ich versuche einfach, eine Oberfläche und ihre Kontur in 3D, genau wie in this Beispiel zu plotten.3D-Plot mit Matplotlib

Dies ist der Code verwende ich es zu tun:

import matplotlib.pyplot as plt 
from mpl_toolkits.mplot3d import axes3d 
from matplotlib import cm 
import numpy 

def plot_3d_contour(x_dim, y_dim, x_steps, y_steps, scalar_field, file_path): 
    fig = plt.figure() 

    x, y = numpy.mgrid[-x_dim/2:x_dim/2:x_steps*1j, -y_dim/2:y_dim/2:y_steps*1j] 
    v_min = numpy.min(scalar_field) 
    v_max = nupmy.max(scalar_field) 

    ax = fig.gca(projection='3d') 

    cset = ax.contourf(x, y, scalar_field, zdir='z', offset=v_min, cmap=cm.coolwarm) 
    cset = ax.contourf(x, y, scalar_field, zdir='x', offset=-x_dim/2-1, cmap=cm.coolwarm) 
    cset = ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm) 

    ax.plot_surface(x, y, scalar_field, rstride=10, cstride=10, alpha=0.3) 

    ax.set_xlabel('X') 
    ax.set_xlim(-x_dim/2-1, x_dim/2+1) 
    ax.set_ylabel('Y') 
    ax.set_ylim(-y_dim/2-1, y_dim/2+1) 
    ax.set_zlabel('Z') 
    ax.set_zlim(v_min, v_max) 

    plt.savefig(file_path + '.jpg') 
    plt.close() 

scalar_field = numpy.loadtxt('../scalar_field', delimiter=",") 
plot_3d_contour(12, 12, 100, 100, scalar_field, 'scalar_field3D') 

Aber ich bin immer ein seltsames Verhalten, bei dem die eine Kontur (zdir=y) über die Oberfläche zu sein. Außerdem bin ich eine seltsame Kontur in z_dir=z bekommen (mit einem Abschnitt fehlt):

enter image description here

Ich frage mich, was mir fehlt. Das Skalarfeld kann here gefunden werden.

+1

Haben Sie beim Plotten versucht, 'zorder' zu ändern? – nicoguaro

+0

Ja, das war mein erster Versuch, @nicoguaro. – pceccon

+4

Ich kann dies in Matplotlib 1.4.0 reproduzieren. Einer der Devs mag kommentieren, aber ich denke, das hängt mit allgemeinen 3D-Plotproblemen zusammen - wenn Sie diese Figur interaktiv machen, können Sie sehen, dass das Ändern des Ansichtswinkels ändert, welche Elemente "im Vordergrund" gezeichnet werden. Was den witzigen Streifen von "Nicht-Daten" in der xy-Ebenen-Kontur betrifft, kann es etwas Lustiges sein, das mit dem "Schließen" einer der Konturen passiert. Ich glaube nicht, dass mit deinem Code etwas nicht stimmt. – Ajean

Antwort

6

Ich stimme Ajean. Ich glaube, dass das Problem entsteht, weil jeder Künstler der Matplotlib (d. H. PolygonCollection) separat gerendert wird. Es gibt keine Möglichkeit, unterschiedliche Gesichter von demselben Objekt auf verschiedenen Seiten eines anderen Objekts in der Szene darzustellen.

Hier ist ein nützliches Stück Code:

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

file_path = "./3D_surface_and_contour.jpg" 
p = 0.05 
f = -0.01 

def get_data(p): 
    x, y, z = axes3d.get_test_data(p) 
    z = f * z 
    return x, y, z 

def plot_3d_contour(p, f): 
    nrows = 4 
    ncols = 5 

    x, y, z = get_data(p) 

    x_min, x_max = np.min(x), np.max(x) 
    y_min, y_max = np.min(y), np.max(y) 
    z_min, z_max = np.min(z), np.max(z) 

    fig = plt.figure(figsize=(15, 10)) 
    for n in range(nrows * ncols): 
     i = n % ncols 
     j = n/ncols 
     k = n + 1 
     if j == 0: 
      azim = -60 + (i - 2) * 15 
      elev = 30 
     elif j == 1: 
      azim = -60 
      elev = 30 + (i - 2) * 5 
     elif j == 2: 
      azim = 60 + (i - 2) * 10 
      elev = 30 
     elif j == 3: 
      azim = 60 
      elev = 30 + (i - 2) * 5 
     ax = fig.add_subplot(nrows, ncols, k, projection='3d') 
     ax.set_title("azim=" + str(azim) + " elev=" + str(elev)) 
     ax.tick_params(labelsize=8) 
     ax.view_init(azim=azim, elev=elev) 
     ax.plot_surface(x, y, z, rstride=10, cstride=10, alpha=0.3) 
     ax.contourf(x, y, z, zdir='z', offset=z_min, cmap=cm.coolwarm) 
     ax.contourf(x, y, z, zdir='x', offset=x_min, cmap=cm.coolwarm) 
     if j == 0 or j == 1: 
      ax.contourf(x, y, z, zdir='y', offset=y_max, cmap=cm.coolwarm) 
     elif j == 2 or j == 3: 
      ax.contourf(x, y, z, zdir='y', offset=y_min, cmap=cm.coolwarm) 

     ax.set_xlabel('X') 
     ax.set_xlim(x_min, x_max) 
     ax.set_ylabel('Y') 
     ax.set_ylim(y_min, y_max) 
     ax.set_zlabel('Z') 
     ax.set_zlim(z_min, z_max) 

    plt.savefig(file_path, dpi=80) 
    plt.close() 

plot_3d_contour(p, f) 

, das folgende Bild ergibt:

enter image description here

Die ersten beiden Reihen durch einen Code ähnlich wie bei Ihnen erzeugt werden. Sie werden feststellen, dass das Festlegen der Höhe mit view_init auf einen höheren Wert das Problem löst. Aber es ist nicht befriedigend. Ich habe auch den Einfluss des Bereichs der Z-Werte bestimmt (hier nicht gezeigt), der Bug scheint nur zu erscheinen, wenn dieser Bereich klein ist (Sie können den f Parameter verwenden, um ihn zu testen), der erklärt, warum die example nicht leidet davon.

Die Lösung, die ich vorschlage, ist zu ersetzen:

ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm) 

von:

ax.contourf(x, y, scalar_field, zdir='y', offset=-y_dim/2-1, cmap=cm.coolwarm) 

in Ihrem Code und fügen Sie diese zusätzliche Zeile:

ax.view_init(azim=60, elev=30) 

Wie in den letzten beiden gezeigt Zeilen des vorherigen Bildes, so können Sie die Launen von Matplotlib vermeiden.

+0

Wusste nicht, dass @Baptiste! Vielen Dank!! – pceccon