آمار , داده کاوی 241 بازدید

اغلب برای انجام کارها یا محاسبات یکسان بر روی ستون‌های یک مجموعه داده، از حلقه‌های تکرار یا loop استفاده می‌شود. به این منظور در زبان برنامه‌نویسی R خانواده توابع apply مانند $$apply(), lapply(), sapply(), mapply()$$ و $$tapply()$$ وجود دارند که این عملیات تکراری را به واسطه محاسبات ماتریسی انجام می‌دهند، در نتیجه سرعت بالا و انعطاف زیادی دارند.

در این آموزش به کمک محیط Rstudio و زبان برنامه‌نویسی R، با مجموعه یا خانواده توابع apply آشنا شده، کاربردهای هر یک را به کمک مثال‌هایی مرورر می‌کنیم. البته دستورات و کدها را می‌توانید به تنهایی در محیط زبان برنامه‌نویسی R نیز اجرا کنید.

خانواده توابع Apply

در زبان برنامه‌نویسی R، یکی از معروف‌ترین گروه توابع، اعضای خانواده توابع apply‌ هستند که بخصوص در انجام محاسبات روی ستون‌ها یا سطرهای ماتریس به خوبی عمل می‌کنند. به همین علت در این مطلب قصد داریم با ارائه مثال‌های کاربردی، از این توابع استفاده کنیم.

اعضای خانواده توابع apply قادر به انجام محاسبات براساس توابع دیگر روی مجموعه‌ای از «چارچوب داده» (Dataframe)، «لیست‌ها» (List)، «بردار» (Vector) یا« ماتریس» (Matrix) و … هستند. به این ترتیب با استفاده از این توابع قادر هستیم محاسبات مربوط به یک تابع دیگر را بر روی داده‌ها «اعمال» (apply) کنیم. بر همین اساس مشخص است که باید یکی از پارامترهای این گروه توابع، نام تابع دیگری باشد که قرار است به صورت تکراری روی اجزای «ساختارهای داده‌ها» (Data Structure)، اعمال شود.

تابع $$apply()$$

برای محاسبه یک تابع روی ماتریس‌ یا آرایه می‌توان از تابع $$apply()$$‌ استفاده کرد. این تابع دارای 3 پارامتر اصلی و یک یا چند پارامتر اختیاری است که در ادامه با آن‌ها آشنا می‌شویم.

به منظور اطلاع از نحوه عملکرد هر یک از این پارامترها بهتر است به جدول زیر توجه کنید.

پارامتر عملکرد
x معرفی یک ماتریس یا آرایه
MARGIN تعیین بُعدی که باید محاسبات در آن اجرا شود. اجرای محاسبات روی سطرهای ماتریس، MARGIN=1، اجرای محاسبات روی ستون‌های ماتریس، MARGIN=2، اجرای محاسبات روی تقاطع سطرها و ستون‌ها، MARGIN=$$c(1,2)$$، که در حقیقت این شکل پارامتر، سلول‌ها را نشانه خواهد گرفت.
FUN نام تابع که باید برای سطرها یا ستون‌ها اعمال شود. مانند mean, median, max, … و حتی توابعی که توسط کاربر معرفی شده‌اند.
اگر تابع FUN‌ احتیاج به پارامترهایی داشته باشد، می‌توان آن را به عنوان پارامترهای دیگر تابع $$apply()$$ معرفی کرد.

مثال

به عنوان یک مثال ساده، ماتریسی را در نظر بگیرید که دارای ۵ سطر و ۶ ستون است که مولفه یا درایه‌های آن از 1 تا 1۰ چیده شده‌اند. هدف از اجرای تابع $$apply()$$ محاسبه جمع هر ستون است. این ماتریس در متغیر m1‌ ذخیره شده است و مجموع ستون‌ها نیز در متغیر a_m1 محاسبه می‌شود. کدهای زیر به این منظور نوشته شده‌اند.

با اجرای این کد، محاسبات صورت گرفته و خروجی به صورت زیر در خواهد آمد.

apply

نکته: از آنجایی که ماتریس دارای 3۰ درایه است و فقط مقدارهای 1 تا 1۰ باید در آن قرار گیرند، نرم‌افزار با تکرار اعداد 1 تا 1۰ ماتریس را به ترتیب ستون‌ها، کامل کرده است.

نتیجه اجرای تابع $$apply()$$ می‌تواند یک بردار، یک ماتریس یا حتی یک عدد باشد. برای مثال فرض کنید که کد بالا را به صورتی در آوریم که هر درایه از ماتریس را به توان 2 رسانده و یک ماتریس جدید بسازد. به این منظور تابعی به نام sq ایجاده کرده‌ایم تا هر مقداری را به توان 2 برساند. در انتهای کد نیز مشخص است که با استفاده تابع $$class()$$ ماهیت a_m1 درخواست شده است.

در این حالت خروجی به صورت زیر ظاهر خواهد شد. در سطر آخر کلاس متغیر a_m1 به صورت matrix معرفی شده است.

مثال

با استفاده از 3۰ عدد تصادفی، یک ماتریس ۵ سطری و ۶ ستونی ایجاد کرده‌ایم. هدف محاسبه میانگین برای سطرهای این ماتریس است. البته این کار را با استفاده از تابع $$mean()$$ انجام داده‌ایم. همچنین برای حذف نقطه‌های دورافتاده یا پرت از میانگین پیراسته (Trimmed mean) در محاسبات بهره برده‌ایم.

همانطور که در کد زیر دیده می‌شود، پارامتر trim=0.2 باعث می‌شود که 2۰ درصد از بزرگترین و کوچکترین داده‌ها در محاسبه میانگین اصلاح شده نقشی نداشته باشند. این پارامتر مربوط به تابع $$mean()$$ است که به عنوان پارامتر اختیاری تابع $$apply()$$ در انتها ظاهر شده است.

با توجه به خروجی ارائه شده، تفاوت در نتایج حاصل شده در متغیرهای a_m1 و a_m2 دیده می‌شود. مشخص است که a_m1 بدون میانگین و a_m2 میانگین پیراسته را محاسبه کرده است.

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

apply function

تابع $$lapply()$$

یکی دیگر از توابع خانواده apply، تابع $$lapply()$$ است که قالب خروجی آن به صورت یک لیست است. به همین علت حرف انگلیسی «l» به عنوان پیشوند این تابع به کار رفته است. برای انجام محاسبات روی لیست‌ها و حتی چارچوب داده (Dataframe) از این تابع استفاده می‌شود. تابعی که در پارامتر FUN معرفی می‌کنید برای همه درایه‌ها یا مولفه‌های ساختار داده محاسبه خواهد شد و حاصل محاسبات به صورت «ساختار داده لیست» (List Structure) ثبت و ارائه خواهد شد.

همانطور که دیده می‌شود، در این تابع پارامتر MARGIN احتیاجی نیست و تابع مورد نظر روی همه درایه‌ها اعمال خواهد شد. با توجه به مثال قبل فرض کنید تابع sq را می‌خواهیم برای همه عناصر ماتریس به کار ببریم. کافی است با تابع $$lapply()$$ مطابق با کد زیر محاسبات را انجام دهیم. نتیجه درست به مانند حالت قبل خواهد شد با این تفاوت که ماهیت خروجی در اینجا یک لیست است.

البته در انتهای کد، متغیر حاصل از اجرای تابع $$lapply()$$ را به کمک تابع $$unlist()$$ از حالت لیست خارج کرده‌ایم. خروجی به صورت زیر است:

مثال

در اینجا به بررسی یک مثال برای داده‌های متنی می‌پردازیم. فرض کنید در متغیر pnames اسامی چهار نفر با حروف بزرگ نوشته شده است. می‌خواهیم همه اسامی را به حروف کوچک تبدیل کنیم. با استفاده از تابع $$lapply()$$ این کار به راحتی امکان‌پذیر است.

مشخص است که با استفاده از تابع $$tolower()$$ حروف بزرگ به کوچک تبدیل شده‌اند و این کار برای همه عناصر لیست صورت گرفته. به این ترتیب خروجی به صورت زیر خواهد بود.

به منظور تبدیل این لیست به یک بردار متنی، از تابع $$unlist()$$ استفاده خواهیم کرد. به این ترتیب خواهیم داشت:

و خروجی به شکل زیر در خواهد آمد.

تابع $$sapply()$$

اگر می‌خواهید خروجی محاسبات به صورت یک بردار درآید از تابع $$sapply()$$ به جای $$lapply()$$ استفاده کنید. پارامترهای این تابع به صورت زیر است.

برای مثال فرض کنید که می‌خواهیم برای مجموعه داده‌های cars که به صورت پیش‌فرض در R قرار دارد، حداقل «سرعت» (Speed) و «مسافت توقف» (dist) را محاسبه کنید. دستورات زیر براساس توابع $$lapply()$$ و $$sapply()$$ این محاسبات را انجام می‌دهند.

به شکل خروجی هر دو تابع توجه کنید. اولین خروجی به صورت لیست توسط تابع $$lapply()$$ و دومین خروجی به صورت یک بردار و توسط تابع $$sapply()$$‌ ایجاد شده است.

حال فرض کنید که بخواهیم به کمک یک تابع، براساس حداکثر و حداقل مقدارهای محاسبه شده، میانگین را محاسبه کنیم. به این ترتیب باید با تعریف یک تابع جدید و استفاده از این دو مقدار، محاسبات را پیش ببریم.

پس از محاسبه حداقل و حداکثر مقدارها با تابع $$sapply()$$، به تعریف تابع $$avg()$$ می‌پردازیم، سپس از تابع $$sapply()$$ برای محاسبه میانگین هر دو متغیر سرعت و مسافت به کمک تابع $$avg()$$ می‌پردازیم.

از آنجایی که از تابع $$sapply()$$‌ استفاده کرده‌ایم، خروجی براساس این کد یک آرایه یا بردار با مقدارهای 14.5 و 61.0 خواهد بود که به ترتیب میانگین سرعت و مسافت را نشان می‌دهند.

مشخص است که متغیرهای lmn و smn برای محاسبه حداقل و متغیرهای lmx و smx برای حداکثر در نظر گرفته شده‌اند.

طبقه‌بندی داده‌ها

یکی دیگر از کاربردهای تابع $$lappy()$$ و $$sapply()$$ تفکیک یک چارچوب داده است. در ادامه خواهید دید که به کمک تابع $$below\_avg()$$ داده‌های مربوط به سرعت و فاصله را به دو دسته «بیشتر از میانگین» و «کمتر از میانگین» تقسیم کرده‌ایم. ابتدا به تعریف تابع اصلی که براساس آن تفکیک صورت می‌گیرد، می‌پردازیم.

همانطور که مشخص است در تابع $$below\_avg()$$، پس از محاسبه میانگین برای هر یک از بردارها، مقدارهایی که کمتر از میانگین هستند، جدا شده‌اند. به این ترتیب با استفاده از تابع $$sapply()$$ یا $$lapply()$$ عمل تفکیک صورت می‌گیرد.

همانطور که در انتهای کد می‌بینید، یکسان بودن نتایج دو تابع $$sapply()$$ و $$lapply()$$ با تابع $$identical()$$ بررسی شده است. خروجی به صورت زیر خواهد بود.

تابع $$tapply()$$

تا اینجا مشخص شد که محاسبات روی سطر، ستون و یا درایه‌های یک ماتریس یا چارچوب داده، توسط توابع $$apply()$$، $$lapply()$$ و $$sapply()$$ صورت می‌گیرد. ولی تابع $$tapply()$$ یک ویژگی مهم نسبت به دیگر خانواده توابع $$apply()$$ دارد. با استفاده از تابع $$tapply()$$ می‌توان محاسبه را براساس یک متغیر «عامل» (Factor) جداگانه انجام داد. به این ترتیب داده‌ها براساس سطوح مختلف یک متغیر عامل طبقه‌بندی شده و محاسبه تابع (مثلا میانگین) برای هر طبقه جداگانه صورت می‌گیرد.

پارامترهای این تابع به صورت زیر هستند.

مشخص است که بیشتر پارامترهای این تابع به جز INDEX مانند توابع دیگر خانواده apply هستند. در اینجا INDEX بیانگر لیستی است که شامل متغیر عامل است. از آنجایی که این پارامتر نقش مهم در محاسبه تابع $$tapply()$$ ایفا می‌کند، از یک مثال به منظور روشن شدن نقش این پارامتر، کمک می‌گیریم.

فرض کنید با مجموعه داده‌های iris که ویژگی‌های کمی یک نمونه 1۵۰ تایی از سه نوع گل زنبق مختلف را ثبت کرده است سروکار داریم. این مجموعه داده تقریبا در همه مثال‌های «یادگیری ماشین» (Machine Learning) استفاده می‌شود. این ویژگی‌ها، شامل طول و عرض کاسبرگ و گلبرگ سه نوع زنبق (setosa, versicolor, virginica) است که توسط دانشمند آمار «رونالد فیشر» (Roanld Fisher) جمع‌آوری شده. در اینجا هدف محاسبه میانگین برای عرض کاسبرگ (Sepal.Width) است و  متغیر عامل هم نوع گل زنبق یعنی Species است. در کد زیر ابتدا چند مشاهده مختلف از این مجموعه داده نمایش داده شده، سپس به کمک تابع $$tapply()$$ محاسبه میانگین برای عرض کاسبرگ به تفکیک نوع گل صورت می‌پذیرد.

خروجی برای کد بالا به صورت زیر خواهد بود.

همانطور که دیده می‌شود، دستور sample یک نمونه از اعداد 1 تا 1۵۰ تولید می‌کند. سپس اعضای مجموعه داده iris با شماره این سطرها نمایش داده شده است. در انتها نیز نتایج حاصل از تابع $$tapply()$$ ظاهر شده که میانگین عرض کاسبرگ را برای هر سه نوع گل‌ زنبق نشان می‌دهد. اگر لازم باشد که میانگین برای همه چهار ویژگی‌ گل‌ها، محاسبه و به تفکیک نوع گل ظاهر شود، بهتر است از کد زیر استفاده کنید.

مشخص است که برای محاسبه میانگین برای همه ویژگی‌ها، مجبور شده‌ایم که به ازای هر ویژگی، یکبار از تابع $$tapply()$$ استفاده کنیم. در انتها نیز با ترکیب و نام‌گذاری آن‌ها خروجی را به صورت زیر تشکیل داده‌ایم.

نکته: تابع $$tapply()$$ نمی‌تواند همزمان بر روی چند متغیر، تابع مورد نظر را محاسبه کند، به همین علت برای هر ستون از ویژگی‌ها یکبار از تابع $$tapply()$$ استفاده کرده‌ایم.

ترکیب توابع در $$tapply()$$

فرض کنید که همزمان به چند شاخص آماری برای یک متغیر در مجموعه داده احتیاج دارید و می‌خواهید براساس یک متغیر عامل، آن ها را محاسبه کنید. همانطور که به یاد دارید، خانواده توابع apply فقط می‌توانند یک تابع را برای انجام محاسبات بپذیرند. به این ترتیب اگر در مثال قبل احتیاج به چند شاخص آماری نظیر میانگین، میانه و مجموع متغیر عرض کاسبرگ (Sepal.width) داشته باشید بهتر است به صورت زیر عمل کنید. ابتدا یک تابع تعریف می‌کنیم و شاخص‌های مورد نیاز را در آن محاسبه می‌کنیم. سپس از این تابع به عنوان پارامتر خانواده توابع apply کمک می‌گیریم. به مثال زیر دقت کنید.

مثال

میانگین، میانه و مجموع عرض کاسبرگ‌های گل‌های زنبق به تفکیک نوع گل براساس کدی که در زیر نوشته شده است، امکان‌پذیر است.

همانطور که می‌بینید، ابتدا تابع s همه محاسبات را انجام داده و در قالب یک لیست قرار می‌دهد. سپس با اجرای تابع $$tapply()$$ به همراه پارامتر تابع s محاسبات مورد نظر انجام گرفته و در انتها نیز لیست ارائه شده، نام‌گذاری و نمایش داده شده است. خروجی این برنامه به صورت زیر خواهد بود.

تابع $$mapply()$$

نسخه چند متغیره تابع $$lapply()$$ را می‌توان تابع $$mapply()$$‌ در نظر گرفت. به این ترتیب می‌توان پارامترها را به صورت برداری، معرفی کرد. فرض کنید براساس مثال قبل لازم است که میانگین، میانه و جمع هر یک از متغیرهای مربوط به داده‌های iris (بدون در نظر گرفتن ستون آخر که نوع گل را نشان می‌دهد) را محاسبه کنیم. برای این کار از کد زیر کمک می‌گیریم و برنامه را با کمترین میزان دستورات اجرا می‌کنیم. کدها را با مثال قبل مقایسه کنید.

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

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

مثال

فرض کنید می‌خواهیم یک ماتریس 4 در 4 بسازیم که دارای مقادیر از 1 تا 4 باشد. البته می‌دانید که این کار به واسطه دستور matrix‌ نیز امکان پذیر است. به کد زیر توجه کنید. مشخص است که پارامتر byrow=TRUE تعیین می‌کند که مقدارها باید سطر به سطر در ماتریس چیده شوند.

حلا سعی می‌کنیم به کمک تابع $$mapply()$$ و تعیین پارامترهای آن طوری عمل کنیم که این همین ماتریس ایجاد شود.

به این ترتیب تابع rep که عمل تکرار را انجام می‌دهد با پارامترهای 1:4 و 4 اجرا شده و در نتیجه ماتریس ایجاد می‌شود. مشخص است که اینجا، پارامتر اول که با 1:4 مشخص شده است ارقامی است که باید ماتریس را بسازند و پارامتر دوم یعنی 4 تیز تعداد تکرار را تعیین می‌کند. بنابراین یک ماتریس 4 در 4 با درایه‌های تکراری 1، 2، 3 و 4 ساخته می‌شود.

خلاصه

براساس ویژگی‌ها و شیوه محاسبه هر یک از اعضای خانواده توابع apply جدول زیر تهیه شده است تا بهتر عملکرد هر یک از توابع مشخص و با دیگر توابع این گروه مقایسه شود.

تابع پارامترها شیوه محاسبه ورودی خروجی
$$apply()$$ apply(x, MARGIN, FUN) محاسبه روی سطر یا ستون ماتریس یا آرایه بردار، لیست یا آرایه
$$lapply()$$ lapply(x, FUN) محاسبه روی مولفه‌ها لیست، بردار یا آرایه لیست
$$sapply()$$ sapply(x, FUN) محاسبه روی مولفه‌ها لیست، بردار یا آرایه بردار یا ماتریس
$$tapply()$$ tapply(x, INDEX, FUN) محاسبه به تفکیک عامل لیست، بردار یا آرایه بردار
$$mapply()$$ mapply(FUN,x) محاسبه با پارامترهای برداری لیست و تابع لیست، ماتریس یا آرایه

اگر مطلب بالا برای شما مفید بوده، آموزش‌ها و مطالب زیر نیز که در زمینه به کار‌گیری آمار و محاسبات در نرم‌افزار R هستند، به شما پیشنهاد می‌شوند:

^^

بر اساس رای 1 نفر

آیا این مطلب برای شما مفید بود؟

یک نظر ثبت شده در “توابع Apply در زبان برنامه نویسی R — راهنمای کاربردی

نظر شما چیست؟

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