Scipy Seyrek Matrisleri (Sparse Matrices)
Scipy kütüphanesinde seyrek matrisler üzerinde işlemi sağlayan pek çok class, metot var. Seyrek matrislerde bilindiği gibi sıfır değeri depolanmaz, kıyasla Numpy "yoğun (dense)" matrislerde herşey depolanır, seyrek durumda bu sebeple sadece sıfır harici değerler için yer ayrılacaktır. Tabii bu seyrek matrisler hala çarpım, toplama, vs, gibi işlemlerin yapılmasına izin verirler, mesela toplama durumunda iki seyrek matris A,B üzerinden A+B arka planda şöyle kodlanabilir, B'de olmayan öğe ile A'da olan bir değer toplanıyorsa sonuç direk A'daki değer olur, çünkü olmayan değer otomatik olarak sıfırdır, bu şekilde pek çok hesap daha hızlı şekilde yapılabilir.
Örnek: diyelim ki Netflix, kullanıcı filmi seyretmişse müşterileri satırda, filmler kolonda olacak şekilde bir matriste ona verdiği beğeni değerini 1-5 arası bir değerle kaydetmiş olabilir. Bir kullanıcı çoğunlukla filmlerin hepsini seyretmiş olamaz, o zaman bu matriste satır başı en fazla 100-200 civarı öğe / kolon değeri "dolu" olacak, gerisi boş olacaktır. Eğer bu matrisi scipy.sparse üzerinden işlersek, bellek ve işlem hızında ilerleme sağlarız.
Seyrek matris yaratmak icin onceden boyut tanımlanabilir, fakat o boyut kadar yer bellekte onceden ayrilmaz. Mesela
import scipy.sparse as sps
A = sps.lil_matrix((N,D))
N,D ne olursa olsun A bellekte hiç / çok az yer tutar.
Scipy kütüphanesinde pek çok farklı seyrek matris çeşitleri var, mesela coomatrix, csrmatrix, vs.. Bu matrislerin hepsinin duruma göre avantajları / dezavantajları var. İyi haber birinden diğerine geçiş çok kolay, mesela A bir lilmatris'ten coomatrix'e geçiş için A.tocoo() yeterli. Avantajlara örnek, mesela lil ile dilimleme (slicing) yapılabilir, i.e. "bana sadece 3. kolonu ver" demek gibi, ama coo ile bu yapılamaz.
Bazı Numaralar
X = sps.lil_matrix((2,5))
X[0,2] = 2.0
X[1,3] = 5.0
olsun. Yogun hali
print X.todense()
[[ 0. 0. 2. 0. 0.]
[ 0. 0. 0. 5. 0.]]
Sıfır Olmayan Öğeleri Gezmek
Direk lil_matrix
ile
rows,cols = X.nonzero()
for row,col in zip(rows,cols):
print row,col, ' = ', X[row,col]
Eger coo_matrisi olsaydi
cx = X.tocoo()
for i,j,v in zip(cx.row, cx.col, cx.data):
print i,j,' = ',v
Bir satırı bir matrisin her satırıyla ögesel olarak çarpmak,
X2 = sps.lil_matrix((1,5))
X2[0,3] = 9
print X2.todense()
[[ 0. 0. 0. 9. 0.]]
Bu matrisi alıp önceki X'in her satırı ile öğesel çarptırmak için
print X.multiply(X2).todense()
[[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 45. 0.]]
Sadece sıfır olmayan ogelerinin log'unu almak,
X=X.tocoo()
X2=X2.tocoo()
X2.data = np.log(X2.data)
print X2.todense()
[[ 0. 0. 0. 2.19722458 0. ]]
Ortalama Çıkartmak
Scipy seyrek matrislerde ortalamayı almak külfetli olabiliyor, Scipy ortalamayı çıkartmayı izin vermez (olmayan değerler ortalama alırken sıfır mı kabul edilecektir? bu tam bilinmediği için izin verilmemiş). Fakat bu özellik gerekiyorsa, şöyle yapılır,
import scipy.sparse as sps
def center(mat):
mat = mat.T
vec = sps.csc_matrix(mat.mean(axis=1))
mat_row = mat.tocsr()
vec_row = vec.T
mat_row.data -= np.repeat(vec_row.toarray()[0],np.diff(mat_row.indptr))
return mat_row.T
mat = sps.csc_matrix([[1, 2, 3, 5.],
[2, 3, 4, 5.],
[3, 4, 5, 5.]])
print center(mat).todense()
[[-1. -1. -1. 0.]
[ 0. 0. 0. 0.]
[ 1. 1. 1. 0.]]
Normalize Etmek
Standardize etmek hem ortalamayı çıkartmak (demean), sonra normalize etmek demektir. Bu iki işlem birbirinden bağımsız yapılabilir, bazen biri bazen diğeri kullanılabilir. Normalize etmek, mesela satir L2 normalizasyonu diyelim, oyle bir islemdir ki o islem ardindan her satirdaki ogelerin karesini toplayip karekoku alinca 1 sonucunu elde edebilmek mumkun olacaktir, Normalize etmek için scikit-learn paketinin fonksiyonları vardır,
from sklearn.preprocessing import normalize
print normalize(mat, norm='l1', axis=0).todense()
[[-0.5 -0.5 -0.5 0. ]
[ 0. 0. 0. 0. ]
[ 0.5 0.5 0.5 0. ]]
Üstteki çağrı matrisin kolonlarını (çünkü axis=0 seçildi, bu kolon demek) L1 normu kullanarak normalize etti; yani her kolonun L1 büyüklüğü hesaplandı ve o kolonun her hücresi bu büyüklük ile bölündü. L2 norm kullabilirdik,
print normalize(mat, norm='l2', axis=0).todense()
[[-0.70710678 -0.70710678 -0.70710678 0. ]
[ 0. 0. 0. 0. ]
[ 0.70710678 0.70710678 0.70710678 0. ]]
Satırları normalize edebilirdik,
[[-0.33333333 -0.33333333 -0.33333333 0. ]
[ 0. 0. 0. 0. ]
[ 0.33333333 0.33333333 0.33333333 0. ]]
Kosegenlik
Eğer bir seyrek matrisi köşegeninde belli değerler ile yaratmak istiyorsak, köşegene gelecek değerleri bir vektör olarak tanımlayıp matrisi yaratabiliriz. Hatta köşegenin bir, iki, vs. üstü ya da altına gelecek değerleri yine vektör şeklinde tanımlamak ta mümkün.
data = np.array([[-1, -1, -1, -1], [1, 1, 1, 1]])
diags = np.array([0, 1])
spdiags(data, diags, 4, 4).toarray()
Out[1]:
array([[-1, 1, 0, 0],
[ 0, -1, 1, 0],
[ 0, 0, -1, 1],
[ 0, 0, 0, -1]])
Diske Yazmak, Okumak
En kullanışlı yaklaşım scipy.io
içindeki mmread
ve mmwrite
kullanımı. Matrisi diske yazmak için
import scipy.sparse as sps, scipy.io as io
A = sps.lil_matrix((4,4))
A[2,3] = 10
io.mmwrite("/tmp/A",A)
Yazılan dosya metin bazlı, düz okunabilen bir dosyadır,
! cat /tmp/A.mtx
%%MatrixMarket matrix coordinate real general
%
4 4 1
3 4 1.000000000000000e+01
Okumak için
X = io.mmread("/tmp/A").tolil()
print (X.shape, X[2,3])
(4, 4) 10.0
Yukarı