for() Döngüleri ve Onlardan Nasıl Kaçınılır?

R’a başladığımda döngü yazınca genellikle döngüde birkaç if() ifadesi ile verileri temizlemeye ve yeniden kodlamaya çalışırdım. Bu yüzden her şeyi karmaşık ve sinir bozucu bulurdum.

Bu yazıda hem programlamanızın kalitesini ve hızını artırmak için hem de akıl sağlığınız için for() döngülerinden nasıl kaçınabileceğinizi ele alacağım.

Burada mydata adı verilen klasik veri setimiz var. Rdata (Eğer isterseniz bağlantı adresinden indirebilirsiniz)

Eğer Stata’yı kullanıyorsam yaş grubu değişkeni oluşturmak için yapmam gereken sadece şu:

gen Agegroup=1
replace Agegroup=2 if Age>10 & Age<20
replace Agegroup=3 if Age>=20

Ama bunu R’da denediğimde şu hatayı veriyor:

Neden hata veriyor? Çünkü yaş bir vektördür. Bu yüzden if(mydata$Age<10) sorgusu “Yaş vektörü 10’dan küçük mü” sorgusu yapıyor bu da bizim bilmek istediğimizi karşılamıyor. Biz satır satır her bir ögenin Age<10 sorgusunu yapmasını istiyoruz. Bu yüzden bahsettiğimiz vektörün elemanını belirtmemiz gerekiyor. Öğeyi belirtmediğimiz için de bu uyarıyı alıyoruz (gerçekten, hata).

“Sadece ilk eleman kullanılacak.”  Yani bu başarısız olduğunda, insanlar bu sorunu çözmek ilk şuna benzer bir döngü kullanıyor:

########### Gereksiz yere uzun ve çirkin kod ###########
mydata$Agegroup1<-0
for (i in  1:10){
  if(mydata$Age[i]>10 & mydata$Age[i]<20){
    mydata$Agegroup1[i]<-1
  }
  if(mydata$Age[i]>=20){
    mydata$Agegroup1[i]<-2
  }
}

Burada R’ye i = 1‘den i = 10‘a kadar olan satırlardan aşağı inmesini ve i ile indekslenen bu satırların her biri için yaşın hangi değerin olduğunu kontrol etmesini söylüyor ve sonra Agegroup’a 1 veya 2 değerini atıyor. Bu işe yarar, ancak yüksek bir maliyete sebep olabilir – Tüm bu indeksli vektörlerle kolayca hata yapabilirsiniz ve ayrıca for() döngüleri çok fazla hesaplama zamanı alır, bu da bu veri seti 10 yerine 10000 gözlem olsaydı büyük bir sorun oluştururdu..

Peki bunu yapmaktan nasıl kaçınabiliriz?

Bulduğum en yararlı işlevlerinden biri şimdiye kadar benim bloğumda birkaç kez bahsettiğim bir işlev –“ifelse ()” fonksiyonu.  ifelse() işlevi bir koşulu değerlendirir ve doğruysa bir değer atar ve yanlışsa bir değer atar.  Bunun en güzel yanı, bir vektörde okuyabilmesi ve vektörün her elemanını tek tek kontrol edebilmesidir. Böylece indekslere veya döngüye gerek duymazsınız. Kodu çalıştırmadan önce yeni bir değişkenin başlatılmasına bile gerek yoktur.  Bunun gibi:

mydata$newvariable<-ifelse(Condition of some variable,
                    Value of new variable if condition is true, 
                    Value of new variable if condition is false)

Yani mesela;

mydata$Old<-ifelse(mydata$Age>40,1,0)

Buna göre, mydata$Age vektörünün öğelerinin 40’tan büyük olup olmadığını kontrol edin: Bir öğe 40’tan büyükse, mydata$Old’a 1 değerini atar ve 40’tan büyük değilse, 0 değerini mydata$Old’a atar.

Ama biz 0, 1 ve 2 değerlerini bir Agegroup değişkenine atamak istedik.  Bunu yapmak için iç içe geçmiş ifelse() ifadelerini kullanabiliriz:

mydata$Agegroup2<-ifelse(mydata$Age>10 & mydata$Age<20,1,     
                  ifelse(mydata$Age>20, 2,0))

Şimdi burada şunu söylüyor; önce yaş vektörünün her bir elemanın >10 ve <20 olup olmadığını kontrol edin. Eğer varsa, Agegroup2’ye 1 değerine atayın.  Değilse bir sonraki ifelse () ifadesini değerlendirin Age>20 olup olmadığına bakın, öyleyse Agegroup2’ye 2 değerini atayın. Eğer bunlardan herhangi biri değilse, 0 değerini atayın. Hem döngü hem de ifelse () ifadelerinin bize aynı sonucu verdiğini görebiliriz:

ifelse () ifadesini istediğiniz kadar iç içe yerleştirebilirsiniz. Sadece son kategorinize dikkat edin – Herhangi bir koşulu karşılamayan değerlerin üzerine kalan son değeri atar. (Bu değer NA olsa bile!) Bu yüzden bunun olmasını istediğinizden emin olmalısınız.

Ifelse() ifadesinin kullanıldığı diğer örnekleri:

  • Her bir birey için Cinsiyete göre ağırlık ortalaması olan bir sütun eklemek istiyorsanız, bunu aşağıdaki gibi ifelse () ile yapabilirsiniz:
mydata$meanweight.bysex<-ifelse(mydata$Sex==0,  
               mean(mydata$Weight[mydata$Sex==0], na.rm=TRUE),         
               mean(mydata$Weight[mydata$Sex==1], na.rm=TRUE))
  • Eksik değerleri yeniden kodlamak isterseniz:
mydata$Height.recode<-ifelse(is.na(mydata$Height),
                      9999, 
                      mydata$Height)
  • İki değişkeni bir araya getirmek istiyorsanız, (örnek olarak bu veri çerçevesine ekledim) yıla ve kimliğe dayalı yeni bir ID değişkeni oluşturmak gibi:
mydata$ID.long<-ifelse(mydata$ID<10, 
                paste(mydata$year, “-0″,mydata$ID,sep=””), 
                paste(mydata$year, “-“, mydata$ID, sep=””))

Döngüyü önlemenin diğer yolları:

Apply fonksiyonu : Verilerinizdeki her bir gözlem için bir çeşit fonksiyon uygulamak zorunda olduğunuz için döngü kullanmanız gerektiğini düşünüyorsanız, tekrar düşünün! Bunun yerine Apply () işlevlerini kullanın. Örneğin:

  • Çok fazla eksik değeriniz varsa ve hepsini bir kerede yeniden kodlamak istiyorsanız veya bir satırda belirli bir değeri kaç kez gördüğünüzü özetlemek istiyorsanız, buradaki apply fonksiyonu hakkındaki yazıma bakabilirsiniz.
  • Yukarıdaki yaş gruplamasını yapmak için cut() gibi diğer işlevleri de kullanabilirsiniz. Bu fonksiyonun nasıl çalıştığı ile ilgili yazının olduğu bağlantı adresini buradan erişebilirsiniz, bu yüzden tekrar üzerinden geçmeyeceğim. Eğer bir faktörden bir sayıya dönüştürürseniz, sayıya dönüştürmeden önce her zaman bir karaktere dönüştürün.
mydata$Agegroup3<-as.numeric(as.character(cut(mydata$Age, c(0,10,20,100),labels=0:2)))

Temel olarak, bir döngü yapmanız gerektiğini düşündüğünüzde, bunu başka bir fonksiyonla nasıl yapabileceğinizi düşünün. Bu size fazlasıyla zaman kazandıracak ve kodunuzda hata yapmaktan koruyacaktır.

Kaynak: For loops (and how to avoid them) | R-bloggers (r-bloggers.com)