R ile Yüksek Performanslı Programlama — 5
Büyük Veri Setlerini Sınırlı RAM ile İşlemek
Bu yazımızda veri dönüşümlerini kullanarak RAM’in yükünü azaltacak yöntemleri öğreneceğiz. RAM dostu veri tiplerini öğrenecek ve bunların etkilerini inceleyeceğiz. Performansımızı 20 kata kadar arttıracak yöntemler öğreneceğiz!
İlk olarak farklı veri tiplerinin kapladıkları alanları görelim.
> object.size(logical(1e6))
4000048 bytes> object.size(integer(1e6))
4000048 bytes> object.size(numeric(1e6))
8000048 bytes> object.size(complex(1e6))
16000048 bytes> object.size(rep.int(NA_character_, 1e6))
8000048 bytes> object.size(raw(1e6))
1000048 bytes> object.size(vector("list", 1e6))
8000048 bytes
Gördüğünüz gibi hepsi 40'lı bir bit değeri ile bitiyor. Buradaki 40 bitin varlığının sebebi hepsinin vektör olması ve vektörlerin ‘Header’larının 40 bitlik bir yer kaplaması. 40 bitlik boyutu hepsinden çıkarıp kalan miktarı vektör uzunluklarına böldüğümüzde ‘raw’ değerlerin 1 bit, logical ve integer değerlerinin 4 bit, numeric değerlerin 8 bit ve complex değerlerin 16 bitlik yer kapladıklarını görüyoruz.(Bunlar 64 bitlik R kullanımında alınan değerlerdir. 32 bitlik R kullanıyorsanız bu değerler farklılık gösterebilir.)
Buradaki basit karşılaştırmadan mümkün olan yerde numeric yerine integer değişken kullanmanın daha etkili olduğunu ve en az yer kaplayan veri tipinin raw olduğunu gördük. Yazının sonlarında ‘bit’ veri tipini tanıyacak ve logical için daha etkili bir alternatif öğrenmiş olacağız.
Sparse Matrix Kullanımı
Sparse Matrix (Seyrek Matris)’ler içindeki değerlerin büyük bir bölümü sıfır olan matrislerdir. O kadar değerin sıfır olması durumu da seyrek rastlanılan bir durum değildir. Bu tarz matrisleri normal bir matris formunda tatmak yerine seyrek matris formunda tutmak performansı artırıcı bir etkiye sahip olacaktır. Şimdi bunu örnekle görelim.
> library(Matrix)
> n <- rnorm(1e6)
> n[sample.int(1e6, 7e5)] <- 0
> m.dense <- Matrix(n, 1e3, 1e3, sparse = FALSE)
> m.sparse <- Matrix(n, 1e3, 1e3, sparse = TRUE)> class(n)
[1] "numeric"> object.size(n)
8000048 bytes> class(m.dense)
[1] "dgeMatrix"
attr(,"package")
[1] "Matrix"> object.size(m.dense)
8001176 bytes> class(m.sparse)
[1] "dgCMatrix"
attr(,"package")
[1] "Matrix"> object.size(m.sparse)
3605504 bytes
m.sparse değişkeni m.dense değişkeninden çok daha küçük boyutta. Bu da seyrek matrislerin ne kadar kullanışlı olduğunu gösteriyor.
Seyrek matrisler mantıksal değişkenleri tutmak için de idealdir. Mantıksal değişkenleri 0 ve 1 olarak gruplar ve daha çok gözlemlenen gruba da 0 değerini atarsak artık seyrek matrisin getirdiği avantajları kullanabiliriz demektir. Örnek verirsek,
> l <- sample(c(FALSE, TRUE), 1e6, TRUE, c(0.7, 0.3))
> m2.dense <- Matrix(l, 1e3, 1e3, sparse = FALSE)
> m2.sparse <- Matrix(l, 1e3, 1e3, sparse = TRUE)> object.size(l)
4000048 bytes> object.size(m2.dense)
4001176 bytes> object.size(m2.sparse)
2413872 bytes
Bit Vectors
Mantıksal değişkenleri saklamanın çok daha etkili bir yöntemi onları bit şeklinde saklamaktır. Mantıksal değişkenler 32 bitlik yer tutarken bit vektörleri sadece 1 bit yer tutarlar. Bu da 32 katlık bir değişim demektir. Burada dikkat etmemiz gereken nokta bit tipindeki vektörlerin NA değerlerini tutamıyor olmasıdır. Yani eğer vektörümüzde NA değerleri varsa ve onlardan kurtulamıyorsak bite çeviremeyiz.
> library(bit)
Attaching package bit
package:bit (c) 2008-2012 Jens Oehlschlaegel (GPL-2)
creators: bit bitwhich
coercion: as.logical as.integer as.bit as.bitwhich which
operator: ! & | xor != ==
querying: print length any all min max range sum summary
bit access: length<- [ [<- [[ [[<-
for more help type ?bit
Attaching package: ‘bit’
The following object is masked from ‘package:data.table’:
setattr
The following object is masked from ‘package:rJava’:
clone
The following object is masked from ‘package:base’:
xor
> l <- sample(c(TRUE, FALSE), 1e6, TRUE)
> head(l)
[1] FALSE TRUE FALSE TRUE FALSE TRUE
> b <- as.bit(l)
> head(b)
[1] FALSE TRUE FALSE TRUE FALSE TRUE
attr(,"vmode")
[1] "boolean"
> object.size(l)
4000048 bytes
> object.size(b)
126440 bytes
Görüldüğü üzere RAM tasarrufu açısından şu ana kadar gördüğümüz en etkili yöntemlerden birisi logical -> bit dönüşümü oldu.
Şimdi bir de mantıksal ve bit değişkenleriyle yapılan işlemlerin performanslarını karşılaştıralım.
> library(microbenchmark)
> l2 <- sample(c(TRUE, FALSE), 1e6, TRUE)
> b2 <- as.bit(l2)
> microbenchmark(!l, !b)
Unit: microseconds
expr min lq mean median uq max neval
!l 1516.4 1616.10 1654.105 1654.9 1700.7 1828.4 100
!b 51.5 56.15 78.797 82.1 95.5 159.7 100
>
> microbenchmark(l == l2, b == b2)
Unit: microseconds
expr min lq mean median uq max neval
l == l2 1534.9 1549.6 1572.858 1557.1 1568.75 2309.7 100
b == b2 62.2 68.5 96.275 73.7 123.45 230.4 100
Median değerlerine baktığımızda yaklaşık 20 katlık bir performans farkı görüyoruz. Harika bir ilerleme!
R ile ilgili daha fazla içerik ve video dersler için R Programlama ile Veri Bilimi isimli Udemy kursumu inceleyebilirsiniz.