👩🏻‍💻 Programlama

🎯 Ödevin Amacı

Bu ödevin amacı programlama kodları ile ilgili öğrendiklerimi özetlemektir.

Temel Kavramlar

Bir fonksiyonun temel bileşenlerini anlamak, hem R’ın kütüphanelerindeki mevcut fonksiyonları etkin bir şekilde kullanmak hem de kendi çözümlerimizi geliştirmek için önemlidir.

Standart normal dağılımdan rastgele sayılar üreten rnorm fonksiyonunu bir örnek olarak kullanarak bir R fonksiyonunun temel yapısını inceleyelim:

rnorm(n, mean = 0, sd = 1)

rnorm fonksiyonu için formal argümanlar n, mean ve sd’dir. Bu argümanlar kullanıcının hangi parametreleri ayarlayabileceğini belirtir.

Default değerler fonksiyon tanımında bir argümana atanan başlangıç değerleridir. mean = 0 ve sd = 1 gibi varsayılan değerler buna örnektir. Kullanıcı bu değerleri belirtmediğinde fonksiyon bu varsayılan değerlerle çalışır. Örneğin, rnorm(n = 5) , mean ve sd argümanları belirtilmediği için otomatik olarak mean = 0 ve sd = 1 değerlerini kullanır.

Bir fonksiyonun argümanlarını, varsayılan değerlerini ve kullanım amacını tam olarak anlamanın en güvenilir yolu, R’ın yardım dokümantasyonuna başvurmaktır. Konsola ?rnorm yazarak erişilebilen bu dokümanlar, bir fonksiyonun nasıl çalıştığını anlamak için önemli ve incelenmesi gereken bir kaynaktır.

Argüman isimleri belirtilmediğinde, R, fonksiyonun değerlerini fonksiyon tanımındaki argüman sırasına göre atar. İlk değer ilk argümana, ikinci değer ikinci argümana atanır ve bu şekilde devam eder. Bunun yanında kullanıcı argümanların isimlerini ve değerlerini yazarak kendisi bir sıralama da belirleyebilir.

Tekrarlanan görevleri otomatikleştirmek, karmaşık iş akışlarını basitleştirmek ve analizleri standartlaştırmak için özel fonksiyonlar yazmak, R programlamanın temel yetkinliklerinden biridir. Kendimizi sürekli olarak aynı kod bloklarını kopyalayıp yapıştırırken buluyorsak bu genellikle bir fonksiyon yazmanız gerektiğine işaret eder. Aşağıda fonksiyon yazma adımları yer almaktadır:

  • Bir fonksiyon, function() komutu kullanılarak oluşturulur ve bir R nesnesi olarak değişkene atanır.

  • Fonksiyon gövdesine ({ } arasına) R komutları eklenerek fonksiyona işlevsellik kazandırılır.

  • Fonksiyonun esnekliğini artırmak için argümanlara varsayılan değerler atanabilir. Bu, kullanıcının bu argüman için bir değer girmemesi durumunda fonksiyonun standart bir davranış sergilemesini sağlar.

R’da fonksiyonlar, birinci sınıf nesneler (first-class objects) olarak kabul edilir. Bu onların diğer R nesneleri (vektörler, listeler vb.) gibi ele alınabileceği anlamına gelir. Bu özelliğin en önemli pratik sonuçları, fonksiyonların başka fonksiyonlara argüman olarak aktarılabilmesi (lapply() gibi döngü fonksiyonlarında) ve bir fonksiyonun içinde başka bir fonksiyonun tanımlanabilmesidir (iç içe fonksiyonlar).

R’daki ... (üç nokta) argümanı, özellikle mevcut fonksiyonların yeteneklerini genişletmek veya kullanıcıdan ne kadar girdi geleceği bilinmeyen durumları yönetmek için kullanılır. Kendi yazdığımız fonksiyon, argüman olarak aldığı ... içindeki tüm ek girdileri doğrudan başka bir fonksiyona aktarır. Bazı fonksiyonlar ise kaç adet argüman alacaklarını önceden bilemezler. Örneğin paste() veya cat() fonksiyonları, birleştirilecek veya yazdırılacak eleman sayısını sınırlamaz; ... argümanı, bu tür fonksiyonların istenilen sayıda girdiyi kabul etmesini sağlar. ... argümanını kullanmanın potansiyel hata kaynağı, ...’dan sonra gelen argümanların kullanım şeklidir. Bu argümanlar, konumlarına göre veya kısmi isimlerle eşleştirilemezler dolayısıyla açıkça ve tam olarak isimlendirilmeleri gerekir.

Örneğin paste() fonksiyonunda sep argümanı ...’dan sonra gelir:

DOĞRU: ‘sep’ argümanı tam olarak isimlendirilmiş

paste("a", "b", sep = ":")
## [1] "a:b"

YANLIŞ: Kısmi eşleştirme (‘se’) çalışmaz, ‘se = “:”’ ifadesi ‘…’ içinde bir karakter olarak yorumlanır

paste("a", "b", se = ":")
## [1] "a b :"

Fonksiyon tasarlama:

  • Fonksiyonun ne yapacağını, hangi girdileri (argümanları) alacağını ve ne tür bir çıktı üreteceğini netleştirin.

  • Fonksiyonun mantığını, sabit değerler veya örnek bir veri seti kullanarak doğrudan R konsolunda adım adım çalıştırın ve test edin.

  • Çalışan komut satırlarınızı fonksiyon_adi <- function() { ... } yapısının içine kopyalayın.

  • Kodunuzdaki sabit değerleri, fonksiyonun argümanları olarak tanımlayın ve bu argümanları kodun ilgili yerlerine yerleştirin.

Aşağıda, bu ilkeleri uygulayan iki uygulama bulunmaktadır.

Uygulama 1: hesapla_geometrik_ortalama_v2

Bu fonksiyon, bir vektörün geometrik ortalamasını hesaplar ve sonuçları karşılaştırır.

hesapla_geometrik_ortalama_v2 <- function(x) {
    # Hata Kontrolü: Geometrik ortalama için girdilerin pozitif olması gerekir.
    if (any(x <= 0)) {
        stop("Geometrik ortalama hesaplanacak tüm değerler pozitif olmalıdır.")
    }

    n <- length(x)
    
    # 1. Doğrudan Çarpım Yöntemi
    g_mean_carpim <- prod(x)^(1/n)
    
    # 2. Logaritmik Yöntem (Sayısal olarak daha kararlı)
    g_mean_log <- exp(mean(log(x)))
    
    # 3. Harici Paketlerle Sonuçların Karşılaştırılması
    g_mean_psych <- psych::geometric.mean(x)
    
    # Sonuçları düzenli bir veri çerçevesi olarak oluşturma
    sonuclar <- data.frame(
        Yontem = c("Doğrudan Çarpım", "Logaritmik", "psych::geometric.mean()"),
        Geometrik_Ortalama = c(g_mean_carpim, g_mean_log, g_mean_psych),
        row.names = NULL # Temiz bir çıktı için varsayılan satır isimlerini engeller
    )

    print(sonuclar)
    
    # Sonucu atama imkanı sunarken konsola tekrar yazdırmamak için 'invisible' kullanılır.
    return(invisible(sonuclar))
}
x_test <- 200:250
hesapla_geometrik_ortalama_v2(x_test)
##                    Yontem Geometrik_Ortalama
## 1         Doğrudan Çarpım           224.5172
## 2              Logaritmik           224.5172
## 3 psych::geometric.mean()           224.5172

Uygulama 2: Çoklu veri seti oluşturma ve dışa aktarma

Bu örnek, iç içe for döngüleri, sonuçları geçici olarak saklamak için list kullanımı (df_list) ve dinamik dosya adları oluşturmak için paste() fonksiyonunun nasıl bir arada kullanılabileceğini göstermektedir. Bu tür bir fonksiyon, manuel olarak yapılması saatler sürebilecek tekrarlı bir görevi tek bir komutla gerçekleştirir.

fonksiyon_adi <- function(sayi=3, satir=c(5,5,5), sutun=c(10,10,10)){
    # Veri setlerini geçici olarak saklamak için boş bir liste oluşturulur
    df_list <- list() 
    
    # Dış döngü, oluşturulacak her bir veri seti için çalışır
    for(i in 1:sayi){
        # Belirtilen boyutlarda sıfırlardan oluşan bir veri çerçevesi oluşturulur
        df_list[[i]] <- data.frame(matrix(0, nrow=satir[i], ncol=sutun[i]))
        
        # İç döngü, veri çerçevesinin sütunlarını doldurur
        for(j in 1:sutun[i]){
            # Her sütun standart normal dağılıma uygun rastgele sayılarla doldurulur
            df_list[[i]][,j] <- round(rnorm(satir[i], 0, 1), 2)
        }
        
        # DİKKAT: Dosya yazma işlemi, ilgili veri çerçevesi tamamen oluşturulduktan sonra, yani dış döngünün sonunda yapılmalıdır.
        # Bu, gereksiz ve yavaş disk işlemlerini önler.
        writexl::write_xlsx(df_list[[i]], paste("veri", i, ".xlsx", sep=""))
    }
}

Bu örnekleri asla bakmadan yazamam ama belki bir gün bakmadan yazarsam ve bu cümlemi görürsem aşırı mutlu olurum.

Koşullu İfadeler

Koşullu ifadeler analizlerin belirli kriterlere veya veri özelliklerine göre adapte olmasını sağlayan bir mekanizmadır.

if-else

if, else ve else if kombinasyonu, R’da bir koşulu test etmek ve testin sonucuna göre belirli bir kod bloğunu çalıştırmak için kullanılır.

  • if: Yalnızca koşul doğru olduğunda bir işlem gerçekleştirir.

  • if-else: Koşul doğruysa bir işlemi, yanlışsa başka bir işlemi gerçekleştirir.

  • if-else if-else: Birden fazla koşulu sıralı olarak test eder.

Bu yapının pratik uygulamasını bir öğrenci not değerlendirme örneği üzerinden inceleyebiliriz. Geçme notunu bir değişkende saklayalım. Örneğin 75 alan bir öğrencinin durumu basit bir if ifadesiyle belirlenebilir:

not <- 75
gecme_notu <- 65
if (not >= gecme_notu) {
    print("Basarılı") # print sadece kategorik/karakter değer yazdırır. cat () ise nesne değeri de yazdırır
}
## [1] "Basarılı"

Not 60 olduğunda, yukarıdaki kod bir çıktı üretmez. Bu durumu yönetmek için else eklenir:

not <- 60
gecme_notu <- 65
if (not >= gecme_notu) {
    print("Basarılı")
} else {
    print("Basarisiz")
}
## [1] "Basarisiz"

Daha karmaşık ve çok kategorili senaryolarda else if zinciri kullanılır. Bu yapı, not aralıklarına göre harf notu ataması gibi durumlar için idealdir ve tüm not aralıklarını kapsayacak şekilde genişletilebilir:

x <- 75
if (x >= 90) {
    print("AA")
} else if (x >= 80) {
    print("BA")
} else if (x >= 70) {
    print("BB")
} else if (x >= 65) {
    print("CB")
} else if (x >= 60) {
    print("CC")
} else if (x >= 50) {
    print("DD")
} else if (x >= 30) {
    print("FD")
} else {
    print("FF")
}
## [1] "BB"

all ()- any ()

Standart bir if ifadesi, tek bir mantıksal değer (TRUE veya FALSE) bekler. Bu durum, birden fazla eleman içeren vektörlerle çalışırken bir zorluk teşkil eder. R, bu sorunu çözmek için all() ve any() fonksiyonlarını sunar.

all(): Bir vektördeki tüm elemanların belirtilen koşulu karşılayıp karşılamadığını test eder. Eğer tüm elemanlar koşulu sağlıyorsa TRUE, aksi takdirde FALSE döndürür.

x <- c(1, 2, -3, 4)
if (all(x > 0)) {
    print("tum sayilar 0'dan buyuktur")
} else {
    print("tum sayilar 0'dan buyuk degildir")
}
## [1] "tum sayilar 0'dan buyuk degildir"

any(): Bir vektördeki elemanlardan en az birinin belirtilen koşulu karşılayıp karşılamadığını test eder. Eğer en az bir eleman koşulu sağlıyorsa TRUE, aksi takdirde FALSE döndürür.

x <- c(1, 2, -3, 4)
if (any(x < 0)) {
    print("nesne en az bir negatif sayi icerir")
} else {
    print("nesne negatif sayi icermez")
}
## [1] "nesne en az bir negatif sayi icerir"

ifelse()

ifelse() fonksiyonu, standart if-else yapısının vektörel karşılığıdır. Vektör girdilerini işlemek ve sonuç olarak bir vektör çıktısı üretmek üzere özel olarak tasarlanmıştır.

Genel sözdizimi şöyledir: ifelse(koşul, Doğru İfade, Yanlış İfade)

İterasyonlar - Döngüler

for Döngüsü: for döngüsü, R’da bir dizi (örneğin bir vektör, liste veya matris) üzerindeki elemanlar arasında yineleme yapmak için kullanılan en yaygın mekanizmadır. Belirlenen sayıda tekrar gerçekleştirir ve her adımda bir ara değişkene dizinin sıradaki elemanını atar. for döngüsünün içinde 1:length(x) de kullanabilirsiniz, seq_along (x) de; seq_along fonksiyonu nesnenin uzunluğunca yazdırır.

Döngüler özellikle sonuçların her adımda birikimli olarak hesaplanması gereken görevler için kritik öneme sahiptir. Örneğin, bir matrisin her bir satırının toplamını hesaplamak ve sonucu yeni bir vektörde saklamak için, for döngüsü her bir satır üzerinde sıralı olarak gezinmek ve toplama işlemini gerçekleştirmek için kullanılabilir:

X <- cbind(a = 1:5, b = 2:6)
Y <- array()
for (i in 1:nrow(X)) {
    Y[i] <- X[i, 1] + X[i, 2]
}
print (Y)
## [1]  3  5  7  9 11

while Döngüsü: while döngüsü başlamadan önce bir koşulu test eder ve bu koşul doğru TRUE olduğu sürece kod bloğunu çalıştırmaya devam eder. Koşul FALSE olduğunda döngü sona erer. Aşağıdaki örnek, bir sayacı 10’dan küçük olduğu sürece artıran basit bir while döngüsünü göstermektedir:

count <- 0
while (count < 10) {
    print(count)
    count <- count + 1
}
## [1] 0
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9

while döngüleri düzgün yazılmazsa sonsuz döngülere neden olabilir. Koşulun bir noktada FALSE olmasını sağlayacak bir nokta içermesi oldukça önemlidir.

break ve next Komutları: break ve next, döngülerin yürütülmesi üzerinde daha hassas kontrol sağlamak için kullanılan temel komutlardır.

  • next: Bu komut, mevcut iterasyonun geri kalanını atlar ve döngünün bir sonraki iterasyonuna geçer. Örneğin, 1’den 6’ya kadar olan sayıları yazdırırken 3’ü atlamak için kullanılabilir.

  • break: Bu komut, belirli bir koşul karşılandığında döngüyü tamamen sonlandırır. Örneğin, 12 kez tekrarlanması planlanan bir döngü, i değeri 3’e ulaştığında durdurulabilir.

İç İçe Döngüler: İç içe döngüler bir döngünün başka bir döngü içine yerleştirilmesiyle oluşturulur. Bu yapı matrisler gibi çok boyutlu veya hiyerarşik veri yapılarının işlenmesi için temel bir tekniktir. Aşağıdaki örnek, 2x3 boyutunda bir matrisin her bir elemanını sırayla yazdırır:

x <- matrix(1:6, 2, 3)
for (i in seq_len(nrow(x))) {
    for (j in seq_len(ncol(x))) {
        print(x[i, j])
    }
}
## [1] 1
## [1] 3
## [1] 5
## [1] 2
## [1] 4
## [1] 6

Veri Manipülasyonu

  • lapply(): Bir liste üzerinde döngü kurar ve her elemana bir fonksiyon uygular, sonuç olarak bir liste döndürür.

  • sapply(): lapply() ile aynı işlevi görür ancak çıktıyı mümkün olduğunca basitleştirir (vektör veya matris gibi).

  • apply(): Bir matris veya dizinin satır ya da sütunları üzerinde bir fonksiyon çalıştırır.

  • tapply(): Bir vektörü faktörlere göre gruplara ayırır ve her grup üzerinde bir fonksiyon uygular.

  • mapply(): lapply() fonksiyonunun çok değişkenli versiyonudur, birden fazla veri yapısı üzerinde paralel olarak çalışır.

  • split(): Veri yapılarını bir faktöre göre alt kümelere ayırmak için kullanılan bir yardımcı fonksiyondur. (Psikolojide de bu anlamda kullanılıyor, örneğin kişiliğin alter kişiliklere bölünmesi gibi - dissosiasyon)

Bu kısa bilgilerden sonra her fonksiyona daha detaylı bakalım:

lapply(): lapply fonksiyonunun çalışma prensibi oldukça basittir: Bir liste (x) alır, listedeki her bir elemana belirtilen fonksiyonu uygular ve sonuçları içeren yeni bir liste döndürür. Aşağıdaki örnekte iki farklı eleman içeren bir listenin her elemanının ortalamasını alıyoruz:

x <- list(a = 1:5, b = rnorm(10))
lapply(x, mean)
## $a
## [1] 3
## 
## $b
## [1] 0.1412257
x$a
## [1] 1 2 3 4 5
x$b
##  [1] -1.8032663  1.1180193 -1.3378644  0.8158406  1.1571149  0.2515800
##  [7]  0.7245854  0.5537014 -0.3076721  0.2402188

Bu örnekte dikkat edilmesi gereken önemli bir nokta, mean fonksiyonunun lapply’e parantez olmadan yani bir nesne gibi argüman olarak geçirilmesidir. Bu durum, R’daki fonksiyonların birinci sınıf nesneler olmasının bir sonucudur. lapply, x listesindeki her bir elemanı (önce 1:5 vektörünü, sonra rnorm(10) vektörünü) alır ve bu elemanı mean fonksiyonuna birinci argüman olarak geçirir.

lapply, uygulanan fonksiyona ek parametreler aktarmak için ... argümanını kullanır. Aşağıdaki örnekte runif fonksiyonuna min ve max argümanları lapply üzerinden iletilmektedir:

x <- 1:4
lapply(x, runif, min = 0, max = 10)
## [[1]]
## [1] 5.925041
## 
## [[2]]
## [1] 5.652907 6.451986
## 
## [[3]]
## [1] 2.3142448 0.7904008 5.4908682
## 
## [[4]]
## [1] 4.802843 4.174071 1.034231 4.269380

İsimsiz/anonim fonksiyonlar lapply gibi fonksiyonlarla birlikte kullanıldığında büyük bir esneklik sağlar. Bu fonksiyonlar bir isimle tanımlanmadan doğrudan lapply çağrısı içinde anında oluşturulur ve sadece o anki işlem için kullanılır. Aşağıdaki örnekte, iki matris içeren bir listenin her bir matrisinden sadece ilk sütunu çıkaran anonim bir fonksiyon kullanılmıştır:

x <- list(a = matrix(1:4, 2, 2), b = matrix(1:6, 3, 2))
lapply(x, function(elt) { elt[,1] })
## $a
## [1] 1 2
## 
## $b
## [1] 1 2 3

sapply(): sapply fonksiyonu, lapply’nin bir uzantısı olarak düşünülebilir. Temel amacı lapply ile aynı işlemi yapmak ancak sonuçları mümkün olduğunca daha kullanışlı bir veri yapısına (genellikle bir vektör veya matris) basitleştirmektir. sapply veri setindeki her sütunda istediğimiz şeyi uygular. NA olduğunda ne yapacağını bilemez.

sapply’nin basitleştirme algoritması şu kurallara dayanır:

  • Eğer sonuç, her elemanı 1 uzunluğunda olan bir liste ise vektör döndürür.

  • Eğer sonuç, her elemanı aynı uzunlukta (>1) vektörler olan bir liste ise matris döndürür.

  • Bu kurallar uygulanamıyorsa, lapply gibi bir liste döndürür.

Aşağıdaki kod lapply ve sapply arasındaki farkı net bir şekilde göstermektedir. Her iki fonksiyon da aynı veri üzerinde çalıştırılarak geçerli bir karşılaştırma yapılır:

x <- list(a = 1:4, b = rnorm(10), c = rnorm(20, 1), d = rnorm(100, 5))

# lapply çıktısı
lapply(x, mean)
## $a
## [1] 2.5
## 
## $b
## [1] 0.01396625
## 
## $c
## [1] 0.9715022
## 
## $d
## [1] 5.013248
x$a
## [1] 1 2 3 4
x$b
##  [1] -0.49226030 -0.06392351  0.78021473  1.15500022 -0.05036885  0.30407561
##  [7] -0.18118498 -1.80760641 -0.60520118  1.10091717
x$c
##  [1] -0.05971664  0.85176400  1.30702987  0.22578095  0.55102634  2.04928272
##  [7]  1.39246844 -0.17649720  1.57598152  1.96481590 -0.13283627  0.66535616
## [13]  0.99505836  0.29508474  2.17886080  0.37179991  0.53655292  2.58845796
## [19]  1.19632093  1.05345275
x$d
##   [1] 5.537001 5.305254 6.053580 4.251660 3.683680 5.415274 6.991016 4.485191
##   [9] 5.423172 5.917712 5.115880 6.702921 4.517690 5.822840 5.507308 3.759658
##  [17] 5.423304 4.676342 5.826799 5.685579 5.463887 4.188835 4.789117 5.194599
##  [25] 6.227388 4.837814 4.532059 3.137714 4.576625 5.296899 6.541633 3.831409
##  [33] 5.504963 6.658999 5.939947 4.853988 3.499069 5.072516 5.638252 4.928784
##  [41] 6.280831 6.332559 4.971662 4.976141 4.332640 3.971132 2.488610 4.232301
##  [49] 3.457664 6.307306 2.815280 5.873478 4.415726 6.009798 4.740273 5.178686
##  [57] 4.160577 6.377033 5.249920 4.486004 7.358989 4.332710 4.974997 3.966184
##  [65] 6.125716 6.537541 5.138973 4.469182 3.494731 6.900159 3.786910 4.703171
##  [73] 4.155066 3.144329 6.226345 3.508729 6.038548 4.602794 6.118706 3.020241
##  [81] 4.904680 4.783315 5.458609 5.299040 3.538304 5.771802 4.839185 5.287712
##  [89] 4.565974 5.805470 4.428336 4.353514 5.528330 4.972386 5.464552 5.931597
##  [97] 4.854560 5.697585 3.661877 4.099965
# sapply çıktısı (basitleştirilmiş vektör)
sapply(x, mean)
##          a          b          c          d 
## 2.50000000 0.01396625 0.97150221 5.01324794

Bunun bu kadar uzun olmaması gerekiyordu ama silemiyorum.

Bu kısmın devamındaki fonksiyonları derste uygulamadık ama anladığım kadarıyla yazacağım.

split(): split fonksiyonunun temel görevi, bir vektör veya veri çerçevesini bir faktör değişkenine göre gruplara ayırarak bir liste oluşturmaktır. Her liste elemanı faktörün bir seviyesine karşılık gelen veri alt kümesini içerir.

set.seed(10)
x <- c(rnorm(10), runif(10), rnorm(10, 5)) # 30 elemanlı vektor
f <- gl(3, 10) # 3 kategorili bagimsiz değişken
split(x, f)
## $`1`
##  [1]  0.01874617 -0.18425254 -1.37133055 -0.59916772  0.29454513  0.38979430
##  [7] -1.20807618 -0.36367602 -1.62667268 -0.25647839
## 
## $`2`
##  [1] 0.8647212 0.6153524 0.7751099 0.3555687 0.4058500 0.7066469 0.8382877
##  [8] 0.2395891 0.7707715 0.3558977
## 
## $`3`
##  [1] 5.089347 4.045056 4.804850 5.925521 5.482979 4.403689 2.814713 4.325134
##  [9] 2.880939 3.734802
# Her grubun ortalamasını hesapla
lapply(split(x, f), mean)
## $`1`
## [1] -0.4906568
## 
## $`2`
## [1] 0.5927795
## 
## $`3`
## [1] 4.350703

tapply(): tapply fonksiyonu, split ve lapply kombinasyonunun daha doğrudan ve kompakt bir alternatifidir. Temel amacı, bir vektörü (x) bir faktöre göre gruplamak ve her gruba belirtilen fonksiyonu uygulamaktır.

Yukarıdaki split + lapply örneğinin tapply ile tek satırda nasıl yazılabileceğini görelim:

x <- c(rnorm(10), runif(10), rnorm(10, 1))
f <- gl(3, 10)
tapply(x, f, mean)
##          1          2          3 
## -0.4447055  0.4161593  0.9242280

tapply özellikle veri çerçeveleri üzerinde çalışırken oldukça kullanışlıdır. Aşağıdaki örnekte bir veri çerçevesindeki (df) boy verileri, cinsiyet faktörüne göre gruplanarak hem ortalaması hem de sıralanmış boy listesi hesaplanmıştır:

# Örnek veri çerçevesi
isim <- c("Ali","Elif","Su","Deniz","Aras","Berk","Can","Ece","Efe","Arda")
boy <- c(160,165,170,155,167,162,169,158,160,164)
cinsiyet <- factor(c("erkek","kadin","kadin","kadin","erkek", "erkek","erkek","kadin","erkek","erkek"))
df <- data.frame(isim=isim, boy=boy, cinsiyet=cinsiyet)

# Cinsiyete göre boy ortalaması
tapply(df$boy, df$cinsiyet, mean)
##    erkek    kadin 
## 163.6667 162.0000
# Cinsiyete göre sıralanmış boy listesi
tapply(df$boy, df$cinsiyet, sort)
## $erkek
## [1] 160 160 162 164 167 169
## 
## $kadin
## [1] 155 158 165 170

MARGIN: apply fonksiyonu üç temel argüman alır: x (bir matris veya dizi), MARGIN (işlemin uygulanacağı boyut) ve uygulanacak fonksiyon.

  • MARGIN = 1: İşlemin satırlar üzerinde yapılacağını belirtir. Fonksiyon, matrisin her bir satırına ayrı ayrı uygulanır.

  • MARGIN = 2: İşlemin sütunlar üzerinde yapılacağını belirtir. Fonksiyon, matrisin her bir sütununa ayrı ayrı uygulanır.

Aşağıda 20 satır ve 10 sütundan oluşan rastgele bir matris oluşturalım:

x <- matrix(rnorm(200), 20, 10)

Bu matrisin her bir sütununun ortalamasını hesaplamak için MARGIN = 2 kullanılır:

apply(x, 2, mean)
##  [1]  0.150394922 -0.002591708  0.043601703 -0.006161931  0.045354753
##  [6] -0.375514332  0.234989071 -0.318744293  0.038555223 -0.086109213

Benzer şekilde, her satırın toplamını hesaplamak için MARGIN = 1 kullanılır:

apply(x, 1, sum)
##  [1] -1.83942979  0.13164053 -2.13465796 -0.80746484 -2.10147527 -2.27627352
##  [7] -3.45299000 -0.66254558 -2.54248474  0.43311997  0.00681385 -0.37519585
## [13] -1.30682914  2.10514899  1.42161755  4.58688317  4.73222962 -2.06794500
## [19]  2.26313786 -1.63781592

apply’ın gücü, sadece mean veya sum gibi basit fonksiyonlarla sınırlı değildir. summary gibi daha karmaşık ve birden çok değer döndüren fonksiyonlarla da kullanılabilir. Aşağıdaki örnek Matris1 adlı matrisin her bir sütunu için özet istatistikleri hesaplar:

set.seed(12)
S1 <- sample(rnorm(10000, 50, 5), 100, replace=TRUE)
Matris1 <- matrix(S1, nrow=20, ncol=5)

apply(Matris1, 2, summary)
##             [,1]     [,2]     [,3]     [,4]     [,5]
## Min.    39.00080 40.23309 39.04749 39.32974 37.74364
## 1st Qu. 45.21933 48.44165 45.57123 47.36401 43.71252
## Median  49.31295 52.24410 49.49029 51.08794 47.62144
## Mean    48.20485 52.13701 49.38658 50.61689 48.60479
## 3rd Qu. 52.40540 55.97719 52.70180 54.36235 53.32016
## Max.    55.24910 63.33272 58.88203 59.93019 60.51715

apply ile sıkça yapılan satır/sütun toplamları ve ortalamaları gibi temel işlemler için R, daha hızlı çalışan ve daha okunabilir olan optimize edilmiş fonksiyonlar sunar.

  • rowSums(x) eşdeğeri apply(x, 1, sum)

  • rowMeans(x) eşdeğeri apply(x, 1, mean)

  • colSums(x) eşdeğeri apply(x, 2, sum)

  • colMeans(x) eşdeğeri apply(x, 2, mean)

mapply(): mapply fonksiyonunun temel amacı, bir fonksiyonu birden fazla argüman listesi veya vektörü üzerinde paralel olarak uygulamaktır. lapply’den farklı olarak, mapply’de uygulanacak fonksiyon ilk argüman olarak belirtilir ve ardından üzerinde işlem yapılacak veri yapıları gelir.

Örneğin, rep(1, 4), rep(2, 3), rep(3, 2) ve rep(4, 1) gibi bir dizi çağrıyı tek tek yazmak yerine, mapply ile bu işlemi kolaylıkla tek satırda yapabiliriz:

mapply(rep, 1:4, 4:1)
## [[1]]
## [1] 1 1 1 1
## 
## [[2]]
## [1] 2 2 2
## 
## [[3]]
## [1] 3 3
## 
## [[4]]
## [1] 4
  1. bölümdeki şekilli ödevleri yapmak istemiştim. Bunlar hem R hem de python koduyla yapılıyormuş.
n = 5
for _ in range(n):
    print("* " * n)
## * * * * * 
## * * * * * 
## * * * * * 
## * * * * * 
## * * * * *
n <- 5
for (i in 1:n) {
  cat(rep("* ", n), "\n")
}
## *  *  *  *  *  
## *  *  *  *  *  
## *  *  *  *  *  
## *  *  *  *  *  
## *  *  *  *  *
n <- 5
for (i in 1:n) {
  for (j in 1:n) {
    if (i == 1 || i == n || j == 1 || j == n) {
      cat("*")
    } else {
      cat(" ")
    }
  }
  cat("\n")
}
## *****
## *   *
## *   *
## *   *
## *****
n = 5
for i in range(n):
    for j in range(n):
        if i == 0 or i == n-1 or j == 0 or j == n-1:
            print("*", end="")
        else:
            print(" ", end="")
    print()
## *****
## *   *
## *   *
## *   *
## *****
n <- 5
for (i in 1:n) {
  cat(rep("* ", i), "\n")
}
## *  
## *  *  
## *  *  *  
## *  *  *  *  
## *  *  *  *  *
n = 5
for i in range(1, n+1):
    print("* " * i)
## * 
## * * 
## * * * 
## * * * * 
## * * * * *
n <- 5
for (i in 1:n) {
  spaces <- paste(rep(" ", n - i), collapse = "")
  stars  <- paste(rep("*", 2*i - 1), collapse = "")
  cat(spaces, stars, "\n", sep = "")
}
##     *
##    ***
##   *****
##  *******
## *********
n = 5
for i in range(1, n+1):
    spaces = " " * (n - i)
    stars  = "*" * (2*i - 1)
    print(spaces + stars)
##     *
##    ***
##   *****
##  *******
## *********
n <- 5
for (i in 0:(n-1)) {
  cat(paste(rep(" ", i), collapse = ""), "*\n", sep = "")
}
## *
##  *
##   *
##    *
##     *
n = 5
for i in range(n):
    print(" " * i + "*")
## *
##  *
##   *
##    *
##     *
n <- 5
for (k in 1:2) {
  for (i in 0:(n-1)) {
    cat(paste(rep(" ", i), collapse = ""), "*\n", sep = "")
  }
}
## *
##  *
##   *
##    *
##     *
## *
##  *
##   *
##    *
##     *
n = 5
for _ in range(2):
    for i in range(n):
        print(" " * i + "*")
## *
##  *
##   *
##    *
##     *
## *
##  *
##   *
##    *
##     *

Derste yazdığım notlardan seçmeler

📍 console– ls() çalışma nesnelerini görmek için, rm () fonksiyonu ile nesne silebilirsin

📍rm (list=ls()) tüm nesneleri siler

📍rm ("nesne1","nesne2") istediklerini seçip silmek için kullanılır

📍 getwd () setwd () *Files-more’dan da ayarlanabilir

📍setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) çalıştığın dosya neyse çalışma dizinini o yapıyor

📍 save.image () fonksiyonu oluşan tüm nesneleri kaydeder, yanlışlıkla kaydetmeden kapatmayı önler

📍 session – restart R

📍 packet::function – paket çakışmaları için

📍 list () istatistiksel fonksiyonşarın tutulduğu biçim; çok büyük dosyaları filtleyerek save () fonksiyonuyla kaydedebilirsin. load () fonksiyonuyla yüklenebilir

📍 save.Rds () ile kaydet, load.Rds () ile yükle

📍 chunk ayarlarında echo=TRUE, prompt= FALSE, collapse = TRUE yapınca konsoldaki görünüm gibi yazdırıyor

📍 while, for’dan farklı olarak bir koşula bağlı olarak çalışır (bkz. madde tepki kuramları). for kaç defa çalışacağını bildiğimiz durumlar içindir. Eğer fonksiyonun default değerinde bir sınır varsa örneğin 1000 iterasyon sayısına sabit bir sınır koyulduysa yakınsama olmadan durur ve uyarı verir. Böyle bir durumda iterasyon sayısını 1000’den örneğin 2000’e çıkarıp çalıştırmaya devam etmek gerekir.

Aşağıdaki kodda derste hata yapmıştım. Unutmamak için buraya ekliyorum:

set.seed(987)
sayilar <- sample(-10:10,27,replace=TRUE)
sayilar
##  [1]   4   3   4   2   1   7 -10   5   6  -8   7  -3   9   7  -9  10   4  -1  -8
## [20]   8  -3   0   4   5   8   1   3
ifelse(sayilar> 0, "pozitif" , 
       ifelse (sayilar < 0, "negatif", "sifir"))
##  [1] "pozitif" "pozitif" "pozitif" "pozitif" "pozitif" "pozitif" "negatif"
##  [8] "pozitif" "pozitif" "negatif" "pozitif" "negatif" "pozitif" "pozitif"
## [15] "negatif" "pozitif" "pozitif" "negatif" "negatif" "pozitif" "negatif"
## [22] "sifir"   "pozitif" "pozitif" "pozitif" "pozitif" "pozitif"

Bu örnekler de kalsın:

X <- cbind(1:5, 21:25)
X
##      [,1] [,2]
## [1,]    1   21
## [2,]    2   22
## [3,]    3   23
## [4,]    4   24
## [5,]    5   25
for (i in 1:nrow(X)){
  cat(i,"1 satirdaki degerlerin carpimi", X[i,1]* X[i,2], "olarak hesaplanmistir.\n")
}
## 1 1 satirdaki degerlerin carpimi 21 olarak hesaplanmistir.
## 2 1 satirdaki degerlerin carpimi 44 olarak hesaplanmistir.
## 3 1 satirdaki degerlerin carpimi 69 olarak hesaplanmistir.
## 4 1 satirdaki degerlerin carpimi 96 olarak hesaplanmistir.
## 5 1 satirdaki degerlerin carpimi 125 olarak hesaplanmistir.
if (interactive()) { # örerken bu kısımda hata verdi, kodu interaktif kullandığım için örmemiş gpt2ye göre. if (interactive ()) ile denememi önerdi ve oldu
    
n <- as.numeric(readline(prompt="kare matrisin boyutu kac olsun")) 
}

matris <- matrix (0,n,n) # yukarıdaki fonksiyonu çalıştırınca konsola sayı yazmadan durmaz

for (i in 1:n) {
  for (j in 1:n) {
    matris [i,j] <- i*j
  }
}
if (nrow(matris) <= 10){
  matris
} else {
  matris [17:10, 1:10]
}
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    2    3    4    5
## [2,]    2    4    6    8   10
## [3,]    3    6    9   12   15
## [4,]    4    8   12   16   20
## [5,]    5   10   15   20   25

#readline konsola yazılan sayıyı nesnenin içine alıyor. prompt ise konsola yazı yazdırmak için kullanılıyor. Konsola yazdığımız sayıyı karakter olarak okumasın diye kodun başına as.numeric yazdık.

Konsola sayı yazmadan durmayacağını biliyordum ve bir süre durmasını bekledim 🤦🏻‍♀️ sonrasında 7 yazdığım anlaşılıyordur.

Fibonacci dizisini for ve while ile inceleyelim:

fib <- c ()
for (i in 3:20) {# ilk iki eleman 1 olduğu için diziye dahil etmedik, 3'ten başladık. n-1, n-2
  fib [1] <- 1
  fib [2] <- 1
  fib [i] <- fib [i-1] + fib [i-2]
} 
fib 
##  [1]    1    1    2    3    5    8   13   21   34   55   89  144  233  377  610
## [16]  987 1597 2584 4181 6765
fib <- c ()
i = 3
while (i <=20) {
  fib [1] <- 1
  fib [2] <- 1
  fib [i] <- fib [i-1] + fib [i-2]
  i=i+1
} 
fib 
##  [1]    1    1    2    3    5    8   13   21   34   55   89  144  233  377  610
## [16]  987 1597 2584 4181 6765