المتجه vector (ويُسمى تقنيًا atomic vector) في R هو مجموعة من القيم من النوع type نفسه: الشائع منها integer وnumeric وcharacter وlogical؛ ونادرًا complex أو raw. وتُخزَّن هذه القيم متجاورة في الذاكرة، على غرار مصفوفة في لغة C.
إذا أُدخلت قيم مختلطة، فسيحوّلها R بصمت كلها إلى نوع مشترك واحد، هو عادةً character، وقد يؤدي ذلك إلى نتائج غير متوقعة.
v <- c(2, 5.4, "txt")
v
#> [1] "2" "5.4" "txt"
typeof(v)
#> [1] "character"
إذا كانت كل القيم بحوزتك أصلًا، فاستخدم c() (اختصارًا لكلمة "concatenate"):
x <- 10
v <- c(4, 7, x)
دالة c() مرنة جدًا، ويمكن أن تشمل مدخلاتها متجهات ونطاقات أخرى. وهي تقبل أي عدد وأي تنوع من المدخلات، ثم تسطّحها في متجه واحد.
إذا بدأ المتجه صغيرًا ثم كبر كثيرًا أثناء التنفيذ (مثلًا عند إضافة قيم داخل حلقة)، فقد يكون من الأسرع بكثير أن تحجز مسبقًا متجهًا طويلًا يمكن ملؤه بالبيانات الفعلية لاحقًا:
v <- vector(length = 100)
وهذا يقلّل الحاجة إلى نسخ الكائن، وهي عملية بطيئة وشرهة للذاكرة مع المتجهات الطويلة.
للحصول على نطاق من القيم المتتالية (المتزايدة أو المتناقصة) يمكنك استخدام الصيغة :
v <- 1:5 # equivalent to c(1, 2, 3, 4, 5)
w <- 10:7 # c(10, 9, 8, 7)
في R، النطاقات ليست نوعًا مستقلًا، بل مجرد صيغة مختصرة لإنشاء متجه.
وتوفّر دالة seq() تحكّمًا أدق:
# specify step size
seq(2, 11, by = 3) # c(2, 5, 8, 11)
# specify number of values, evenly-spaced
seq(2.5, 4, length.out = 5) # c(2.500, 2.875, 3.250, 3.625, 4.000)
وتسهّل دالة rep() إنشاء أنماط متكررة:
count <- 3
rep(7, count)
#> [1] 7 7 7
rep(1:3, count)
#> [1] 1 2 3 1 2 3 1 2 3
rep(1:3, each = 2) # the "each" keyword is required in this case
#> [1] 1 1 2 2 3 3
وثمة نطاقات قليلة معرّفة مسبقًا كثوابت مدمجة في اللغة:
letters # LETTERS is the UPPERCASE equivalent
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
month.name
#> [1] "January" "February" "March" "April" "May" "June" "July" "August"
#> [9] "September" "October" "November" "December"
month.abb
#> [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
تُطبَّق العمليات تلقائيًا عنصرًا بعنصر على طول المتجه. ولا حاجة إلى الحلقات واستيعابات القوائم والاستدعاءات الذاتية الشائعة في لغات أخرى، بل (لأسباب تتعلق بالأداء) يُفضَّل تجنّبها إن أمكن.
2 + 1:3 # c(3, 4, 5)
2 * 1:3 # c(2, 4, 6)
12 / 1:3 # c(12 6 4)
2 ^ (1:3) # c(2, 4, 8) : exponentiation, operator precedence order here needs ()
وتعمل أزواج المتجهات أيضًا، إذ تُعالَج العناصر زوجًا زوجًا:
v <- 1:3
w <- 5:7
v + w
#> [1] 6 8 10
v * w
#> [1] 5 12 21
v ^ w
#> [1] 1 64 2187
وهذا أقوى مما يبدو للوهلة الأولى، وسيكون هناك المزيد لاستكشافه في مفاهيم لاحقة.
كما في لغات كثيرة، يمكننا استخدام الفهرس بين قوسين:
v <- 4:7
v[2]
#> [1] 5
v[2:4]
#> [1] 5 6 7
لكن انتبه: يستخدم R فهرسة تبدأ من 1 افتراضيًا. وهذا شائع في اللغات العلمية (Fortran و Matlab و Julia)، لكنه يختلف عن الفهرسة التي تبدأ من صفر في لغة C وخلفائها الكثيرين.
ويمكن استخدام النطاقات، كما في المثال أعلاه، وكذلك داخل دالة c() شديدة المرونة:
v <- c(4, 7, 10)
w <- c(2, v[2:3], 11) # c(2, 7, 10, 11)
ويمكن التعامل مع vectors (كما مع معظم الأشياء في R) كقيم قابلة للتعديل، باستخدام صيغة مرنة مماثلة للكتابة إلى عنصر واحد أو إلى عدة عناصر:
v <- 1:10
v[2] <- 42
v[4:6] <- c(11, 12, 13)
v
#> [1] 1 42 3 11 12 13 7 8 9 10
قد يظن المبرمجون المعتادون على لغات أخرى أن v[-1] وسيلة للوصول إلى العنصر الأخير في المتجه.
خطأ!
استخدم tail() لذلك.
v <- c(2, 4, 6) # defaults to last 6 entries
tail(v)
#> [1] 2 4 6
tail(v, n=1) # just the last entry
#> [1] 6
بل إن الفهارس السالبة وسيلة لحذف عنصر:
v <- c(2, 4, 6)
v[-2]
#> [1] 2 6 # second element gone
فيُرجع هذا نسخة أقصر من المتجه، بينما يبقى v نفسه دون تغيير.
إن تطبيق المقارنات على متجه سيُرجع متجهًا بالقدر نفسه من القيم المنطقية.
وقد يكون ذلك مريحًا جدًا داخل دوال مثل 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
وللتعبيرات المنطقية الأكثر تعقيدًا، من المهم استخدام العاملين & و|، فهما يعملان على المتجهات.
أما && و|| المكوّنان من محرفين مزدوجين فالمقصود بهما مقارنة قيمة واحدة فقط، كما نوقش في [مفهوم القيم المنطقية][concept-boolean].
v <- c(4, 7, 10)
v %% 2 == 0 & v > 5
#> [1] FALSE FALSE TRUE
سيعود هذا الموضوع في موضوعات لاحقة. اكتفِ الآن بملاحظة شكل الإخراج في وحدة التحكم:
1:20
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#> [18] 18 19 20
تبدأ الأسطر بأرقام داخل [ ] للإشارة إلى فهرس العنصر الأول (وهو أمر مفيد مع المتجهات الطويلة الممتدة على عدة أسطر).
ولكن لماذا تُسبق القيم المفردة بـ [1]؟
لا توجد في R قيم قياسية حقًا!
وما يبدو أنه رقم أو حرف واحد هو في الحقيقة متجه طوله 1.
وقد رأينا تلميحات على ذلك في مفهومَي الأساسيات والقيم المنطقية مع الأنواع العددية والمنطقية.
المتجهات الموصوفة أعلاه تُعرف باسم أكثر رسمية هو atomic vectors. وذلك لأنها لا يمكن أن تحتوي إلا على قيم بسيطة غير قابلة للتجزئة (ومن هنا وصف "atomic") مثل numeric وcharacter وboolean. أما البنى الأكثر تعقيدًا فقد تشكّل recursive vectors، وهو ما سنتعرّف عليه عند الحديث عن lists.