تصفية الصفوف: القناع المنطقي
لتصفية data frame في R، ضع شرطًا في موضع الصفوف داخل الأقواس: df[condition, ]. يُقيَّم الشرط إلى متجه من TRUE/FALSE، قيمة لكل صف، ويحتفظ R بصفوف TRUE:
اقرأ scores[scores$score > 80, ] من الداخل نحو الخارج: تُنتج scores$score > 80 القيم TRUE FALSE TRUE TRUE FALSE، وتحتفظ الأقواس بالصفوف 1 و3 و4. الفاصلة الأخيرة ليست اختيارية. فالأقواس على data frame تأخذ الصيغة [rows, columns]، وترك خانة الأعمدة فارغة يعني "كل الأعمدة". وإذا نسيت الفاصلة فستكون بذلك تفهرس الأعمدة بدلًا من الصفوف، وهو من أشيع أخطاء المبتدئين في R.
لاحظ أن أرقام الصفوف المطبوعة هي 1 3 4 وليست 1 2 3، إذ تحافظ التصفية على تسميات الصفوف الأصلية. الأمر غير ضار، لكنه يفاجئ كثيرين.
شروط متعددة: & و| (لا &&)
ادمج الشروط باستخدام & (و) و| (أو). ولكل شرط مقارنته الكاملة الخاصة:
أما المصيدة فهي أن R يملك أيضًا && و||، وهما ليسا بديلين عن & و| هنا. تعمل الصيغتان المزدوجتان على القيم المفردة (وهما موجودتان من أجل شروط if)، وعند تمرير أعمدة كاملة إليهما يتوقف R الحديث (4.3 فما فوق) بخطأ مثل 'length = 5' in coercion to 'logical(1)'، بينما كانت الإصدارات الأقدم تقارن الصف الأول فقط بصمت، وهو سلوك أسوأ على الأرجح. داخل الأقواس، استخدم دائمًا & و| المفردتين. ويغطي مستند المعاملات هذا الفرق بالتفصيل.
%in%: المطابقة مع مجموعة من القيم
عندما يجب أن يطابق عمود ما أيًّا من عدة قيم، لا تصنع سلسلة من == موصولة بـ |، بل استخدم %in%:
تُرجع x %in% set القيمة TRUE في كل موضع تكون فيه x إحدى قيم set. وهي أوضح في القراءة من status == "pending" | status == "shipped"، وتتوسع لأي عدد من القيم، وتُنفى بسهولة: !(orders$status %in% c("refunded")).
اختيار الأعمدة
تقبل خانة الأعمدة داخل الأقواس أسماءً أو مواضع:
ملاحظتان. الاختيار بالاسم يصمد أمام إعادة ترتيب الأعمدة، أما الاختيار بالموضع (scores[, c(1, 3)]) فينهار يوم يُدرج أحدهم عمودًا جديدًا. كما أن طلب عمود واحد يُرجع متجهًا عاديًا لا data frame، ولهذا يعرض print() الثاني القيمتين 91 88 دون جدول حولهما. وحين تحتاج إلى الحفاظ على شكل data frame، أضف drop = FALSE: scores[, "score", drop = FALSE].
subset(): سطر واحد ودود من base
يوفّر base R غلافًا يتعامل مع الصفوف والأعمدة في استدعاء واحد سهل القراءة، بلا $ وبلا حساب للفواصل:
داخل subset() تكتب أسماء الأعمدة مجردة (score لا scores$score)، إذ تُقيَّم في سياق data frame. ويُسمى هذا التقييم غير القياسي، وله تحفّظ موثّق: فهو يحلّ الأسماء وقت التنفيذ بطرق تسيء التصرف داخل دوالك الخاصة (فمتغير اسمه score في دالتك قد يحجبه عمود اسمه score). والقاعدة العملية، كما وردت حرفيًا في ?subset: ممتازة في العمل التفاعلي، وتجنّبها في البرمجة، واستخدم الفهرسة بالأقواس هناك.
مصيدة NA
المقارنة مع NA تُنتج NA لا FALSE، والتصفية بالأقواس تُبقي صفوف قناع NA صفوفًا مملوءة بـ NA:
تحتوي النتيجة الأولى على صف زائف مملوء بـ NA لأن شرط الصف الثاني لم يكن TRUE ولا FALSE. والحل هو اشتراط !is.na() صراحةً. وتحذف كل من subset() وfilter() في dplyr صفوف شرط NA بصمت، وهو أمر مريح لكن ينبغي أن تدرك حدوثه. ويوضح مستند القيم المفقودة سبب انتشار NA بهذه الطريقة.
أسلوب dplyr: filter() وselect()
تقسم حزمة dplyr المهمة إلى فعلين: filter() للصفوف وselect() للأعمدة، مع أسماء أعمدة مجردة وبلا تكرار لـ df$ (المثال ثابت، فالبيئة التجريبية تشغّل base R فقط):
library(dplyr)
scores |> filter(score > 80)
scores |> filter(score > 80, team == "red") # comma = AND
scores |> filter(status %in% c("pending", "shipped"))
scores |> select(name, score)
scores |> filter(team == "red") |> select(name, score)
تحذف filter() صفوف شرط NA تلقائيًا وتُرجع دائمًا data frame (لا متجهًا مجردًا)، ما يزيل مصيدتَي base المذكورتين. والمقابل هو الاعتماد على حزمة إضافية، إضافة إلى تحفّظات التقييم غير القياسي نفسها التي تنطبق على subset(). راجع مقدمة dplyr لجولة كاملة على الأفعال.
الخلاصة
- الصيغة
df[condition, ]هي التعبير الأساسي، والفاصلة فيها إلزامية. - ادمج الشروط بـ
&و|المفردتين، فـ&&تُطلق خطأ مع المتجهات. - تتفوق
%in%على سلاسل==عند المطابقة مع مجموعة من القيم. - اختر الأعمدة بالاسم، وتذكّر أن العمود الواحد يتحول إلى متجه ما لم تستخدم
drop = FALSE. -
subset()هي السطر التفاعلي اللطيف، والأقواس هي الخيار القابل للبرمجة. - المقارنات مع
NAتُنتج صفوفًا زائفة داخل الأقواس، فاحتَط بـ!is.na().
في المستند التالي: إضافة الأعمدة وتحويلها عبر df$new <- ... وifelse() وmutate() في dplyr.
الأسئلة الشائعة
كيف أصفّي صفوف data frame في R؟
ضع شرطًا منطقيًا في موضع الصفوف داخل الأقواس: df[df$score > 80, ]. ينتج الشرط متجهًا من قيم TRUE/FALSE، ويحتفظ R بالصفوف التي قيمتها TRUE. الفاصلة مهمة، فهي تفصل مرشّح الصفوف عن مرشّح الأعمدة الفارغ. وتؤدي subset(df, score > 80) المهمة نفسها بكتابة أقل.
كيف أصفّي بعدة شروط في R؟
ادمج الشروط باستخدام & (و) و| (أو): df[df$score > 80 & df$team == "red", ]. استخدم & المفردة لا &&، فالصيغة المزدوجة تعمل على القيم المفردة فقط وتُطلق خطأ مع المتجهات في إصدارات R الحديثة.
ما الفرق بين subset() والتصفية بالأقواس في R؟
كلاهما يحتفظ بالصفوف نفسها، مع فارقين: تحذف subset() بصمت الصفوف التي يكون الشرط فيها NA (بينما تُبقيها الأقواس صفوفًا مملوءة بـ NA)، كما تعتمد subset() على التقييم غير القياسي، إذ تكتب أسماء الأعمدة مجردة، وهو أمر مريح في العمل التفاعلي لكنه غير موثوق داخل دوالك الخاصة.
كيف أصفّي الصفوف التي يطابق فيها عمود ما قائمة من القيم؟
استخدم %in%: تحتفظ df[df$team %in% c("red", "blue"), ] بالصفوف التي يكون فيها team أيًّا من القيم المذكورة. وهي تغني عن سلسلة من مقارنات == موصولة بـ |، وخلافًا لـ == فإنها لا تُرجع NA أبدًا.