działki powierzchniowe w matplotlib

105

Mam listę 3-krotek reprezentujących zbiór punktów w przestrzeni 3D. Chcę wykreślić powierzchnię obejmującą wszystkie te punkty.

plot_surfaceFunkcji w mplot3dopakowaniu wymaga jako argumenty X, Y i Z może być 2d tablice. Czy plot_surfaceodpowiednia funkcja do kreślenia powierzchni i jak przekształcić moje dane do wymaganego formatu?

data = [(x1,y1,z1),(x2,y2,z2),.....,(xn,yn,zn)]
Graddy
źródło
Zacznij oznaczać wszystkie te duplikaty na powierzchni i zamykać je ze sobą. Oznacz także numpy , mesh dla tych, które dotyczą generowania meshgrid.
smci

Odpowiedzi:

121

W przypadku powierzchni jest to trochę inne niż lista 3-krotek, należy przekazać siatkę dla domeny w tablicach 2d.

Jeśli masz tylko listę punktów 3D, a nie jakąś funkcję f(x, y) -> z, będziesz mieć problem, ponieważ istnieje wiele sposobów triangulacji chmury punktów 3D na powierzchnię.

Oto przykład gładkiej powierzchni:

import numpy as np
from mpl_toolkits.mplot3d import Axes3D  
# Axes3D import has side effects, it enables using projection='3d' in add_subplot
import matplotlib.pyplot as plt
import random

def fun(x, y):
    return x**2 + y

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-3.0, 3.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array(fun(np.ravel(X), np.ravel(Y)))
Z = zs.reshape(X.shape)

ax.plot_surface(X, Y, Z)

ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')

plt.show()

3d

wim
źródło
1
Cześć, dzięki za to. Czy możesz wyjaśnić, w jaki sposób posiadanie funkcji f(x,y) -> zpozwala uzyskać więcej informacji, niż zwykłe stosowanie podejścia opartego na liście, takiego jak początkowo PO.
Gregory Kuhn
16
Ale co robisz, gdy z jest zmienną niezależną, a nie funkcją x i y?
Labibah,
4
W takim przypadku być może powinieneś plot_trisurfzamiast tego spojrzeć . Ale jak już wspomniałem, jest to nietrywialne, ponieważ musisz triangulować powierzchnię i istnieje wiele rozwiązań. Jako podstawowy przykład rozważ tylko 4 punkty podane przez (0, 0, 0,2), (0, 1, 0), (1, 1, 0,2), (1, 0, 0). Oglądany z góry wygląda jak kwadrat z lekkim fałdem. Ale na której przekątnej występuje „fałda”? Czy jest to „wysoka” przekątna przy 0,2 czy „mała” przekątna przy 0? Obie są ważnymi powierzchniami! Musisz więc wybrać algorytm triangulacji, zanim będziesz miał dobrze zdefiniowane rozwiązanie.
wim
Dlaczego z mpl_toolkits.mplot3d import Axes3D, skoro Axes3D nie jest nigdzie używane w powyższym kodzie?
絢 瀬 絵 里
5
Ten import ma skutki uboczne. Używanie kwarg projection='3d'w wywołaniu fig.add_subplotbędzie niedostępne bez tego importu.
wim
34

Możesz czytać dane bezpośrednio z jakiegoś pliku i wykresu

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from sys import argv

x,y,z = np.loadtxt('your_file', unpack=True)

fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.savefig('teste.pdf')
plt.show()

W razie potrzeby możesz podać vmin i vmax, aby zdefiniować zakres kolorów, np

surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1, vmin=0, vmax=2000)

powierzchnia

Sekcja bonusowa

Zastanawiałem się, jak wykonać interaktywne wykresy, w tym przypadku ze sztucznymi danymi

from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
from IPython.display import Image

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

def f(x, y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))

def plot(i):

    fig = plt.figure()
    ax = plt.axes(projection='3d')

    theta = 2 * np.pi * np.random.random(1000)
    r = i * np.random.random(1000)
    x = np.ravel(r * np.sin(theta))
    y = np.ravel(r * np.cos(theta))
    z = f(x, y)

    ax.plot_trisurf(x, y, z, cmap='viridis', edgecolor='none')
    fig.tight_layout()

interactive_plot = interactive(plot, i=(2, 10))
interactive_plot
Emanuel Fontelles
źródło
5
Ściśle mówiąc, pandy są tutaj niepotrzebne.
downer
Trudno mi odtworzyć tę fabułę. Jakie byłyby niektóre (mniejsze) wartości próbek, aby to osiągnąć?
JRsz
21

Właśnie natknąłem się na ten sam problem. I równomiernie rozmieszczone dane, które są w 3 1-D tablic zamiast macierzy 2-D, że matplotlib„S plot_surfacepragnie. Moje dane znalazły się w postaci, pandas.DataFramewięc oto matplotlib.plot_surfaceprzykład z modyfikacjami wykresu 3 tablic 1-D.

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
    linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title('Original Code')

To jest oryginalny przykład. Dodanie tego następnego bitu powoduje utworzenie tego samego wykresu z 3 tablic 1-W.

# ~~~~ MODIFICATION TO EXAMPLE BEGINS HERE ~~~~ #
import pandas as pd
from scipy.interpolate import griddata
# create 1D-arrays from the 2D-arrays
x = X.reshape(1600)
y = Y.reshape(1600)
z = Z.reshape(1600)
xyz = {'x': x, 'y': y, 'z': z}

# put the data into a pandas DataFrame (this is what my data looks like)
df = pd.DataFrame(xyz, index=range(len(xyz['x']))) 

# re-create the 2D-arrays
x1 = np.linspace(df['x'].min(), df['x'].max(), len(df['x'].unique()))
y1 = np.linspace(df['y'].min(), df['y'].max(), len(df['y'].unique()))
x2, y2 = np.meshgrid(x1, y1)
z2 = griddata((df['x'], df['y']), df['z'], (x2, y2), method='cubic')

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(x2, y2, z2, rstride=1, cstride=1, cmap=cm.coolwarm,
    linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)

ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

fig.colorbar(surf, shrink=0.5, aspect=5)
plt.title('Meshgrid Created from 3 1D Arrays')
# ~~~~ MODIFICATION TO EXAMPLE ENDS HERE ~~~~ #

plt.show()

Oto wynikowe liczby:

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Steven C. Howell
źródło
Zastanawiałem się, czy można usunąć linie pojawiające się na powierzchni (obrazek powyżej), chodzi mi o to, czy można nadać powierzchni połysk zamiast łuszczenia? Dziękuję Ci. @ stvn66
diffracteD
@diffracteD, spróbuj użyć mniejszego rozmiaru siatki. Jestem prawie pewien, że to właśnie określa szerokość między konturami. Oceniając na drobniejszej siatce, powinieneś zasadniczo zmniejszać „rozmiar piksela” i zwiększać rozdzielczość, zbliżając się do gładszego gradientu.
Steven C. Howell
Czy istnieje sposób na pokolorowanie powyższej powierzchni według określonych kategorii? Na przykład. Kategoria x, y, z to format danych i chciałbym pokolorować powierzchnię przechodzącą przez x, y, z według określonej kategorii.
Rudresh Ajgaonkar
@RudreshAjgaonkar, powinieneś być w stanie użyć trzech oddzielnych poleceń fabuły, po jednym dla każdej kategorii, używając dowolnej kolorystyki dla każdej z trzech.
Steven C. Howell
czy możesz podać przykładowy kod? Jestem całkiem nowy w Matplotlib i Pythonie.
Rudresh Ajgaonkar
4

Żeby się wtrącić, Emanuel miał odpowiedź, której ja (i prawdopodobnie wielu innych) szukam. Jeśli masz rozproszone dane 3D w 3 oddzielnych tablicach, pandy są niesamowitą pomocą i działają znacznie lepiej niż inne opcje. Aby rozwinąć, załóżmy, że twoje x, y, z są pewnymi dowolnymi zmiennymi. W moim przypadku były to błędy c, gamma i błędy, ponieważ testowałem maszynę wektorów nośnych. Istnieje wiele potencjalnych możliwości wykreślenia danych:

  • scatter3D (cParams, gammas, avg_errors_array) - to działa, ale jest zbyt uproszczone
  • plot_wireframe (cParams, gammas, avg_errors_array) - to działa, ale będzie wyglądać brzydko, jeśli twoje dane nie są dobrze posortowane, jak to potencjalnie ma miejsce w przypadku ogromnych kawałków prawdziwych danych naukowych
  • ax.plot3D (cParams, gammas, avg_errors_array) - podobny do wireframe

Wykres szkieletowy danych

Wykres szkieletowy danych

Rozrzut danych w 3D

Rozrzut danych w 3D

Kod wygląda następująco:

    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.set_xlabel('c parameter')
    ax.set_ylabel('gamma parameter')
    ax.set_zlabel('Error rate')
    #ax.plot_wireframe(cParams, gammas, avg_errors_array)
    #ax.plot3D(cParams, gammas, avg_errors_array)
    #ax.scatter3D(cParams, gammas, avg_errors_array, zdir='z',cmap='viridis')

    df = pd.DataFrame({'x': cParams, 'y': gammas, 'z': avg_errors_array})
    surf = ax.plot_trisurf(df.x, df.y, df.z, cmap=cm.jet, linewidth=0.1)
    fig.colorbar(surf, shrink=0.5, aspect=5)    
    plt.savefig('./plots/avgErrs_vs_C_andgamma_type_%s.png'%(k))
    plt.show()

Oto ostateczny wynik:

plot_trisurf danych xyz

ArtifexR
źródło
3

sprawdź oficjalny przykład. X, Y i Z są rzeczywiście tablicami 2d, numpy.meshgrid () to prosty sposób na uzyskanie siatki 2d x, y z wartości 1d x i y.

http://matplotlib.sourceforge.net/mpl_examples/mplot3d/surface3d_demo.py

oto pythonowy sposób na konwersję 3-krotek na 3 tablice 1d.

data = [(1,2,3), (10,20,30), (11, 22, 33), (110, 220, 330)]
X,Y,Z = zip(*data)
In [7]: X
Out[7]: (1, 10, 11, 110)
In [8]: Y
Out[8]: (2, 20, 22, 220)
In [9]: Z
Out[9]: (3, 30, 33, 330)

Oto triangulacja mtaplotlib delaunay (interpolacja), konwertuje 1d x, y, z na coś zgodnego (?):

http://matplotlib.sourceforge.net/api/mlab_api.html#matplotlib.mlab.griddata

Dima Tisnek
źródło
Nie ... XYZ są w tym przykładzie 2-wymiarowe.
wim
Poprawiono mnie. Użyj funkcji meshgrid (), jeśli dane są rozmieszczone równomiernie, jak w połączonym przykładzie. Interpoluj np. Za pomocą griddata (), jeśli dane nie są równomiernie rozmieszczone.
Dima Tisnek
2

Dodam tylko kilka myśli, które mogą pomóc innym z problemami z nieregularnym typem domeny. W sytuacji, gdy użytkownik ma trzy wektory / listy, x, y, z reprezentujące rozwiązanie 2D, gdzie z ma być narysowane na prostokątnej siatce jako powierzchni, mają zastosowanie komentarze „plot_trisurf ()” autorstwa ArtifixR. Podobny przykład, ale z domeną inną niż prostokątna, to:

import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D 

# problem parameters
nu = 50; nv = 50
u = np.linspace(0, 2*np.pi, nu,) 
v = np.linspace(0, np.pi, nv,)

xx = np.zeros((nu,nv),dtype='d')
yy = np.zeros((nu,nv),dtype='d')
zz = np.zeros((nu,nv),dtype='d')

# populate x,y,z arrays
for i in range(nu):
  for j in range(nv):
    xx[i,j] = np.sin(v[j])*np.cos(u[i])
    yy[i,j] = np.sin(v[j])*np.sin(u[i])
    zz[i,j] = np.exp(-4*(xx[i,j]**2 + yy[i,j]**2)) # bell curve

# convert arrays to vectors
x = xx.flatten()
y = yy.flatten()
z = zz.flatten()

# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = Axes3D(fig)
ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0,
                antialiased=False)
ax.set_title(r'trisurf example',fontsize=16, color='k')
ax.view_init(60, 35)
fig.tight_layout()
plt.show()

Powyższy kod daje:

Wykres powierzchni dla problemu siatki nieprostokątnej

Jednak może to nie rozwiązać wszystkich problemów, zwłaszcza gdy problem dotyczy nieregularnej domeny. Również w przypadku, gdy domena ma jeden lub więcej obszarów wklęsłych, triangulacja delaunaya może skutkować generowaniem fałszywych trójkątów na zewnątrz domeny. W takich przypadkach te nieuczciwe trójkąty muszą zostać usunięte z triangulacji, aby uzyskać prawidłową reprezentację powierzchni. W takich sytuacjach użytkownik może być zmuszony do jawnego włączenia obliczeń triangulacji delaunaya, aby można było programowo usunąć te trójkąty. W takich okolicznościach poniższy kod mógłby zastąpić poprzedni kod wydruku:


import matplotlib.tri as mtri 
import scipy.spatial
# plot final solution
pts = np.vstack([x, y]).T
tess = scipy.spatial.Delaunay(pts) # tessilation

# Create the matplotlib Triangulation object
xx = tess.points[:, 0]
yy = tess.points[:, 1]
tri = tess.vertices # or tess.simplices depending on scipy version

#############################################################
# NOTE: If 2D domain has concave properties one has to
#       remove delaunay triangles that are exterior to the domain.
#       This operation is problem specific!
#       For simple situations create a polygon of the
#       domain from boundary nodes and identify triangles
#       in 'tri' outside the polygon. Then delete them from
#       'tri'.
#       <ADD THE CODE HERE>
#############################################################

triDat = mtri.Triangulation(x=pts[:, 0], y=pts[:, 1], triangles=tri)

# Plot solution surface
fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
ax.plot_trisurf(triDat, z, linewidth=0, edgecolor='none',
                antialiased=False, cmap=cm.jet)
ax.set_title(r'trisurf with delaunay triangulation', 
          fontsize=16, color='k')
plt.show()

Poniżej przedstawiono przykładowe wykresy ilustrujące rozwiązanie 1) z fałszywymi trójkątami i 2) z których zostały usunięte:

wprowadź opis obrazu tutaj

trójkąty usunięte

Mam nadzieję, że powyższe może być pomocne dla osób z sytuacjami wklęsłymi w danych rozwiązania.

Graham G
źródło
1

W Matlab Zrobiłem coś podobnego za pomocą delaunayfunkcji na x, ykoordy tylko (nie z), a następnie wykreślenie z trimeshlub trisurf, używając zjako wysokość.

SciPy ma klasę Delaunay , która jest oparta na tej samej podstawowej bibliotece QHull, co funkcja Matlab delaunay, więc powinieneś uzyskać identyczne wyniki.

Stamtąd powinno być kilka wierszy kodu, aby przekonwertować ten przykład Plotting 3D Polygons w python-matplotlib na to, co chcesz osiągnąć, ponieważ Delaunaypodaje specyfikację każdego trójkątnego wielokąta.

Evgeni Sergeev
źródło
Zobacz tę odpowiedź na podstawie ax.plot_trisurf(..).
Evgeni Sergeev
0

Nie ma możliwości bezpośredniego wykonania powierzchni 3D przy użyciu danych. Poleciłbym zbudowanie modelu interpolacji za pomocą narzędzi, takich jak pykridge . Proces będzie obejmował trzy kroki:

  1. Wytrenuj model interpolacji przy użyciu pykridge
  2. Zbuduj siatkę z Xi Yużywającmeshgrid
  3. Interpoluj wartości dla Z

Po utworzeniu siatki i odpowiadających jej Zwartości możesz teraz zacząć plot_surface. Zwróć uwagę, że w zależności od rozmiaru danych meshgridfunkcja może działać przez chwilę. Sposób obejścia problemu polega na utworzeniu równo rozmieszczonych próbek przy użyciu osi np.linspacefor Xi Y, a następnie zastosowaniu interpolacji w celu ustalenia niezbędnych Zwartości. Jeśli tak, to interpolowane wartości mogą różnić się od oryginału Z, ponieważ Xi Yuległy zmianie.

lenhhoxung
źródło