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

Functions

R’da fonksiyonlari istedigimiz bir islemi gerceklestirmek icin kullaniriz. Kodu daha okunur, anlasilir ve yeniden kullanilabilir yaparlar. R’da built-in olarak belirli fonksiyonlar vardir. Bunlardan bazilarini daha onceden kullanmistik. Bu derste isleyemecegimiz ama ozellikle string ile calisirken cok isimize yarayacak baska functionlar da var. Ancak sayi fonkisyonlari isimizi kolaylastir. Bu ozellikle cok tekrar gerektiren ve cok asamali islerde yardimci olur. Ayni zamanda fonkisyonu bir kere yazarsaniz istediginiz kadar kullanirsiniz ve baska projelerde de yardimci olur.

Fonksiyonlara gecmeden once bir sayi verisi olusturalim ve islemlerimizi bu veri ustunden yapalim.

function_numbers <- 1:10

R’da built in olarak sayilarla calisirken en cok kullanacagimiz fonksiyonlar bunlardir.

min(function_numbers)
[1] 1
max(function_numbers)
[1] 10
sum(function_numbers)
[1] 55
mean(function_numbers)
[1] 5.5
length(function_numbers)
[1] 10
sd(function_numbers)
[1] 3.02765

Bunun disinda kendi functionlarimizi da olusturabiliriz. R’da kendi functionimizi olusturdugumuz zaman bu bizi ayni kodu kopyala yapistir yapma derdinden kurtarabilir. Kendi fonkisoyunumuzu yaziyorsak fonksiyonlarin anatomisine dikkat etmemiz gerekir. Fonksyionlar 4 parcadan olusur. Bunlar:

  • Fonkisyonun adi: Fonksiyonumuza verecegimiz ismi biz seceriz. Anlasilir olmasi onemlidir. Degisken olarak kaydedecegimiz icin isim formatini ona gore yapmaliyiz.

  • Argumanlari: function() komutu sonrasinda parantez icine koyacagimiz elementlerdir. Bunlar fonksiyonu cagirabilmemiz icin zorunludur. Bazi durumlarda bos birakabiliriz. Bos biraktigimiz durumda ne yapilmasi gerektigini function icinde ayrica belirtmemiz gerekir. Mesela 2 sayiyi toplayacaksak x ve y olarak bu kisma yazmamiz gerekir. Bazi durumlarda optional arguman ekleyebiliriz. Boyle bir durum soz konusuysa da ona default bir atama yapmamiz gerekir, yoksa function calismaz.

  • Body: Bu function ile yapmak istedigimiz islemin gerceklestigi yerdir. Functionin verdigimiz argumanlar ile ne yapmasini istedigimizi bu kisimda anlatiriz. {} arasina yazilir. Fonksiyonlarda kullanilan hesaplama araclarini veya diger built in r functionlarini bu kisma gireriz.

  • Return: Functionin vermesini istedigimiz elementi belirttigimiz yerdir. Yani function icinde belirttigimiz hesaplamalardan hangi sonucu cikarmasini istiyorsak onu belirtiriz. Opsiyonel bir argumandir. Eger doldurulmnazsa function body kisminda en son yaptigi islemin sonucunu verecektir. Ancak belirli bir ciktiyi almak istiyorsak, return() olarak belirtmemiz gerekir.

add_numbers <- function(a, b) {
  sum <- a + b
  return(sum)
}
add_numbers(5,7)
[1] 12

Burada return belirtmedik ve function icinde return olmadigi icin ek olarak print etmek zorunda kaldik. Print yerine bir objeye kaydedip onu da goruntuleyebilirdik. Bu fonksiyonun amacina gore degismekle beraber eger sonucu goruntulemeyi gerekturen bir durum yoksa return eklememize gerek yok. Ozellikle fonksiyonun amaci belliyse ek olarak return almamiza gerek yoktur. Bazi programcilar ise stil olarak tercih eder.

add_numbers_2 <- function(a, b) {
  sum <- a + b
}
print(add_numbers_2(5,7))
## [1] 12

Burada yazdigimiz function gereksiz duruyor. Sonucta 5 + 7 diyerek cok daha basit bir sekilde ayni islemi yapabilirdik. Ancak fonksiyon kullanmanin avantaji ayni isi cok fazla veride tekrar etmemiz gerekiyorsa ortaya cikiyor. Ozellikle yapacagimiz islem cok asamaliysa 4-5 asamali bir kodu 7 farkli veri ustunde yapmak bize 40-50 satir civarinda kod yazdirir. Ancak basta biraz daha komplike gozuken bir function yazarsak ayni isi cok daha rahat bir sekilde yapabiliriz.

Yukaridaki function sadece iki sayiyi toplamamiza yarar. Cunku argumanlar kisminda sadece a ve b sayilarindan bahsettik ve funtion bizden bunu bekliyor. Eger islem yapacagimiz element sayisi belirsiz ise bunu … kullanarak cozebiliriz.

sum_numbers <- function(...) {
  args <- list(...)
  sum(unlist(args))
}

Benzer sekilde default argumanlari da functionlara ekleyebiliriz. Eger kullanici arguman icin bir sayi belirtmezse dogrudan default olani kullaniriz.

calculate_rectangle_area <- function(length = 1, width = 1) {
  length * width
}
calculate_rectangle_area()
[1] 1

Burada parantez icinde bir arguman belirtmedigimiz icin ikisini de belirttigimiz uzere 1 olarak aldi. Eger belirtseydik bu sefer belirttigimiz sayilar ise hesaplama yapacakti.

calculate_rectangle_area(5, 3)
[1] 15

Eger fonksiyonlarda bir degiskeni kullandiysak onu argumanlarda belirtmek zorundayiz yoksa R error verir. Bunu degisken sonrasina = ve deger vererek cozebiliriz.

rectangle_function = function(length, width){
  area = length * width
  perimeter = 2 * (length + width)
  result = list("Area" = area, "Perimeter" = perimeter)
  return(result)
}
rectangle_function(5)

Bazi fonksiyonlarda neyin nereye gelecegi onemli olabilir. Mesela bolume ilgili bir function hazirliyorsak a + b sayilarinda oldugu gibi rastgele sayi yazamayiz. Elementlerin yerlerinin ayrica belirtilmedigi functionlara positional denir. Belirttigimiz durumda ise named argument girmis oluruz. Ornek verecek olursak

divide <- function(numerator, denominator) {
  numerator / denominator
}
divide(numerator = 10, denominator = 2)
[1] 5
divide(10, 2)
[1] 5

Ikisi de ayni sonucu vermesine ragmen ilkinde hangi sayinin ne oldugunu belirttik. Bu okuyucu acisindan veya koda sonradan donmemiz acisindan rahatlik saglayabilir.

Fonksiyon sonuclarini bir objeye kaydedip daha sonra kullanabiliriz.

bolum_sonucu <- divide(10, 2)
bolum_sonucu * 5
[1] 25

Functionlardan illa tek sonuc cikarmamiza gerek yok. Birden cok degeri de fonksiyonlardan cikarabiliriz

descriptive_statistics <- function(numbers) {
  list(
    mean = mean(numbers),
    median = median(numbers),
    sd = sd(numbers),
    range = range(numbers)
  )
}
descriptive_statistics(function_numbers)
$mean
[1] 5.5

$median
[1] 5.5

$sd
[1] 3.02765

$range
[1]  1 10

Data frame olarak cikartabiliriz. Bu ciktimizi tabular formatta daha okunabilir yapar.

descriptive_statistics_df <- function(numbers) {
  data.frame(
    statistic = c("mean", "median", "sd", "min", "max"),
    value = c(
      mean(numbers),
      median(numbers),
      sd(numbers),
      min(numbers),
      max(numbers)
    )
  )
}
result_df <- descriptive_statistics_df(function_numbers)
result_df
  statistic    value
1      mean  5.50000
2    median  5.50000
3        sd  3.02765
4       min  1.00000
5       max 10.00000

Vector olarak cikarabiliriz. Basit ve hizlica okunabilir oldugu icin tum veri tiplerinin ayni oldugu fonksiyonlarda isimize yarar.

descriptive_statistics_vec <- function(numbers) {
  c(
    mean = mean(numbers),
    median = median(numbers),
    sd = sd(numbers),
    min = min(numbers),
    max = max(numbers)
  )
}
descriptive_statistics_vec(function_numbers)
    mean   median       sd      min      max 
 5.50000  5.50000  3.02765  1.00000 10.00000 

Named list olarak cikarabiliriz. Bu farkli veri tiplerini bulunduruyorsak isimize yarar ve daha fazla cikti almamiz gerekirse onlari gruplamamiza yardimci olur.

descriptive_statistics_list <- function(numbers) {
  list(
    central_tendency = list(
      mean = mean(numbers),
      median = median(numbers)
    ),
    dispersion = list(
      sd = sd(numbers),
      range = range(numbers)
    ),
    summary = summary(numbers)
  )
}
descriptive_statistics_list(function_numbers)
$central_tendency
$central_tendency$mean
[1] 5.5

$central_tendency$median
[1] 5.5


$dispersion
$dispersion$sd
[1] 3.02765

$dispersion$range
[1]  1 10


$summary
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    3.25    5.50    5.50    7.75   10.00 

Karakter ile yapilan functionlara kisa bir giris yapip onlar ustundeki kullanimi da gorelim. String ustunde yapilacak islemleri ileride daha detaylica inceleyecegiz. Burada da default degerlerin kullanimini goruyoruz. Functionlar hem karakter hem de sayilari ayni anda barindirabildigi icin cok genis islem kapasitesi sunuyor.

tanisma <- function(name = "Onur", age = 30) {
  message <- paste("My name is", name, "and I am", age, "years old.")
  return(message)
}

tanisma("Ece", 27)
[1] "My name is Ece and I am 27 years old."
tanisma()
[1] "My name is Onur and I am 30 years old."

Fonkisyonlari nested sekilde de kullanabiliriz. Burada icerideki iki fonksiyon bagimsiz olarak calisiyor ve sonrasinda asil fonksiyonumuz islem yapiyor.

add_numbers(add_numbers(6, 7), add_numbers(8,9))
[1] 30

Ayni anda 2 hesaplamayi da function ile yapip 2 cikti alabiliriz.

rectangle_function = function(length, width){
  area = length * width
  perimeter = 2 * (length + width)
  result = list("Area" = area, "Perimeter" = perimeter)
  return(result)
}

Bazi fonksiyonlar cok kisa oldugu icin 1 satirda fonksiyonu yazip gecebiliriz

function_line <- function(x) x^2*4+x/3
function_line(5)
[1] 101.6667

Ileride stringler ile daha karmasik hallerini gorecegiz ama matematik islemleri ile string yazilarini function icinde karistirabiliriz. Burada if else gibi komutlar kullandik. Bunlar bir sonraki konumuz olacak.

divisible <- function(a, b){
if(a %% b == 0)
{
    return(paste(a, "is divisible by", b))
}
else
{
    return(paste(a, "is not divisible by", b))
}
}
divisible(20, 5)
[1] "20 is divisible by 5"
divisible(13, 4)
[1] "13 is not divisible by 4"

Control Structures

R’in programlama noktasinda en diger programlama dillerine benzeyen kismidir. Bundan sonra ogrenecegimiz konular veri tiplerini donusturme gibi meseleler olacak ama bu programlama temelini bilmek yuzlerce satir kod yazmaktan ve scriptimizin gereksiz yere dolu gozukmesinden bizi kurtarir. 5 dakialik bir control structure yazimi hem diger kodlarin ayrica yazimindan, hem hesaplama zamanindan hem de script icinde yerden tasarruf ettirir.

Temel olarak ogrenecegimiz control structurelar sunlardir:

  • If
  • If else
  • For loop
  • While loop
  • Repeat loop
  • Apply fonksiyon grubu
  • Diger

If

Conditional statement bu tarz islemlerin temelinde yatan mantiktir. If bunlarin en basit versiyonu olmakla beraber if else, if else if ve nested if else gibi konulara da genisletebiliriz. Bu tip islemlerde eylem kondisyonlarin karsilandigi durumda gerceklestirilir. Conditional statementlar boolean da dedigimiz TRUE ve FALSE kondisyonlarini inceler ve bu incelemenin sonucu olarak islemi yapar.

If statementi bunun en basit orneklerindendir. Asagidaki ornekte hava sicakligini 22 derece olarak belirledik ve eger hava 20 derecenin ustundeyse Bugun hava sicak demesini istedik. Hava 22 derece oldugu yani 20 derecenin ustu conditionini karsiladigi icin yaziyi gorebiliyoruz.

temperature <- 22
if (temperature > 20) {
  print("Bugun hava sicak.")
}
[1] "Bugun hava sicak."

Eger bu durum karsilanmasaydi yani hava bu ornekte oldugu gibi 20 derecenin altinda oldugu gibi if komutumuzdan bir sonuc alamayiz. Cunku islemin gerceklesmesi icin istenen 20 ustu conditioni karsilanmamis olur.

temperature2 <- 16
if (temperature2 > 20) {
  print("Bugun hava sicak.")
}

Meslea elimizde 5 sehirden gelen hava sicaklik verisi olsun. 1 verinin ustunden degil 5 verinin ustunden islem yapalim. Bu ornekte 5 sehrin sicaklik verisini girdim ve if statement kisminda eger bunlardan herhangi birinde hava 20 derecenin ustundeyse yaziyi print etmesini soyledim. Bu durumu karsilayan sehirler oldugu icin de yazi print edildi.

temperature3 <- c(15, 18, 21, 23, 34)
if (any(temperature3 > 20)){
  print("Bugun hava bazi sehirlerde sicak.")
}
[1] "Bugun hava bazi sehirlerde sicak."

Bu ornekte ise all komutunu kullandim. Burada ise vektor icindeki butun elementlerin istedigim conditioni karsilamasi gerekiyor ve biri bile karsilamadigi durumda if statementi cevap vermiyor.

temperature4 <- c(15, 18, 21, 23, 34)
if (all(temperature4 > 14)){
  print("Bugun hava ulke genelinde sicak.")
}
[1] "Bugun hava ulke genelinde sicak."

If statementini tek bir degiskene bagli kullanmamiza gerek yok. Bu ornekte iki farkli veri verdim ve bunlardan birisinin karsilandigi durumu istedim. Nem 60 ile sinirin altinda kalmasina ragmen hava sicakligi 30 oldugu icin print etti.

temperature5 <- 30
humidity <- 60
if (any(temperature5 > 25, humidity > 65)) {
  print("Bugun hava sicak veya nemli olacak.")
}
[1] "Bugun hava sicak veya nemli olacak."

Benzer sekilde all kullanip, 2 degiskenin de istenilen conditionlari karsilayip karsilamadigina bakabiliriz. Burada hava sicakligi hala istenilenden yuksek olmasina ragmen nem istenilen seviyenin altinda kaldigi icin print etmedi.

temperature5 <- 30
humidity <- 60
if (all(temperature5 > 25, humidity > 65)) {
  print("Bugun hava sicak ve nemli olacak.")
}

If else

Ilk ornegimizde conditionlar karsilanmadigi zaman bir sonuc alamiyorduk. Eger sonucun karsilanmadigina dair bir bildirim istiyorsak da if else komutunu kullanabiliriz. Eger belirtigimiz sartlar karsilaniyorsa if kismindan sonrasi alinir ancak karsilanmiyorsa else kismina gecilir.

yas <- 19

if (yas >= 18) {
  print("18 yasindan buyuk oldugunuz icin alisveris yapabilirsiniz.")
} else {
  print("18 yas altina icki satisi yapamiyoruz.")
}
[1] "18 yasindan buyuk oldugunuz icin alisveris yapabilirsiniz."
yas2 <- 17

if (yas2 >= 18) {
  print("18 yasindan buyuk oldugunuz icin alisveris yapabilirsiniz.")
} else {
  print("18 yas altina icki satisi yapamiyoruz.")
}
[1] "18 yas altina icki satisi yapamiyoruz."

Bu ornekte iki durumu da gorebiliyoruz. Ilk ornegimizde condition karsilandigi icin ona gore cikti geliyor ve karsilanmadigi zaman ona gore bildirim veriyor. Bu ikisinin kullanimi amacimiza gore degisir. Mesela notu 50 ustu olan ogrencileri gecir ve 50 altinda olanlari dersten birak diyorsak if else kullanmak daha dogru olacaktir. Ancak sinavdaki zor bir soruyu dogru yapanlara 3 puan ek verme amacimiz varsa diger gruplarla bir isimiz olmadigi icin if yeterli olacaktir.

If else if

Bazi durumlardaysa ikiden fazla derecelendirme yapmamiz gerekebilir. If-else dikotomik ayrimlarda ise yararken bu sayinin ikiyi asmasi durumunda if else if komutunu kullanmamiz gerekir. Ornek olarak:

score <- 85

if (score >= 90) {
  print("Grade: A")
} else if (score >= 60) {
  print("Grade: B")
} else if (score >= 70) {
  print("Grade: C")
} else if (score >= 80) {
  print("Grade: D")
} else {
  print("Grade: F")
}
[1] "Grade: B"

Bu ornekte harf notlandirmasi yapiyoruz. Notun belli notlarin ustunde olmasi durumunda aldigi birden cok deger var. Bu komutta R en tepeden baslayarak hepsini deniyor ve goruldugu uzere else if komutlarinin ustunden geciyor. Bunlar karsilandigi durumda gereken islemi yapiyor ama karsilanmazsa else ile belirttigimiz kisima gelip tamamliyor. Burada dikkat edilecek bir nokta en sonra else if kullanmamamiz. Else if en son sonuc icin degil, sartlarin saglanabilecegi durumlarda kullanilan bir komut. Istatistik alaninda bu karar mekanizmalarina decision tree denir.

Nested if else

Bu en komplike if-else statementlarindan birisi ancak yine de basit bir mantik ustune kurulu. Bir sartin saglanmasi durumunda ek sart ariyorsak ve hatta o sartin da saglanmasi durumunda ek bir sart daha ariyorsak bu tip islem yapmak en mantiklisi olacaktir. Ornek olarak bir siyasetcinin partiden adayligina bakabiliriz. Bu siyasi parti ilk olarak kadin adaylara oncelik kotasi taniyor. Eger adaylar bu sarti saglamiyorsa engelli olup olmadiklarina bakiliyor. Eger bu sarti da saglamiyorsa yaslarinina bakiliyor. Bu sartlardan herhangi birisini saglayan adaylar oncelik kazanirken saglamayanlar normal aday oluyor.

Ornek adayimiz:

aday_cinsiyet <- "male"
is_disabled <- FALSE
aday_yas <- 40
if (aday_cinsiyet == "female") {
  print("Kadin kotasindan aday olmaya hak kazandiniz")
} else {
  if (is_disabled) {
    print("Engelli kotasindan aday olmaya hak kazandiniz")
  } else {
    if (aday_yas < 35) {
      print("Genc kotasindan aday olmaya hak kazandiniz")
    } else {
      print("Bir kotadan aday olma hakkiniz yok.
            Normal adaylik surecinden gececeksiniz.")
    }
  }
}
[1] "Bir kotadan aday olma hakkiniz yok.\n            Normal adaylik surecinden gececeksiniz."

Burada yaptigimiz nested if else en disaridan iceriye dogru isliyor. Ilk olarak adayin kadin olup olmadigina bakiliyor ve bunu karsiliyorsa en tepedeki sonucu aliyoruz. Eger karsilamiyorsa else kismina gecip yeni bir if kismina geciyoruz ve boyle ilerliyor. En sonundaysa else ile biten yerde sonuc aliniyor. Aslinda mantik olarak else if ile benzer ama sadece duz else if yazmak yerine baska kondisyonlari da inceliyoruz.

Logical Operators

Daha onceden mantik operatorlerini gormustuk. Kisaca hatirlamak gerekirse

  • & ve anlamina geiyordu ve iki kondisyonun da dogru olmasi gerekiyordu
  • veya anlamina geliyordu ve sadece bir kondisyonun dogru olmasi yeterdi
  • ! ise terse ceviriyordu.

Bunlari if else icinde kullanip benzer bir sistem kurabiliriz

sicaklik <- 22 
yagmur <- TRUE
if (sicaklik >= 20 & !yagmur) {
  print("Hava sicak ve yagmur yok, parkta oturalim.")
} else if (sicaklik >= 20 & yagmur) {
  print("Hava sicak ama yagmurlu. Yagmur durana kadar iceride olalim.")
} else if (sicaklik < 20 & !yagmur) {
  print("Hava serin ama yagmur yok, yuruyus yapalim.")
} else if (sicaklik < 20 & yagmur) {
  print("Hava soguk ve yagisli. Evde aktivite yapalim.")
}
[1] "Hava sicak ama yagmurlu. Yagmur durana kadar iceride olalim."

Bu ornekte farkli mantik operatorleri kullanarak farkli sonuclar elde ettik. Burada dikkat edilmesi gereken bir nokta else ile bitirmemem. Eger bu durumlarin hicbiri karsilamasaydi else ile bitirip bir sonuc cikartmam gerekirdi ancak karsilandigi icin else ile bitirmeden else if ile tamamlayabildim. Karmasik bir yapisi oldugu icin hazirlarken kafa karistirabilir ama dikkatli bir planlamayla bu sikintiya dusmezsiniz.

Vektorler ustunden ifelse ile islem yapmamiz mumkundur. Mesela yaslarin bulundugu bur vektor alalim ve oy kullanip kullanamaycaklarina bakalim

yaslar <- c(16, 21, 17, 19, 18, 23)
oy_hakki <- ifelse(yaslar >= 18, "Yes", "No")
oy_hakki
[1] "No"  "Yes" "No"  "Yes" "Yes" "Yes"

Benzer sekilde nested islemle de daha onceden yaptigimiz gibi islem yapabiliriz. Ders notlari ornegine donecek olursak:

scores <- c(85, 92, 78, 95, 88, 70, 55)
grades <- ifelse(scores >= 90, "A",
                 ifelse(scores >= 80, "B",
                        ifelse(scores >= 70, "C", "F")))
print(grades)
[1] "B" "A" "C" "A" "B" "C" "F"

Ozellikle kategorik degiskenlerde kullanabilecegimiz swtich komutu ise bu uzun ifelse siralamalarina bir alternatif olusturuyor. Bunun mantigi alternatifler arasindan secim yapip ifelse komutlarina kullanmaya gerek birakmamak. Mesela haftanin gunlerine gore aksam aktivitesi planlayalim. Bunu ifelse ile yapmak isteseydik boyle uzun ve cok anlasilmaz bir kod yazacaktik.

aksam_aktivitesi_ifelse <- function(gun) {
  if (gun == "pazartesi") {
    return("Muze etkinligine git")
  } else if (gun == "sali") {
    return("Sinemaya git")
  } else if (gun == "carsamba") {
    return("Spora git")
  } else if (gun == "persembe") {
    return("Kahve icmeye git")
  } else if (gun == "cuma") {
    return("Is cikisi puba git")
  } else if (gun == "cumartesi") {
    return("Arkadaslarina gece kulubune git")
  } else if (gun == "pazar") {
    return("Film izleyip erkenden yat")
  } else {
    return("Gecersiz gun")
  }
}
aksam_aktivitesi_ifelse("sali")
[1] "Sinemaya git"

Switch kullanarak ise cok rahatca hallediyoruz

aksam_aktivitesi_switch <- function(gun) {
  switch(gun,
    monday = "Muze etkinligine git",
    tuesday = "Sinemaya git",
    wednesday = "Spora git",
    thursday = "Kahve icmeye git",
    friday = "Is cikisi puba git",
    saturday = "Arkadaslarina gece kulubune git",
    sunday = "Film izleyip erkenden yat",
    "Gecersiz gun"
  )
}
aksam_aktivitesi_switch("monday")
[1] "Muze etkinligine git"

SON