ساختارهای کنترلی (Control Structures) در زبان R به شما اجازه میدهد تا نحوه اجرای عبارات نوشتهشده را پایش کنید. بهاینترتیب با قرار دادن عبارات منطقی، بسته به اینکه ورودیها چگونه باشد، دستورات متفاوتی اجرا میشود. ساختارهای کنترلی عمده در زبان R به شرح زیر است:
دستور if و else: بررسی یک شرط و عمل بر اساس آن
دستور switch: بررسی رابطه برابری
دستورfor: اجرای یک حلقه به تعداد مشخص
دستور while: اجرای یک حلقه تا زمانی که یک شرط برقرار است
دستور repeat: اجرای بینهایت بار یک حلقه (باید همراه دستور break بکار رود تا متوقف شود)
دستور break: متوقف کردن یک حلقه
دستور next: پریدن از روی یک عبارت
در این مقاله من به نحوه کدنویسی ساختارهای کنترلی در R میپردازم. رویکرد من در آموزش برنامهنویسی R بیشتر مبتنی بر استفاده از مثال است تا خواننده با اجرای کدها بتواند این زبان را یاد بگیرد.
برای آشنایی با نحوه شروع به کار با زبان R به مقاله “آموزش زبان R برای علوم داده: مباحث مقدماتی” مراجعه کنید.
عبارات شرطی
در مثال زیر سادهترین حالت برای ایجاد یک عبارت شرطی را در R میبینید. در ابتدا من به متغیر x مقدار ۵ را تخصیص دادم. سپس یک عبارت شرطی نوشتم که مقدار متغیر x را بررسی میکند. اگر بزرگتر از صفر بود، دستور print اجرا و اعلام میشود که آن عدد مثبت است. در این مثال اگر x منفی باشد، دستور print اجرا نمیشود و R از عبارت شرطی عبور میکند.
1 2 3 4 5 6 7 8 | > #if > x <- 5 > if (x > 0){ + print("Positive Number")} [1] "Positive Number" > x <- -2 > if (x > 0){ + print("Positive Number")} |
حال اگر بخواهم یک عبارت شرطی بنویسم که درصورتیکه متغیر x بزرگتر از صفر باشد، اعلام شود که آن عدد مثبت است و درصورتیکه متغیر x کوچکتر از صفر باشد، اعلام شود آن عدد منفی است، میتوان از ترکیب if و else به شکل زیر استفاده کرد:
1 2 3 4 5 6 | > #if...else > if (x > 0){ + print("Positive Number") + } else { + print("Negative Number")} [1] "Negative Number" |
عبارات شرطی ساده را میتوان در یک خط هم نوشت. برای نمونه کد قبلی را به این شکل در یک خط خلاصه کردم:
1 2 | > if (x > 0) print("Positive Number") else print("Negative Number") [1] "Negative Number" |
ایراد کد بالا در این است که اگر مقدار صفر به متغیر x داده شود، عبارت عدد منفی چاپ میشود که غلط است. برای آنکه بتوانم همه حالتها را در نظر بگیرم، باید از عبارات شرطی به شکل تودرتو استفاده کنم.
1 2 3 4 5 6 7 8 9 10 | > #if…else ladder > x <- 0 > if (x < 0) { + print("Negative Number") + } else { + if (x > 0) { + print("Positive Number") + } else { + print("Zero")}} [1] "Zero" |
دستور switch
وقتی بخواهیم رابطه برابری را بهخصوص درباره یک string چک کنیم میتوان از switch استفاده کرد. برای مثال کد زیر بررسی میکند که اولین آرگومان switch یعنی مقدار b با کدام یک از کاراکترها برابر است و کد مقابل آن کاراکتر را اجرا میکند:
1 2 3 4 5 6 | > switch('b', + 'a' = {x <- 1}, + 'b' = {x <- -1}, + 'c' = {x <- 0}) > x [1] -1 |
اگر اصرار داشته باشیم مثال قبلی را که با if else نوشتم، با switch حل کنیم کد زیر پاسخ درست را بهدست میدهد:
1 2 3 4 5 6 7 | > a <- -5 > switch(as.character(sign(a)), + '1' = 'The number is positive', + '-1'= 'The number is negative', + '0' = 'The number is zero') [1] "The number is negative" |
عبارات شرطی روی قالبهای داده
روش بالا برای زمانی که با بردارها، ماتریسها و قالبهای داده یا دیتا فریمها در R کار میکنیم، کاربرد ندارد. برای نوشتن عبارات شرطی در این حالت باید از تابع ifelse استفاده کرد. در مثال زیر من ابتدا یک دیتا فریم به نام data ایجاد کردم. این دیتا فریم دارای سه ستون است. ستون اول (x1) اعداد فرد بین ۱ تا ۲۰ را شامل میشود. ستون دوم (x2) 10 عدد از مجموعه اعداد بین ۵۰ تا ۱۰۰ را که با استفاده از تابع sample بهصورت تصادفی انتخاب شدند، شامل میشود. ستون سوم (x3) شامل ۱۰ حرف اول الفبای انگلیسی است. و درنهایت با استفاده از data.frame این سه بردار را به شکل دیتا فریم درآوردم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | > #Creat data frame > x1 <- seq(1, 20, by = 2) > x1 [1] 1 3 5 7 9 11 13 15 17 19 > x2 <- sample (50 : 100, 10, replace = F) > x2 [1] 62 71 83 69 68 51 54 70 65 91 > x3 <- LETTERS[1 : 10] > x3 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" > data <- data.frame(x1, x2, x3) > head(data) x1 x2 x3 1 1 62 A 2 3 71 B 3 5 83 C 4 7 69 D 5 9 68 E 6 11 51 F |
حال فرض کنید، میخواهم در دیتا فریم data ستون چهارمی ایجاد کنم که مقادیر این ستون صفر یا یک هستند. مقدار هر درایه در ستون چهارم بر این اساس تعیین میشود که عدد متناظر آن در ستون دوم از ۷۰ بزرگتر است یا نه. برای مثال درایه ردیف اول واقع در ستون دوم، از ۷۰ کوچکتر است، بنابراین درایه واقع در ستون چهارم در ردیف اول باید مقدار صفر به خود بگیرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | > #ifelse in data frame > data$x4 <- ifelse(data$x2 > 70, 1, 0) > data x1 x2 x3 x4 1 1 62 A 0 2 3 71 B 1 3 5 83 C 1 4 7 69 D 0 5 9 68 E 0 6 11 51 F 0 7 13 54 G 0 8 15 70 H 0 9 17 65 I 0 10 19 91 J 1 |
در مثال زیر حالت دیگری را بررسی کردم که ستون جدیدی به نام z در دیتا فریم data ایجاد کردم. در این کد، تابع شرطی ستون سوم دیتا فریم را بررسی میکند و اگر هر درایه واقع در این ستون منطبق با یکی از حروف B ،A و C باشد، مقدار درایه متناظر در ستون z چهار برابر مقدار متناظر در ستون اول است، در غیراینصورت ده برابر مقدار متناظر در ستون اول خواهد شد. برای مثال، در ردیف اول درایه ستون سوم A است، پس شرط برقرار است و مقدار ستون سوم، از حاصلضرب عدد ۱ در ۴ به دست میآید.
1 2 3 4 5 6 7 8 9 10 11 12 13 | > data$z <- ifelse(data$x3 %in% c("A", "C", "F"), data$x1*4, data$x1 * 10) > data x1 x2 x3 z 1 1 62 A 4 2 3 71 B 30 3 5 83 C 20 4 7 69 D 70 5 9 68 E 90 6 11 51 F 44 7 13 54 G 130 8 15 70 H 150 9 17 65 I 170 10 19 91 J 190 |
عبارات شرطی را میتوانید با عملگرهای منطقی “یا” (Or) و “و” (And) هم بکار ببرید. در کد زیر من از ترکیب عبارت شرطی و عملگر “یا” به همراه تابع sum استفاده کردم تا تعداد حالتهای موردنظر را که یک شرط برقرار است به دست آورم. در این مثال، من میخواهم بدانم چند درایه در ستون اول هستند که از ۵ کوچکترند یا از ۱۵بزرگترند.
1 2 | > sum(ifelse(data$x1 < 5 | data$x1 > 15, 1, 0)) [1] 4 |
حلقه for
دستور for یکی از پرکاربردترین روشها برای نوشتن حلقهها در R است. در کدهای زیر، به تعداد ۱۰ بار دستور print اجرا شده است. عددی که چاپ میشود از جمع ۵ با چندمین باری است که دستور اجرا میشود. حلقههای ساده for را میتوان بهصورت خطی نیز نوشت.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | > #Loops > for (i in 1 : 10) { + print(i + 5) + } [1] 6 [1] 7 [1] 8 [1] 9 [1] 10 [1] 11 [1] 12 [1] 13 [1] 14 [1] 15 > for (i in 1 : 10) print(i + 5) [1] 6 [1] 7 [1] 8 [1] 9 [1] 10 [1] 11 [1] 12 [1] 13 [1] 14 [1] 15 |
در این کد، من حلقه را روی دیتا فریم data اجرا کردم. بهاینترتیب که درایههای ستون دوم به ترتیب چاپ میشوند:
1 2 3 4 5 6 7 8 9 10 11 12 13 | > for (i in 1 : 10) { + print(data$x2[i]) + } [1] 62 [1] 71 [1] 83 [1] 69 [1] 68 [1] 51 [1] 54 [1] 70 [1] 65 [1] 91 |
معمولاً تابع seq_along در حلقههایی که برای ماتریسها یا دیتا فریمها مینویسیم، زیاد کاربرد دارد. در نمونه زیر، بهجای آنکه تعداد ردیفهای دیتا فریم را مستقیم به حلقه بدهم، از تابع seq_along استفاده کردم تا خودش تعداد ردیف دیتا فریم را تشخیص دهد و به تعداد آن حلقه را تکرار کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 | > for (i in seq_along(data$x2)){ + print(data[i, 2]) + } [1] 62 [1] 71 [1] 83 [1] 69 [1] 68 [1] 51 [1] 54 [1] 70 [1] 65 [1] 91 |
مانند عبارات شرطی میتوانیم، حلقههای تودرتو هم داشته باشیم. در زیر من از دو حلقه بهصورت تودرتو استفاده کردم تا یک ماتریس مربعی ۱۰ در ۱۰ مشابه جدولضرب ایجاد کنم. توجه کنید در این مثال من ابتدا ابعاد ماتریس را تعریف کردم ولی مشخص نکردم درایههای آن باید چگونه باشند. سپس با استفاده از حلقههای تودرتو، درایههای آن را تخصیص دادم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | > #Nested for loops > m <- matrix(nrow = 10, ncol = 10) > m [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] NA NA NA NA NA NA NA NA NA NA [2,] NA NA NA NA NA NA NA NA NA NA [3,] NA NA NA NA NA NA NA NA NA NA [4,] NA NA NA NA NA NA NA NA NA NA [5,] NA NA NA NA NA NA NA NA NA NA [6,] NA NA NA NA NA NA NA NA NA NA [7,] NA NA NA NA NA NA NA NA NA NA [8,] NA NA NA NA NA NA NA NA NA NA [9,] NA NA NA NA NA NA NA NA NA NA [10,] NA NA NA NA NA NA NA NA NA NA > for (i in 1 : nrow(m)) { + for(j in 1 : ncol(m)){ + m[i, j] <- i * j + } + } > m [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [1,] 1 2 3 4 5 6 7 8 9 10 [2,] 2 4 6 8 10 12 14 16 18 20 [3,] 3 6 9 12 15 18 21 24 27 30 [4,] 4 8 12 16 20 24 28 32 36 40 [5,] 5 10 15 20 25 30 35 40 45 50 [6,] 6 12 18 24 30 36 42 48 54 60 [7,] 7 14 21 28 35 42 49 56 63 70 [8,] 8 16 24 32 40 48 56 64 72 80 [9,] 9 18 27 36 45 54 63 72 81 90 [10,] 10 20 30 40 50 60 70 80 90 100 |
حلقه while
در بیشتر کاربردها، حلقه for استفاده میشود. اما در کاربردهای مربوط به شبیهسازی احتمال زیادی دارد که از حلقه while استفاده کنیم. در زیر، من نحوه استفاده از حلقه while را با یک مثال ساده نشان دادم. هدف از این حلقه چاپ اعداد صحیح ۰ تا ۹ است. همانطور که مشخص است این حلقه تا زمانی ادامه مییابد که مقدار متغیر c کوچکتر از ۱۰ باشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | > #Loops with While > c <- 0 > while(c < 10){ + print(c) + c <- c + 1 + } [1] 0 [1] 1 [1] 2 [1] 3 [1] 4 [1] 5 [1] 6 [1] 7 [1] 8 [1] 9 |
در اینجا از حلقه while استفاده کردم تا پرتاب سکه را شبیهسازی کنم. هدف از شبیهسازی این است که آنقدر سکه پرتاب کنم تا تعداد کل برآمدهای شیر ۳ شود. پس از اینکه سه بار برآمد شیر داشتم، پرتاب سکه متوقف میشود. در آخر نیز باید تعداد کل پرتاب سکهها در این آزمایش به نمایش دربیاید.
برای این منظور دو متغیر flips و nheads با مقدار اولیه صفر تعریف کردم که به ترتیب تعداد پرتابهای سکه در آزمایش و تعداد برآمدهای شیر را نشان میدهد. چون قرار است تعداد پرتابها پس از برقرار شدن یک عبارت شرطی متوقف شود، از while استفاده کردم. از تابع sample بهره بردم تا با هر بار اجرای دستور شرطی از بین شیر و خط یکی را بهصورت تصادفی انتخاب کند. اگر این برآمد برابر شیر باشد، دستور شرطی مقدار nheads را بهروزرسانی میکند. با اجرای حلقه باید یک واحد به متغیر flips اضافه شود. به این فکر کنید در دستور زیر چرا حلقه تا زمانی که متغیر nheads کوچکتر از ۳ است، باید ادامه یابد.
1 2 3 4 5 6 7 8 9 | > #Tossing coin > flips <- 0 > nheads <- 0 > while (nheads < 3) { + if (sample(c("H", "T"), 1) == "H") nheads <- nheads + 1 + flips <- flips + 1 + } > flips [1] 8 |
توجه کنید هر بار که کد بالا اجرا شود، مقدار متفاوتی برای flips به دست میآید. در شبیهسازی بالا پس از ۸ بار پرتاب سکه، سومین برآمد شیر ظاهر و فرآیند پرتاب سکه متوقف میشود.
حلقه repeat و دستور break
با شروع اجرای دستور repeat حلقه به تعداد بینهایت بار تکرار میشود. معمولاً در کاربردهای تحلیل داده با چنین شرایطی روبرو نیستیم که بخواهیم از این دستور استفاده کنیم. تنها راه برای متوقف کردن repeat استفاده از دستور break است. در مثال ساده زیر من یک حلقه با استفاده از دستور repeat و break ساختم:
1 2 3 4 5 6 7 8 9 10 | > #Repeat & break > a <- 1 > repeat { + print(a) + a <- a + 1 + if(a > 4) break} [1] 1 [1] 2 [1] 3 [1] 4 |
در کد بالا، حلقه تا زمانی که مقدار متغیر a از ۴ بزرگتر شود، ادامه مییابد.
توجه کنید دستور break را در حلقههای دیگر نیز میتوان استفاده کرد:
1 2 3 4 5 6 7 8 9 10 11 | > x <- 1 : 10 > for (i in x){ + if (i == 5){ + break + } + print(i) + } [1] 1 [1] 2 [1] 3 [1] 4 |
یک حالت قابلتصور برای استفاده از دستور repeat آن است که در الگوریتم تکرارشوندهای استفاده شود که به دنبال یک جواب میگردد و ما میخواهیم تا وقتی بهاندازه کافی الگوریتم به آن جواب نزدیک نشده، حلقه متوقف نگردد. در این مواقع تعداد تکرارها برای رسیدن به نزدیکی جواب از پیش مشخص نیست. گرچه در این حالت هم باید مراقب بود تا الگوریتم در نزدیکی جواب شروع به نوسان نکند و حلقه تا بینهایت تکرار شود.
دستور next
این دستور زمانی بکار میرود که بخواهیم از تکرارهایی در داخل یک حلقه عبور کنیم. در مثال زیر، زمانی که i به مقدار ۲ میرسد، حلقه از دستور print میپرد.
1 2 3 4 5 6 7 8 9 | > #Next statement > x = 1 : 4 > for (i in x) { + if (i == 2){ + next} + print(i)} [1] 1 [1] 3 [1] 4 |
مقالات آموزش زبان R در آنالیکا
آموزش زبان R برای علوم داده: مباحث مقدماتی
آموزش زبان R برای علوم داده: خواندن و نوشتن دادهها
آموزش زبان R برای علوم داده: عبارات شرطی و حلقهها
سلام و وقت بخیر..چطور در دستور ifelseبرای خلاف شرط دو خروجی بنویسیم ؟؟؟از چه دستوری استفاده کنم
سلام خدمت شما
ممنونم از زحماتتون خیلی عالی بود و به لطف شما خیلی از ابهامات برطرف شد
یه دنیا مهربونی تقدیمتون .
کدوم ورژن R فضای برنامه اش بهتره ؟
اگه امکانش هست لینکش و لطف کنین .
سلام و وقت بخیر
بهتر است از آخرین نسخه R که در حال حاضر نسخه ۴٫۱٫۲ است استفاده کنید:
https://cran.r-project.org/bin/windows/base
توجه کنید R محیط توسعه ساده ای دارد و برای داشتن محیط توسعه بهتر، توصیه می کنم از RStudio استفاده کنید.
پس از نصب R می توانید نسخه رایگان RStudio برای دسکتاپ را از لینک زیر دانلود کنید:
https://rstudio.com/products/rstudio/download
توجه کنید شرکت مذکور دسترسی با آی پی ایران را بسته است. بنابراین نیاز دارید با VPN به وبسایت بالا متصل شوید. نصب نرم افزار سرراست است.
موفق باشید.
سلام وقتتون بخیر
تابع تفاضل دو تاریخ در R چیه ؟
سلام و وقت بخیر و شادی
اگر تاریخ میلادی را که معمولا در حالت اولیه به شکل کاراکتر است با تابع as.Date به کلاس Date برگردانید می توانید عمل تفریق را روی آن انجام دهید. برای نمونه:
d1 <- "11/15/2020"
d1 <- as.Date(d1, format = "%m/%d/%Y")
d2 <- "12/28/2021"
d2 <- as.Date(d2, format = "%m/%d/%Y")
as.numeric(d2 - d1)
[۱] ۴۰۸
سلامی دوباره .
ممنونم ازتون
امکان داره که بجای روز شماری ، سال و ماه و روز رو محاسبه کنه ؟
مثلا خروجیش بشه ۵ سال و ۳ ماه و ۲۰ روز
سلام و وقت بخیر،
طبیعتا با یک محاسبه ساده ریاضی ممکن است. برای مثال اگر خارج قسمت صحیح روز بر ۳۶۵ را محاسبه کنیم تعداد سال محاسبه می شود. اگر باقی مانده صحیح این تقسیم را بر ۱۲ تقسیم کنید، خارج قسمت صحیح آن ماه را محاسبه می کند و الی آخر.
سلام وقتتون بخیر
برنامه های نوشته شده در R رو میشه بصورت فایل اجرایی و گرافیکی در آورد؟
سلام و وقت بخیر،
بله امکان دارد. کتابخانه Shiny برای ساخت رابط کاربری و ساخت اپلیکیشن در R استفاده می شود.
سلام و عرض ادب خدمت شما
با چه دستوری میشه مقایسه ی دو بردار و انجام داد ، و اعدادی که در بردار دوم شبیه بردار اول هست و پاک کنه؟
x<-c(1:10)
y<-c(5,15,12,11,10,0)
سلام و وقت بخیر، به مثال زیر توجه کنید:
x <- 1:10
y <- c(5, 15, 12, 11, 10, 0)
y[! y %in% x]
درود برشما .
لطف کردین ممنون از راهنماییتون