img

آموزش برنامه‌نویسی آماری با R

/
/
/

یادگیری اصول اولیه استفاده از زبان برنامه‌نویسی R برای آنالیز داده‌ها و اجرای شبیه‌سازی‌ها

 

از زمان شروع به کار آن در سال ۱۹۹۳، زبان R تکامل یافته تا به یکی از پرکاربردترین پکیج‌های برنامه‌ریزی در جهان تبدیل شود و معروفترین زبان برنامه‌نویسی است که به طور خاص برای آمار طراحی شده است.
زبان R از جمله داستان‌های موفقیت‌آمیز در حوزه توسعه منبع باز است که زبان غیرآزاد قبل از خودش یعنی S را تحت تاثیر قرار داده و بر اساس جامعه برنامه‌نویسان بزرگ ساخته شده تا با پکیج‌های خارجی بسیار مهم، یک کتابخانه غنی بسازد.
R یک زبان مبتنی بر ماتریس همچون MATLAB یا Octave است و برای استفاده از پارادایم سبک تابعی (functional-style paradigm) طراحی شده است. نتیجه آن یک زبان بسیار انعطاف‌پذیر و بهینه سازی شده برای تحلیل آماری است.
البته در این مقاله نمی‌توانیم به تمام خصوصیات زبان R بپردازیم، اما سعی می‌کنیم ایده‌ای از رنگ این زبان و عملکردهای متفاوتی که پشتیبانی می‌کند را ارائه نماییم. در این مقاله به چند مطالعه موردی مبتنی بر موضوعات آماری می‌پردازیم، پارادوکس سیمپسون و مسئله مونتی هال به ما کمک می‌کند تا برخی پارادایم‌هایی که R پشتیبانی می‌کند را درک کنیم.
وقتی اصول اولیه برنامه‌نویسی R را فهمیدید، منابع آنلاین زیادی وجود دارند که می‌توانید جستجو کنید تا به شما در مسائل آماری خاص کمک نمایند.

 

پس شروع می‌کنیم
اولین مرحله برای کار کردن با R ، باز کردن REPl (read-execute-print loop) است که ما برای اجرای فرمان‌ها در زمان واقعی استفاده می‌کنیم.
اگر RStudio IDE دارید، پیشنهاد می‌کنیم آن را راه‌اندازی کنید. در غیر اینصورت می‌توانید

$ R

را در خط فرمان تایپ کنید تا به ورژن محدود شده‌تر دسترسی داشته باشید.
ما با یک موضوع آماری کلاسیک آغاز می‌کنیم: پارادوکس سیمپسون. مثال خاصی که استفاده می‌کنیم، معروف است و پیرامون دو روش درمان سنگ کلیه متمرکز است.
اولین درمان که تهاجمی‌تر هم هست، جراحی باز (OS) می‌باشد که اغلب شامل یک برش بزرگ در پهلو و یک برش در خود کلیه است. درمان دوم نفرولیتوتومی از طریق پوست (PN) است که شامل یک برش بسیار کوچکتر در بیمار و وارد کردن یک سیم در داخل کلیه است که موجب از بین رفتن سنگ‌ها می‌شود. اگر سنگ بزرگ باشد، ممکن است با استفاده از پروب اولتراسوند خرد شود.
ما قصد داریم از دیتا شیت یا برگه اطلاعات kidney-stones.cvs استفاده کنیم که مبتنی بر داده‌های واقعی از مقاله شیراگ و دیگران است که در مورد تاثیر این دو درمان می‌گوید. فایل CSV به ستون‌هایی که نوع درمان صورت گرفته (OS یا PN )، جنسیت بیمار، موفقیت یا عدم موفیت درمان و همچنین «گروه» که بزرگی سنگ کلیه را نشان می‌دهد، تقسیم شده است. بیمارانی که سنگ کلیه آنان کمتر از ۲ سانتی متر قطر دارد در گروه ۱ قرار می‌‌گیرند و بیمارانی که سنگ کلیه آنان حداقل ۲ سانتی متر قطر دارد در گروه ۲ قرار می‌گیرند.
اولین مرحله، لود کردن این فایل CSV در R است. وقتی فایل را در جایی از سیستم خود دانلود کردیم (/path/to/kidney-stones.cs در مثال زیر) از تابع توکار read.csv برای انجام این کار استفاده می‌کنیم:

> patients <- read.csv(‹/path/to/kidney-stones
csv›)

R از <- operator برای تعیین مقادیر متغیرها استفاده می‌کند. در اینجا ما یک متغیر جدید به نام patients ( بیمار) ایجاد کرده‌ایم و با استفاده از تابع read.csv آن را مستقر نمودیم. برای آن که خوب درک کنید که متغیر patients شامل چه چیزیست، می‌توانیم نام آن را در REPL تایپ کنیم، مانند تصویر ۱٫ از آنجایی که فایل CSV دارای ۷۰۰ ورودی است، خروجی این فرمان بسیار بزرگتر از آن است که در یک صفحه جای بگیرد. اگر از RStudio IDE استفاده می‌کنید، می‌توانید ظاهری دوستانه‌تر به محتویات متغیر patients در قاب ‘Environment’ بدهید، همانند شکل ۲٫ نام‌ها را در قاب Environment کلیک کنید تا آن‌ها را به شکل جدول درآورید همانند شکل ۳٫
read.csv اطلاعات را از فایل CSV می‌گیرد و آن را در ماتریسی که R می‌تواند درک کند، قرار می‌دهد. یک ماتریس شبیه آرایه ۲ بعدی از مقادیر است. read.csv به طور خودکار اولین خط فایل CSV را به عنوان لیست عناوین تفسیر می‌کند، که می‌توانیم از آن استفاده کنیم و ستون‌های منحصر به فرد ماتریس را بدست بیاوریم. برای مثال، patients$Group یک بردار ( آرایه یک بعدی) است که شامل تمام مقادیر ‘Group’از فایل CSV می‌باشد:

> patients$Group
[۱] ۲ ۲ ۱ ۲ ۱ ۲ ۱ ۱ ۱ ۲ ۱ ۱ ۲ ۲ ۱

از آنجایی که ماتریس یک آرایه دو بعدی است، می‌توانیم خانه‌های منحصر به فرد آن را با استفاده از index notation، بدست بیاوریم. برای مثال:

> patients[1, 4]
[۱] N
Levels: N Y

در اینجا R به ما می‌گوید که درمان بیمار شماره ۱ ناموفق بوده است. شماره ۴ ، به ستون ۴ ماتریس ، یعنی ستون ‘Success’ اشاره دارد، پس راه دیگر بدست آوردن همان نتیجه، تایپ کردن patient$success[1] است.
در R، بردار و شاخص‌گذاری ماتریکس در ۱ شروع می‌شود، نه ۰٫ شاخص‌گذاری ماتریس یکی از قدرتمندترین ویژگی‌های نحوی در R است. برای مثال، ما گاهی اوقات می‌خواهیم کل ستون یا دریف کامل ماتریس را داشته باشم. در این مورد، می‌توانیم مختصات دیگر را خالی بگذاریم.

> patients[7, ]
Treatment.Type Group Sex Success
۷ PN 1 M Y

این پرونده مربوط به بیمار شماره ۷ است که مرد می‌باشد و روند PN موفقی داشته است. خالی گذاشتن دومین عدد مختصات، یک ردیف کامل ماتریس به ما ارائه می‌دهد. همچنین، اگر patients[,2] را تایپ کنیم، همان نتیجه‌ای را می‌گیریم که از patients$Group بدست می‌آوردیم.

استفاده از شاخص‌گذاری شرطی
کار متداولی که می‌توانید با این داده‌ها انجام دهید این است که سعی کنید نرخ موفقیت هر نوع درمان را محاسبه کنید. اولین قدم این است که این داده‌ها را با توجه به نوع درمان جدا کنید. فرمانی که می‌توان برای بدست آوردن ماتریس تمام بیمارانی که تحت جراحی باز قرار گرفته‌اند، استفاده کرد از این قرار است:

> open.surgery.patients <-
+ patients[patients$Treatment.Type == «OS»,
]

 

چه اتفاقی می‌افتد؟ اگر این یک فرمان را اجرا کنیم

patients$Treatment.Type == «OS»

لیست طولانی از مقادیر TRUE و FALSE را دریافت می‌کنید، که هر کدام مشخص می‌کنند که آیا بیمار مورد نظر تحت عمل جراحی باز قرار گرفته‌ است یا خیر. این نمونه‌ای‌ از یک عملیات برداری است که در هر ردیف ماتریس اعمال می‌شود و برداری حاصل می‌شود که عناصر آن نتایج هر ردیف هستند. حال به بخش شاخص‌گذاری می‌رسیم. اگر، به جای عدد، برداری از مقادیر TRUE و FALSE را به عنوان یک شاخص در بردار یا ماتریس قرار دهیم، نتیجه، زیر- بردار یا زیر- ماتریس ورودی‌هایی است که به مقادیر TRUE مربوط می‌شوند.

> x <- c(1, 2, 3, 4)
> x[c(TRUE, FALSE, FALSE, TRUE)]
[۱] ۱ ۴

در اینجا ما از تابع c استفاده کرده‌ایم که برداری می‌سازد که برخی مقادیر ارائه شده را نگاه می‌دارد. بردار x اعداد ۱ تا ۴ را نگاه می‌دارد و ما از بردار TRUE FALSE FALSE TRUE برای انتخاب اولین و چهارمین عنصر استفاده می‌کنیم. در این مورد بخصوص، patients$Treatment.Type==”OS” برداری از مقادیر TRUE یا FALSE را به ما ارائه می‌دهد: true اگر بیمار مربوطه تحت عمل جراحی باز قرار گرفته و false اگر بیمار تحت PN قرار گرفته است. سپس شاخص‌گذاری

patients[patients$Treatment.Type==”OS”, ]

دقیقاً همان ردیف‌هایی را انتخاب می‌کند که به بیماران جراحی باز مربوط می‌شوند و تمام ستون‌ها را باز می‌گرداند. نتیجه، یک زیر-ماتریس از ماتریس اصلی است که شامل دقیقاً همان ردیف‌های مربوط به بیماران جراحی باز می‌باشد.
علاوه بر لیست‌های TRUE/FALSE برای شاخص‌گذاری، R لیستی از شاخص‌هار ا می‌پذیرد. برای مثال، تایپ کردن

patients[c(1:5), ]

پنج بیمار اول را به ما ارائه می‌دهد.

 

نوشتن توابع در R
وقتی لیستی از تمام بیماران جراحی باز در اختیار ما قرار گرفت، می‌خواهیم بفهمیم که چه تعداد از آن‌ها این جراحی را با موفقیت گذرانده‌اند.
یک فرمان مفید برای انجام این کار، فرمان which است، که لیست TRUE/FALE را می‌گیرد و شاخص‌های تمام مقادیر TRUEرا باز می‌گرداند.

 

which(patients$Sex == «F»)
[۱] ۳ ۶ ۱۳ ۱۴ ۱۵ ۱۷ ۱۸ ۱۹
۲۱ ۲۲ ۲۵ ۲۶
….

البته این روش برای اهداف شاخص‌گذاری خیلی مفید نیست، بنابراین می‌توانیم از لیست TRUE/FALSE به طور مستقیم استفاده کنیم، اما این روش برای شمارش تعداد ورودی‌ها در ماتریس یا بردار فوق‌العاده است. برای مثال،

length(which(patient$Sex == “F”)

عدد ۲۸۹ را باز می‌گرداند، یعنی جمع بیماران زن در این مطالعه. با این روش محاسبه نرخ موفقیت جراحی باز آسانتر می‌گردد:

> length(which(open.surgery.patients$Success
== «Y»)) /
+ nrow(open.surgery.patients)

[۱] ۰٫۷۸

در اینجا، تابع nrow، تعداد ردیف‌ها در یک ماتریس را باز می‌گرداند. همچنین تابع ncol نیز وجود دارد که تعداد ستون‌ها را ارائه می‌دهد. جایگزین length(which(something)) در واقع sum(something). Sum است که عناصر یک بردار عددی را جمع می‌کند و نتیجه را بازمی‌گرداند: از آنجایی که R، TRUE را به عنوان ۱ و FALSE را به عنوان ۰ در نظر می‌گیرد، همان نتیجه به عنوان جمع تمام مقادیر TRUE ارائه می‌گردد.
ما می‌خواهیم همین کار را در مورد بیماران PN انجام دهیم. به جای تکرار همین کد، تابعی می‌نویسیم که این کار را برای ما انجام دهد.

> print.success.rate <- function (patient.
list) {
+ successes <- which(patient.list$Success
== «Y»)
+ length(successes) / nrow(patient.list)
+ }

بیایید نگاهی به ساختار آن بیاندازیم. برای تعریف یک تابع، ما از کلید واژه خاص function استفاده می‌کنیم که همراه لیستی از آرگومان‌ها در پرانتز ( … ) می‌باشد. متن تابع به دنبال آن می‌آید در داخل آکولاد { … } . در اینجا patient.list یک پارامتر است که وارد تابع می‌شود. تابع نرخ موفقیت را محاسبه می‌کند و سپس آن را پرینت می‌نماید.

> print.success.rate(open.surgery.patients)
[۱] ۰٫۷۸
> pn.patients <- patients[patients$Treatment.
Type == «PN», ]
> print.success.rate(pn.patients)
[۱] ۰٫۸۲۵۷۱۴۳

مشخص است که PN دارای نرخ موفقیت بالاتر از جراحی باز است، حداقل برای این نمونه خاص.

بازگرداندن مقادیر از تابع
ممکن است ثابت شود که برای مثال، جراحی باز برای بیماران گروه (Group2) که سنگ‌ کلیه بزرگتری دارند، بهتر است. ما می‌توانیم دقیقاً از همین ابزارهای شاخص‌گذاری استفاده کنیم تا open.surgery.patients و pn.patients را به دو گروه تجزیه کنیم، اما باید تابعی بنویسیم که تا به جای تکرار کد این کار را برای ما انجام دهد.

> get.group <- function(patient.list, group.
number) {
+ group <-
+ patient.list[patient.list$Group ==
group.number, ]
+ return(group)

+ }

R از کلید واژه return برای بازگردندان مقادیر از توابع استفاده می‌کند، درست مانند زبان‌هایی همچون C. ما حالا می‌توانیم تمام گروه را به یکباره داشته باشیم:

> print.success.rate(get.group(open.surgery.
patients, 1))
[۱] ۰٫۹۳۱۰۳۴
> print.success.rate(get.group(pn.patients, 1))
۰٫۸۶۶۶۶۶۷

بنابراین می‌بینیم در گروه ۱، درمورد کسانی که سنگ کلیه کوچکتردارند، جراحی باز در واقع موثرتر از PN است. ما می‌توانیم این کار را برای گروه ۲ تکرار کنیم.

> print.success.rate(get.group(open.surgery.
patients, 2))
[۱] ۰٫۷۳۰۰۳۸
> print.success.rate(get.group(pn.patients, 2))
[۱] ۰٫۶۸۷۵

حالا می‌بینیم که در حقیقت، جراحی باز برای هر دو گروه از بیماران موثرتر از PN بوده است، گرچه در کل به نظر می‌رسد PN موفقتر باشد. این موقعیت که به نظر می‌رسد دارای پارادوکس است ( که تحت عنوان پارادوکس سیپسون شناخته می‌شود) را می‌توان با نگاه کردن به اندازه مختلف گروه‌ها ( شکل ۴) توضیح داد.
با نگاه کردن به این گروه‌ها می‌توان متوجه قضیه شد: جراحی باز یک روند تهاجمی‌تر و اندکی گرانتر است، بنابراین اساساً برای سنگ‌هایی با اندازه ۲ سانتی‌متر یا بیشتر مورد استفاده قرار می‌گیرد. برای یک عمل مشخص، جراحی باز شانس بیشتری برای موفقیت در مقایسه با PN دارد، همانطور که نتایج ما نشان می‌دهند. بهرحال، وقتی نگاهی به تمام بیماران به عنوان یک گروه می‌اندازیم، به نظر می‌رسد PN نرخ موفقیت بیشتری داشته باشد، چرا که برای کیس‌های « آسانتر» که دارای سنگ با قطر کمتر از ۲ سانتی‌متر هستند، مورد استفاده قرار می‌گیرد.

 

نوشتن اسکریپت‌های R
علاوه بر REPL، زبان برنامه نویسی R به ما امکان نوشتن اسکریپت‌هایی را می‌دهد که می‌توانیم در REPL آن‌ها را لود کنیم. اسکریپت‌ها معمولاً شامل تعاریف متغیر و تابع هستند، گرچه ممکن است شامل دستورهای دیگری برای پرینت کردن باشد. این کمک می‌کند که کد ما سازمان‌یافته بماند و از تکرار در امان بمانیم.
اسکریپت‌های R فایل‌های طبیعی هستند ( معمولاً به پسوند .R ختم می‌شوند) که شامل کد R می‌باشند. اگر از خط فرمان کار می‌کنید، لازم است با استفاده از ویرایشگر متن خود، فایل‌ها را بسازید. اگر از RStudio استفاده می‌کنید، می‌توانید همانند شکل ۵ ‘New> R Script’ را انتخاب کنید، که به شما امکان ویرایش مستقیم اسکریپت در IDE را می‌دهد.
بیایید اسکریپتی بسازیم تا تمام کارهایی را که تا به حال انجام داده‌ایم را ذخیره نماید. با استفاده از هر کدام از متدهایی که عنوان کردیم، یه اسکریپت جدید به نام kidney-stones.R بسازید و تعاریف توابع print.success.rate و get.group را در آن کپی کنید. شما می‌توانید تعاریف دیگر تابع یا متغییر را که فکر می‌کنید می‌تواند مفید باشد را بیافزایید. بهترین روش برای پرینت کردن تعریف یک تابع، تایپ کردن نام آن در R PERL است.

> print.success.rate
function (patient.list) {
successes <- which(patient.list$Success
== «Y»)
length(successes) / nrow(patient.list)
}

سپس می‌توانیم آن را مستقیم در اسکریپت خودمان کپی و پیست کنیم، قبل از تعریف حتماً print.success.rate <- را اضافه کنیم. به منظور لود کردن یک اسکریپت در R، ما از تابع منبع استفاده می‌کنیم. بنابراین اگر

> source(«/path/to/kidney-stones.R»)

را در جلسه R تایپ کنیم، به تمام توابع خود از اسکریپت دسترسی پیدا خواهیم کرد. اگر در RStudio کار می‌کنید، به لودینگ خودکار منبع دسترسی خواهید داشت. تیک زدن کادر کنار ‘source on save’ موجب می‌شود تا R فرمان source را اجرا کند تا هر بار که ما اسکریپ را ذخیره می‌کنیم، آن را مستند نماید.
روش دیگر برای ذخیره کردن کارهایی که انجام داده‌اید آن است که کل فضای کار R را ذخیره کنید. برای خارج شدن از R، ما از فرمان q() استفاده می‌کنیم، که به طور خودکار به ما اعلام می‌کند تا کارهایی که انجام داده‌ایم را ذخیره نماییم:

> q()
Save workspace image to ~/.RData? [y/n/c]:

اگر y را تایپ کنیم و Return را فشار دهیم، R تمام تعاریف متغیر و تابع را که ما در REPL داخل فایل ~/.Rdata وارد کرده‌ایم را ذخیره خواهد کرد. سپس، وقتی R را دوباره راه‌اندازی کردیم، به طور خودکار تمام کار قبلی را مجدداً لود می‌کند.

 

نمونه‌برداری و تصادفی
مثال بعدی ما « مسئله مونتی هال» معروف است که در شکل ۶ آمده است. این معما بر اساس یک مسابقه تلوزیونی آمریکایی به نام «بیا معامله کنیم» طرح ریزی شده و نامش را نیز از نام مجری اصلی این مسابقه مونتی هال گرفته است. او به شما سه درب را نشان می‌دهد و توضیح می‌دهد که یک ماشین پشت یکی از درب‌هاست و پشت دو درب دیگر هم دو بز وجود دارند.
او از شما می‌خواهد که یک درب را انتخاب کنید و وقتی شما این کار را انجام می‌دهید او یکی از دو درب دیگر را باز می‌کند و یک بز را نشان شما می‌دهد. و سپس به شما یک پیشنهاد می‌دهد: شما می‌توانید همچنان انتخاب اول خود را داشته باشید یا این که به سراغ درب دیگر بروید.
این مسئله را می‌توان به صورت ریاضی حل کرد، اما این مسئله به دلیل گیج کننده بودنش معروف است. در ابتدا به نظر می‌رسد که تفاوتی ندارد شما کدام دو درب را انتخاب می‌کنید. اما بگذارید یک اسکریپت R بنویسیم که این آزمایش را برای ما انجام دهد.
یک فایل جدید به نام monty-hall.R بسازید که شامل توابعی است که ما باید بنویسیم. با تابعی آغاز می‌کنیم که در وهله اول سه در را آماده می‌کند.

set.up.doors <- function() {
doors <- c(«car», «goat1», «goat2»)
doors <- sample(doors)
return(doors)
}

تنها چیز جدید در اینجا تابع sample است. این تابع برای انتخاب تصادفی یک نمونه از بردار داده شده را انتخاب می‌کند. برای مثال، در بخش اول، اگر اندازه جمعیت ما بسیار بیشتر باشد، امکان آن که تک تک بیماران را بررسی کنیم وجود نداشت. با استفاده از نمونه، می‌توانیم برخی زیر مجموعه‌های مناسب را انتخاب کنیم. برای مثال،patient.sample<-sample (patients,1000) یک نمونه از ۱۰۰۰ بیمار را باز می‌گرداند.
به طور پیش فرض، sample غیر-جایگزین است، یعنی تضمین شده که هرگز عنصر مشابه را دو بار باز نگرداند.اگر آرگومان اندازه نمونه (sample size) را حذف کنیم، نمونه از اندازه بردار اصلی استفاده می‌کند. بنابراین sample(doors) شبیه sample(doors,length(doors)) است که شبیه (sample(doors,3) می‌باشد. از آنجایی که عناصر جایگزین نمی‌شوند، پس تاثیر بُرخوردن (shuffle) بردار اصلی را دارد. می‌توانیم این موضوع را در REPL آزمایش کنیم.

> set.up.doors()
[۱] «goat1» «goat2» «car»

 

تابع بعدی که می‌نویسیم، تابعی است که توسط این بازی استفاده شده تا دری را انتخاب کند که پشت آن یک بز است. دو محدودیت برای دری که مونتی هال باز می‌کند وجود دارد: اولاً، آن در هرگز دری نیست که ما در ابتدا انتخاب کرده‌ایم، و این که حتماً باید یک بز پشت آن در باشد. اگر ما اتفاقی دری را انتخاب کنیم که پشت آن ماشین باشد، مونتی دو گزینه احتمالی برای باز کردن دارد؛ در غیر این صورت، تنها یک در برای انتخاب او باقی می‌ماند.
در موردی که یک انتخاب وجود دارد، ما از نمونه یا sample برای انتخاب در استفاده می‌کنیم.

get.goat.door <- function(doors, chosen.door)
{
unchosen.doors <- doors[doors != chosen.
door]
possible.goat.doors <-
unchosen.doors[unchosen.doors !=
«car»]
return(sample(possible.goat.doors, 1))
}

تابع اصلی که شبیه‌سازی را اجرا می‌کند، در شکل ۷ نشان داده شده است. تابع اصلی شامل تابع pick.final.door به عنوان یک آرگومان است، که از آن برای انتخاب درب آخر استفاده می‌کند. R دارای یک سبک بسیار کاربردی است که مبتنی بر سبک Scheme است و انتقال توابع به عنوان آرگومان به توابع دیگر بسیار آسان است.
بعد از آماده کردن درب‌ها، تابع فرض را بر این می‌گذارد که ما برای شروع درب ۱ را انتخاب می‌کنیم. با این کار هیچ خللی به کلیت موضوع وارد نمی‌شود چون درب‌ها از قبل به صورت تصادفی بُر خورده بودند. سپس تابع get.goat.door ما را فرا می‌خواند تا یکی از درب‌های دیگر را باز کند. در آخر، تابع pick.final.door را فرا می‌خواند تا انتخاب کند کدام درب آخرین درب باشد و آنچه را که پشت آن درب است را بازمی‌گرداند.

انجام چندین آزمایش
آخرین بخش پازل، توابعی هستند که ما برای انتخاب آخرین درب استفاده می‌کنیم. این توابع در تصویر ۸ آمده‌اند. اولین تابع آسان است: دربی را که ما با آن آغاز کردیم، انتخاب کن. در مورد تابع دوم، ما از vector indexing برای انتخاب تنها دربی که انتخاب اول ما نبوده و باز هم نشده است، استفاده می‌کنیم. حالا می‌توانیم هر کدام از استراتژی‌های مختلف را به ترتیب امتحان کنیم.

> monty.hall(stick.with.first.door)
[۱] «car»
> monty.hall(switch.door)
[۱] «goat2»

 

پاسخ شما ممکن است با این‌ها متفاوت باشد، چون پیکربندی اولیه سه درب به صورت تصادفی صورت می‌گیرد. برای آن که بدانید کدام استراتژی بهتر است، می‌خواهیم چندین بار آن‌ها را آزمایش کنیم.
برای انجام این کار، از تابع replicate از R استفاده می‌کنیم که تابعی را به تعداد مشخص اجرا می‌کند و سپس تمام نتایج را در یک بردار قرار می‌دهد.
می‌توانید نتایج خود را در شکل ۹ ببینید که نشان می‌دهد که با انتخاب درب اول ماشین را در ۳۱۷ مین بار از ۱۰۰۰ بار و در صورت تغییر درب‌ها در ۶۵۰ مین بار از ۱۰۰۰ بار بدست آوردیم. نتایج شما متفاوت از این نتایج خواهد بود، اما آن‌ها در واقع علم ریاضی را تایید می‌کنند، که می‌گوید وقتی شما دربها را تغییر می‌دهید احتمال پیدا کردن ماشین به تقریباً دو سوم برابر می‌رسد و وقتی روی انتخاب اول خود هستید، این احتمال به یک سوم برابر می‌رسد.
البته این نتیجه ای نیست که خیلی سریع به آن برسید، اما آمار دروغ نمی‌گوید- چون R این موضوع را برای ما ثابت کرده است.

نظر بدهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

It is main inner container footer text