Uygulama Notları: 4

FİZ219 - Bilgisayar Programlama I | 30/10/2020

Grafik Çizimi

  • Giriş
  • Veriler
  • Grafik çizimi
    • Markör çeşitleri
    • Renk çeşitleri
    • Markör boyu
    • Markörün iç ve dış renkleri
      • Uzun satırlar
    • Çizgiler
    • Çizgi çeşitleri
    • Çizgi rengi
    • Çizgi kalınlığı
  • Gerekli şeyler & süs+püs
    • Başlıklar, tanımlar
    • "Izgara ve keneler"
  • Grafiğin sınırları
  • Çoklu grafikler
    • Bir ipte iki cambaz (I)
    • Fonksiyon çizimi
    • Bir ipte iki cambaz (II)
    • Çok ipte çok cambaz
  • Saçılım
    • Rastgele sayılar
    • Bonus / Ekstra / Tercihe Bağlı
  • Histogram
  • 3 Boyutlu Çizimler
  • Grafikleri dosyaya yazdırma

Emre S. Tasci emre.tasci@hacettepe.edu.tr

Giriş

Verilerin grafiksel olarak temsili, bizlere elimizdeki bilgilerin birbirleriyle ilişkileri ve birbirlerine bağlı olarak gelişimleri hakkında bilgi verdiği gibi, arkalarında yatan model ve formülleri de görebilme imkanı sunar. Bunlara ilaveten, grafiklere bakarak, elimizdeki aralıkta ölçümünü yapmadığımız noktalara karşılık gelen değerleri ve dahi aralığın dışındaki yerleri de tahmin edebiliriz: ilk işleme içdeğer bulma ("interpolation"); ikinci işleme ise öteleme ("extrapolation") denir.

Grafik çizebilmek için, öncelikle, tabii ki, elimizde veriler olması gerekmekte... 8)

Bu dersimizde kullanacağımız veriler, Horvat & Jecmenica'nın "The free fall experiment" (Resonance 21, 259-275 (2016) | DOI: 10.1007/s12045-016-0321-9) makalesinden alınmıştır.

Veriler

Elimizde, sınıf ortamında, yerden farklı yüksekliklerden bırakılan bir kütlenin düşme sürelerinin ölçüm verileri var:

Yükseklik (m) Düşüş Süresi (s)
0.2 0.201900
0.7 0.377759
0.8 0.403882
1.2 0.494707
1.3 0.514983
1.8 0.606071

İlk iş olarak verileri veriler diye bir matrise girip, saklayalım:

In [1]:
veriler = [0.2    0.201900
0.7    0.377759
0.8    0.403882
1.2    0.494707
1.3    0.514983
1.8    0.606071] # yukseklik (m) | zaman (s)
veriler =

   0.20000   0.20190
   0.70000   0.37776
   0.80000   0.40388
   1.20000   0.49471
   1.30000   0.51498
   1.80000   0.60607

işlemek kolay olsun diye, 1. sütundaki verilere y, 2. sütundakilere de t diyelim:

In [2]:
y = veriler(:,1)
y =

   0.20000
   0.70000
   0.80000
   1.20000
   1.30000
   1.80000

In [3]:
t = veriler(:,2)
t =

   0.20190
   0.37776
   0.40388
   0.49471
   0.51498
   0.60607

Grafik çizimi

Bir deney yaparken, "kontrol parametresi" dediğimiz, değerini bizim belirleyip, ona göre ölçümler aldığımız parametreyi grafikte yatay eksene (x-ekseni) yerleştiririz, kontrol parametresinin farklı değerlerinde ölçtüğümüz değerleri de düşey eksene (y-ekseni) işaretleriz.

Verileri aldığımız serbest düşüş deneyinde, kontrol parametresi yükseklik olduğundan, onu yatay eksene, ölçülen parametre olan süre değerlerini ise düşey eksende işaretleyeceğiz.

Octave'da grafik çizdirmek çok pratiktir (hatta o kadar pratiktir ki, Python'a bile neredeyse doğrudan aktarılmıştır (matplotlib modülü altında)). Grafik çizdirmek için kullandığımız komut, İngilizce'de grafik çizmek anlamına gelen plot() komutudur. plot() fonksiyonuna girilen birinci parametre yatay eksen değerlerini, ikinci parametre ise düşey eksen değerlerini ifade eder.

Elimizdeki verileri en basit şekilde plot(y,t) diyerek çizdiririz:

In [4]:
plot(y,t)

Tebrikler! İlk grafiğinizi çizdiniz. Fakat pek de güzel (/düzgün) görünmüyor gibi sanki. Aslında biraz dikkat bakınca, tablodaki verilerin grafikteki doğruların kırıldıkları noktalar olduğunu görebiliyoruz gibi (gibi)... Tabloyu aşağıya tekrardan alıntılayalım, bir bakın bakalım, siz de kırılma noktalarının oralara geldiğini teyit edebilecek misiniz?

Yükseklik (m) Düşüş Süresi (s)
0.2 0.201900
0.7 0.377759
0.8 0.403882
1.2 0.494707
1.3 0.514983
1.8 0.606071

Böyle zor oluyor hakikaten... Onun yerine, verileri grafik üzerinde göstersek?

In [5]:
plot(y,t,"o")

Bence bu şekilde çok daha açık oldu. Fark ettiyseniz, bunu, plot fonksiyonuna 3. bir parametre ("o") vererek hallettik. Buna biçim parametresi diyoruz ve tırnak içinde belirtiyoruz. o dediğimiz için, veri noktalarını temsil eden işaretçikleri ("markörleri") daireler şeklinde gösterdi.

Markör çeşitleri

Dilersek başka bir sürü şekil markör kullanabiliriz:

Sembol İşaret
+ Artı
o Daire
* Yıldız
. Nokta
x Çarpı
s Kare (square)
d Paralelkenar/Elmas (diamond)
^ Sivri ucu yukarıda olan üçgen
v Sivri ucu aşağıda olan üçgen
> Sivri ucu sağda olan üçgen
< Sivri ucu solda olan üçgen
p 5 köşeli yıldız (pentagram)
h 6 köşeli yıldız (hexagram)
In [6]:
# Verileri karelerle temsil edelim:
plot(y,t,"s")

Grafiğin biçim parametresinde başka özellikler de tanımlayabiliriz, örneğin rengi.

Renk çeşitleri

Renkleri hazır tanımlı renkler arasından belirtebiliriz (dilersek 'color' parametresi ile herhangi bir RGB üçlüsü olarak da belirtebiliriz, ama bu aşamada çok gerekli değil).

Hazır tanımlı renklerin listesine gelirsek:

Sembol Renk
k Siyah (blacK)
r Kırmızı (Red)
g Yeşil (Green)
b Mavi (Blue)
m Mor (Magenta)
c Turkuaz (Cyan)
w Beyaz (White)

Bu renk sembollerini de doğrudan markör sembolünün yanına yazıyoruz - çakışma olmadığı için hangisini önce yazdığımızın bir önemi yok, yeter ki aynı tırnak işaretinin içerisinde olsunlar: örneğin "xr" de, "rx" de, bize kırmızı çarpılı markörler çıkartır:

In [7]:
# Kırmızı çarpılar
plot(y,t,"rx")

Markör boyu

Çarpılar biraz küçük gibi görünüyor, büyütme işini "markersize" (markör boyu) parametresi ile yapıyoruz. Bu parametre markör şekli ve renginden biraz farklı bir şekilde tanımlanıyor yalnız: önce parametreyi, ardından değeri giriyoruz şu şekilde:

In [8]:
# Kırmızı büyük çarpılar
plot(y,t,"rx","markersize",10)

Markörün iç ve dış renkleri

Eğer daire, kare veya yıldız gibi içi olan bir markör kullanıyorsak, içinin ve kontürünün renklerini sırasıyla "markerfacecolor" ve "markeredgecolor" parametreleriyle ayrı ayrı belirleyebiliriz.

Örneğin, içi yeşil, dışı kırmızı daireler kullanalım:

In [9]:
plot(y,t,"o","markersize",10,"markerfacecolor","g","markeredgecolor","r")

Uzun satırlar

Octave'da uzun satırları bölmek için ... operatörü kullanılır. Yukarıdaki uzun komutu, iki satıra bölmek isteseydik:

In [10]:
plot(y,t,"o","markersize",10,...
         "markerfacecolor","g","markeredgecolor","r")

şeklinde yazabilirdik (boşluklar önemli olmadığından, alt satırlara geçtiğimizde yazmaya başlamadan önce istediğimiz kadar boşluk bırakabiliriz). Bu taktiği çok fazla elemanı olan dizileri yazmak için de kullanabiliriz:

In [11]:
cok_elemanli_dizi = [123,321,123,321,432,543,...
  213,12,34,65,834,23,543,234,2343,4234,432,...
  23423,432423,42342,342,432423,432,234,324]
cok_elemanli_dizi =

 Columns 1 through 8:

      123      321      123      321      432      543      213       12

 Columns 9 through 16:

       34       65      834       23      543      234     2343     4234

 Columns 17 through 24:

      432    23423   432423    42342      342   432423      432      234

 Column 25:

      324

Çizgiler

Markörleri iyice kavradığımıza göre, grafiklerimize biraz daha çeşitlilik katalım. Veri noktalarımızı çizgi ile birleştirmek için, biçim kısmına en temel halde bir - sembolü ekleriz:

In [12]:
plot(y,t,"o-")

Biçimde iki paremetre belirledik, "o" ile "-": ilki veri noktalarını daireler ile temsil edilmesini, ikincisi de bu veri noktalarının düz çizgi ile birleştirilmesini belirtmekte (hatırlarsanız, ilk çizdirdiğimiz grafikte (plot(y,t)) veri noktaları ayrıca gösterilmemiş, sadece doğrudan çizgilerle araları birleştirilmişti).

Her şeye olduğu gibi, çizgi biçimlerine de müdahale edebiliyoruz: çizgilerimizin çeşitlerini (düz, kesikli, nokta nokta, çizgi-nokta); rengini, kalınlığını, onlara karşılık gelen anahtar kelime / sembollerle ayarlayabiliriz.

Çizgi Çeşitleri

Yukarıda da yazdığımız üzere, 4 farklı çeşit çizgi ile çalışabiliriz:

Sembol Çizgi çeşidi
- Düz çizgi
-- Kesikli çizgi
: Nokta nokta
-. Çizgi-nokta

Nasıl göründüklerini altta topluca inceleyebilirsiniz (o 2x2 grafiği nasıl çizdirdiğimizi (subplot) de birazdan göreceğiz ;) :

In [13]:
subplot(2,2,1)
 plot(y,t,"o-")
 title("Duz Cizgi")
subplot(2,2,2)
 plot(y,t,"o--")
 title("Kesikli Cizgi")
subplot(2,2,3)
 plot(y,t,"o:")
 title("Nokta nokta")
subplot(2,2,4)
 plot(y,t,"o-.")
 title("Cizgi-nokta")

Çizgi Rengi

Aslında en başından beri grafikleri çizerken, normal renk parametresi (örn. "or"'deki "r") olarak belirttiğimiz renk, temel olarak çizgi rengidir, markörlerin rengi de ("markerfacecolor" ve/veya "markeredgecolor" ile) aksi belirtilmedikçe bu renkte çizilir.

Yani, grafiğimizi mavi kesikli çizgi üzerine dışı kırmızı, içi yeşil dairesel markörlerle çizdirmek istersek, bunu şu şekilde yaparız:

In [14]:
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r")

Çizgi kalınlığı

Çizgi kalınlığını da linewidth parametresi ile belirtiriz. Yukarıdaki grafiği daha kalın çizgilerle çizmek için:

In [15]:
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4)

Markörler çizgilerin yanında biraz küçük kaldı, onları da coşturup, bu konuyu kapatalım:

In [16]:
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

Gerekli şeyler & süs+püs

Grafiklerimiz bu şekilde matematiksel olarak grafik olsalar da, fiziksel açıdan pek de işe yarar şeyler değiller: Neyin grafiği bu? Yatay ve düşey eksenler neyi temsil ediyor, birimleri nedir? Veri noktaları tam olarak neye karşılık geliyor? Bu noktada devreye grafik tanımlayıcıları girer.

Başlıklar, tanımlar

Eksenlerin tanımlarını xlabel() ve ylabel() fonksiyonları ile ve grafiği plot ile çizdirdikten sonra belirtiriz. title() fonksiyonu ile de grafiğimizin başlığını yazarız. Örneğimiz üzerinde çalışalım, çok daha anlaşılır olacak:

In [17]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

"Izgara ve keneler" 8P

Grafik üzerindeki verilerimizin değerleri rahatça anlaşılıp, kıyaslanabilsin diye, eksenleri böldüğümüz aralıklardan çizgiler uzatırız. Bu kontrolü grid komutu ile gerçekleştiriyoruz:

  • grid on bu çizgilerin görüntülenmesini açar
  • grid off da kapar

Eksenler üzerindeki hangi değerlerin hangi aralıklarla (örneğin yukarıdaki grafikte yatay eksen 0'dan 2'ye 0.5'lik aralıklarla; düşey eksen ise 0.2'den 0.7'ye 0.1'lik aralıklarla bölünmüş durumda) bölüneceğini, ya da bir başka deyişle çentik konumlarını ise sırasıyla xticks() ve yticks() fonksiyonları ile belirtiriz.

Yukarıdaki örnek üzerinde çalışmaya devam edersek, önce ızgarayı (grid'i) açalım:

In [18]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

# Izgarayı açalım
grid on

sonrasında da yatay ve düşey eksendeki çentikleri (ticks) sıklaştıralım:

In [19]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

# Izgarayı açalım
grid on

# Çentikleri yeniden tanımlayalım
xticks(0:0.1:2)
yticks(0.2:0.05:0.7)

Grafik aynı grafik olsa da, okunabilirliği epey artmış oldu. Çentik tanımlamasını yaparken, geçen dersimizde tanıştığımız aralık operatörünü (başlangıç:adım uzunluğu:bitiş) kullandığımıza dikkatinizi çekerim.

Pek sık karşılaşılmasa da, çentikleri dilerseniz kafanıza göre, eşit olmayan aralıklarla da tanımlayabilirsiniz:

In [20]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b--o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

# Izgarayı açalım
grid on

# Çentikleri yeniden tanımlayalım

# Yatay eksendekiler için sadece veri noktalarını
xticks([0.2, 0.7, 0.8, 1.2, 1.3, 1.8])

# Düşey eksendekiler içinse kafamıza göre alalım...
yticks([0.23,1/3,0.65])

(tahmin edebileceğiniz üzere, yatay eksendeki çentikleri tek tek yazmak yerine, doğrudan xticks(y) de diyebilirdik - ne de olsa y değişkeni doğal olarak yukarıda girdiğimiz veri noktalarını tutmakta ;)

Grafiğin sınırları

Grafiğimizin çizim sınırlarını axis() fonksiyonu ile belirliyoruz. Parametre olarak bir dizi alır ve dizinin eleman sayısı 2 ise bunlar yatay eksenin minimum ve maksimum değerlerini; 4 elemanlı bir dizi ise o zaman da dizinin elemanlarını sırasıyla yatay eksenin minimum ve maksimum değerleri ve düşey eksenin minimum ve maksimum değerleri olarak yorumlar.

Yukarıdaki örneğimizi önce bir düzgün çizdirelim:

In [21]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b-o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

# Izgarayı açalım
grid on

Yatay eksenin gösterim aralığını [-1,3], düşey eksen aralığını ise [0,1] yapalım:

In [22]:
# Grafiğimizi çizdiriyoruz
plot(y,t,"b-o","markerfacecolor","g","markeredgecolor","r",...
     "linewidth",4,"markersize",30)

# Tanımlamaları yapıyoruz
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

# Izgarayı açalım
grid on

axis([-1,3,0,1])

Çoklu grafikler

Bir ipte iki cambaz (I)

Bir grafik üzerinde iki veri setini göstermek için, sanki iki ayrı grafik çizdirecekmişiz gibi tanımlayıp, sonra bunları aynı plot() fonksiyonu içerisinde birleştiririz.

Ders boyunca kullanageldiğimiz veri setini hatırlayalım:

In [23]:
veriler
veriler =

   0.20000   0.20190
   0.70000   0.37776
   0.80000   0.40388
   1.20000   0.49471
   1.30000   0.51498
   1.80000   0.60607

Bu serbest düşüş deneyini ellerinde kronometre olmadığından, süreyi saatin saniye kısmıyla ölçen ikinci bir grubun da yapmış olduğunu ve şu sonuçarı aldığını varsayalım:

In [24]:
veriler_2 = [0.20000   0.3
   0.70000   0.4
   0.80000   0.4
   1.20000   0.5
   1.30000   0.6
   1.80000   0.7]
   
# 2. sütunu t_2 değişkenine atayalım
t_2 = veriler_2(:,2)

# (y değerleri (1.sütun) ilk veri setiyle
# aynı olduğundan, onu ayrıca bir y_2 değişkenine 
# atamadık)
veriler_2 =

   0.20000   0.30000
   0.70000   0.40000
   0.80000   0.40000
   1.20000   0.50000
   1.30000   0.60000
   1.80000   0.70000

t_2 =

   0.30000
   0.40000
   0.40000
   0.50000
   0.60000
   0.70000

Normalde, ikisini ayrı ayrı çizdirecek olsak:

In [25]:
plot(y,t,"^-b") # 1. grafik
In [26]:
plot(y,t_2,"o-r") # 2. grafik

Şeklinde yazardık. Aynı grafik üzerinde ikisini birlikte çizdirmek içinse tek bir plot() fonksiyonunda birleştiriyoruz (birleştirirken aralarına virgül koyuyoruz):

In [27]:
# plot(y,t,"^-b") ve plot(y,t,"o-r") yerine:
plot(y,t,"^-b",y,t_2,"o-r")

Hangisinin hangisi olduğunun açıklamasını ise legend() komutu ile belirtebiliriz (hazır yapıyorken, tanımları da koyalım):

In [28]:
plot(y,t,"^-b",y,t_2,"o-r")
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

legend("Hassas Olcum (1. Grup)","Kaba Olcum (2. Grup)")

# Izgarayı açalım
grid on

Yukarıdaki grafikte gördüğünüz üzere, açıklama kutusu verilerle çakıştı. Bu tür durumlarda kutuyu 8 uç yöne taşımak için "location" parametresini devreye sokabiliriz. Parametre değerleri olarak 8 temel yönün İngilizce karşılıkları ("north", "south", "east", "west", "northeast", "northwest", "southeast", "southwest") kullanılır. Örneğimizdeki kutucuğu sol-üste, yani "kuzeybatı"ya taşıyalım:

In [29]:
plot(y,t,"^-b",y,t_2,"o-r")
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

legend("Hassas Olcum (1. Grup)","Kaba Olcum (2. Grup)",...
       "location","northwest")

# Izgarayı açalım
grid on

Kutucuğu tümden grafiğin dışına almak içinse location parametresinin değerine dışarı anlamına gelen outside terimini yamarız:

In [30]:
plot(y,t,"^-b",y,t_2,"o-r")
xlabel("Yukseklik (m)")
ylabel("Dusus suresi (s)")
title("Serbest Dusus Deneyi")

legend("Hassas Olcum (1. Grup)","Kaba Olcum (2. Grup)",...
       "location","northwestoutside")

# Izgarayı açalım
grid on

legend parametresi ile başka birçok şey daha yapmak mümkün, detaylar için ilgili komutun açıklama sayfasına göz atabilirsiniz.

Fonksiyon çizimi

Octave, temel olarak sembolik bir dil olmadığı için, fonksiyon çizdirirken de fonksiyonun belli yerlerdeki değerlerinden bir veri dizisi oluşturup, onların grafiğini çizdiririz.

Örneğin $y = x^2$ grafiğini $x\in[0,1]$ aralığı için çizdirelim.

  • Öncelikle bu aralıktaki x değerlerini tanımlarız:
In [31]:
x = 0:0.2:1
x =

    0.00000    0.20000    0.40000    0.60000    0.80000    1.00000

  • Sonrasında $y$ değerlerini $x$ değerleri üzerinden tanımlarız:
In [32]:
y = x.**2
y =

   0.00000   0.04000   0.16000   0.36000   0.64000   1.00000

(üs alma operatöründen önce "." sembolünü koyarak, x vektörünün karesini değil, eleman bazında kare alma işlemini gerçekleştirdiğimizi belirttik - aman dikkat!)

Grafiğimizi artık çizdirebiliriz:

In [33]:
plot(x,y,"-")

Pek kesik kesik görünüyor. Bunun sebebi ele aldığımız değer adedi. 6 parça ([0, 0.2, 0.4, 0.6, 0.8, 1]) ile ancak bu kadar oluyor. Arttıralım bakalım, neye benzeyecek...

In [34]:
x = 0:0.1:1;
y = x.**2;
plot(x,y)

Epey düzelme var. peki aralığı 125 çentiğe ayırmak istesek? Aralık operatörü ile bunu yapmak mümkün ama oturup adım uzunluğunu hesaplamak gerekecek. Bu tür durumlarda ise yardımımıza, aralık operatörünün kardeşi linspace() fonksiyonu yetişiyor. linspace ile başlangıç ve bitiş değerlerini verip, kaç çentik istediğimizi bildiriyoruz, o da eşit aralıklara bölüp, bize çentikleri döndürüyor.

Örneğin: 0 ile 1 arasını 3 çentikle bölelim:

In [35]:
linspace(0,1,3)
ans =

   0.00000   0.50000   1.00000

Grafiğimizi bu sefer 125 çentikle çizelim:

In [36]:
x = linspace(0,1,125);
y = x.**2;
plot(x,y)

Aynı grafik üzerinde $y_1 = x$ ve $y_2 = x^2$ grafiklerini güzelce çizdirelim:

In [37]:
x = linspace(0,1,125);
y_1 = x;
y_2 = x.**2;
plot(x,y_1,"linewidth",4,x,y_2,"linewidth",4)
legend("y=x","y=x^2","location","southeast")

Bir ipte iki cambaz (II)

$y=x$ ve $y=x^2$ fonksiyonlarının grafiklerini $x \in [-10,10]$ aralığında çizdirmeyi deneyelim:

In [38]:
x = linspace(-10,10,250);
y_1 = x;
y_2 = x.**2;
plot(x,y_1,"linewidth",4,x,y_2,"linewidth",4)
legend("y=x","y=x^2","location","southeast")

Bu grafikteki sorun nedir? $x$ 0 ile 1 arasındayken iki grafiğin değerleri gayet güzel anlaşıyorlardı fakat aralık artınca, değerler arasındaki farklar da uçtu gitti, bu nedenle $y_2 = x^2$ fonksiyonu baskın hale gelip, $y_1 =x$ fonksiyonunu gölgeledi. Bu türden, farklı değer aralıklarına sahip fonksiyonları birlikte gösterebilmek için, iki farklı düşey eksen kullanmamızı sağlayan plotyy() fonksiyonu ile çizim yaparız:

In [39]:
x = linspace(-10,10,250);
y_1 = x;
y_2 = x.**2;
plotyy(x,y_1,x,y_2)
legend("y=x","y=x^2","location","southeast")

Çok ipte çok cambaz

Artık grafik çizmeyi epey epey öğrendiğimize göre, aynı grafik tuvali üzerinde farklı grafikleri sıralamayı da görebiliriz. Bu sıralama işini subplot() komutu ile tanımlıyoruz.

Gözünüzün önüne (2x2)'lik bir matris getirin, örneğin:

$$ \begin{bmatrix}1&2\\3&4\end{bmatrix}$$

olsun.

Matrise bir isim vermediğimizden, bir yeri belirtmek için, her seferinde matrisin boyutunu ve yerin indisini belirtiriz. Mesela sağ alt köşedeki elemanı belirtmek için "2'ye 2'lik bir matrisi gözünüzün önüne getirip, onun 4. elemanının yerini düşünün" demek gibi. İşte subplot()'un parametreleri de bu şekilde çalışıyor. Her birini ayrı ayrı belirtip, oraya ne koyacağımızı tanımlıyoruz sonra. Aşağıdaki örneklere bakarsanız çok daha açıklayıcı olacağını umut ediyorum:

In [40]:
# 1x2'lik bir düzen

subplot(1,2,1)
    # 1x2'lik matrisin 1. elemanı, yani soldaki penceredeyiz
    plot(x,y_1,"-r")
    title("y=x")
subplot(1,2,2)
    # şimdi 1x2'lik matrisin 2. elemanı, yani sağ penceredeyiz
    plot(x,y_2,"-b")
    title("y=x^2")
In [41]:
# 2x1'lik bir düzen

subplot(2,1,1)
    # 2x1'lik matrisin 1. elemanı, yani yukarıdaki penceredeyiz
    plot(x,y_1,"-r")
    title("y=x")
subplot(2,1,2)
    # şimdi 1x2'lik matrisin 2. elemanı, yani aşağıdaki penceredeyiz
    plot(x,y_2,"-b")
    title("y=x^2")

İlle de bütün hücreler dolacak diye bir kural da yok bu arada:

In [42]:
# 2x3'lük bir düzen

subplot(2,3,2)
    # 2x3'lik matrisin 2. elemanı, yani yukarı satır, orta sütun
    plot(x,y_1,"-r")
    title("y=x")
subplot(2,3,3)
    # 2x3'lük matrisin 3. elemanı, yani sağ-üst hücre
    plot(x,y_1-y_2,"-g")
    title("y=x-x^2")
subplot(2,3,4)
    # 2x3'lük matrisin 4. elemanı, yani sol-alt hücre
    plot(x,y_1+y_2,"-k")
    title("y=x+x^2")
subplot(2,3,6)
    # şimdi 2x3'lük matrisin 6. elemanı, yani sağ-alt hücre
    plot(x,y_2,"-b")
    title("y=x^2")

Saçılım

Yukarıdaki örneklerdeki verilerimiz hep sıralı verilerdi, yani noktalarımızı art arda çizgilerle birleştirince anlamlı bir şekil elde ediyor, gelişim sürecini izleyebiliyorduk. Fakat saçılma deneyleri gibi deneylerde (ya da elinizdeki vişne suyu dolu bardağı beyaz halıya düşürdüğünüzde oluşan desenler gibi kazalarda) bu şekilde bir sıralılık aranmaz. Bu tür durumlarda, plot yerine scatter fonksiyonunu kullanmak birtakım avantajlar (farklı şekilde büyüklük ve renklendirme yapabilmek gibi) sunar.

Saçılım için genelde çok fazla veriye ihtiyaç duyulur. Vişne bardağımızı kare şeklinde halımızın tam ortasına düşürdüğümüzü ve 300 adet damlanın rastgele yönlere sıçradığını varsayalım.

Rastgele sayılar

Rastgele işlemler için rand() komutunu kullanırız, bize bu haliyle 0 ile 1 (0 dahil, 1 hariç) arasında eşit olasılıkla rastgele bir sayı üretir (düzgün dağılım):

In [43]:
# Hesaplara başlamadan...
rand("seed",219) # Bu şekilde her seferinde aynı "rastgele" sayıların
                 # çıkmasını sağlıyoruz (yani sizin ürettiğiniz sayıların
                 # bu ders notundakilerle aynı olmasını)
In [44]:
rand()
ans =  0.65708
In [45]:
rand()
ans =  0.014786
boyut bildirirsek, o boyuttaki bir matrisi rastlantısal sayılarla doldurur:
In [46]:
rand(3,5)
ans =

   0.15056   0.44253   0.64676   0.96474   0.74250
   0.51622   0.57650   0.30993   0.31049   0.49251
   0.22114   0.43923   0.45263   0.42314   0.14048

Aralığı 0 ile 1'den a ile b'ye nasıl değiştiririz? $\(a-b\) ile çarpıp, $a$ ekleyerek! ;)
In [47]:
# -2 ile 2 arasında rastgele sayılar:
rand(3,5)*4-2
ans =

  -1.36827   1.89567  -0.66630   1.66031   0.90189
   0.98473   1.92246   1.38515  -1.44258   1.41858
   0.35271  -0.24558  -1.34027   1.20135   0.69483

Halımızın merkezini (0,0), boyunu da 4x4 $m^2$ alırsak, üzerine rastgele şekilde dökülen 10 damlanın konumlarını (x,y) cinsinden şu şekilde üretebiliriz:

In [48]:
damlalar = rand(10,2)*4-2
damlalar =

  -1.852137   0.978591
  -1.012549   0.364333
  -0.274938   1.093289
  -0.407501  -1.564739
   0.108775   0.753838
   0.759887  -1.285785
   0.884586  -1.492475
  -1.409493  -0.092157
  -1.501641  -0.610016
   0.305351  -1.798407

Çizdirmek için scatter'ı kullanalım:

In [49]:
scatter(damlalar(:,1),damlalar(:,2),500)

Yukarıdaki "500" parametresi boyla orantılı bir değer.

Damla sayısını 300'e çıkaralım:

In [50]:
damlalar = rand(300,2)*4-2;
scatter(damlalar(:,1),damlalar(:,2),500)

Kar beyaz halının ortasına dökülen vişne suyunun damlacıklarının konumu için düzgün dağılım çok da gerçekçi değil bu arada, sonuçta tahmin edebilirsiniz ki, normalde halının merkezine yakın düşen damlacıkların sayısı (/yoğunluğu) halının uçlarına kadar ulaşan damlacık sayısından (/yoğunluğundan) çok daha fazla olacaktır.

Burada "normalde" terimi anahtar kelimemiz. Doğada pek çok kez gözlemlediğimiz, belli bir ortalama değerin etrafında gerçekleşen dağılımlar için "normal dağılım"ı kullanıyoruz (Gauss dağılımı olarak da bilinir), bunu da rand yerine, randn fonksiyonunu kullanarak yapıyoruz. (normal dağılımı bütün güzellikleri ve formüler gösterimi ile birlikte "FİZ316 - İstatistik Fizik" dersinizde göreceksiniz 8):

In [51]:
damlalar = randn(1000,2)*4-2;
scatter(damlalar(:,1),damlalar(:,2),500)

Bonus / Ekstra / Tercihe Bağlı

(istemeyen okumasın! ;)

scatterın plotdan farkını, renk ve boy çeşitlemeleri yapabilmek olarak belirtmiştik. Her bir veri noktasının boyunu da, rengini de tek tek belirleyebiliriz.

Yukarıdaki örnekte boyu 500 almıştık, onun yerine 1. noktanın boyunu 1, ikincisinin 2, ..., 1000.sininkini 1000 alalım:

In [52]:
scatter(damlalar(:,1),damlalar(:,2),1:1000)

Çok mantıklı bir şey değil ama fikir veriyor. Peki örneğin merkeze ne kadar uzaksa o kadar küçük olsun? Nasıl yaparız?

Önce merkeze olan mesafelerini hesaplarız. Elimizde her bir damlanın x ve y koordinatları olduğuna göre, karelerinin toplamının kökü, ya da daha doğrudan koordinatların normu bize mesafeleri verir:

In [53]:
# ilk 10 damlanın koordinatları:
damlalar(1:10,:)

# Kötü yoldan hesaplanmış mesafeler vektörünün ilk 10 elemanı:
mesafeler_kotu = (damlalar(:,1).**2 + damlalar(:,2).**2).**0.5;
mesafeler_kotu(1:10)

# Güzel yoldan hesaplanmış mesafeler vektörünün ilk 10 elemanı:
mesafeler_guzel = norm(damlalar,"rows");
mesafeler_guzel(1:10)
ans =

   4.458101  -1.958335
  -1.009483  -4.639653
  -1.456757  -1.501038
  -0.092184  -2.475710
  -2.907051  -0.344995
  -1.664433   1.439826
  -2.764086   1.477501
   4.645195  -0.194080
  -1.197024  -3.200934
   1.041247  -7.869998

ans =

   4.8693
   4.7482
   2.0917
   2.4774
   2.9275
   2.2008
   3.1342
   4.6492
   3.4174
   7.9386

ans =

   4.8693
   4.7482
   2.0917
   2.4774
   2.9275
   2.2008
   3.1342
   4.6492
   3.4174
   7.9386

In [54]:
scatter(damlalar(:,1),damlalar(:,2),mesafeler_guzel*100)

Neredeyse olacak gibi -- yukarıdakinde uzaklaştıkça büyüyorlar, halbuki biz uzaklaştıkça küçülmelerini istiyoruz, o halde:

In [55]:
scatter(damlalar(:,1),damlalar(:,2),(1./mesafeler_guzel)*1500)

Renkleri içeriden dışarıya açalım, içlerini de doldularalım...

In [56]:
scatter(damlalar(:,1),damlalar(:,2),...
       (1./mesafeler_guzel)*1500,...
       mesafeler_guzel,"filled")

Histogram

Histogram grafikleri dağılımları gösteren türden grafiklerdir. Örneğin, veri olarak geçen senenin (2019-2020 Güz Dönemi) "FİZ219 - Bilgisayar Programlama" dersinin genel sınav notlarını ele alalım:

In [57]:
notlar = [1, 1, 1, 1, 1, 2, 7, 7, 10, 11, 11, 13, 13,...
          13.5, 14, 14, 15.5, 17, 18, 20, 21, 21, 22,...
          23, 23, 23, 24, 24, 24, 24, 25, 25, 29, 30,...
          30, 32.5, 36, 37, 37, 37, 38, 40, 41, 42.5,...
          42.5, 43, 46, 46, 53, 59.5, 60, 60.5, 62, ...
          63.5, 67, 67, 70, 83, 84, 99];

Toplam 60 kişi sınava girmiş, notların dağılımını görmek için yazmamız gereken komut çok basit:

In [58]:
hist(notlar)

Birazcık daha anlaşılır kılalım:

In [59]:
hist(notlar)
xticks(0:10:100)
yticks(0:20)
title("2019-2020 Guz Donemi FIZ219 Genel Sinav Not Dagilimi")
grid on

Grafiğe bakarak kolayca (0,10] aralığında 9 not olduğunu, (10,20] arasında 11 not olduğunu, vs. görebiliyoruz, ki zaten histogramın amacı da budur.

hist fonksiyonu varayılan olarak, verileri 10 kutuda toplar. İkinci bir paremetre ile kutu sayısını da belirleyebiliriz:

In [60]:
hist(notlar,20)
xticks(0:5:100)
yticks(0:20)
title("2019-2020 Guz Donemi FIZ219 Genel Sinav Not Dagilimi")
grid on

hist() fonksiyonu ve parametreleri hakkında daha fazla bilgiyi Octave'ın ilgili kılavuz sayfasında bulabilirsiniz.

3 Boyutlu Çizimler

Nihayet en süper görünen çizimlere gelebildik! Ama işin kod kısmına geçmeden önce, 3 boyutlu çizimin ne anlama geldiğini tartışalım biraz. 3 boyutlu çizimlerde, $f(x,y,z)$ gibi 3 parametreli fonksiyonların grafikleri çizilmez (o 4 boyutlu bir uzay gerektiriyor maalesef), $f(x,y)$ gibi 2 parametreli fonksiyonların grafikleri çizilir.

Kareli bir kağıt alıp, kesişim noktalarına pozitif veya negatif, çeşitli değerler yazdığınızı düşünün. Sonrasında her noktayı üzerinde yazan değer kadar pozitifse yukarı kaldırdığınızı, negatifse aşağı çektiğinizi varsayın -- işte 3 boyutlu grafik böyle bir şey.

Örneğin, 2x2'lik bir matrisin 9 köşesini aşağıdaki gibi işaretlediğimizi (indislediğimizi) düşünelim:

3boyutXY.png

(Köşelerin koordinatlarını (indislerini) iyice inceleyin, aklınıza yatsın)

Şimdi de, her bir köşenin "z" değerini koordinatlarının karesinin toplamı olarak yazalım. Örneğin (0,-1) noktasının değeri: $0^2+(-1)^2 = 1$ olur. Her köşenin yanına bu değerleri de yazalım:

3boyutXY_2.png

3 boyutlu grafik çizerken de tam olarak benzer bir süreçten geçiyoruz. Öncelikle, $x=\{-1,0,1\}$ değerleri ile $y=\{-1,0,1\}$ değerlerini harmanlayıp, (-1,-1)'den (1,1)'e olası 9 kombinasyonu üreteceğiz, sonrasında da bu koordinatlara karşılık gelen değer olarak indislerin karelerin toplamını atayacağız.

Önce harmanlama işini yapalım, bunu meshgrid() fonksiyonu ile gerçekleştiriyoruz:

In [61]:
tx = linspace(-1,1,3)
ty = linspace(-1,1,3)
[xx,yy] = meshgrid(tx,ty)
tx =

  -1   0   1

ty =

  -1   0   1

xx =

  -1   0   1
  -1   0   1
  -1   0   1

yy =

  -1  -1  -1
   0   0   0
   1   1   1

Biz tx ile ty değişkenlerini tanım aralığımız olarak tanımlıyoruz, meshgrid de bunları öyle bir harmanlıyor ki, ürettiği xx ile yy üst üste bindirirsek, tam olarak yukarıdaki koordinat sistemi ortaya çıkıyor. 8)

Şimdi de her bir koordinatın değerini koordinatlarının karesi olarak tanımlayalım:

In [62]:
tz = xx.^2 + yy.^2
tz =

   2   1   2
   1   0   1
   2   1   2

İyi gidiyoruz, az da kaldı... Şimdi değerleri "1" olan ortaları 1 birim; değerleri "2" olan köşeleri ise 2 birim yukarı kaldırırken, orta noktayı "0" olan değerinden ötürü, masanın seviyesine raptiye ile tutturduğumuzu düşünelim. Görüntüyü kafanızda canlandırabildiniz mi? Şuna benziyor mu?:

In [63]:
mesh(tx,ty,tz)

Elinizi korkak alıştırmayın, tutun ucundan, döndürün, çevirin, ilk üç boyutlu grafiğiniz hayırlı olsun, tadını çıkarın! 8)

Kafamızda rahat canlandırabilelim diye 3x3 = 9 noktada çizdirdik ama tabii ki bu epey kaba bir grafik oldu, biz her eksende 100 nokta alalım (yani ağımız (meshgrid) 100x100 = 10000 köşe olsun):

In [64]:
tx=ty=linspace(-3,3,100);
[xx,yy] = meshgrid(tx,ty);
tz = xx.^2+yy.^2;
mesh(tx,ty,tz)

2 boyutlu grafiklerde yaptığımız gibi, 2 boyutlu grafiklerde de açıklayıcı bilgiler girebiliriz:

In [65]:
mesh(tx,ty,tz);
xlabel ("x");
ylabel ("y");
zlabel ("z");
title ("x^2");

Ya da dilersek, 3 boyutlu çizimin altına iki boyuttaki izdüşümü olan kontür haritasını da ekleyebiliriz. Bunun için mesh fonksiyonunu değil de meshc fonksiyonunu kullanıyoruz:

In [66]:
meshc(tx,ty,tz);

sadece kontür haritasını çizdirmek içinse contour() fonksiyonu var:

In [67]:
contour(tx,ty,tz)

3-boyutlu grafikte dikkat ederseniz sadece tel var, yani ağların arası açık gibi. Ağ yerine yüzey varmış da, onu bükmüşüz gibi olsun istersek surf() fonksiyonu devreye giriyor:

In [68]:
surf(tx,ty,tz);

surf'ün de mesh gibi, kontür haritalı versiyonu var: surfc():

In [69]:
surfc(tx,ty,tz);

Grafikleri dosyaya yazdırma

Aktif grafiği bir dosyaya aktarmak için, print komutunu kullanırız:

print -d{biçim} {dosya_adı} şeklinde. Biçim, png olur, pdf, jpg, gif, ps, eps...; dosya adı size kalmış ama uyumluluk açısından boşluksuz, Türkçe karakter olmasını tavsiye ederim.

In [70]:
surfc(tx,ty,tz);
print -dpng "3D_Grafik.png"

Grafiğimizi yukarıdaki komutla "3D_Grafik.png" dosyasına yazdık ama acaba o dosya nerede? Tabii ki Octave hangi klasörü çalışma klasörü olarak aldıysa orada... Peki o klasörün o anda hangi klasör olduğunu nasıl anlarız? "Print Working Directory" (Çalışma klasörünü yazdır) komutunun baş harfleri olan pwd komutuyla! 8)

In [71]:
pwd
ans = /home/sururi/ownCloud/Jupyter_notebooks/FIZ219