Vektörler ve Matrisler#
Ders Notları: 2#
Matrisler (NumPy Dizi (numpy.ndarray) Nesneleri)
Tanımlama
Matris Elemanlarına Erişim ve Değiştirme
Bölgesel erişim
Matris Elemanlarını Değiştirmek
Matrise Eleman Ekleme
Sihirbazlık: Değişkenleri kopyalamak (‘copy’)
append
insert
concatenate
Matristen Eleman Silme
Matrislerin Özellikleri
size
shape
reshape
ndim
dtype
Tipik Matrisler
zeros
ones
fill ile matrisi doldurmak
birim matris
rastgele matrisler
arange ile aralıklar
linspace
Matris işlemleri
Dört temel işlem (eleman bazında)
Skaler ve Vektörel Çarpımlar
Skaler Çarpım
Vektörel Çarpım
Matrisin transpozesi, tersi ve determinantı
Transpozesi
Tersi
‘Tersimsisi’
Determinantı
Vektörler ve Matrisler: Bilgi metotları; tipik matrisler (zeros, ones, doldurulmuş, birim, rastgele); aralıklar (arange ve linspace); matrisler arasındaki işlemler: eleman bazlı işlemler, skaler çarpım, vektörel çarpım; matris işlemleri (transpoze, tersi, ‘tersimsisi’, determinantı). Matris Uygulamaları: n bilinmeyenli n denklem setinin matris çarpımı temsili ve çözümü; dönüş matrisi. Uygulama: Elektrik devresinin Kirchhoff metotu ile lineer denklem seti haline getirilip çözülmesi.
Dr. Emre S. Taşcı, emre.tasci@hacettepe.edu.tr
Fizik Mühendisliği Bölümü
Hacettepe Üniversitesi
NumPy’ın N-boyutlu Dizileri (ndarray)#
Bu dersimizde bundan böyle epey bir zaman kullanacağımız etkili ve hızlı NumPy dizilerini göreceğiz. Aslında teknik olarak dizi dense de, bizim temel kullanım amacımız matrisler şeklinde olacak.
Tanımlama#
Bir NumPy dizisini, cinsiyle (yani “np.ndarray”), boş veya ilk değerli olarak tanımlarız. Python dizileri ile benzer olarak, bir liste bir kez oluşturulduktan sonra eleman ekleme/çıkarma yaparken aslında -çok da çaktırmadan- yerine yeni bir liste oluşturulur.
Tanımlama için normal dizilerdeki gibi köşeli parantezler kullanırız:
import numpy as np
mat1 = np.array([0,1,2,3,4])
print(mat1)
[0 1 2 3 4]
İki boyutlu bir matrisi iç içe geçmiş köşeli parantezler içinde (satır gruplu olarak) belirtiyoruz:
mat2 = np.array([[0,1,2],[3,4,5]])
print(mat2)
[[0 1 2]
[3 4 5]]
Peki ya üç boyutlu bir matrisi?… (Siz düşünün cevabı sonra 8)
Matris Elemanlarına Erişim ve Değiştirme#
Matrisimizin elemanlarına tıpkı normal dizilerde veya matrix nesnelerinde olduğu gibi köşeli parantezlerle erişebiliriz. Yalnız, onlardan farklı olarak, birden yüksek boyutlu olan matrislerin -tek bir elemana dair(!)- çoklu indislerini belirtirken, hepsini aynı köşeli parantezlerde yazabileceğimiz gibi, ayrı köşeli parantezlerde de belirtebiliriz (fakat ileride bahsedileceği üzere, özellikle çoklu seçimlerde ayrı köşeli parantezlerin bambaşka bir anlamı olduğundan, mümkün mertebe aynı parantezler içindeki notasyonu kullanın).
mat2 = np.array([[0,1,2],[3,4,5]])
print("Matrisimiz mat2:\n",mat2)
print("---------")
print("Matrisimizin sağ alttaki elemanı: mat2[1,2] = ",mat2[1,2])
print("Aynı elemanı bir de mat2[1][2] ile çağıralım: ",mat2[1][2])
Matrisimiz mat2:
[[0 1 2]
[3 4 5]]
---------
Matrisimizin sağ alttaki elemanı: mat2[1,2] = 5
Aynı elemanı bir de mat2[1][2] ile çağıralım: 5
Bölgesel erişim#
Matrisimizin elemanlarına ‘bölgesel’ (yani, aralık belirterek) de erişebiliriz: bunun için “:” aralık operatörünü kullanabiliriz (aralık operatörünün kullanımı ve özelliklerine dair detaylı bilgiyi 2. kısım olan Listeler’in “Liste elemanlarına ulaşma” bölümünde bulabilirsiniz):
mat2 = np.array([[0,1,2],[3,4,5],[6,7,8]])
print(mat2)
print(mat2[1:3,1:3])
[[0 1 2]
[3 4 5]
[6 7 8]]
[[4 5]
[7 8]]
Eğer çağrı aralığımız bir sütun vektörü döndürüyorsa, Python bunu satır vektörü olarak sunacaktır:
mat2 = np.array([[0,1,2],[3,4,5],[6,7,8]])
print(mat2)
# Butun satirlarin son sutunu
mat2_son_sutun = mat2[:,2]
print(mat2_son_sutun)
# Butun satirlarin son iki sutunu
mat2_son_2sutun = mat2[:,1:3]
print(mat2_son_2sutun)
[[0 1 2]
[3 4 5]
[6 7 8]]
[2 5 8]
[[1 2]
[4 5]
[7 8]]
Bu (tek sütunlu vektörlerin satır vektörü olarak dönmesi) çok büyük bir sorun değil, çaresini şimdi verelim, detayını ileride göreceğiz: reshape() metodu:
mat2 = np.array([[0,1,2],[3,4,5],[6,7,8]])
print(mat2)
# Butun satirlarin son sutunu
mat2_son_sutun = mat2[:,2]
print(mat2_son_sutun)
print("\nSihirli 'reshape' çekelim:\n",mat2_son_sutun.reshape(-1,1))
[[0 1 2]
[3 4 5]
[6 7 8]]
[2 5 8]
Sihirli 'reshape' çekelim:
[[2]
[5]
[8]]
Dizilerin aksine, bir blok olmayan (yani aralarında boşluk olan) bölgelerden de elemanlara erişebiliriz. Örneğin, mat2 matrisimizin ilk ve son sütunlarını alalım:
mat2 = np.array([[0,1,2],[3,4,5],[6,7,8]])
print(mat2)
print("\nİlk (0.) ve son (2.) sütunlar:")
mat2_ilk_ve_son_sutunlar = mat2[:,[0,2]]
print(mat2_ilk_ve_son_sutunlar)
[[0 1 2]
[3 4 5]
[6 7 8]]
İlk (0.) ve son (2.) sütunlar:
[[0 2]
[3 5]
[6 8]]
İndis belirtirken, başlangıç için nasıl 0 kullanıyorsak, sonuncu için de end veya -1 kullanabiliriz; benzer şekilde -2 “sondan ikinci”, -3 “sondan üçüncü”, vs.. olarak işlenir. Uygulama olarak \((3\times5)\)’lik bir matrisin sondan 2. ve 3. sütunlarına erişelim:
mat_3x5 = np.arange(0,15).reshape(3,5)
mat_3x5 = np.array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
print(mat_3x5)
print("---------")
print("Matrisimizin sondan 2. ve 3. sütunları:")
print(mat_3x5[:,[-2,-3]])
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
---------
Matrisimizin sondan 2. ve 3. sütunları:
[[ 3 2]
[ 8 7]
[13 12]]
Aralıkları tersten arttırarak matrisimizin sütunlarının sırasını tersine de çevirebiliriz:
mat_3x5 = np.arange(0,15).reshape(3,5)
mat_3x5 = np.array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
print(mat_3x5)
print("---------")
print("Matrisimizin sütunlarının tersten sıralanmış hali:")
print(mat_3x5[:,4::-1])
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
---------
Matrisimizin sütunlarının tersten sıralanmış hali:
[[ 4 3 2 1 0]
[ 9 8 7 6 5]
[14 13 12 11 10]]
Yukarıdaki örnekte 0. sütun dahil olduğu için aralığımızı 4:0:-1 şeklinde yazamadık çünkü bu (aralık tanımının {başlangıç}:{bitiş (kadar/dahil değil}:{artış miktarı} olmasından ötürü): 4,3,2,1 değerlerini içerir. 0 yerine -1 koymak başta makul görünse de, -1’in özel bir anlamı var (yukarıda, satır vektörünü sütun vektörüne çevirirken de kullanmıştık, daha sonra kapsamlı olarak değineceğiz), onu da kullanamıyoruz, indislerin tam sayı (ve \(\ge0\) - yani -1 de olmuyor teknik olarak ;) olma zorunluluğundan ötürü -0.2 gibi bir cinlik de yapamıyoruz. Bu tür durumlarda işi “oluruna” yani boş bırakıyoruz: boş bıraktığımızda “tümü” ya da biraz daha gevşek bir deyişle “gittiği yere kadar…” şeklinde bir anlam çıkmakta.
Matris Elemanlarını Değiştirmek#
Erişebilmeyi öğrendikten sonra, değiştirmek çok kolay. Eriştiğiniz matris elemanları ile aynı boyutta olduktan sonra, istediğiniz gibi değiştirebilirsiniz:
mat_3x5 = np.arange(0,15).reshape(3,5)
mat_3x5 = np.array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
print(mat_3x5)
print("---------")
print("Baştan 2. (indis:1), sondan 2. sütunlar:")
print(mat_3x5[:,[1,-2]])
# Erisebildigimize gore, bunlari degistirelim:
# --(3x2)'lik matris olduguna dikkat ederek--
mat_3x5[:,[1,-2]] = np.array([[11, 13],[21,23], [31,33]])
print("\nBu kısmın değiştirilmiş olduğu hali:")
print(mat_3x5)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
---------
Baştan 2. (indis:1), sondan 2. sütunlar:
[[ 1 3]
[ 6 8]
[11 13]]
Bu kısmın değiştirilmiş olduğu hali:
[[ 0 11 2 13 4]
[ 5 21 7 23 9]
[10 31 12 33 14]]
Matrise Eleman Ekleme#
Bir matrisi (NumPy dizisini) oluşturmayı, mevcut olan elemanlara erişip, onları değiştirmeyi gördük. Peki ya yeni bir eleman eklemek istersek? İlk akla gelen şekilde “olmayanı yoktan tanımlamak” işe yarar mı? (pek olacak gibi görünmüyor, bir deneyelim bakalım…)
Aldığımız hatanın gerisinde yatan temel sebep, teknik olarak bir matrisin (NumPy dizisinin - bir daha bu ek açıklamayı yapmayacağım) sadece ilk tanımlanışı sırasında boyutunun belirlenebilmesi.
Pratikte tabii ki bu sıkıntıya düşmüyoruz, birazdan göreceğimiz üzere, genişleme de yapacağız, boyutunu da değiştireceğiz, sileceğiz de, ekleyebileceğiz de. Ama bu teknik kısıtlamanın altında ince bir programlama numarası yatmakta. Önceki ders notlarında bundan bahsettiysek de, bunların ders değil de aslında bir referans notu olmasından ötürü burada bir kez daha değineceğim. 8)
Sihirbazlık: Değişkenleri kopyalamak (‘copy’)
Bu sihirbazlık numarası için yardımınıza ihtiyacım var:
adiye bir değişken düşünüp, ona bir değer atayın (örneğin 3)bdiye bir değişken düşünüp, onu daaya eşitleyin.
(şu andaada,bde 3’e eşit, buraya kadar hemfikirizdir umarım)anın değerini başka bir sayı yapın (örneğin 5).bnin şu andaki değeri nedir?
Psikoloji bilimi doğru çalışıyorsa, büyük ihtimalle “5” demişsinizdir (“hoca sorduğuna göre o kadar bariz (3) cevap olamaz, vardır bir şaşırtmaca/hinlik) ama gerçekten de o kadar bariz (yani b değerimiz hala 3’e eşit), kendi gözlerinizle görün:
a = 3
b = a
print("a: %d\tb: %d"%(a,b))
print("Şimdi a'nın değerini 5 yapıyoruz...\n(b'ye dokunmuyoruz)")
a = 5
print("a: %d\tb: %d"%(a,b))
print("\n¯\_(ツ)_/¯ -- Duh!")
a: 3 b: 3
Şimdi a'nın değerini 5 yapıyoruz...
(b'ye dokunmuyoruz)
a: 5 b: 3
¯\_(ツ)_/¯ -- Duh!
<>:7: SyntaxWarning: invalid escape sequence '\_'
<>:7: SyntaxWarning: invalid escape sequence '\_'
/tmp/ipykernel_13620/3930200487.py:7: SyntaxWarning: invalid escape sequence '\_'
print("\n¯\_(ツ)_/¯ -- Duh!")
E sihirbazlık neresinde kaldı bu işin? Tekrar deneyelim… Bir şans daha. Bu sefer dizilerle yapalım:
dizi_a = np.array([3])
dizi_b = dizi_a
print("a dizisi: ",dizi_a)
print("b dizisi: ",dizi_b)
print("\nŞimdi a dizisinin elemanının değerini 5 yapıyoruz...\n(b dizisine dokunmuyoruz)\n")
dizi_a[0] = 5
print("a dizisi: ",dizi_a)
print("b dizisi: ",dizi_b)
a dizisi: [3]
b dizisi: [3]
Şimdi a dizisinin elemanının değerini 5 yapıyoruz...
(b dizisine dokunmuyoruz)
a dizisi: [5]
b dizisi: [5]

(James Hence - “The Meep”)
Nasıl oldu? Biz b dizisine dokunmamıştık, nasıl oldu da gitti a’yı değiştirince b de değişiverdi? Cevap: dizilerin (yani birden fazla elemanı olan değişkenlerin) hafızada nasıl tutulduğu. Bize ad diye verilen şey aslında onun hafızada tutulduğu yeri gösteren bir işaretçi (pointer). Birebir aynı olmasa da bu ilişkiyi id’lere bakarak gözlemleyebiliriz:
dizi_a = np.array([3])
dizi_b = dizi_a
print("a dizisi: ",dizi_a)
print("a dizisinin id'si: ",id(dizi_a))
print("b dizisi: ",dizi_b)
print("b dizisinin id'si: ",id(dizi_b))
print()
print("\nŞimdi a dizisinin elemanının değerini 5 yapıyoruz...\n(b dizisine dokunmuyoruz)\n")
dizi_a[0] = 5
print("a dizisi: ",dizi_a)
print("a dizisinin id'si: ",id(dizi_a))
print("b dizisi: ",dizi_b)
print("b dizisinin id'si: ",id(dizi_b))
a dizisi: [3]
a dizisinin id'si: 123667408309392
b dizisi: [3]
b dizisinin id'si: 123667408309392
Şimdi a dizisinin elemanının değerini 5 yapıyoruz...
(b dizisine dokunmuyoruz)
a dizisi: [5]
a dizisinin id'si: 123667408309392
b dizisi: [5]
b dizisinin id'si: 123667408309392
Görüldüğü üzere, ikisi de değişiklik yapıldıktan sonra bile aynı yeri işaret ediyorlar. Halbuki ilk örneğimizde:
a = 3
b = a
print("a: %d\tb: %d"%(a,b))
print("a'nın id'si: ",id(a))
print("b'nin id'si: ",id(b))
print("Şimdi a'nın değerini 5 yapıyoruz...\n(b'ye dokunmuyoruz)")
a = 5
print()
print("a: %d\tb: %d"%(a,b))
print("a'nın id'si: ",id(a))
print("b'nin id'si: ",id(b))
a: 3 b: 3
a'nın id'si: 99775078292840
b'nin id'si: 99775078292840
Şimdi a'nın değerini 5 yapıyoruz...
(b'ye dokunmuyoruz)
a: 5 b: 3
a'nın id'si: 99775078292904
b'nin id'si: 99775078292840
Python’da bir değişken tanımladığımızda, hafızada onun büyüklüğünde bir alan aranır ve o bölgenin başlangıç adresi o değişkenin adı ile ilişkilendirilir. Örneğin (3x4)’lük bir tamsayılar matrisi tanımladığınızda 3x4=12’lik tamsayının kapladığı yer bulunur, o yerin başlangıcı da matrisinizin adına atanır. Yani matrisin adı dediğimiz şey -hemen hemen- onun tutulduğu bölge. dizi_b = dizi_a demekle, a dizisinin adresini b dizisine de atamış olduk – bu bir anahtarı çoğaltmak gibi bir şey! Bu yüzden a dizisinde yaptığımız değişiklik b dizisini de etkiliyor çünkü ikisi de aynı evi gösteriyor! Kardeşinizde de, sizde de birer anahtar var, kardeşiniz halıya vişne suyu döktüğünde, siz eve girince bu lekeyi görüyorsunuz! 8)
Peki değerleri cinsinden birbirinin aynı ama göbekleri birbirlerine bağlı olmayan iki diziyi nasıl tanımlayacağız o halde? Cevap: copy komutu ile:
dizi_a = np.array([3])
dizi_b = np.copy(dizi_a)
print("a dizisi: ",dizi_a)
print("a dizisinin id'si: ",id(dizi_a))
print("b dizisi: ",dizi_b)
print("b dizisinin id'si: ",id(dizi_b))
print()
print("\nŞimdi a dizisinin elemanının değerini 5 yapıyoruz...\n(b dizisine dokunmuyoruz)\n")
dizi_a[0] = 5
print("a dizisi: ",dizi_a)
print("a dizisinin id'si: ",id(dizi_a))
print("b dizisi: ",dizi_b)
print("b dizisinin id'si: ",id(dizi_b))
a dizisi: [3]
a dizisinin id'si: 123667408309776
b dizisi: [3]
b dizisinin id'si: 123667408309872
Şimdi a dizisinin elemanının değerini 5 yapıyoruz...
(b dizisine dokunmuyoruz)
a dizisi: [5]
a dizisinin id'si: 123667408309776
b dizisi: [3]
b dizisinin id'si: 123667408309872
Yine bu hafıza/adres meselesi yüzünden, kafamıza göre olmayan bir elemanı yoktan tanımlayamıyoruz: bilgisayar bize diyelim 12’lik yer bulup ayırmış, hemen dibinden bir başka değişken başlıyor, siz kalkıp da 13. elemanı tanımlayamazsınız, kalkıp yandaki komşunun odasına girecek haliniz yok (bu vesileyle son yıllarda hayli güvenli bilgisayarları yakıp kavuran Heartbleed bug’ı tam da bu şekilde çalışıp bilgileri sızdırıyordu). O nedenle siz “bana 12 odalı ev yetmiyor, 13. odaya ihtiyacım var” dediğinizde, Python da 13 odalı bir ev oluşturup, sizi -pek de çaktırmadan- oraya taşıyor (adresiniz değişiyor haliyle ama no prob. ;).
append() komutu ile eleman ekleme
appendile bir matrisin sonuna elemanlar eklenebilir. Dikkat edilmesi gereken husus, bu komutun yeni bir matris döndürdüğüdür: girdi olarak kullanılan matris doğal olarak -siz çıktı olarak kendisini işaret etmezseniz- değişiklik göstermez.
dizi = np.array([0,1,2,3,4])
print("dizi: \n",dizi)
print("diziye ek yapılmış hali:\n",np.append(dizi,[5,6,7]))
print("dizide bir değişiklik yok: \n",dizi)
print("-----------")
# Degisikligin diziye yapilmasi icin:
dizi = np.append(dizi,[5,6,7])
print("dizi: \n",dizi)
dizi:
[0 1 2 3 4]
diziye ek yapılmış hali:
[0 1 2 3 4 5 6 7]
dizide bir değişiklik yok:
[0 1 2 3 4]
-----------
dizi:
[0 1 2 3 4 5 6 7]
(tahmin edeceğiniz üzere, baştaki dizi ile, ekleme yapılmış dizinin adresleri de, id’leri de farklı).
insert() komutu ile eleman ekleme
insertkomutu işleyiş açısındanappende benzese de, ondan temel farkı ille de sona değil, listenin herhangi bir yerine aradan ekleme yapma imkanı vermesindedir. İlk parametre olarak ekleme yapılacak dizi; ikinci parametre olarak hangi indisin yerinden ekleme yapılacağı (örneğin “2” dersek, 2 indisli (3.) elemanın soluna eklenir, yani eski 2 indisindeki (3.) eleman sağa kayar:
dizi = np.array([0,1,2,3,4])
print("dizi: \n",dizi)
print("--------")
dizi = np.insert(dizi,2,[99,98,97])
print("dizi: \n",dizi)
dizi:
[0 1 2 3 4]
--------
dizi:
[ 0 1 99 98 97 2 3 4]
concatenate() ile dizi birleştirme
Python’da “normal” (standard) dizileri+operatörü ile birleştirebiliyorduk. NumPy matrislerinde bu operatör ile gerçek toplama yapabildiğimizden ötürü, yeni bir komuta gerek olmuş, o komut da işteconcatenate:
dizi1 = np.array([0,1,2,3,4])
dizi2 = np.array([9,10,15])
dizi3 = np.concatenate((dizi1,dizi2))
print("dizi3: \n",dizi3)
dizi3:
[ 0 1 2 3 4 9 10 15]
concatenateile yaptığımız her türlü işlemiappendile yapabileceğimiz doğrudur fakatappendfonksiyonu bizzat concatenate ile tanımlanmıştır. Kaynak koduna bakacak olursanız (append en altta yer alıyor; korkmayın, bir bakın bence, açık kaynağın, özgür yazılımın en güzel yanlarından biri de bu! 8):
def append(a, b, axis=None):
şeklinde başlıyor tanımı, bir dolu açıklama metni ve tüm bir fonksiyonun kod olarak aslında tek bir satırda tanımlandığını görüyorsunuz:
return concatenate([a, b], axis)
concatenate’i burada, bir boyutlu matrisler üzerinde çalışırken gördük ama ileride hem daha yüksek boyutlu matrislere ekleme yapacağız, hem devstackolsun,hstackolsun, daha ince işler, kurulumlar yapacağız.
Matristen Eleman Silme#
Silme işlemini delete komutu ile yapıyoruz ama hemen kullanmaya kalkmadan biraz düşünelim:
Silmekile ne kast ediyoruz?
Tamam, [0,1,2,3,4,5] gibi bir dizinin diyelim 2 ve 4 indisli elemanlarını silmekten bahsedebiliriz, o zaman [0,1,3,5] gibi bir şey olur elimize dönen, hatta:
dizi1 = np.array([0,1,2,3,4,5])
print("dizi1:\n",dizi1)
print("------------")
print(np.delete(dizi1,[2,4]))
dizi1:
[0 1 2 3 4 5]
------------
[0 1 3 5]
Nefis!… Bravo… vs..
Peki ya elimizde 2 boyutlu bir dizi varsa?
dizi2 = np.array([[1,2],[3,4],[5,6]])
print("dizi2:\n",dizi2)
dizi2:
[[1 2]
[3 4]
[5 6]]
Bunun 2 ve 4 indisli elemanlarını silmek ne demek??? (bkz. what ne demek? 8P).. 1. satırı silelim, bunu anlarız (Python da anlar), haydi 2. sütunu silelim bu da tamam ama 2 ve 4 indisi bırakın, 2 indisli elemanı silelim’in bile 2 boyutlu bir matriste anlamı yok (jenga oynayanlarınız bilir, yapıyı korumak lazım: bir satırında 2 elemanı, bir başka satırında 1 elemanı olan bir matris matematik dünyasında yok (olana da hilkat garibesi muamelesi çekiliyor, girmiyorum o konuya ama çok istiyorsanız bkz. dizi dizileri ya da hücre dizileri)).
Önce bir satırı, sonra bir sütunu uçurup, sonra da tek bir elemanı uçurmaya kalkıp sonuçlara bakalım:
dizi2 = np.array([[1,2,3],[4,5,6],[7,8,9]])
print("dizi2:\n",dizi2)
print("---------")
print("2. satırı uçurduğumuz hali:\n",np.delete(dizi2,1,0))
print("---------")
print("2. sütunu uçurduğumuz hali:\n",np.delete(dizi2,1,1))
print("---------")
print("1. & 3. sütunu uçurduğumuz hali:\n",np.delete(dizi2,[0,2],1))
dizi2:
[[1 2 3]
[4 5 6]
[7 8 9]]
---------
2. satırı uçurduğumuz hali:
[[1 2 3]
[7 8 9]]
---------
2. sütunu uçurduğumuz hali:
[[1 3]
[4 6]
[7 9]]
---------
1. & 3. sütunu uçurduğumuz hali:
[[2]
[5]
[8]]
Satırlar ve sütunlar gayet iyi uçuyor (satır uçururken 0, sütun uçururken 1 değerini alan parametre “eksen” parametresi: 2-boyutlu bir matriste birincil (0) eksen x-ekseni (yani satırlar); ikincil (1) eksen y-ekseni (yani sütunlar)).
Şimdi gelelim bir tek, diyelim 2. elemanı uçurmaya:
dizi2 = np.array([[1,2,3],[4,5,6],[7,8,9]])
print("dizi2:\n",dizi2)
print("---------")
print("2. elemanı uçurduğumuz hali:\n",np.delete(dizi2,1))
dizi2:
[[1 2 3]
[4 5 6]
[7 8 9]]
---------
2. elemanı uçurduğumuz hali:
[1 3 4 5 6 7 8 9]
2. eleman olan 2 (indis:1) hakikaten de uçtu ama artık matrisin bütünlüğü bozulduğundan, otomatik olarak 1 boyuta indirgendi ki, siz de takdir ederseniz, en mantıklı çözüm bu.
Matrislerin Özellikleri#
Sıklıkla, elimize geçirdiğimiz bir NumPy dizisinin özelliklerini bilmek isteriz: boyu nedir, boyutu nedir, kimlerdendir, içinde sevdiceğimiz var mıdır vs. Bu bölümde temel bilgi komut ve metotlarını işleyeceğiz.
O halde, bir tane 2 boyutlu \((4\times5)\) matris tanımlayıp, işimize bakalım:
mat1 = np.array([[0,1,2,3,4],[10,11,12,13,14],[20,21,22,23,24],[30,31,32,33,34]])
print(mat1)
[[ 0 1 2 3 4]
[10 11 12 13 14]
[20 21 22 23 24]
[30 31 32 33 34]]
aa = np.array([[1],[2],[3]])
print(aa.ndim,aa.shape)
print([np.array(mat1[:,2])])
2 (3, 1)
[array([ 2, 12, 22, 32])]
Fark ettiyseniz, değerler aynı zamanda satır ve sütun indislerini veriyor (ilk satırın (0 satırı) misal 2 indisli elemanını 02 yazamadığımdan, 2 ile yetiniyoruz ama artık o kadar kusur kadı kızında da bulunur).
size: Matrisin eleman sayısı#
size metodu bize matrisin toplam eleman sayısını verir. Örneğimizdeki matrisimizin 20 elemanı var, o halde, “size” diye sorunce, “20” demesini bekliyoruz, bakalım:
print("Matrisimizin eleman sayısı: ",mat1.size)
Matrisimizin eleman sayısı: 20
shape: Matrisimizin boyutları (90-60-90)#
shape metodu matrisimizin “şeklini” yani boyutlarını bir demet (tuple) olarak döndürür:
print("Matrisimizin boyutları: ",mat1.shape)
print("Matrisimizin ",len(mat1.shape)," adet boyutu var.")
print("Asıl (0 - satırlar) eksendeki 'kat' sayısı: ",mat1.shape[0])
print("İkincil (1 - sütunlar) eksendeki 'daire sayısı: ",mat1.shape[1])
Matrisimizin boyutları: (4, 5)
Matrisimizin 2 adet boyutu var.
Asıl (0 - satırlar) eksendeki 'kat' sayısı: 4
İkincil (1 - sütunlar) eksendeki 'daire sayısı: 5
Burada dikkatinizi çektiyse, boyutların döndürüldüğü mat1.shape demetinin boyunu bulurken size metodunu değil, len komutunu kullandık. Bunun sebebi, sizeın bir ndarray metodu olup, demetlere uygulanabilir olmaması. len komutu epey evrensel bir komut olup, hemen her değişkene sorulabilir. O halde niye len dururken, matrislerde size ile uğraşıyoruz? Hemen bakalım:
print(len(mat1))
4
Gördüğünüz üzere, sadece ilk eksenin (satırların) sayısını veriyor, gayet de mantıklı zira Python bunu -teknik olarak- her birinde 5 elemanlı 1 boyutlu dizi olan, 4 elemanlı bir derleme olarak görüyor (matrisi nasıl tanımladığımızı hatırlayın: [ [1. eleman: 1. dizi], [2. eleman: 2. dizi], …]). Bu yüzden, n-boyutlu matrislerin eleman sayısını len ile değil, size ile öğreniyoruz (bu konu sanırım bölüm sonlarında olan “bunları bilmeseniz de olur ama işte…” kısmına daha uygundu!.. 8)
Casablanca, “Play it -again- Sam” (tamam, Bogart hiç again lafını söylemiyor aslında ama olsun)
reshape: yeniden şekillendir, Sam..#
Matrisin şekli çok önemlidir zira o şekli yeniden (“re-”) biçimlendirebiliriz (“shape”) => reshape!
Elimizdeki matris (4x5)’ti, 20 elemanı vardı. Elemanları düzgünce dağıtabileceğimiz (yani bütün boyutlarının çarpımı 20 olduğu sürece) her boyuta çıkabiliriz. Çarpımı 20 olan sayılardan bazılarını sıralayalım:
1x20
2x10
2x2x5
20x1
Gördüğünüz üzere, yukarıdakilerden 1. ve 4. örnekler bir boyutlu matrisler (ilki satır, ikincisi sütun vektörü); 2. örnek 2 satırlı, 10 sütunlu iki boyutlu bir matris; 3. ilginç çünkü 3 boyut var. Tek tek deneyelim:
print(mat1.reshape(1,20))
[[ 0 1 2 3 4 10 11 12 13 14 20 21 22 23 24 30 31 32 33 34]]
print(mat1.reshape(2,10))
[[ 0 1 2 3 4 10 11 12 13 14]
[20 21 22 23 24 30 31 32 33 34]]
print(mat1.reshape(2,2,5))
print(mat1.reshape(2,2,5)[1,1,3])
[[[ 0 1 2 3 4]
[10 11 12 13 14]]
[[20 21 22 23 24]
[30 31 32 33 34]]]
33
(2x2x5) üç boyutlu matrisimizde [1,1,3] indisli elemanı istediğimizde, önce 1. eksende ilerleyip, 1 indisli diziye gittik:
[[20 21 22 23 24] [30 31 32 33 34]]
dizisi. Sonra bu dizinin 1 indisli dizisine gittik:
[30 31 32 33 34]
dizisi. Sonra da, bu dizinin 3 indisli elemanına (yani 4. elemanına) gittik: 33
Bu vesileyle, çaktırmadan, 3 boyutlu bir diziyi nasıl oluşturabileceğimize dair bir yolu da keşfetmiş olduk: reshape metodu ile.
print(mat1.reshape(20,1))
[[ 0]
[ 1]
[ 2]
[ 3]
[ 4]
[10]
[11]
[12]
[13]
[14]
[20]
[21]
[22]
[23]
[24]
[30]
[31]
[32]
[33]
[34]]
ndim: kısa yoldan kaç boyutlu bir matrisimiz olduğuna dair…#
Elimizdeki matrisimizin kaç boyutlu olduğunu şeklinin kaç elemanı olduğunu sorarak öğrenebilmiştik:
print("Matrisimizin ",len(mat1.shape)," adet boyutu var.")
Matrisimizin 2 adet boyutu var.
Bunu tek bir metotla da yapmak mümkün, bu iş için ndim metotu yardımımıza koşuyor:
print("Matrisimizin ",mat1.ndim," adet boyutu var.")
Matrisimizin 2 adet boyutu var.
dtype: elemanlarının cinsi#
Aşağıdaki üç diziye bir bakın – sizce aralarında çok fark var mı?
dizi_1 = np.array([0,1,2])dizi_2 = np.array([0,1,2.0])dizi_3 = np.array([0,1.0,"iki"])
…
Herhalde, 3.yü listeye koymasaydık, “fark yok” cevabı normal karşılanacaktı ama 3. işi bozduğundan, bu sefer 1. ile 2. arasında da bir bit yeniği çıkmasını bekliyoruz. Python’a verelim, bakalım o ne diyecek:
dizi_1 = np.array([0,1,2])
dizi_2 = np.array([0,1,2.0])
dizi_3 = np.array([0,1.0,"iki"])
print("dizi_1:\n",dizi_1,"\n")
print("dizi_2:\n",dizi_2,"\n")
print("dizi_3:\n",dizi_3,"\n")
dizi_1:
[0 1 2]
dizi_2:
[0. 1. 2.]
dizi_3:
['0' '1.0' 'iki']
Hemen fark edenler kendilerine bir 10 puan yazsınlar bakalım! Detaylıca incelediğimizde, ilkinde sayılarımızın ondalık noktalarının olmadığını, ikincisinde diziyi girerken sadece 2’yi ondalıklı girdiğimiz halde (o da hani “2.3” gibi değil, bildiğiniz “2.0” masumane şekliyleydi!) 0 ve 1’in kuyruklarına da ondalık noktasının takılmış olduğunu, üçüncü dizide ise hepsinin (0’ın ve 1.0’ın bile!) bir kelimeymişçesine kesme (’) işaretleri arasına alınmış olduğunu görüyoruz. Bunun sebebi şu:
NumPy dizileri sadece tek bir eleman cinsini tutarlar. İlk dizimizde bütün elemanlarımız tam sayı, o yüzden tam sayı olarak tutulabiliyorlar; ikinci dizimizde iki tam sayı, bir tane ondalıklı sayı girdiğimizde, ondalıklı sayının varlığı (değerinden bağımsız olarak), dizinin elemanları tuttuğu cins olarak tam sayıyı değil, hepsini içerebilecek ondalıklı sayı cinsini seçmesini gerektiriyor (genel olarak: tam sayıları bilgi kaybı olmadan ondalıklı olarak tutabilsek de, tersi mümkün değil); üçüncüsünde ise, tam sayı olsun, ondalıklı sayı olsun, bunları string olarak tutabiliyoruz ama string değişkenlerini -genel olarak- sayı olarak ifade edemediğimizden, elemanların cinsi “en küçük ortak cins” olan string olarak tanımlanıyor.
Bir dizinin tuttuğu eleman cinsini de dtype metodu ile öğreniyoruz:
print("dizi_1'in elemanlarının cinsi :",dizi_1.dtype,"\n")
print("dizi_2'nin elemanlarının cinsi:",dizi_2.dtype,"\n")
print("dizi_3'ün elemanlarının cinsi :",dizi_3.dtype,"\n")
dizi_1'in elemanlarının cinsi : int64
dizi_2'nin elemanlarının cinsi: float64
dizi_3'ün elemanlarının cinsi : <U32
Tipte yazılı kısım cinsi (integer: tam sayı; float: ondalıklı sayı; U: Unicode string) verirken, peşinden gelen sayı her bir eleman için hafızada ayrılan yer miktarını (bit cinsinden) verir.
Bir dizinin elemanlarının cinsini zorla bir başka cinse dönüştürmek mümkündür: bunu astype metotu ile yaparız:
dizi_2[2] = 2.6
print(dizi_2.astype(int))
[0 1 2]
dizi_2
array([0. , 1. , 2.6])
Ama bunun mümkün olmadığı yerde de zorlamanın pek bir alemi yok:
150 tane tam sayı elemanı olan bir diziye, 151. eleman olarak ondalıklı sayı eklediğimizde bütün elemanlarının tipi nasıl da tümden ondalıklı sayılara dönüşüyorsa, iki sayıyı topladığımızda da bu kural otomatikman uygulanır:
# Tam sayı + tam sayı = tam sayı:
5 + 7
12
# Tam sayı + ondalıklı sayı = ondalıklı sayı:
5 + 7.5
12.5
# Çok mu barizdi? Öyleyse bir de şuna bakalım:
5 + 7.0
12.0
Tipik Matrisler#
Şimdi eğri oturup, doğru konuşalım: NumPy’da matris elemanlarını tanımlamak çoğu kez epey sıkıcı. Bu yüzden hayatı kolaylaştırıcı birkaç tipik hazır matris türü var.
zeros : sıfır sıfır sıfır…#
Adı üzerinde, istediğimiz boyutta, tüm elemanları sıfır olan bir matris döndürür - yapmamız gereken, istediğimiz matris boyutunu belirlemekten ibaret:
mat_0 = np.zeros([2,3,4])
print (mat_0)
[[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]]
ones : bir bir bir… (nereye kadar gideceğiz böyle, haydi bakalım…)#
Bu da, bütün elemanları 1 olan bir matris döndürür:
mat_1 = np.ones([2,5,2])
print(mat_1)
[[[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]
[[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]]
fill ile matrisi doldurmak#
Bütün elemanları sıfır veya bir olan bir matrisi nasıl tanımlayacağımızı artık biliyoruz, peki bütün elemanlarının 2.7 olmasını istiyorsak? Bu durumda, iki-üç(-çok?) yolumuz var:
0-matrisi oluşturup, bütün elemanlarına 2.7 ekleriz:
mat_0 + 2.7
array([[[2.7, 2.7, 2.7, 2.7],
[2.7, 2.7, 2.7, 2.7],
[2.7, 2.7, 2.7, 2.7]],
[[2.7, 2.7, 2.7, 2.7],
[2.7, 2.7, 2.7, 2.7],
[2.7, 2.7, 2.7, 2.7]]])
1-matrisi oluşturup, bütün elemanlarını 2.7 ile çarparız:
mat_1 * 2.7
array([[[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7]],
[[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7],
[2.7, 2.7]]])
Herhangi bir matris oluşturup (ya da hazır alıp), bütün elemanlarını 2.7 ile doldururuz:
mat_n = np.array([[1,2,3.],[4,5,6]])
print(mat_n)
print("----------")
mat_n.fill(2.3)
print(mat_n)
[[1. 2. 3.]
[4. 5. 6.]]
----------
[[2.3 2.3 2.3]
[2.3 2.3 2.3]]
fill metodunu kullanırken dikkat etmeniz gereken iki şey var:
Metodu kullandığınızda doğrudan ilgili matrise etki eder, yeni bir matris döndürmez.
Elinizdeki matrisin cinsi ne ise, doldururken kullandığınız değerin cinsini matrisin cinsine döndürür.
İkinci noktanın nasıl çalıştığını görmek için üstteki örneği bir kez daha, bu kez “hilesiz, el çabukluğu kerametsiz”, ağır çekimde izleyelim:
mat_n = np.array([[1,2,3],[4,5,6]])
print(mat_n)
print("----------")
mat_n.fill(2.3)
print(mat_n)
[[1 2 3]
[4 5 6]]
----------
[[2 2 2]
[2 2 2]]
Hoppala! Yukarıdakinin aynısını yazdık ama bu sefer 2.3 yerine, 2 ile doldurdu… nereyi farklı girdik ki?.. (haydi bulun bakalım, size 1 dakika süre…)
…
Buldunuz mu? İki girişte de ilk satırdaki “3”e bakın. Evet, tam orası işte! İlk kodda “3.” diyerek, çaktırmadan bütün diziyi ondalıklı tipine dönüştürmüşüz, ikincisinde hepsi tam sayı. Bu yüzden ikincisinde “2.3” ondalıklı sayısı ile doldur deyince matrisimize, “hiç kusura bakma, ben tam sayı matrisiyim, çok istiyorsa tam sayı kılığına girsin, 2 olarak eklerim” diyor, olayımız bundan ibaret. 8P 8)
birim matris#
Birim matris, tanım itibarı ile -ve tabii ki boyutları ile uyumlu olmak şartıyla- bir matrisle çarpıldığı zaman, o matrisi değiştirmeyen matristir. Bunu da sağlamanın tek yolu, köşegeni boyunca “1” değerini almasıdır. “Identity” (birim, etkisiz) olarak isimlendirilip “I” harfi ile temsil edildiğinden, okunuşundan yola çıkıp, ufak bir kelime oyunu ile eye olarak tanımlanır:
mat_birim_5x5 = np.eye(5,5)
print(mat_birim_5x5)
[[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0.]
[0. 0. 0. 0. 1.]]
# Kare matris olması zorunluluğu yoktur:
mat_birim_3x5 = np.eye(3,5)
print(mat_birim_3x5)
[[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0.]]
rastgele matrisler#
Bazen uğraşmak istemeyiz, “sen kafana göre doldur, ben sonra bakarım” deriz. Bu durumlarda numpy.random kütüphanesinin rand ve randint’i epey yardımcı olur:
mat_rastgele_tamsayilar = np.random.randint(5,10,[2,3,4])
print(mat_rastgele_tamsayilar)
[[[9 5 6 8]
[5 9 5 7]
[5 7 7 7]]
[[6 7 8 8]
[6 8 9 8]
[5 5 7 6]]]
mat_rastgele_ondaliklar = np.random.rand(2,3,4)
print(mat_rastgele_ondaliklar)
[[[0.38393522 0.04918966 0.96330341 0.42173995]
[0.67436847 0.80837279 0.99439132 0.47738754]
[0.94789398 0.66823345 0.37837814 0.2270273 ]]
[[0.7078637 0.87374734 0.78669365 0.4041161 ]
[0.84084571 0.99785125 0.97208458 0.2801183 ]
[0.90863449 0.76821831 0.63668881 0.76409077]]]
Parametreler anlaşılıyor mu çağrıştan? randint’e ilk parametre olarak alt limiti (dahil), ikinci parametre olarak üst limiti (hariç), üçüncü parametre olarak da matrisimizin arzu ettiğimiz boyutunu belirtiyoruz (Bu arada, Python’da harikulade bir parametre sıralama opsiyonu var, birkaç haftaya göreceğiz inşallah 8)
rand’da ise parametre olarak sadece boyutu verebiliyoruz, o da bize, alışık olduğumuz üzere 0 ile 1 arasında (0 dahil, 1 hariç) sayılar üretiyor (teknik not: düzgün dağılımdan).
arange ile aralıklar#
arange komutu bize bir boyutlu diziler verir ama elemanlarını istediğimiz adımla türetebildiğimiz için, peşine bir de reshape çekersek tadından yenmez olur. İlk parametre başlayacağı sayı (dahil), ikinci parametre biteceği sayı (hariç), üçüncü parametre de adım boyu olur:
mat_aralik = np.arange(4,10.1,2.3)
print(mat_aralik)
[4. 6.3 8.6]
Gördüğünüz üzere, ne başlangıcın, ne bitişin, ne de adım boyunun tam sayı olmak gibi bir zorunluluğu yok. İleriye doğru gidebildiğimiz gibi, geriye doğru da gidebiliriz (Nasıl?.. Adım boyumuzu negatif alarak tabii ki!):
mat_aralik_gerigeri = np.arange(10,2,-2)
print(mat_aralik_gerigeri)
[10 8 6 4]
# Bir de arange + reshape kombosu yapalım, tam olsun:
mat_kombo_3x3 = np.arange(1,10).reshape(3,3)
print(mat_kombo_3x3)
[[1 2 3]
[4 5 6]
[7 8 9]]
Yukarıdaki örnekte nokta ardından nokta birleşik yazımı (metodun dönüşüne metot) kafanızı karıştırıyorsa, parantez içinde de güzel güzel belirtebilirsiniz:
mat_kombo_3x3 = (np.arange(1,10)).reshape(3,3)
print(mat_kombo_3x3)
[[1 2 3]
[4 5 6]
[7 8 9]]
(Selçuk Erdem, Karikatürler 1, s.72)
linspace: düzenli, disiplinli#
Sayısal örneklerle haşır neşir olacağımız için bir fonksiyonun belli -ve hemen her zaman düzenl aralıklı- noktalarda değerini hesaplatmak istediğimizde arange yardıma koşar. Ama mesela doğrudan “23 ile 30 arasındaki değerleri hesaplayalım, 100 tane değer alalım” dersek, o zaman biraz zorlanıyoruz (ben zorlanıyorum en azından… adım_boyu = (30 - 23) / 100?..
Bu gibi durumlarda, bu işi yapan hazır linspace komutumuz var:
mat_duzenli_guzel_aralik = np.linspace(23,30,100)
print(mat_duzenli_guzel_aralik)
print("---------------------")
print("Bu güzel, düzenli dizinin eleman sayısı: ",mat_duzenli_guzel_aralik.size)
[23. 23.07070707 23.14141414 23.21212121 23.28282828 23.35353535
23.42424242 23.49494949 23.56565657 23.63636364 23.70707071 23.77777778
23.84848485 23.91919192 23.98989899 24.06060606 24.13131313 24.2020202
24.27272727 24.34343434 24.41414141 24.48484848 24.55555556 24.62626263
24.6969697 24.76767677 24.83838384 24.90909091 24.97979798 25.05050505
25.12121212 25.19191919 25.26262626 25.33333333 25.4040404 25.47474747
25.54545455 25.61616162 25.68686869 25.75757576 25.82828283 25.8989899
25.96969697 26.04040404 26.11111111 26.18181818 26.25252525 26.32323232
26.39393939 26.46464646 26.53535354 26.60606061 26.67676768 26.74747475
26.81818182 26.88888889 26.95959596 27.03030303 27.1010101 27.17171717
27.24242424 27.31313131 27.38383838 27.45454545 27.52525253 27.5959596
27.66666667 27.73737374 27.80808081 27.87878788 27.94949495 28.02020202
28.09090909 28.16161616 28.23232323 28.3030303 28.37373737 28.44444444
28.51515152 28.58585859 28.65656566 28.72727273 28.7979798 28.86868687
28.93939394 29.01010101 29.08080808 29.15151515 29.22222222 29.29292929
29.36363636 29.43434343 29.50505051 29.57575758 29.64646465 29.71717172
29.78787879 29.85858586 29.92929293 30. ]
---------------------
Bu güzel, düzenli dizinin eleman sayısı: 100
Yani, linspace ile tek yapmamız gereken başlangıcı (dahil), bitişi (o da dahil, aman dikkat!) ve toplamda kaç tane nokta istediğimiz belirtip, arkamıza yaslanmak!
Matris işlemleri (dört işlem + bir ters, bir düz)#
Dört temel işlem (eleman bazında)#
Matrislerimizi -birbirleriyle boyutları uyumlu olduğu sürece- dört temel işlemi kullanarak eleman bazında toplayabilir, çıkarabilir, çarpabilir ve hatta bölebiliriz.
np.random.seed(227)
mat_a_3x2 = np.random.randint(1,10,[3,2])
print("mat_a_3x2:\n",mat_a_3x2)
print("-"*50)
mat_b_3x2 = np.random.randint(1,10,[3,2])
print("mat_b_3x2:\n",mat_b_3x2)
print("-"*50)
mat_c_2x4 = np.random.randint(1,10,[2,4])
print("mat_c_2x4:\n",mat_c_2x4)
print("-"*50)
mat_a_3x2:
[[1 1]
[9 3]
[7 5]]
--------------------------------------------------
mat_b_3x2:
[[5 9]
[4 5]
[2 1]]
--------------------------------------------------
mat_c_2x4:
[[9 8 2 4]
[1 6 2 6]]
--------------------------------------------------
print("mat_a_3x2 + mat_b_3x2")
print(mat_a_3x2 + mat_b_3x2)
mat_a_3x2 + mat_b_3x2
[[ 6 10]
[13 8]
[ 9 6]]
print("mat_a_3x2 * mat_b_3x2")
print(mat_a_3x2 * mat_b_3x2)
mat_a_3x2 * mat_b_3x2
[[ 5 9]
[36 15]
[14 5]]
print("mat_a_3x2 / mat_b_3x2")
print(mat_a_3x2 / mat_b_3x2)
mat_a_3x2 / mat_b_3x2
[[0.2 0.11111111]
[2.25 0.6 ]
[3.5 5. ]]
Skaler ve Vektörel Çarpımlar#
Skaler Çarpım
Matrislerin skaler çarpımını ise dot fonksiyonu ile sağlarız:
skaler_carpim_3x4 = np.dot(mat_a_3x2,mat_c_2x4)
print("skaler_carpim_3x4 = np.dot(mat_a_3x2,mat_c_2x4):")
print(skaler_carpim_3x4)
skaler_carpim_3x4 = np.dot(mat_a_3x2,mat_c_2x4):
[[10 14 4 10]
[84 90 24 54]
[68 86 24 58]]
Vektörel Çarpım
Vektörel çarpım ise cross fonksiyonu ile yapılmakta:
$\(\hat{i}\times \hat{j} = \hat{k}\)$
i_hat = np.array([1,0,0])
j_hat = np.array([0,1,0])
k_hat = np.array([0,0,1])
print("i_hat x j_hat = ",np.cross(i_hat,j_hat))
print("-"*50)
print("mat_a_3x2 x mat_b_3x2 =",np.cross(mat_a_3x2,mat_b_3x2))
i_hat x j_hat = [0 0 1]
--------------------------------------------------
mat_a_3x2 x mat_b_3x2 = [ 4 33 -3]
Matrisin transpozesi, tersi ve determinantı#
Transpozesi
Matrislerimizin transpozesini T metodu ile alırız (Uzun uzadıya yazmak isterseniz transpose() komutunu kullanabilirsiniz)
print(mat_a_3x2)
print("-"*50)
print(mat_a_3x2.T)
print()
print(np.transpose(mat_a_3x2))
mat_d_2x3x4 = np.random.randint(1,10,[2,3,4])
print("="*50)
print(mat_d_2x3x4)
print("-"*50)
print(mat_d_2x3x4.T)
[[1 1]
[9 3]
[7 5]]
--------------------------------------------------
[[1 9 7]
[1 3 5]]
[[1 9 7]
[1 3 5]]
==================================================
[[[1 6 2 1]
[2 4 5 4]
[2 3 7 2]]
[[3 3 1 6]
[3 9 6 3]
[9 3 4 9]]]
--------------------------------------------------
[[[1 3]
[2 3]
[2 9]]
[[6 3]
[4 9]
[3 3]]
[[2 1]
[5 6]
[7 4]]
[[1 6]
[4 3]
[2 9]]]
Tersi
Kare bir matrisin tersini linalg (linear algebra) kütüphanesinin inv (inverse) komutu ile alabiliriz:
np.random.seed(227)
mat_e_3x3 = np.random.randint(1,10,[3,3])
mat_e_3x3_tersi = np.linalg.inv(mat_e_3x3)
print(mat_e_3x3)
print(mat_e_3x3_tersi)
[[1 1 9]
[3 7 5]
[5 9 4]]
[[ 0.22368421 -1.01315789 0.76315789]
[-0.17105263 0.53947368 -0.28947368]
[ 0.10526316 0.05263158 -0.05263158]]
Bildiğiniz üzere, bir matrisin tersi, o matrisle çarpıldığında, birim matrisini veren matristir, yani: $\(A\cdot A^{-1} = \mathbb{1}\)$
Yukarıdaki hesabımızı kontrol edelim:
print(np.dot(mat_e_3x3,mat_e_3x3_tersi))
[[ 1.00000000e+00 1.45716772e-16 -2.77555756e-17]
[-3.33066907e-16 1.00000000e+00 -1.94289029e-16]
[ 0.00000000e+00 3.60822483e-16 1.00000000e+00]]
\(10^{-16}\) küçüklüğünü pratik olarak 0 alabileceğimiz için, çarpımın gayet güzel bir birim matris vermiş olduğunu görüyoruz! 8)
‘Tersimsisi’
Bir matrisin tersi hesaplanırken, analitik olarak determinantından da faydalanır. Determinant ise sadece kare matrislerin bir özelliği olduğundan, bu nedenle kare olmayan matrislerin analitik olarak tersleri yoktur. Fakat, nümerik olarak kare olmayan (nxm)’lik bir matris ile çarpılıp, (nxn)’lik bir birim matrisi verebilecek (mxn)’lik bir matris bulunabilir. Kare olmayan bu ters matrisler de sanki-ters, ‘tersimsi’ (pseudo-inverse) olarak adlandırılıp, buradan kısaltmayla, yine linalg kütüphanesinin pinv komutu ile bulunur (yalnız tabii her matrisin tersi olacak diye bir garanti yoktur).
np.random.seed(227)
mat_a_3x5 = np.random.randint(1,10,[3,5])
print("mat_a_3x5:")
print(mat_a_3x5)
print("-"*50)
print("mat_a_3x5_tersimsi:")
mat_a_3x5_tersimsi = np.linalg.pinv(mat_a_3x5)
print(mat_a_3x5_tersimsi)
print("-"*50)
print("mat_a_3x5 x mat_a_3x5_tersimsi =")
print(np.dot(mat_a_3x5,mat_a_3x5_tersimsi))
mat_a_3x5:
[[1 1 9 3 7]
[5 5 9 4 5]
[2 1 9 8 2]]
--------------------------------------------------
mat_a_3x5_tersimsi:
[[-0.09098056 0.12033452 -0.02276971]
[-0.08144814 0.13321803 -0.04835983]
[ 0.04811415 -0.00919829 0.02856778]
[-0.05332259 -0.03392362 0.12458971]
[ 0.1284813 -0.00985672 -0.07996423]]
--------------------------------------------------
mat_a_3x5 x mat_a_3x5_tersimsi =
[[ 1.00000000e+00 -2.04697370e-16 4.16333634e-17]
[ 7.77156117e-16 1.00000000e+00 1.80411242e-16]
[ 4.44089210e-16 -6.93889390e-18 1.00000000e+00]]
Determinantı
Kare bir matrisin determinantını da şaşırtıcı olmayan bir biçimde, linalg kütüphanesinin det komutu ile elde ederiz:
print("mat_e_3x3")
print(mat_e_3x3)
print("-"*50)
print("det(mat_e_3x3):")
print(np.linalg.det(mat_e_3x3))
mat_e_3x3
[[1 1 9]
[3 7 5]
[5 9 4]]
--------------------------------------------------
det(mat_e_3x3):
-76.0