R Dersleri - Ali Onur Gitmez

Ana Sayfa Ders 1 Ders 2 Ders 3 Ders 4 Ders 5 Ders 6 Ders 7 Ders 8 Ders 9 Ders 10 Ders 11 Ders 12 Ders 13 Ders 14 Ders 15

LOOPS

R programlamasinda ve genel olarak programlamada en onemli konulardan birisi de looplardir. Iteration olarak da tanimaylabilecegimiz islemi gerceklestirirler. Ozetle verdigimiz talimatlara dayanarak verinin ustynden gecip belli bir noktaya gelene kadar islemi yaparlar ve sartlar saglandigi zaman dururlar. Buyuk boyutlu veri setleri ile calisirken ayni islemi defalarca tekrarlamak yerine loop yazarak bu islemi halledebiliriz. R’da yaygin olarak kullan 2 ve daha az yaygin olsa da bilinmesi gereken 1 sekli vardir. Bunlar:

  • For

  • While

  • Repeat

For

For loop bir kodu istedigimiz sayida calistirmayi veya veri icindeki her bir element ustunde calistirmamiza yardimci olur. Bu loop tipinin temel yapisi su sekildedir:

for (variable in loop_data) {
  
}

for kismi kullanacagimiz loopun for loopu oldugunu ve belirtecegimi elementlere uygulanaycagini belirtir.

i kismi gecici olarak atatidigimiz degisken ismidir. bu degisken ismi sadece loop icinde gecerli olur ve gecicidir. Bu yuzden i basit olmasi sebebiyle de siklikla tercih edilir. Sequence kismindaki butun degerleri alir.

loop_data ise genellikle loopun ustunde calismasini istedigimiz verimizdir. Onceden atayabildigimiz gibi loop icinde de ne oldugunu belirtebiliriz. Bu noktada tercih kullaniciya kalmistir ama basit islemler icin noldugunu icinde belirtmek daha iyi olabilirken kompleks islemlerde veya tekrar edecek islemlerde onceden belirtmek daha iyi olur.

in ise i ve sequence noktasini ayirir ve yine for gibi loop icin elzem olan keywordlerdendir. i in loop_data diyerek i degiskenin loop_data datasindan alacagi degerleri belirtmis oluruz.

{} ile belirttigimiz kisim ise loopu olusturma sebebimiz olan islemin yapildigi kisimdir. Buraya matematik islemi, string islemi gibi farkli seyler ekleyebiliriz.

Loop icinde veriyi belirttigimiz bir ornek olarak:

for (i in 1:5) {
  print(paste("Bu loopun", i , "'inci iterationi"))
}
[1] "Bu loopun 1 'inci iterationi"
[1] "Bu loopun 2 'inci iterationi"
[1] "Bu loopun 3 'inci iterationi"
[1] "Bu loopun 4 'inci iterationi"
[1] "Bu loopun 5 'inci iterationi"

Loop oncesinde belirttigimiz bir ornek olaraksa:

numbers <- c(1, 2, 3, 4, 5)
for (num in numbers) {
  print(num)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

Burada gordugumuz uzere verimize numbers diyoruz ve bunu loop icinde veri kismina yaziyoruz. degisken adi olaraksa istedigimizi secebiliriz. ANCAK, bunu her calistirdigimiz zaman veri guncellenecegi icin degisken adini eger ileride kullanacaksak ozel secmemiz gerekir.

Benzer sekilde karakter vektorlerini de loop kullanarak print edebiliriz

meyveler <- c("muz", "portakal", "elma")
for (meyve in meyveler) {
  print(meyve)
}
[1] "muz"
[1] "portakal"
[1] "elma"

Listeler gibi farkli veri tipi barindiran veri yapilarinda da loop kullanabiliriz

loop_list <- list(isim = "Ece", yas = 28, sehir = "Ankara", 
                  kadin = TRUE)
for (item in loop_list) {
  print(item)
}
[1] "Ece"
[1] 28
[1] "Ankara"
[1] TRUE

Matematik islemlerini de belli bir vektor ustunden loop kullanarak yapabiliriz

for (i in 1:5) {
  result <- i * 2
  print(paste(i,  "'in 2 ile carpiminin sonucu ", result))
}
[1] "1 'in 2 ile carpiminin sonucu  2"
[1] "2 'in 2 ile carpiminin sonucu  4"
[1] "3 'in 2 ile carpiminin sonucu  6"
[1] "4 'in 2 ile carpiminin sonucu  8"
[1] "5 'in 2 ile carpiminin sonucu  10"

Logical argumanlar kullanarak looplari kullanabiliriz

sayilar <- c(3, 7, 2, 9, 4)
for (num in sayilar) {
  if (num %% 2 == 0) {
    print(paste(num, "cift sayi"))
  } else {
    print(paste(num, "tek sayi"))
  }
}
[1] "3 tek sayi"
[1] "7 tek sayi"
[1] "2 cift sayi"
[1] "9 tek sayi"
[1] "4 cift sayi"

Matrislerde genellikle nested loop dedigimiz loop icinde loop kullaniriz. Ancak butun elementlerin ustunden gecmemiz gerekiyorsa tek bir loop ile de halledebiliriz. Nested loop kullanilmasinin sebebi verimiz row ve columnlardan olustugu icin islemi nereye yapacagimiz belli olmasi ve sutun veya satir bazli islem yapmaya olanak tanimasi.

matrix_loop <- matrix(1:9, nrow = 3, byrow = TRUE)
for (element in matrix_loop) {
  print(paste(element, "sayisinin karesi", element^2))
}
[1] "1 sayisinin karesi 1"
[1] "4 sayisinin karesi 16"
[1] "7 sayisinin karesi 49"
[1] "2 sayisinin karesi 4"
[1] "5 sayisinin karesi 25"
[1] "8 sayisinin karesi 64"
[1] "3 sayisinin karesi 9"
[1] "6 sayisinin karesi 36"
[1] "9 sayisinin karesi 81"

Nested loop ise matrisler gibi cok boyutlu verilerde veya birden cok seviyede iteration yapilmasi gerektigi zaman kullanilirlar. Bu tip verilerde icerdeki loop, disardaki loopun butun degerleri icin calisir ve sonunda operasyon tamamlanir. Matrislerde loop kullanimina bir ornek olarak:

notlar <- matrix(c(80, 85,
                   75, 90), 
                 nrow = 2, byrow = TRUE)

ogrenciler <- c("Ece", "Zeynep")
dersler <- c("Istatistik", "IR")

for (i in 1:2) {
  for (j in 1:2) {
    print(paste(ogrenciler[i], dersler[j], "sinavindan", 
                notlar[i,j], "aldi" ))
  }
}
[1] "Ece Istatistik sinavindan 80 aldi"
[1] "Ece IR sinavindan 85 aldi"
[1] "Zeynep Istatistik sinavindan 75 aldi"
[1] "Zeynep IR sinavindan 90 aldi"

Burada loop su sekilde calisiyor. Once i in 1 kismina gidiyoruz. Vektorlerdeki ilk ogrenci kim? Ece. Ece’yi cekiyoruz. Sonra bunun altindaki loopa gidiyoruz. Bu loop ders adina ulasiyor. 1 ile basladigi icin ilk ders istatistik. Bu loopun altinda notlar[i,j] ise iki element aliyor. Bu da i’den gelen 1 ve j’den gelen 1’i hesaba katiyor ve o matrise veya dataframe’e gidiyor. Orada 1,1 80 oldugu icin onu aliyor ve altta yazdigimiz komutu yapiyor. Sonra icerdeki loopa geri donuyor ve 1 yaptigi ancak biz islemin 2de bitmesini istedigimiz icin sureci bu sefer j’yi 2 yaparak tekrardan baasltiyor ve bu sekilde devam ediyor ve notlarda [1,2] ye ulasiyor. Ierdeki loop tamamlaninca disardaki loopa geri donuyor. Orada da hala 2 kaldigi icin aynisini [2,1] ile baslatiyor ve en sonunda loop tamamlaniyor.

Nested looplarin baska bir kullanim alani da birden fazla seviyede iteraasyon iceren islemlerdir. Mesela ilk asamada 1 ve 5 arasindaki sayilari aliriz. Sonrasinda aldigimiz her sayi icin alttaki looptan yine 1 ve 5 arasinda bir sayi aliriz. Bu icerdeki loopun icindeyse bu iki sayisi carp ve bu iki sayinin caprimi olarak sonucu goster deriz. Bunu tekrar ede ede oncelikle disardaki sayinin 1 oldugu durumda icerdeki 5 sayiyi sonra disardaki sayi 2 oldugu durumda icerdeki sayiyi diye ilerler ve en sonunda loop tamamlanir.

for (i in 1:5) {
  for (j in 1:5) {
    product = i * j
    print(paste(i, "x", j, "=", product))
  }
  print("-----")
}
[1] "1 x 1 = 1"
[1] "1 x 2 = 2"
[1] "1 x 3 = 3"
[1] "1 x 4 = 4"
[1] "1 x 5 = 5"
[1] "-----"
[1] "2 x 1 = 2"
[1] "2 x 2 = 4"
[1] "2 x 3 = 6"
[1] "2 x 4 = 8"
[1] "2 x 5 = 10"
[1] "-----"
[1] "3 x 1 = 3"
[1] "3 x 2 = 6"
[1] "3 x 3 = 9"
[1] "3 x 4 = 12"
[1] "3 x 5 = 15"
[1] "-----"
[1] "4 x 1 = 4"
[1] "4 x 2 = 8"
[1] "4 x 3 = 12"
[1] "4 x 4 = 16"
[1] "4 x 5 = 20"
[1] "-----"
[1] "5 x 1 = 5"
[1] "5 x 2 = 10"
[1] "5 x 3 = 15"
[1] "5 x 4 = 20"
[1] "5 x 5 = 25"
[1] "-----"

Bazi durumlarda pre-allocation yapmak loopun performansini arttirabilir. Bunun ciddi fark yaratacagi durum cok buyuk verisetleridir ve sosyal bilimlerde bunlarla karsilasmayabiliriz. Yine de ogrenmek gerekirse en basta vektorumuzun boyutunu 5 olarak belirlersek loop yaratip yaratip doldurmak yerine sadece doldurma islemi yapacaktir ve bu da kodumuzu hizlandiracaktir.

results <- numeric(5) 
for (i in 1:5) {
  results[i] <- i^2
}
print(results)
[1]  1  4  9 16 25

Looplari en cok kullanacagimiz yerlerden birisi de dataframeler. Basit bir ornek olarak:

df_loop <- data.frame(
  A = 1:5,
  B = 6:10,
  C = 11:15
)

for (col in names(df_loop)) {
  print(paste("The sum of column", col, "is", 
              sum(df_loop[[col]])))
}
[1] "The sum of column A is 15"
[1] "The sum of column B is 40"
[1] "The sum of column C is 65"

Bu loop su sekilde calisiyor. names komutu ile df_loop icindeki column isimlerini aliyoruz ve onu col objesine kaydediyoruz. Sonrasinda (df_loop[[col]] ile bu columna ulasiyoruz ve sum ile topluyoruz. Kalani ise yazi olarak bunu belirtmemize kaliyor. Bu islem butun columnlar icin tamamlanana kadar loop calismaya devam ediyor.

Benzer skeilde rowlar ustunden de islem yapabiliriz. Ancak rowlarin bir isimlendirmesi olmadigi icin bunu row endekslerine gore yapmamiz lazim.

for (i in 1:nrow(df_loop)) {
  print(paste("The sum of row", i, "is", sum(df_loop[i,])))
}
[1] "The sum of row 1 is 18"
[1] "The sum of row 2 is 21"
[1] "The sum of row 3 is 24"
[1] "The sum of row 4 is 27"
[1] "The sum of row 5 is 30"

Dataframe icindeki veriler ustunde islem yapmamiz mumkunudur. Burada ise dataframe icindeki her elementi 2 ile carpiyoruz.

for (col in names(df_loop)) {
  df_loop[[col]] <- df_loop[[col]] * 2
}
print(df_loop)
   A  B  C
1  2 12 22
2  4 14 24
3  6 16 26
4  8 18 28
5 10 20 30

Bu uc islemi apply family of functions denilen bir grup komut ile cok daha hizli bir sekilde yapabiliriz. Looplrdan sonra konumuz onlar ile islem yapmak olacak.

Benzer sekilde, her durumda loop kullanmak avantajli olmuyor elbette. Bazi durumlarda vektorler ustunden islem yapmak loop kullanmaya gore hem daha hizli hem de daha basit bir kod gerektiriyor. Mesela bu ikisi de bize ayni sonucu verecek.

result <- numeric(5)
for (i in 1:5) {
  result[i] <- i^2
}
result2 <- (1:5)^2
result
[1]  1  4  9 16 25
result2
[1]  1  4  9 16 25

Break and next butun loop tiplerinde onemli olan cikis noktalaridir. Bunlara jump statement denir. Looplar duzgun hazirlanmadigi zaman surekli calisacagi icin bu tarz eklemeler yaparak bu durumlarin onune gecebiliriz. Veya loopun belli bir noktada durmasini istiyorsak ama bu noktanin neresi oldugunu loop kullanmadan bilemiyorsak da ise yarayacaktir.

Break ornegi:

for (i in 1:100) {
  if (i^2 > 100) {
    print(paste("Karesi 100'u gecen ilk sayi", i))
    break
  }
}
[1] "Karesi 100'u gecen ilk sayi 11"

Burada break komutunu i sayisinin karesi 100u gecince kullanmasini istedik. Loop nested oldugu icin o ana kadar surekli calisti ve i^2 100un ustune ciktigi zaman loop komutlari calismaya basladi. Boylece hem yaziyi print etti hem de loopu kapatti. Eger bunu koymasaydim 11 ve 100 arasindaki butun sayilari print edecekti.

Next ise loop icinde bir iterasyonu atlayip diger iterasyona gecmek icin kullanilir. Bu ozellikle bir sarta bagli olarak loop yazdigimiz aman ise yarar.

for (i in 1:10) {
  if (i %% 2 == 0) next
  print(i)
}
[1] 1
[1] 3
[1] 5
[1] 7
[1] 9

Bu durumda 1 ve 10 arasindaki sayilari almasini ancak sayi 2 ile tam bolunuyorsa onu gecmesini soyluyoruz. Eger bunu belirtmeseydik tum sayilari bize verecekti. Next dedikten sonra bu sarti saglamayan bir sayi geldiyse de o sayiyi print ediyor ve boyle devam ede ede loop tamamlanmis oluyor.

Break ve next komutlarini nested looplar icinde de kullanabiliriz

for (i in 1:5) {
  for (j in 1:5) {
    if (i * j > 10) {
      print(paste("Carpimlari 10'u gecen ilk sayilar", 
                  i, "x", j, "=", i*j))
      break 
    }
  }
  if (i * j > 10) break 
}
[1] "Carpimlari 10'u gecen ilk sayilar 3 x 4 = 12"

Burada 1 ve 5 arasindaki sayilari alip 1 ve 5 arasindaki sayilar ile carpip bu carpimin 10u gecip gecmedigine bakiyoruz. Loop sayilar 1x1, 1x5 2x3 diyerek almaya basladigi icin ilk olarak kapatilmasi gereken inner loop oluyor ve sonrasainda tekrardan sartlarin saglanip saglanmadigina bakip loopu tamamliyoruz. Bu da bize dogal olarak 3x4 carpimini veriyor.

While Loop

Sikca karsilacagimiz ikinci bir loop tipi ise while looplardir. While looplar belirttigimiz sart gerceklesene kadar calisir. Az onceki ornekte gordugumuz for loop while loop ile de daha basit bir sekilde break kullanmadan yazilabilirdi.

Loop’un yapisi su sekildedir:

count <- 1
while (count <= 5) {
    print(paste("Sayi:", count))
    count <- count + 1
}
## [1] "Sayi: 1"
## [1] "Sayi: 2"
## [1] "Sayi: 3"
## [1] "Sayi: 4"
## [1] "Sayi: 5"

Loop yapisina baktigimiz zaman, while icinde sarti verdigimiz temel komutumuz, {} arasinda kalan ise bu sart saglanana kadar calisan loopumuz oluyor. Burada bunu count olarak belirttik ancak dogal olarak her degeri alabilir.

Burada sayi 5 ve altindaysa print et ve sonrasinda 1 ekle ve loopu tekrardan baslat diyoruz. While loop kulandigimiz icinse 5e geldigi zaman sartlari check ediyor ve artik sagladigi icin durduruyor. Bu ozellikle bizim olusturmadigimiz veriler ustunden is yaparken cok kullanisli olur.

While looplari bir kondisyonun dogru olmasini bekledigi icin iyi yazilmayan bir kod ile beraber sonsuza kadar print etme ihitmali vardir. Bunun onune gecmek icin belli yontemler kullaniriz. Bunu loopun sartlarinin eninde sonunda karsilandigini garanti altina alarak veya bir noktaya break statement ekleyerek yapabiliriz.

while (TRUE) {
    print("Sonsuza kadar print edecek")
}
count <- 1
while (TRUE) {
    print(paste("Iteration:", count))
    count <- count + 1
    if (count > 10) {
        break
    }
}
[1] "Iteration: 1"
[1] "Iteration: 2"
[1] "Iteration: 3"
[1] "Iteration: 4"
[1] "Iteration: 5"
[1] "Iteration: 6"
[1] "Iteration: 7"
[1] "Iteration: 8"
[1] "Iteration: 9"
[1] "Iteration: 10"

Ilk komut bir bitis yeri olmadigi icin devam edecekti ancak biz ikincisinde 10u gectigi anda durmasini istedik ve istedigimiz gibi yapti.

Benzer sekilde break ve next de for looplarda oldugu gibi while looplarda kullanilabilinir.

i <- 1
while (i <= 10) {
    if (i == 3) {
        i <- i + 1
        next
    }
    if (i == 8) {
        break
    }
    print(i)
    i <- i + 1
}
[1] 1
[1] 2
[1] 4
[1] 5
[1] 6
[1] 7

Burada gordugumuz uzere i 10dan dusukse o sayiyi print et diyoruz. Ancak 3e geldigi zaman ilk if kondisyonu karsilanmis oluyor ve onun ic loopuna giriyoruz. Orada sayiyi 1 arttirmamizi ve iterationi bitirmemizi soyluyo. Burada bir arttirmamizin sebebi iterationin normalde loop bittikten sonra olmasi ama bizim 3u atlama istegimizden dolayi print etmememiz. Ayni sekilde sayi 8 oldugu zaman ekledigimiz break asil kural olan 10 altini ignore edip loopun calismasini durduruyor.

While looplari da nested olarak kullanabiliriz

i <- 1
while (i <= 3) {
    j <- 1
    while (j <= 3) {
        print(paste(i, ",", j))
        j <- j + 1
    }
    i <- i + 1
}
[1] "1 , 1"
[1] "1 , 2"
[1] "1 , 3"
[1] "2 , 1"
[1] "2 , 2"
[1] "2 , 3"
[1] "3 , 1"
[1] "3 , 2"
[1] "3 , 3"

Burada iki farkli degisken tanimlamasi yaptik: i ve j. ilk olarak i’yi 1 olarak tanimladik ve 3 kucuk esittir oldugu surece j’yi de 1den baslatik ikisinin de anlik degerini yazmasini istedik. bunu yazdiktan sonra ic loop 3 kosulusunu saglamadigi icin devam etti ve onu yine arttirip yine devam ettirdik. Bu ic loop sartlari saglayana kadar devam etti. Ssonrasinda bundan cikip i degiskenini bir arttirdik ve butun islem bastan baslamis oldu. En sonunda iki degisken de belirtilen maksimum degere ulasinca loop tamamlandi.

Ayni for looplar gibi while loop yaparken de bazi islemler vektorler ile cok daha hizli yurutulur.

Ornek olarak

while_loop_sonuc <- numeric(1000)
i <- 1
while (i <= 1000) {
    while_loop_sonuc[i] <- i^2
    i <- i + 1
}

yerine bu komutu kullanirsak cok daha hizli sonuc elde ederiz.

result <- (1:1000)^2

While looplari da farkli data yapilari ile beraber kullanabiliriz.

satis_verisi <- data.frame(
  ay = c("Ocak", "Subat", "Mart", "Nisan", "Mayis"),
  miktar = c(100, 150, 200, 120, 180)
)
satis_verisi$toplam_satis <- 0
bu_ay <- 1
satis <- 0
while (bu_ay <= nrow(satis_verisi)) {
  satis <- satis + satis_verisi$miktar[bu_ay]
  satis_verisi$toplam_satis[bu_ay] <- satis
  bu_ay <- bu_ay + 1
}
print(satis_verisi)
     ay miktar toplam_satis
1  Ocak    100          100
2 Subat    150          250
3  Mart    200          450
4 Nisan    120          570
5 Mayis    180          750

Bu ornekte biraz komplike bir sekilde de olsa while loop kullanip toplam verisi hazirliyoruz. bu_ay 1 degerini aldi bunun sebebi iste ilk olusturdugumuz dataframede veriyi satirlardan cekecek olmamiz ve satirlarin isminin degil numarasinin olmasi. Bu ornekte while loopu bu_ay degiskeni dataframe’in satir sayisini gecene kadar calisacak. Satis degiskenimiz ise 0 olarak belirlendi. Loop icinde satis_verisi dataframe’i icinden miktar degiskenini gidiyoruz ve onu satis degiskenine ekliyoruz. Sonrasinda o desigkeni de verisetimizdeki toplam_satis degiskenine atiyoruz ve sonrasinda ayi bir yukseltiyoruz. Boylece kendi olusturdugumuz satis degiskeni surekli olarak yukseliyor ve ve her yukselmesinde bir alttaki rowa inerek degerini orada belirtiyor. En sonundaysa veri bitince duruyor ve toplam satisi en son satirda gorebiliyoruz.

While looplari liste icindeki elemanlari gormek icin de kullanabiliriz.

list_while <- list(c(1, 2, 3), c(4, 5, 6), c(7, 8, 9))
index <- 1
while (index <= length(list_while)) {
  print(list_while[[index]])
  index <- index + 1
}
[1] 1 2 3
[1] 4 5 6
[1] 7 8 9

Burada while loopu altinda belirttigimiz index loopun karakter sayisinin altindaysa o indexe gore karakter cikart diyoruz ve en son olarak endekse bir arttirip onu tekrardan loopa sokuyoruz.

En basta ucuncu tip bir looptan bashetmistim repeat loop diye. Bu R’a ozgu bir loop tipi ve bunun genel adi do-while loop. Bu loop icinde for veya whileda oldugu gibi bir sarti incelemek yerine break komutunu kullanip bitiririz. Ornek olarak

counter <- 1

repeat {
  print(counter)
  counter <- counter + 1
  
  if (counter > 5) {
    break
  }
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

Burada herhangi bir break komutu belirtmeseydim kod sonsuza kadar calisacakti ancak break belirterek komutun calismasini durdurmus oldum ve o ana kadar loopu repeat etmesi gerektigini anladi. Kullanim alani daha az olmasina ragmen cok az da olsa karsimiza cikabilir.

SON