4.3. Üç Boyutlu Grafikler

Kayıt Tarihi:

Son Güncelleme:

Özet:

Üç boyutlu grafikleri çizebilmek için yüksek boyutlu array nesnelerini anlamamız gerekir. Bu derste bu konuyu ve üç boyutlu grafiklerin yanı sıra iki değişkenli fonksiyonların seviye eğrileri gibi bazı diğer çeşit grafiklerini de çizmeyi öğreneceksiniz.

Anahtar Kelimeler: axes3d · color map · colorbar · contour · cstride · imshow · indexing · meshgrid · mpl_toolkits · mplot3d · plot_surface · plot_wireframe · projection · reshape · rstride · shape · size · view_init

Yüksek Boyutlu Array Nesneleri

İç içe geçmiş listeler gibi iç içe array nesneleri de tanımlayabiliriz. Yani bir array nesnesinin elemanları yine array nesneleri olabilir, böyle nesneleri kullanarak çok değişkenli fonksiyonları vektörize edebiliriz. Örneğin bir $f(x,y)$ fonksiyonunu 100 tane nokta ile vektörize etmek için her bir $(x_i, y_i)$ ikilisini bir array nesnesinde saklamalıyız, bu sıralı ikililerin her biri de iki float elemanı olan array nesneleri olacaktır. Sonuçta oluşan iç içe array nesnesi 100 satır ve 2 sütundan oluşan bir tablo gibi düşünülebilir. Böyle nesnelere yüksek boyutlu array nesneleri deriz. Her yüksek boyutlu array nesnesinin bir biçim (shape) değeri vardır, bu örnektekinin shape değeri (100, 2) olur. Ayrıca yüksek boyutlu array nesnelerinin size değerleri ise toplam eleman sayısıdır, bu örnektekinin size değeri 200 olacaktır.

Herhangi bir iç içe listeyi NumPy paketinin array() fonksiyonu yardımıyla bir numpy.ndarray nesnesine dönüştürebiliriz.


>>> x = range(-2, 3)
>>> y = [i**2 for i in x]
>>> tablo = [[a, b] for a, b in zip(x, y)]
>>> import numpy as np
>>> z = np.array(tablo)
>>> z
array([[-2,  4],
       [-1,  1],
       [ 0,  0],
       [ 1,  1],
       [ 2,  4]])
>>> z.size
10
>>> z.shape
(5, 2)

Yüksek boyutlu array nesnelerinde dilimleme konusunu örneklemek için daha büyük bir array verisi oluşturalım. Bunun için kullanacağımız reshape() fonksiyonu, nesnenin biçimini değiştirmeye yarar.


>>> t = np.linspace(1, 20, 20, dtype=int).reshape(4, 5)
>>> t
array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20]])

Böyle bir array nesnesinde iç içe listelerde olduğu gibi t[i][j] biçiminde tekil elemanlarına ulaşabiliriz. Ayrıca array nesnelerinde bunun daha kompakt bir yolu vardır: t[i, j]. Ayrıca s1 ve s2 iki dilim olmak üzere t[s1, s2] biçiminde hızlı bir dilimleme yapabiliriz, t[0:3:2, :-2] gibi.


>>> t[2][3]
14
>>> t[2, 3]
14
>>> t[0:, 2]
array([ 3,  8, 13, 18])
>>> t[:, 2]
array([ 3,  8, 13, 18])
>>> t[1: -1: 2, 2:]
array([[ 8,  9, 10]])

Üç Boyutlu Yüzey Grafikleri

İki değişkenli bir fonksiyonun grafiğini çizmek için, ki bu bir yüzer belirtir ve 3 boyutlu $(x, y, z)$ uzayında bulunur, öncelikle fonksiyonun $x$ ve $y$ eksenlerinin oluşturduğu bir ızgarada (grid) vektörize edilmesi gerekir. Bunu yapmak için ilk olarak bu grid nesnesi oluşturulmalıdır ve bunu yapmak için NumPy paketinin meshgrid() fonksiyonundan faydalanırız.


>>> import numpy as np
>>> x = np.linspace(1, 5, 5)
>>> y = np.linspace(1, 5, 5)
>>> X, Y = np.meshgrid(x, y, indexing='ij')
>>> X
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.],
       [ 4.,  4.,  4.,  4.,  4.],
       [ 5.,  5.,  5.,  5.,  5.]])
>>> Y
array([[ 1.,  2.,  3.,  4.,  5.],
       [ 1.,  2.,  3.,  4.,  5.],
       [ 1.,  2.,  3.,  4.,  5.],
       [ 1.,  2.,  3.,  4.,  5.],
       [ 1.,  2.,  3.,  4.,  5.]])

Bu aşamadan sonra artık fonksiyonu vektörize edebiliriz, bunun için fonksiyona x ve y değişkenlerini değil, X ve Y değişkenlerini vermeliyiz. Burada kullandığımız indexing parametresi 'xy' ve 'ij' olmak üzere iki farklı değer alabilir, eksenlerin doğru sırada görüntülenmesi için 'ij' seçmeliyiz.

Şimdi grafik çizme işlemini bir örnekler üzerinde açıklayalım. Örnek olarak $$f(x, y):=\left(1-(x^2+y^3)\right)\text{e}^{-(x^2+y^2)/2}$$ fonksiyonunu $[-3.5, 3.5]$ aralığında ele alalım. Bu gibi iki değişkenli bir fonksiyonun farklı grafik çeşitleri vardır. Bunlardan birisi $z=f(x,y)$ eşitliğinin belirttiği $(x, y, z)$ noktalarının oluşturduğu yüzeyi çizmektir. Aşağıdaki programda bu yüzeyi renksiz olarak çiziyoruz.


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

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_wireframe(X, Y, f(X, Y), cstride=3, rstride=3)
plt.show()

Şimdi bu programı açıklayalım, öncelikle 5, 6 ve 7. satırlarda vektörizasyonu tamamlıyoruz. Daha sonra fig değişkeniyle isimlendireceğimiz bir figure nesnesi oluşturyoruz. Bundan sonra da bu figure nesnesinin eksenlerini ax isimli bir değişkene atıyoruz, burada eksenlerin gca() (get current axis) fonksiyonu ile getirildiğine dikkat edelim. Ayrıca projection parametresi '3d' olarak belirtilmelidir, tüm üç boyutlu grafiklerde bu işlem yapılmalıdır. Bunun için mpl_toolkits.mplot3d modülünde bulunan Axes3D nesnesi programa ithal edilmelidir. Renksiz olarak doğrudan grid üzerindeki noktaları doğrularla birleştirerek grafik oluşturmak için plot_wireframe() fonksiyonunu kullanırız. Burada cstride ve rstride değişkenleri grid üzerinde $x$ ve $y$ yönlerinde çizgilerin hangi sıklıkta çizileceğini belirtir. Burada bir her 3 noktada bir çizgi istedik, sıklaştırmak için bu sayıyı düşürmemiz gerekir. Bu oluşturulan grafik sonrsındaki şekilde verilen ilk grafiktir.

Yukarıdaki şekil ile verilen ikinci görselde olduğu gibi grafiği renkli bir yüzey olarak çizdirmek için plot_surface() fonksiyonunu kullanırız. Fakat bu durumda renklendirmenin nasıl yapılacağını belirtmemiz gerekiyor. Bunun için renk haritaları (color map) denilen nesneleri kullanıyoruz, bunlar genelde $z=f(x,y)$ değerine göre grafiği renklendirirler. Yani grafiğin yüksekliği değiştikçe rengi de değişir. Bu nesneleri Matplotlib içinde cm modülünde bulabiliriz, mümkün olan tüm seçenekler aşağıda verilmektedir.


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

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, f(X, Y), cmap=cm.jet, rstride=2, cstride=2)
plt.show()

Üç boyutlu bir grafiği çizdirirken görüş açımızı değiştirebiliriz, bunu view_init() fonksiyonu ile yaparız. Bu fonksiyona dikey ve yatay yönde ne kadarlık bir açı ile döndüreceğimizi belirtiriz. Örnek olarak verilen grafikleri inceleyin.

Sıradaki program, grafiği yatay olarak kendi ekseninde döndüren bir animasyon oluşturur. Buna benzer şekilde dikey ekseni etrafında dönen bir animasyonu da siz hazırlayın.


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import os, glob

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure(1)
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, f(X, Y), cmap=cm.jet, rstride=3, cstride=3)
ax.view_init(25, 75)

for aci in range(0, 360, 5):
    ax.view_init(30, aci)
    plt.title('Aci: (30, %d)' % aci, y=1)
    plt.savefig('pltanimasyon%03d.png' % aci)

os.system('convert -delay 10 pltanimasyon*.png pltanimasyon.gif')

for dosya in glob.glob('pltanimasyon*.png'):
    os.remove(dosya)

Seviye Eğrileri ve Diğer Grafikler

Bir $f(x, y)$ fonksiyonunu görsel olarak temsil etmenin başka bir yolu da bazı seviye eğrilerini çizmektir. Bir seviye eğrisi, $C$ bir sabit olmak üzere, $f(x, y)=C$ bağınıtısı ile verilen eğridir. Farklı $C$ sayıları için bir kaç seviye eğrisi aynı grafikte gösterilince yüzeyi anlamamız kolaylaşır. Seviye eğrilerini çizmek için Matplotlib içinde bulunan contour() fonksiyonunu kullanırız, verilecek olan argümanlar plot_surface() fonksiyonu ile benzerdir.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca()
ax.contour(X, Y, f(X, Y), cmap=cm.jet)
plt.show()

Bu program aşağıdaki ile verilen ilk grafiği üretir. Bunu üç boyutlu eksenler üzerine çizdirirsek her bir $C$ sayısı için oluşan seviye eğrisini $z$ ekseninde $z=C$ yüzeyinde gösterilir. Bunu aşağıdaki gibi yaparız ve çıktısı aşağıdaki şekil ile verilen ikinci grafikteki gibidir.


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

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.contour(X, Y, f(X, Y), cmap=cm.jet)
ax.view_init(15, 40)
plt.show()

İstersek kaç tane sevie eğrisi gösterileceğini belirtebiliriz, bunu aşağıdaki gibi yaparız ve çıktısı sıradaki şekil ile verilen ilk grafikteki gibi olur.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca()
ax.contour(X, Y, f(X, Y), 20, cmap=cm.jet)
plt.axis('equal')
plt.show()

Ayrıca istersek seviye eğrileri için kullanılan $C$ sayıları açıkça bir liste içinde belirtilebilir. Aşağıdaki programı inceleyin, sıradaki şekil ile verilen ikinci grafik üretilecektir.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca()
L = [-0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75]
ax.contour(X, Y, f(X, Y), levels=L, cmap=cm.jet)
plt.axis('equal')
plt.show()

Arsu edersek her bir seviye eğrisinin karşılık geldiği $C$ değerini grafik üzerinde belirtebiliriz. Bunun için grafiği bir değişkene atayıp bu değişkeni clabel() fonksiyonunda kullanırız. Aşağıdaki programda bunu örnekliyoruz, çıktısı olan grafik aşağıdaki şekil ile verilmektedir.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca()
seviye = ax.contour(X, Y, f(X, Y), cmap=cm.jet)
plt.clabel(seviye)
plt.show()

Yeteri kadar fazla sayıda seviye eğrisi ile tüm düzlem doldurulursa renkli bir harita oluşur. Bu görüntü yüzeyin kuşbakışı görünümü gibidir ve imshow() fonksiyonu ile yapılabilir. Böyle bir grafikle birlikte bir yükseklik ölçeği vermek faydalı olacaktır, bunu da colorbar() fonksiyonu ile yaparız. Aşağıdaki program böyle bir grafik çizer, ek olarak yukarıdaki programda yaptığımız gibi etiketlenmiş seviye eğrilerini de aynı grafikte gösterir. Bunun çıktısı aşağıda verilecektir. Bu programda ayrıca seviye eğrisi etiketlerini biçimlendirmek için bazı yöntemler de gösteriliyor.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure()
ax = fig.gca()
grafik = ax.imshow(f(X, Y), cmap=cm.jet)
seviye = ax.contour(f(X,Y),linewidths=1, colors='k')
ax.clabel(seviye, inline=True, fmt='%1.1f', fontsize=8)
fig.colorbar(grafik)
plt.show()

Python'da Matplotlib ile parametrik uzay eğrilerinin grafikleri de çizilebilir. Örnek olarak $$\textbf{r}(t)=t\cos(t)\textbf{i} + t\sin(t)\textbf{j} + t\textbf{k}$$ eğrisinin grafiğini çizdirelim. Bunun için plot() fonksiyonu kullanılır, aşağıdaki programda bu fonksiyonun kullanımı örnekleniyor.


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

t = np.linspace(0, 10*np.pi, 1000)
X = t*np.cos(t)
Y = t*np.sin(t)
Z = t

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(X, Y, Z, 'r-', lw=2)

plt.show()

Bu program sıradaki şekil ile verilen grafiği üretir.

Bir fonksiyonun oluşturduğu vektör alanı çizilebilir, bunun için gradiyent alanı hesaplatıp daha sonra çizim yaparız. Burada genellikle okların sayısının uygun görüntü için deneme yanılma yoluyla belirlenmesi gerekir. Aşağıdaki grafikte vektör alanı ile birlikte bazı seviye eğrileri aynı grafikte gösteriliyor. Düzgün bir görüntü elde etmek için vektör alanında farklı, seviye eğrilerinde farklı sayıda nokta ile çalışıyoruz.


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

x = y = np.linspace(-3.5, 3.5, 20)
X, Y = np.meshgrid(x, y, indexing='ij')

x2 = y2 = np.linspace(-3.5, 3.5, 100)
X2, Y2 = np.meshgrid(x2, y2, indexing='ij')

f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)
dfdx, dfdy = np.gradient(f(X, Y))

fig = plt.figure()
ax = fig.gca()
ax.quiver(X, Y, dfdx, dfdy, color='r')
ax.contour(X2, Y2, f(X2, Y2), 10, colors='k')

plt.show()

Bu programla aşağıda görülen grafik elde edilir.

Son olarak farklı grafikleri aynı figure nesnesi içinde çizmeye değinelim. Üç boyutlu grafiklerle bunu yapmak için add_subplot() fonksiyonunu kullanırız, aşağıdaki örnekle bunu açıklıyoruz.


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

x = y = np.linspace(-3.5, 3.5, 100)
X, Y = np.meshgrid(x, y, indexing='ij')
f = lambda x, y: (1-(x**2+y**3))*np.exp(-(x**2+y**2)/2)

fig = plt.figure(figsize=plt.figaspect(0.4))

ax = fig.add_subplot(1,2,1, projection='3d')
ax.plot_surface(X, Y, f(X, Y), cmap=cm.jet, rstride=2, cstride=2)

ax = fig.add_subplot(1,2,2)
grafik = ax.imshow(f(X, Y), cmap=cm.jet)
seviye = ax.contour(f(X,Y),linewidths=1, colors='k')
ax.clabel(seviye, inline=True, fmt='%1.1f', fontsize=8)
fig.colorbar(grafik, shrink=0.92)

plt.show()

Burada colorbar ögesinin boyutunu ayarlamak için shrink argümanı girdik, en iyi görüntüyü elde etmek için bu sayıyı deneme yanılma yoluyla belirleyebiliriz. Bu grafik aşağıda gösterilmektedir.

Önceki Ders Notu:
4.2. Grafik Çizimi
Dersin Ana Sayfası:
Python ve Bilimsel Hesaplama