Tarifler: Verileri Dönüştürme
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?
- Bir fonksiyonu listemde bulunan bütün elemanlara nasıl uygularım?
- Bir fonksiyonu matris veya data frame satırlarına/sütunlarına nasıl uygularım?
- Bir fonksiyonu
split
kullanmadan veride bulunan gruplara nasıl uygularım? - Bir fonksiyonu bir dizi vektöre veya listeye nasıl uygularım?
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.