رأينا في مفهوم Vectors أن المتجه يمكن استخدامه في تعبير شرطي، فينتج عنه متجه منطقي.
ويمكن بدورنا استخدام هذا المتجه في دوال مثل all() وany().
v <- c(4, 7, 10)
v >= 6
#> [1] FALSE TRUE TRUE
all(v > 6)
#> [1] FALSE # not all elements match this condition
any(v > 6)
#> [1] TRUE # at least one element matches
لكن هذه التقنية أقوى بكثير من ذلك.
يمكن استخراج عناصر مختارة من متجه باستخدام رقم فهرس أو متجه من الفهارس:
v <- 5:10
v
#> [1] 5 6 7 8 9 10
v[3]
#> [1] 7
v[c(2, 4)]
#> [1] 6 8
بدلًا من ذلك، استخدم متجهًا منطقيًا لتصفية المتجه الأصلي، فيُرجع مجموعة فرعية من العناصر التي تقابلها قيمة TRUE:
v <- 1:3
bools <- c(FALSE, TRUE, TRUE)
v[bools]
#> [1] 2 3
وخطوة صغيرة تفصلنا عن توليد المتجه المنطقي بتعبير شرطي:
bools <- v >= 2
bools
#> [1] FALSE TRUE TRUE
v[bools]
#> [1] 2 3
# simpler form
v[v >= 2]
#> [1] 2 3
يمكن أن تكون هذه التعبيرات الشرطية معقدة بلا حدود، إذ تشمل عدة متجهات أو دوال:
v <- 1:10
w <- 10:1
# pairwise comparison
v > w
#> [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
# shorter form
v[v > w]
#> [1] 6 7 8 9 10
سمّينا هذا المفهوم "تصفية المتجهات"، آملين أن يكون مفهومًا لأوسع شريحة من المبرمجين.
وداخل مجتمع R، من المرجح أن مصطلح "التقسيم الفرعي" أكثر شيوعًا.
لتبسيط الأمر، قارنت الأمثلة السابقة جميعها متجهات لها الطول نفسه. لكن ماذا يحدث إذا اختلفت الأطوال؟
الجواب شيء فعّال إذا استُخدم عن قصد، لكنه مربك إذا جاء دون توقّع.
مثال:
v <- 1:6
cond <- c(TRUE, FALSE)
v[cond]
#> [1] 1 3 5
ماذا حدث هنا؟ فهرسنا متجهًا طوله 6 بقيمة منطقية طولها 2، لكن النتيجة كانت متجهًا طوله 3.
إذا كان المتجه "أقصر من اللازم" بالنسبة إلى التعبير الذي يُستخدم فيه، فإن R يكرّره حتى يبلغ الطول المطلوب، ويسمى ذلك "إعادة التدوير".
وهكذا حوّل هذا متجهنا c(TRUE, FALSE) إلى c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)، ثم أرجع v[cond] قيم TRUE في v، وهي في هذه الحالة الأعداد الفردية.
فما الذي يحدث في المثال التالي؟
v <- 1:3
0.5 * v
#> [1] 0.5 1.0 1.5
2 + v
#> [1] 3 4 5
يبدو الأمر في البداية كأنه ضرب قياسي وجمع قياسي (غريب بعض الشيء)، لكن لا توجد قيم قياسية في R.
بدلًا من ذلك، تُكرَّر الأعداد للحصول على متجه يطابق طول v، ثم تُطبَّق العملية الحسابية عنصرًا بعنصر باستخدام c(0.5, 0.5, 0.5) أو c(2, 2, 2).
إعادة التدوير مفيدة جدًا جدًا، لكن تأكد من أن هذا ما تريده فعلًا.
قد يؤدي اختلاف طفيف في أطوال المتجهات (مثل 99 مقابل 100) إلى ظهور bugs مربكة.
ويوصي بعض المؤلفين بألا تُستخدم إعادة التدوير إلا عندما يكون طول أحد المتجهين 1.
تأخذ الدالة which() وسيطًا منطقيًا وتُرجع متجهًا من الفهارس التي تكون قيمتها TRUE.
v
#> [1] 2 7 9
v[v > 5] # returns values
#> [1] 7 9
which(v > 5) # returns indices
#> [1] 2 3
كانت الأقسام السابقة عن إيجاد العناصر التي تحقق معيارًا ما.
أما إذا كنت تحتاج فقط إلى معرفة ما إذا كان المتجه يحتوي على عنصر معيّن، فإنك تستخدم العامل %in%.
3 %in% 1:5
#> [1] TRUE
7 %in% 1:5
#> [1] FALSE
وهذا مكافئ وظيفيًا للكود التالي، لكنه أيسر بكثير:
length(v[v == 3]) > 0
#> [1] TRUE
ويوجد أيضًا العامل %notin%، الذي يعطي النتيجة المنطقية not للعامل %in%.