Last chance! 50% off DataCamp Premium
Sale ends in
Bu R döngüleri eğitiminde, R'de yineleme için mevcut yapılar, bu yapıların ne zaman kullanılacağı ve R’nin vektörleştirme özelliği gibi alternatifleri kullanarak döngü görevlerinizi daha verimli şekilde nasıl yerine getireceğiniz ele alınacaktır.
Bu yazı, birkaç döngü örneği sunacak; ardından R'nin zengin kütüphane setinde mevcut olan pek çok seçenek arasındaki en popüler vektörleştirilmiş alternatifler lehine bu örnekleri eleştirecek ve ikinci plana itecektir.
Genel olarak, bu R döngüleri eğitiminin tavsiyesi şu olurdu: döngüler hakkında bilgi edinin. Hem temel düzeyde neler olması gerektiğine ayrıntılı bir bakış sunarlar hem de üzerinde işlem yaptığınız veriyi anlamanızı sağlarlar.
Ve döngüleri net bir şekilde anladıktan sonra, onlardan kurtulun.
Çabanızı vektörleştirilmiş alternatifleri öğrenmeye harcayın. Verimlilik açısından karşılığını verir.
Etkileşimli pratik için, orta düzey R kursumuzdaki döngüler bölümünü deneyin.
“Döngüleme”, “çevrim”, “yineleme” ya da sadece talimatları çoğaltma, bilgisayarların icadından çok önce ortaya çıkmış eski bir pratiktir. Birçok adımlı bir süreci, eylem dizilerini veya ‘toplu’ işlemleri düzenleyerek ve tekrarlanması gereken kısımları gruplandırarak otomatikleştirmekten ibarettir.
Tüm modern programlama dilleri, talimatların veya talimat bloklarının tekrarlanmasına izin veren özel yapılar sağlar.
Genel olarak konuşursak, modern programlama dillerinde bu özel yapıların veya döngülerin iki türü vardır. Bazı döngüler, her yinelemede artırılan bir sayaç veya indeks tarafından kontrol edildiği üzere, önceden belirlenmiş sayıda çalışır. Bunlar for döngüsü ailesinin parçasıdır.
Öte yandan, bazı döngüler mantıksal bir koşulun başlamasına ve doğrulanmasına dayanır. Koşul, döngü yapısının başında veya sonunda test edilir. Bu varyantlar sırasıyla while veya repeat döngüsü ailelerine aittir.
R temel kılavuzuna göre, denetim akışı komutları arasında döngü yapıları for, while ve repeat olup bunlara ek olarak break ve next yan tümceleri bulunur.
Unutmayın ki denetim akışı komutları, bir programın alternatifler arasında dallanmasını ya da tabir yerindeyse “karar vermesini” sağlayan komutlardır.
Bu denetim akışı komutlarını her zaman RStudio komut satırında ?Control çağırarak görebilirsiniz.
?Control
Bu akış şeması, R döngü yapılarını gösterir:

Sonraki bölümler, yukarıdaki şekilde gösterilen bu yapıların her birine yakından bakacaktır. Tartışmaya soldaki yapıyla başlayacağız ve sonraki bölümlerde kademeli olarak sağdaki yapılara doğru ilerleyeceğiz.
For döngülerine videolu bir giriş ve devam egzersizi için, orta düzey R kursumuzdaki For Döngüsü bölümünü deneyin.
Dikdörtgen ‘init’ (veya başlatma) kutusu, elmas (romb) karar sembolü ve i1 dikdörtgen kutusundan oluşan bu döngü yapısı, bilinen sayıda kez yürütülür.
Akış şeması terimleriyle, dikdörtgen kutular “karar gerektirmeyen bir şey yap” anlamına gelir. Romblar ya da elmaslar ise “karar sembolleri”dir ve bu nedenle yalnızca iki olası mantıksal cevabı olan sorulara karşılık gelir: Doğru (T) veya Yanlış (F).
Not olarak, basit tutmak için, şekildeki diğer olası semboller atlanmıştır.
Başlatma dikdörtgenindeki bir veya daha fazla talimatı, belirli bir dizide değerler alabilen bir değişken üzerindeki koşulun değerlendirilmesi izler. Şekilde bu, elmasla temsil edilir: semboller “v değişkeninin mevcut değeri seq dizisine ait mi?” anlamına gelir.
Başka bir deyişle, v'nin mevcut değerinin belirli bir aralıkta olup olmadığını test ediyorsunuz. Bu aralığı genellikle başlatmada 1:100 gibi bir ifadeyle tanımlarsınız ki döngü başlasın.
Koşul sağlanmaz ve sonuç False olursa döngü hiç çalıştırılmaz. Bu, for döngü yapısının sağındaki serbest okla gösterilir. Program daha sonra, döngü bloğundan sonra bulunan ilk talimatı yürütür.
Koşul doğrulanırsa, bir talimat—veya talimatlar bloğu—i1 yürütülür. Ve belki de bu talimatlar bloğu başka bir döngüdür. Bu gibi durumlarda iç içe döngüden söz edilir.
Bu yapıldıktan sonra koşul yeniden değerlendirilir. Bu, i1'den yukarıya, başlatma kutusunun hemen sonrasına giden çizgilerle gösterilir. R'de—ve Python'da—bunu düz İngilizceyle ifade etmek mümkündür: değişkenimizin bir değer aralığına ait olup olmadığını sorarız.
Not ki diğer dillerde, örneğin C'de, koşul daha açık biçimde büyüktür-küçüktür, eşittir gibi mantıksal karşılaştırma operatörleriyle belirtilir…
İşte basit bir for döngüsü örneği:
# Create a vector filled with random normal values
u1 <- rnorm(30)
print("This loop calculates the square of the first 10 elements of vector u1")
# Initialize `usq`
usq <- 0.
for (i in 1:10) {
# i-th element of `u1` squared into `i`-th position of `usq`
usq[i] <- u1[i] * u1[i]
print(usq[i])
}
print(i)
for bloğu süslü parantezler içinde yer alır. Bu parantezler test koşulundan hemen sonra ya da tercihen girintiyle onun altına yerleştirilebilir. Hiçbiri zorunlu değildir, ancak süslü parantezler kesinlikle kodunuzun okunabilirliğini artırır ve döngü bloğunu ve içindeki olası hataları kolayca fark etmenizi sağlar.
Not olarak, kareler vektörü usq başlatılmıştır. Bu, sade RStudio kodunda gerekli olmayabilir; ancak işaretlemeli sürümde, knitr, vektöre döngüde kullanılmadan önce bir başvuru bulunamadığından derleme yapmaz ve RStudio içinde bir hata verir. knitr hakkında daha fazla bilgi için R Markdown sayfasına gidin.
Artık for döngülerinin iç içe de olabileceğini bildiğinize göre, muhtemelen bunu neden ve ne zaman kullanmanız gerektiğini merak ediyorsunuzdur.
Diyelim ki, iki boyutlu bir diziyi elemanlarını belirli değerlere ayarlayarak değiştirmek istiyorsunuz.
O zaman şöyle bir şey yapabilirsiniz:
# Insert your own integer here
my_int <- 42
nr <- as.integer(my_int)
# Create a `n` x `n` matrix with zeros
mymat <- matrix(0, nr, nr)
# For each row and for each column, assign values based on position
# These values are the product of two indexes
for (i in 1:dim(mymat)[1]) {
for (j in 1:dim(mymat)[2]) {
mymat[i, j] = i * j
}
}
İpucu: matrix() fonksiyonu hakkında daha fazla bilgi için R Documentation'ı ziyaret edin.
i runs over the lines and j runs over the columns.Eh, ezbere bilmeniz gereken o çok tanıdık çarpım tablosunu yaptınız.
Ayrıca bir tamsayı seçip seçiminize göre bir tablo üretebilirsiniz: tablo kare ise bir değişkene, dikdörtgen ise iki değişkene bir tamsayı atayabilirsiniz. Bu değişken daha sonra i ve j indeksleri için üst sınır görevi görecektir.
# Show the first 10x10 chunk or the first `nr` x `nr` chunk
if (nr > 10) {
mymat[1:10, 1:10]
} else
mymat[1:nr, 1:nr]
Not olarak, kullanıcının ekranı devasa tablolarla doldurmasını önlemek için, sonunda bir koşul koyarsınız: yalnızca kullanıcı 10'dan büyük bir tamsayı isterse ilk 10 x 10 parçası yazdırılır. Aksi hâlde, bir n x n parça yazdırılacaktır.
Tüm kod şu şekilde görünür:
# Insert your own integer here
my_int <- 42
nr <- as.integer(my_int)
# Create a `n` x `n` matrix with zeroes
mymat <- matrix(0, nr, nr)
# For each row and for each column, assign values based on position
# These values are the product of two indexes
for (i in 1:dim(mymat)[1]) {
for (j in 1:dim(mymat)[2]) {
mymat[i, j] = i * j
}
}
# Show the first 10x10 chunk or the first `nr` x `nr` chunk
if (nr > 10) {
mymat[1:10, 1:10]
} else
mymat[1:nr, 1:nr]
for döngüsü açık ara en popüler olanıdır ve yapısı, yineleme sayısının önceden sabit ve biliniyor olmasını ima eder; “ilk 200 asal sayıyı üret” veya “en önemli 10 müşteriyi listele” gibi durumlarda olduğu gibi.
Ancak, yineleme sayısını bilmediğiniz ya da kontrol edemediğiniz ve önceden öngörülemeyen bir veya birkaç koşulun ortaya çıkabildiği durumlarda ne olur?
Örneğin, belirli bir posta koduyla tanımlanan bir bölgede yaşayan müşteri sayısını ya da son iki gün içinde bir web sayfası afişine tıklanma sayısını saymak isteyebilirsiniz; benzeri öngörülemeyen olaylar gibi.
Bu gibi durumlarda, while döngüsü ve kuzeni repeat imdada yetişebilir…
Yukarıdaki şekilde ortada yer alan while döngüsü, önceden olduğu gibi bir başlatma bloğundan ve onu izleyen mantıksal bir koşuldan oluşur. Bu koşul genellikle bir kontrol değişkeni ile bir değerin karşılaştırılmasıyla; büyük, küçük ya da eşit gibi ifadelerle ortaya konur; ancak Doğru ya da Yanlış olarak değerlendirilen herhangi bir ifade de geçerlidir.
Sonuç Yanlış (F) ise, şekilde sağdaki serbest okun da gösterdiği gibi, döngü hiç çalıştırılmaz. Program daha sonra, döngü bloğundan sonra bulduğu ilk talimatı yürütür.
Eğer Doğru (T) ise, i1 talimatı veya talimat bloğu bir sonraki adımda yürütülür.
Ek olarak bir i2 talimatı veya talimat bloğu eklendiğine dikkat edin: bu, kontrol değişkeni için bir güncelleme işlevi görür; bu da döngünün başındaki koşulun sonucunu değiştirebilir, ancak bu zorunlu değildir. Ya da yürütülen yineleme sayısını izlemek için bir sayaca artış eklemek isteyebilirsiniz. Koşul Yanlış değerlendirildiğinde yinelemeler durur.
Biçim while(cond) expr şeklindedir; burada cond test edilecek koşul, expr ise bir ifadedir.
Örneğin, aşağıdaki döngü, bir Kullanıcı Tanımlı Fonksiyon (UDF) ile kullanıcıdan evrenin ve her şeyin cevabını doğru girmesini ister. Kullanıcı doğru cevabı girene kadar bunu yapmaya devam eder:
# Your User Defined Function
readinteger <- function(){
n <- readline(prompt="Please, enter your ANSWER: ")
}
response <- as.integer(readinteger())
while (response!=42) {
print("Sorry, the answer to whatever the question MUST be 42");
response <- as.integer(readinteger());
}
Başlangıçta, döngüye girmeden önce kullanıcı girdisini almak için bir kullanıcı tanımlı fonksiyon kullanın. Bu döngü, yanıt beklenen 42 olmadığı sürece devam edecektir.
Başka bir deyişle, bunu yaparsınız; çünkü aksi takdirde R, gerekli Doğru ya da Yanlış değeri sağlaması beklenen ifadenin eksik olduğundan şikayet eder—nitekim döngüde kullanmadan önce ‘response’u tanımaz. Ayrıca bunu şunun için de yaparsınız: eğer ilk denemede doğru cevabı verirseniz, döngü hiç çalıştırılmaz.
repeat döngüsü, yukarıdaki akış şemasının en sağında yer alır. Bu döngü, while döngüsüne benzer; ancak i1 ve i2 talimat bloklarının, koşulun sonucundan bağımsız olarak en az bir kez yürütülmesini sağlar.
Diğer dillere uygun olarak, bu döngüye “repeat until” (…’e kadar tekrar et) denebilir; bu, i1 ve i2 talimatlarının koşul Yanlış (F) kaldığı sürece veya eşdeğer şekilde Doğru (T) olduğunda (böylece çıkış yapıldığında) çalıştırıldığını vurgular; ama her hâlükârda en az bir kez.
Önceki örneğin bir varyasyonu olarak şunları yazabilirsiniz:
readinteger <- function(){
n <- readline(prompt="Please, enter your ANSWER: ")
}
repeat {
response <- as.integer(readinteger());
if (response == 42) {
print("Well done!");
break
} else print("Sorry, the answer to whatever the question MUST be 42");
}
Artık tanıdık olan giriş fonksiyonundan sonra, bloğu en az bir kez çalıştırılan ve if koşulu doğrulandığında sona erecek olan repeat döngüsüne sahipsiniz.
Döngü içinde, break yan tümcesiyle çıkış yapılacak bir koşul belirlemeniz gerektiğine dikkat edin. Bu yan tümce, bizi döngüler içinde çevrimleri sonlandırma veya kesme kavramına getirir.
Peki bir döngüden nasıl çıkarsınız?
Diğer bir deyişle, ya döngünün “doğal” sonu dışında—ki bu ya öngörülen yineleme sayısına ulaştığınız için (for) ya da bir koşulu sağladığınız için (while, repeat) gerçekleşir—döngüyü durdurabilir veya kesebilir misiniz?
Ve eğer evetse, nasıl?
break ifadesi ilk soruyu yanıtlar: bunu son örnekte gördünüz.
break ile KırınR yorumlayıcısı bir break ile karşılaştığında, denetimi döngünün hemen sonrasındaki talimata (varsa) devreder. İç içe döngülerde, break yalnızca en içteki döngüden çıkmaya izin verir.
İşte bir örnek.
Bu kod parçası sıfırlardan oluşan bir m x n matrisi tanımlar ve daha sonra, yalnızca iki indeks farklıysa, matrisin konumlarını doldurmak için iç içe bir for döngüsüne girer. Amaç, ana köşegenin altındaki elemanların sıfır olmayan değerlere sahip olduğu bir alt üçgensel matris oluşturmaktır. Diğerleri başlatılmış sıfır değerinde bırakılır.
İndeksler eşit olduğunda ve dolayısıyla sütun indeksi j üzerinde çalışan iç döngüdeki koşul sağlandığında, bir break çalıştırılır ve en içteki döngü, iç döngüyü izleyen talimata doğrudan atlama ile kesilir. Bu talimat bir print() talimatıdır. Daha sonra denetim, tekrar değerlendirilen dış for koşuluna (satırlar, indeks i) gelir.
Eğer indeksler farklıysa, atama yapılır ve sayaç 1 artırılır. Sonunda, atanan elemanların sayısını içeren ctr sayacı yazdırılır.
# Make a lower triangular matrix (zeroes in upper right corner)
m = 10
n = 10
# A counter to count the assignment
ctr = 0
# Create a 10 x 10 matrix with zeroes
mymat = matrix(0, m, n)
for (i in 1:m) {
for (j in 1:n) {
if (i == j) {
break
} else {
# you assign the values only when i<>j
mymat[i, j] = i * j
ctr = ctr + 1
}
}
print(i * j)
}
# Print how many matrix cells were assigned
print(ctr)
Not ki, zorunlu olmasalar bile, bazen aşırı temkinli davranıp süslü parantezleri koymak isteyebilirsiniz. Genellikle, bir { ile açılan her şeyin bir } ile kapandığından emin olmak için yaparsınız. Böylece { veya } sayılarının eşleşmediğini fark ederseniz bir hata olduğunu bilirsiniz—tersi her zaman doğru olmasa da!
next Kullanımınext, belirli bir yinelemeyi sonlandırır ve bir sonraki çevrime atlar. Aslında mevcut döngünün koşulunun değerlendirilmesine sıçrar.
Diğer dillerde (biraz kafa karıştırıcı) eşdeğeri “continue” olarak bulunabilir; anlamı aynıdır: nerede olursanız olun, koşul doğrulanınca döngünün değerlendirilmesine atlayın.
Bir koşul gerçekleştiğinde belirli bir çevrimi atlayıp döngüyü sürdürmenin daha basit bir örneği şudur:
m = 20
for (k in 1:m) {
if (!k %% 2)
next
print(k)
}
Bu kod parçası, 1:m aralığındaki tüm tek sayıları (m=20 burada) yazdırır. Başka bir deyişle, 2 ile bölündüğünde sıfır olmayan kalan veren tüm tamsayılar (bu yüzden %% modülüs işleci kullanılır) if testinde belirtildiği gibi yazdırılacaktır.
Kalanı sıfır olan sayılar yazdırılmayacaktır; çünkü program, i in 1:m koşulunun değerlendirilmesine atlar ve ardından gelebilecek talimatları yok sayar. Bu durumda print(k) yok sayılır.
repeat kullanırken dikkatli olun: bir koşulu test ederek sonlandırmanın açıkça ayarlandığından emin olun; yoksa sonsuz döngüye girebilirsiniz.Tüm bunlar iyi hoş da, R'de döngüleri ne zaman kullanmalı, ne zaman kullanmamalısınız?
Bir işlem(ler)in tekrarlanması gerektiğinde, bir döngü işe yarayabilir.
Yapmanız gereken tek şey, bu işlemlerin kaç kez veya hangi koşullar altında yürütülmesi gerektiğini belirtmektir: bir kontrol döngüsü değişkenine başlangıç değerleri atar, döngüyü yürütür ve döngü bittikten sonra genellikle sonuçlarla bir şeyler yaparsınız.
Peki ne zaman döngü kullanmalısınız?
İstediğiniz talimatı yeterli sayıda kez kopyalayıp yapıştıramaz mıydınız?
Eh, kabaca bir kural olarak, bir eylemi (diyelim ki) üç kez veya daha fazla gerçekleştirmeniz gerekiyorsa, bir döngü daha iyi iş görür. Kodu daha derli toplu, okunabilir ve sürdürülebilir kılar ve yazım zahmetinden tasarruf edersiniz: diyelim ki belirli bir talimatın başlangıçta öngörülenden bir kez daha tekrarlanması gerektiğini keşfettiniz; tüm talimatı yeniden yazmak yerine, test koşulundaki bir değişkenin değerini değiştirmeniz yeterlidir.
Yine de, R'nin kendine has doğası, alternatifler varken hiç döngü kullanmamayı (!) önerir.
Neyse ki alternatifler var!
R, az sayıda programlama dilinde bulunan bir özelliğe sahiptir: vektörleştirme.
Kelimenin de işaret ettiği gibi, vektörleştirme, basit sayılar (“skalerler”) üzerindeki tekrarlı işlemleri, vektörler veya matrisler üzerindeki tekil işlemlere dönüştürme işlemidir. Yukarıdaki alt bölümlerde bunun birkaç örneğini gördünüz.
Artık bir vektör, R'deki temel veri yapısıdır ve R temel kılavuzuna göre “bir şeyler koleksiyonundan oluşan tek bir varlık”tır.
Dolayısıyla bir sayı koleksiyonu, sayısal bir vektördür.
Eşit uzunluktaki vektörleri birleştirirseniz bir matris elde edersiniz. Bunu dikey veya yatay olarak, farklı R talimatları kullanarak yapabilirsiniz. Böylece R'de bir matris, yatay veya dikey vektörlerin bir koleksiyonu olarak görülür. Bu genişletmeyle, vektörler üzerindeki tekrarlı işlemleri de vektörleştirebilirsiniz.
Yukarıdaki döngü yapılarının çoğu, vektörleştirme kullanılarak örtük hâle getirilebilir.
“Örtük” diyorum; çünkü gerçekte ortadan kaybolmazlar. Daha alt düzeyde, alternatif vektörleştirilmiş form, uygulandığı ve derlendiği alt dilde (Fortran, C veya C++) bir veya daha fazla döngü içeren koda dönüşür.
Bunlar kullanıcıdan gizlenir ve genellikle eşdeğer açık R kodundan daha hızlıdır; ancak bu dillerden biriyle kendi R fonksiyonlarınızı yazmayı planlamadığınız sürece, bu sizin için tamamen şeffaftır.
Düşünebileceğiniz en temel durum, v1 ve v2 iki vektörünün v3 vektörüne toplanmasıdır; bu, bir for döngüsüyle eleman bazında yapılabilir:
v1 <- c(3,4,5)
v2 <- c(3,4,6)
v3 <- c(0,0,0)
n = length(v1)
for (i in 1:n) {
v3[i] <- v1[i] + v2[i]
}
v3
Ya da “yerel” vektörleştirilmiş formu da kullanabilirsiniz:
v3 = v1 + v2
v3
Not ki “yerel” dersiniz; çünkü R tüm aritmetik operatörlerin vektörler ve matrisler üzerinde etkili olduğunu bilir.
Benzer şekilde, iki A ve B matrisi için, karşılık gelen konumlarda A[i,j] ve B[i,j] elemanlarını toplamak ve bunun için iki indeks i ve j ile uğraşmak yerine, R'ye şunu söylersiniz:
A <- matrix(1:6, nrow = 3, ncol = 2)
B <- matrix(c(2,4,3,1,5,7), nrow=3, ncol=2)
C= A + B
C
Ve bu gerçekten çok basit!
Görünürde temel işlemlerin sayısı aynıyken, vektörleştirmenin neden daha hızlı çalıştığını merak edebilirsiniz.
Bu, R'nin iç işleyişine bakılarak en iyi şekilde açıklanır; bu da ayrı bir yazı gerektirir; ancak kısaca: her şeyden önce, R yorumlanan bir dildir ve bu nedenle değişken tanımlarıyla ilgili tüm ayrıntılar yorumlayıcı tarafından ele alınır. Örneğin, bir sayının kayan noktalı olduğunu belirtmeniz ya da bellekte bir işaretçi kullanarak bellek ayırmanız gerekmez.
R yorumlayıcısı bu hususları, komutlarınızı girerken bağlamdan “anlar”; ancak bunu komut bazında yapar. Dolayısıyla, belirli bir komutu her verdiğinizde, onu tekrarlasanız bile, bu tanımlarla uğraşması gerekir.
Derleyici ise, tüm tanımları ve bildirileri, herhangi bir şeyi çalıştırmayı denemeden önce, tüm kod üzerinde derleme zamanında çözer; kodu, derli toplu ve optimize edilmiş ikili koda çevirir. Şimdi, R fonksiyonları bu alt düzey dillerden biriyle yazıldığından, daha verimlidir.
Pratikte, alt düzey koda bakılsa, genellikle “sarmalayıcı” kod içinde uygulanmış C veya C++ çağrıları görülecektir.
İkinci olarak, vektörleştirmeyi destekleyen dillerde (R veya Matlab gibi), sayısal bir veriyi kullanan her talimat, tek bir elemandan oluşsa bile, yerel olarak vektör olarak tanımlanan bir nesne üzerinde çalışır. Örneğin, tek bir sayısal değişken tanımladığınızda; R'deki iç temsili, yalnızca bir sayıdan oluşsa da, her zaman bir vektör olacaktır.
Döngüler kaputun altında varlığını sürdürür; ancak daha alt ve çok daha hızlı C/C++ derlenmiş düzeyde. Bir vektöre sahip olmanın avantajı, tanımların, boyutundan bağımsız olarak, yorumlayıcı tarafından tüm vektör için yalnızca bir kez çözülmesidir; skaler üzerinde yapılan bir döngüde ise tanımlar ve ayırmalar eleman bazında yapılmak zorundadır ve bu daha yavaştır.
Son olarak, yerel vektör formatıyla çalışmak, çok verimli Doğrusal Cebir yordamlarını (BLAS gibi—Basic Linear Algebra Subprograms) kullanmayı mümkün kılar; böylece vektörleştirilmiş talimatlar yürütülürken R bu verimli sayısal yordamları devreye alır. Dolayısıyla mesaj şudur: mümkünse, yürütülen tüm kopyalama işlemlerinden kaçınmak için tüm veri yapılarını tek bir fonksiyon çağrısı içinde işleyin.
Fazla dalmadan, şimdi vektörleştirmenin genel bir örneğini yapalım; ardından bir sonraki alt bölümde, döngülerin yerine geçecek daha özel ve popüler R vektörleştirilmiş fonksiyonlarına ineceksiniz.
“Veri büyütme” kavramına geri dönelim; bir veri çerçevesini “güncellemenin” tipik olarak verimsiz bir yolu.
Önce, replicate(m, rnorm(n)) ile m x n bir matris oluşturursunuz; burada m=10 adet, her biri n=10 elemanlı sütun vektörü rnorm(n) ile (rastgele normal sayılar) oluşturulur.
Sonra bunu bir veri çerçevesine dönüştürürsünüz (böylece 10 değişkenin 10 gözlemi) ve iç içe bir for döngüsü kullanarak her eleman üzerinde cebirsel bir işlem yaparsınız: her yinelemede, iki indeksin işaret ettiği her elemana sinüsoidal bir fonksiyon eklenir.
Aşağıdaki örnek biraz yapaydır; ancak rastgele bir gürültüye bir sinyal eklenmesini temsil edebilir:
# This is a bad loop with 'growing' data
set.seed(42)
m = 10
n = 10
# Create matrix of normal random numbers
mymat <- replicate(m, rnorm(n))
# Transform into data frame
mydframe <- data.frame(mymat)
for (i in 1:m) {
for (j in 1:n) {
mydframe[i, j] <- mydframe[i, j] + 10 * sin(0.75 * pi)
print(mydframe)
}
}
Burada, yürütme süresinin çoğu kopyalamaya ve döngünün yönetilmesine gider.
Vektörleştirilmiş bir çözüm nasıl görünüyor, bakalım:
set.seed(42)
m = 10
n = 10
mymat <- replicate(m, rnorm(n))
mydframe <- data.frame(mymat)
mydframe <- mydframe + 10 * sin(0.75 * pi)
mydframe
Bu daha basit görünüyor: son satır, iç içe for döngüsünün yerini alıyor. Not olarak, iki uygulamanın tam olarak aynı sonucu verdiğinden emin olmak için set.seed() kullanımına dikkat edin.
Şimdi iki çözümün yürütme süresini nicelleştirelim.
Bunu, system.time() gibi R sistem komutlarını kullanarak yapabilirsiniz; bir kod parçası şu şekilde geçirilir:
İpucu: değerlendirmek istediğiniz kodu system.time() fonksiyonunun parantezleri arasına koymanız yeterlidir.
# Insert `system.time()` to measure loop execution
for (i in 1:m) {
for (j in 1:n) {
mydframe[i, j] <- mydframe[i, j] + 10 * sin(0.75 * pi)
}
}
# Add `system.time()` to measure vectorized execution
mydframe <- mydframe + 10 * sin(0.75 * pi)
Yukarıdaki kod parçasında, m ve n'in seçimi, matrisin oluşturulması ve veri çerçevesine dönüştürülmesi işini başta bir kez yapar; ardından for parçasını, vektörleştirilmiş sürümün “tek satırlık” hâline karşı, iki ayrı system.time() çağrısıyla değerlendirirsiniz.
m=n=10 gibi asgari bir ayarda bile, vektörleştirilmiş sürümün 7 kat daha hızlı olduğunu görürsünüz; bu kadar düşük değerlerde kullanıcı için pek önemli olmasa da.
Farklılıklar, m=n=100 koyarsanız (insan ölçeğinde) fark edilir hâle gelir; 1000'e çıkarmak, for döngüsünün onlarca saniye takılı kalıyormuş gibi görünmesine neden olurken, vektörleştirilmiş form hâlâ göz açıp kapayıncaya kadar çalışır.
m=n=10000 için for döngüsü bir dakikadan fazla takılı kalır; vektörleştirilmiş sürüm ise 2,54 sn sürer. Elbette bu ölçümler hafife alınmalı ve donanım-yazılım yapılandırmasına bağlı olacaktır; muhtemelen dizüstü bilgisayarınızı tarayıcınızda onlarca açık sekmeyle ve arka planda çalışan uygulamalarla aşırı yüklememek gerekir; ancak bu ölçümler farkları göstermek için yeterlidir.
Hakkaniyet gereği, m ve n'in artırılmasının, çoğaltma fonksiyonu etrafına başka bir system.time() çağrısı koyarak kolayca görebileceğiniz gibi, matris üretimini de ciddi biçimde etkilediğini söyleyelim.
Yürütme süresinin nasıl değiştiğini görmek için m ve n ile oynamanız, yürütme süresini m x n çarpımının bir fonksiyonu olarak çizmeniz teşvik edilir. Bu, oluşturulan matrislerin boyutunu ifade ettiğinden anlamlı bir göstergedir. Böylece, nset edilmiş for döngüsüyle görevi tamamlamak için gereken yineleme sayısını da niceler.
Yani bu, vektörleştirmeye bir örnektir. Ama daha pek çoğu var. R News, R projesinin bülteninde 46. sayfada, diziler veya matrislerde belirli boyutlar için toplam ve ortalamaları hesaplamada çok verimli fonksiyonlardan söz edilir: rowSums(), colSums(), rowMeans() ve colMeans().
Ayrıca, bültende “...‘apply’ ailesindeki fonksiyonların, adları [s,l,m,t]apply olan fonksiyonların, başka bir fonksiyonu nesnelerin öğelerine/boyutlarına uygulamak için sağlandığı; bu ‘apply’ fonksiyonlarının, kötü yazılmış döngülerden daha okunabilir ve daha hızlı olan, bazen oldukça karmaşık görevler için derli toplu bir sözdizimi sunduğu” belirtilir.
apply ailesi: sadece gizli döngüler mi?apply fonksiyonlarının çok zengin ve güçlü ailesi, doğası gereği vektörleştirilmiş fonksiyonlardan oluşur. İlk bakışta bunlar herhangi bir döngü içermiyor gibi görünse de, kaputun altına dikkatle baktığınızda bu özellik belirginleşir.
apply komutu ya da daha doğrusu komutlar ailesi, R base paketine aittir. Matrisler veya diziler biçimindeki verilerin dilimler üzerinde tekrarlı şekilde işlenmesine yönelik pek çok fonksiyonla ([s,l,m,r, t,v]apply) donatılmıştır; veriyi aşmanıza veya çaprazlamanıza olanak tanır ve açık döngü kullanımını önler. Fonksiyonlar, bir giriş matrisi ya da dizi üzerinde çalışır ve bir veya birkaç isteğe bağlı argümanla seçilen isimli bir fonksiyonu uygular.
Not ki bu yüzden, (Hadley Wickham'ın advanced R sayfasında olduğu gibi) ‘fonksiyoneller’ kapsamında yer alırlar.
Çağrılan fonksiyon, basit bir ortalama gibi bir toplulaştırma fonksiyonu ya da başka bir dönüştürme veya alt kümeleme fonksiyonu olabilir.
Bu fonksiyonların uygulanmasının mutlaka daha hızlı yürütmeye yol açmadığı not edilmelidir: farklar büyük değildir; daha ziyade zahmetli döngülerin kodlanmasını önler, hata olasılığını azaltır.
Ailedeki fonksiyonlar: apply(), sapply(), lapply(), mapply(), rapply(), tapply(), vapply().
Peki bunları ne zaman ve nasıl kullanmalısınız?
Şöyle not etmekte fayda var: plyr gibi bir paket, ailenin işlevselliğini kapsar; hepsini resmi dökümana bakmadan hatırlamak zor gelebilir; yine de bu fonksiyonlar, daha karmaşık ve kullanışlı birleşimlerin temelini oluşturur.
İlk üçü en sık kullanılandır:
apply()Belirli bir fonksiyonu bir matrisin satırlarına (indeks “1”) veya sütunlarına (indeks “2”) uygulamak istersiniz. Daha fazla bilgi için bu sayfaya bakın.
lapply()Belirli bir fonksiyonu bir listenin her elemanına uygulamak ve sonuç olarak bir liste elde etmek istersiniz (fonksiyon adındaki “l” bunu açıklar). Bu fonksiyon hakkında R dokümantasyonunda okuyun.
sapply()Belirli bir fonksiyonu bir listenin her elemanına uygulamak istersiniz; ancak bir liste yerine bir vektör elde etmeyi diliyorsunuz. Daha fazlasını mı bilmek istiyorsunuz? R dokümantasyon sayfasını ziyaret edin.
İlgili fonksiyonlar sweep(), by() ve aggregate() olup zaman zaman apply() ailesinin üyeleriyle birlikte kullanılır.
Bu blog yazısındaki tartışmayı apply() fonksiyonuyla sınırlandırıyorum (bu konuya daha geniş bir tartışma gelecekteki bir yazının odağı olabilir).
Bir M matrisi verildiğinde, apply(M,1,fun) veya apply(M, 2,fun) çağrısı, 1 belirtilirse M'in satırlarına; 2 belirtildiğinde M'in sütunlarına belirtilen fun fonksiyonunu uygular. Bu sayısal argüman “margin” olarak adlandırılır ve fonksiyon bir matris üzerinde çalıştığı için 1 ve 2 değerleriyle sınırlıdır. Ancak, bunun yerine 8 boyuta kadar bir diziye sahip olabilirsiniz.
Fonksiyon herhangi bir geçerli R fonksiyonu olabilir; hatta apply() içinde kodlanmış bir Kullanıcı Tanımlı Fonksiyon (UDF) bile olabilir; bu da kullanışlıdır.
apply(): bir örnek# define matrix `mymat` by replicating the sequence `1:5` for `4` times and transforming into a matrix
mymat <- matrix(rep(seq(5), 4), ncol = 5)
# `mymat` sum on rows
apply(mymat, 1, sum)
# `mymat` sum on columns
apply(mymat, 2, sum)
# With user defined function within the apply that adds any number `y` to the sum of the row
# `y` is set at `4.5`
apply(mymat, 1, function(x, y)
sum(x) + y, y = 4.5)
# Or produce a summary column wise for each column
apply(mymat, 2, function(x, y)
summary(mymat))
Veri çerçevelerini sık kullanırsınız: bu özel durumda, verilerin aynı türe sahip olduğundan emin olmalısınız; aksi takdirde, zorunlu veri türü dönüşümleri meydana gelebilir ki bu büyük olasılıkla istemediğiniz bir şeydir. Örneğin, metin ve sayı karışık bir veri çerçevesinde, sayısal veriler dizgelere veya karakterlere dönüştürülür.
Böylece bu yolculuk, programlamada kullanılan temel döngü yapılarından, vektörleştirmenin (temel) kavramına ve R'de sıkça karşımıza çıkan apply() ailesinden birinin kullanımına örneğe kadar bizi getirdi.
Kod akışı denetimi açısından yalnızca döngülerle ilgilendiniz: for, while, repeat ve bunları kesip sonlandırma yolları.
Son alt bölümler R'de döngülerden kaçınılması gerektiğine işaret ettiğine göre, neden bunları öğrenesiniz ki diye sorabilirsiniz.
Bana göre, bu programlama yapılarını öğrenmelisiniz; çünkü:
Bu eğitimi okuduktan sonra R'de for döngüleri artık size zor gelmiyorsa, DataCamp'in Intermediate R kursunu düşünebilirsiniz. Bu kurs, Intermediate R'deki konulara ilişkin bilginizi bir dizi yeni ve eğlenceli egzersizle güçlendirecek. Ancak, döngüler sizin için artık sır barındırmıyorsa, Hadley ve Charlotte Wickham'ın verdiği DataCamp'in Writing Functions in R kursu ilginizi çekebilir. Ayrıca For Loops in R eğitimine de göz atın.
Bu eğitimdeki kodu çevrimiçi olarak çalıştırın ve düzenleyin
Kodu çalıştırDöngüler, bir kod bloğunu birden çok kez tekrarlamanın bir yoludur. Aynı işlemi birden çok veri noktası ya da nesne üzerinde yapmak istediğinizde veya belirli bir koşul sağlanana kadar bir işlemi yinelemek istediğinizde kullanışlıdırlar.
R'de iki temel döngü türü vardır: for döngüleri ve while döngüleri.
R'de bir döngüyü durdurmak için break ifadesini kullanabilirsiniz. Bu, döngüden anında çıkar ve kodun geri kalanıyla devam eder.
R'de bir döngünün bir sonraki yinelemesine atlamak için next ifadesini kullanabilirsiniz. Bu, mevcut yinelemenin kalanını atlar ve bir sonrakine geçer.
Evet, R'de döngüleri diğer döngülerin içine yerleştirebilirsiniz. Buna "iç içe" döngüler denir. Örneğin, bir matrisin elemanları üzerinde yineleme yapmak için bir for döngüsünü başka bir for döngüsünün içine koyabilirsiniz.
R hakkında daha fazlasını öğrenin
Kurs
Kurs
Kurs

blog
Abid Ali Awan
14 dk.
blog
Dario Radečić
15 dk.
Eğitim
Adel Nehme
Eğitim
Kurtis Pykes
8 dk.