Tarifler: Verileri Dönüştürme

Share on:

Bu tarifte R’ın en güçlü özelliklerinden olan fonksiyonel programaya dair kullanışlı bilgiler sunulmaktadır. Fonksiyonel programlama loop olarak adlandırılan for, while gibi program döngüleri kullanmadan daha az kod satırıyla ve çoğunlukla daha hızlı bir biçimde veri üzerinde işlem yapmamıza yardım eden fonksiyon yapılarıdır. Fonksiyonel programlamanın temellerini öğrenmek kod yazımını hızlandıracak ve daha pratik, anlaşılır kodlar yazmanızı sağlayacaktır.

Bir vektördeki veriyi nasıl gruplara ayırabilirim?

Elinizde bir vektör var ve onu belli bir kritere göre gruplara ayırmak istiyorsunuz. split fonksiyonu bu işe uygun. R’da standart gelen mtcars veriseti üzerinden örnek vereyim. mtcars verisetinde araçların bir galon yakıtla aldığı mesafeyi gösteren mpg değişkeni var. Bu değişkeni araçların silindir sayısını gösteren ctl değişkenine göre gruplayalım:

split(mtcars$mpg, mtcars$cyl)
## $`4`
##  [1] 22.8 24.4 22.8 32.4 30.4 33.9 21.5 27.3 26.0 30.4 21.4
## 
## $`6`
## [1] 21.0 21.0 21.4 18.1 19.2 17.8 19.7
## 
## $`8`
##  [1] 18.7 14.3 16.4 17.3 15.2 10.4 10.4 14.7 15.5 15.2 13.3 19.2 15.8 15.0

Bir fonksiyonu listemde bulunan bütün elemanlara nasıl uygularım?

R dili functional programming grubuna girmektedir. Yani karmaşık işleri görmeye yarayan fonksiyonlar dilin yapısında tanımlanmıştır. Böylelikle for, while gibi döngülerle yapılacak işler bir fonksiyonlar kolayca yapılabilir. lapply ve sapply fonksiyonları bu gruptandır. Her ikisi de girdi olarak işlem yapılacak listeyi ve kullanmak istediğimiz fonksiyonu alır.

Örnek olarak split fonksiyonu çıktı olarak liste üretmektedir. Bu listeyi bir değişkene kaydedip gruplar üzerinde analiz yapabiliriz.

mpc <- split(mtcars$mpg, mtcars$cyl)
lapply(mpc, mean)
## $`4`
## [1] 26.66364
## 
## $`6`
## [1] 19.74286
## 
## $`8`
## [1] 15.1
sapply(mpc, mean)
##        4        6        8 
## 26.66364 19.74286 15.10000

Aslında lapply ve sapply hemen hemen aynı fonksiyonlardır. sapply’ın başındaki s simplified anlamına gelmektedir. lapply liste olarak çıktı verirken sapply vektör olarak çıktı vermektedir. sapply fonksiyonun ürettiği değerlere göre matris veya liste olarak ta çıktı verebilir:

sapply(mpc, summary)
##                4        6     8
## Min.    21.40000 17.80000 10.40
## 1st Qu. 22.80000 18.65000 14.40
## Median  26.00000 19.70000 15.20
## Mean    26.66364 19.74286 15.10
## 3rd Qu. 30.40000 21.00000 16.25
## Max.    33.90000 21.40000 19.20

Şayet fonksiyon R’ın yapılandırdığı bir biçimde sonuç veriyorsa bu sonucu listede saklamak daha doğru olacağından lapply kullanılmalıdır:

lapply(mpc, t.test)
## $`4`
## 
##  One Sample t-test
## 
## data:  X[[i]]
## t = 19.609, df = 10, p-value = 2.603e-09
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  23.63389 29.69338
## sample estimates:
## mean of x 
##  26.66364 
## 
## 
## $`6`
## 
##  One Sample t-test
## 
## data:  X[[i]]
## t = 35.936, df = 6, p-value = 3.097e-08
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  18.39853 21.08718
## sample estimates:
## mean of x 
##  19.74286 
## 
## 
## $`8`
## 
##  One Sample t-test
## 
## data:  X[[i]]
## t = 22.07, df = 13, p-value = 1.093e-11
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  13.62187 16.57813
## sample estimates:
## mean of x 
##      15.1

Bir fonksiyonu matris veya data frame satırlarına/sütunlarına nasıl uygularım?

Bir fonksiyonu matris veya data frame üzerine uygulayabilmek için apply fonksiyonunu kullanırız. Aşağıda önce bir matris üretip ardından satır ortalamalarını hesaplayalım:

mtx <- matrix(sample(100:1000,20),4,5)
rownames(mtx) <- c("A","B","C","D")
colnames(mtx) <- c("X1","X2","X3","X4","X5")
apply(mtx, 1, mean)
##     A     B     C     D 
## 728.6 503.6 385.0 501.2
apply(mtx, 2, mean)
##     X1     X2     X3     X4     X5 
## 673.50 341.00 464.75 633.50 535.25

apply ilk argüman olarak hesaplama yapmak istediğimiz verisetini, ikinci argüman olarak verisetinin satırlarında işlem yapacaksak 1, sütunlarında işlem yapacaksak 2 değerini ve son olarakta fonksiyonu almaktadır.

Aynı işlemi data frame üzerinde de yapabiliriz. Ancak numerik olmayan sütunları önceden ayıklamamız gerekir. Numerik olmayan veri tipleri olması halinde farklı sonuçlar ortaya çıkabilir.

apply(mtcars,2,mean)
##        mpg        cyl       disp         hp       drat         wt       qsec 
##  20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
##         vs         am       gear       carb 
##   0.437500   0.406250   3.687500   2.812500

lapply ve sapply girdi olarak liste kabul ettiğinden ve data frame bir çeşit liste olduğundan bu fonksiyonlarla kullanılabilir. Çıktı türleri bir önceki maddede analatıldığı gibi olacaktır.

str(lapply(mtcars,mean))
## List of 11
##  $ mpg : num 20.1
##  $ cyl : num 6.19
##  $ disp: num 231
##  $ hp  : num 147
##  $ drat: num 3.6
##  $ wt  : num 3.22
##  $ qsec: num 17.8
##  $ vs  : num 0.438
##  $ am  : num 0.406
##  $ gear: num 3.69
##  $ carb: num 2.81
sapply(mtcars,mean)
##        mpg        cyl       disp         hp       drat         wt       qsec 
##  20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
##         vs         am       gear       carb 
##   0.437500   0.406250   3.687500   2.812500

lapply ve sapply fonksiyonlarıyla veriseti üzerinde kendi üreteceğimiz fonksiyonları da kullanabiliriz. Aşağıdaki örnekte lapply fonksiyonu ilk eleman olarak mtcars verisetindeki sütun isimlerini alıyor. Daha sonra bu sütun isimleri ürettiğimiz bir fonksiyona argüman olarak giriyor. Çıktı olarak regresyon katsayılarını alıyoruz:

lms <- lapply(colnames(mtcars)[-1], function(x) {
  form <- paste0("mpg ~ ", x)
  lm(form, data=mtcars)$coefficient
})
head(lms,3)
## [[1]]
## (Intercept)         cyl 
##    37.88458    -2.87579 
## 
## [[2]]
## (Intercept)        disp 
## 29.59985476 -0.04121512 
## 
## [[3]]
## (Intercept)          hp 
## 30.09886054 -0.06822828

Bir fonksiyonu split kullanmadan veride bulunan gruplara nasıl uygularım?

apply, lapply, sapply … evet bitmedi. Sırada tapply var. Bir fonksiyonu gruplara nasıl uygularım diye düşünüyorsanız, tapply bu işin yollarından birisi. Ancak tapply kullanabilmek için verisetinde gruplamayı yapabilmek için faktör türünden bir veriye ihtiyacımız var. Geçici bir çöcüm olarak mtcars verisetindeki cyl değişkenini faktör olarak kullanabiliriz:

tapply(mtcars$mpg, as.factor(mtcars$cyl), summary)
## $`4`
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   21.40   22.80   26.00   26.66   30.40   33.90 
## 
## $`6`
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   17.80   18.65   19.70   19.74   21.00   21.40 
## 
## $`8`
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   10.40   14.40   15.20   15.10   16.25   19.20

Yukarıda bahsettiğimiz diğer fonksiyonlar gibi tapply çıktısı da sonuca göre değişecektir. Yukarıda her grup için tek bir sonuç geldiğinden vektör elde ettik. Birden fazla sonuç olursa çıktı liste olacaktır.

tapply(mtcars$mpg, as.factor(mtcars$cyl), range)
## $`4`
## [1] 21.4 33.9
## 
## $`6`
## [1] 17.8 21.4
## 
## $`8`
## [1] 10.4 19.2

Aynı işlemi by ile de gerçekleştirebiliriz. Lakin tapply girdi olarak vektör alırken, by fonksiyonu data frame almaktadır. Bu sebeple kullanacağımız fonksiyon da data frame’e uygulanabilecek bir fonksiyon olmalıdır. Aksi halde hata üretecektir.

by(mtcars[c(1,3,6)], mtcars$cyl, summary)
## mtcars$cyl: 4
##       mpg             disp              wt       
##  Min.   :21.40   Min.   : 71.10   Min.   :1.513  
##  1st Qu.:22.80   1st Qu.: 78.85   1st Qu.:1.885  
##  Median :26.00   Median :108.00   Median :2.200  
##  Mean   :26.66   Mean   :105.14   Mean   :2.286  
##  3rd Qu.:30.40   3rd Qu.:120.65   3rd Qu.:2.623  
##  Max.   :33.90   Max.   :146.70   Max.   :3.190  
## ------------------------------------------------------------ 
## mtcars$cyl: 6
##       mpg             disp             wt       
##  Min.   :17.80   Min.   :145.0   Min.   :2.620  
##  1st Qu.:18.65   1st Qu.:160.0   1st Qu.:2.822  
##  Median :19.70   Median :167.6   Median :3.215  
##  Mean   :19.74   Mean   :183.3   Mean   :3.117  
##  3rd Qu.:21.00   3rd Qu.:196.3   3rd Qu.:3.440  
##  Max.   :21.40   Max.   :258.0   Max.   :3.460  
## ------------------------------------------------------------ 
## mtcars$cyl: 8
##       mpg             disp             wt       
##  Min.   :10.40   Min.   :275.8   Min.   :3.170  
##  1st Qu.:14.40   1st Qu.:301.8   1st Qu.:3.533  
##  Median :15.20   Median :350.5   Median :3.755  
##  Mean   :15.10   Mean   :353.1   Mean   :3.999  
##  3rd Qu.:16.25   3rd Qu.:390.0   3rd Qu.:4.014  
##  Max.   :19.20   Max.   :472.0   Max.   :5.424

Verisetini gruplara ayırarak her iki gruba regresyon analizi de uygulayabiliriz:

by(mtcars, mtcars$am, function(x) lm(mpg ~ disp + wt, data = mtcars)$coefficients)
## mtcars$am: 0
## (Intercept)        disp          wt 
## 34.96055404 -0.01772474 -3.35082533 
## ------------------------------------------------------------ 
## mtcars$am: 1
## (Intercept)        disp          wt 
## 34.96055404 -0.01772474 -3.35082533

Bir fonksiyonu bir dizi vektöre veya listeye nasıl uygularım?

Pek çok R fonksiyonu vektör bazlı çalışır. Yani x ve y iki vektör olarak düşünelim. x + y işleminde her iki vektörün elemanları sırayla toplanmakta ve vektör olarak çıktılanmaktadır. Ancak vektörler üzerinde işlem yapmayan fonksiyonlar da vardır. Bu fonksiyonları vektörler üzerinde çalıştırabilmek için ...pply ailesinden mapply fonksiyonunu yardıma çağırabiliriz.

Mesela aşağıda iki sayının en büyük ortak bölenini bulan bir fonksiyon var, gcd. Bu fonksiyona girdi, olarak iki vektör verirsek hata oluşur. Ancak mapply ile bunu başarabiliriz:

gcd <- function(a,b) {
  if (b == 0) return(a)
  else return(gcd(b, a %% b))
}

mapply(gcd,c(1537,1653,3877), c(6731,7751,3479))
## [1] 53  1  1

Burada mapply her iki vektörün sırasıyla 1., 2. ve 3. elemanlarını gcd fonksiyonuna girdi olarak vermekte ve sonucu vektör olarak sunmaktadır.