المصفوفة متجه ذو أبعاد
المصفوفة في R مستطيل من القيم - صفوف وأعمدة - تحمل فيه كل خلية النوع نفسه، وهو أرقام عادة. وهي تحت السطح متجه حرفيًا مُلحق به السمة dim، وهذا يفسر معظم سلوكها: نوع واحد في كل عناصرها، وحساب متجهي في كل موضع.
وتبني واحدة بإعادة تشكيل متجه عبر matrix():
ست قيم وصفان - وتستنتج R أن ثلاثة أعمدة مطلوبة. وتُبلغ dim() عن البعدين معًا بالصيغة 2 3؛ وتعطيهما nrow() وncol() كلًا على حدة.
وتفحّص المصفوفة المطبوعة بدقة: تسير القيم 1 2 في العمود الأول، ثم 3 4 في التالي. فـ R تملأ المصفوفات عمودًا عمودًا افتراضيًا. وإذا كانت بياناتك تُقرأ صفًا صفًا - وهي الطريقة التي يكتبها بها البشر عادة - فصرّح بذلك عبر byrow = TRUE:
والآن صار الصف الأول 1 2 3. ونسيان byrow = TRUE لا يطلق خطأ - بل يعطيك بصمت ترتيبًا يبدو منقولًا للأرقام نفسها، لذا اجعل فحص التخطيط المطبوع عادة كلما بنيت مصفوفة من قيم خام.
الفهرسة: m[row, column]
تأخذ فهرسة المصفوفات موضعين داخل زوج واحد من الأقواس، الصف قبل الفاصلة والعمود بعدها - وكلاهما يعدّ من 1:
وترك موضع فارغًا يعني "جميعها": فالتعبير m[1, ] هو الصف الأول كاملًا، وm[, 2] هو العمود الثاني كاملًا. ولاحظ أن كليهما يعود متجهًا عاديًا - إذ تسقط R البعد الذي انكمش إلى الحجم 1. وهذا مريح في العمل التفاعلي وفخ في الشيفرة، لأن دالة تتوقع مصفوفة ستختنق بالمتجه. اطلب من R الإبقاء على الشكل عبر drop = FALSE:
تُبلغ dim() الآن عن 1 3 - فهي ما تزال مصفوفة. وكلما اقتطعت صفًا أو عمودًا مفردًا داخل دالة، فاكتب drop = FALSE؛ فالعلة التي يمنعها (شيفرة تعمل مع بيانات عريضة وتنهار مع بيانات ذات عمود واحد) علة بائسة في التعقب.
وتعمل الأقنعة المنطقية هنا أيضًا: فالتعبير m[m > 3] يعيد كل الخلايا التي تتجاوز 3، على هيئة متجه.
البناء باستخدام cbind() وrbind()
بدلًا من إعادة تشكيل متجه طويل واحد، يمكنك تجميع مصفوفة من قطع: إذ تربط cbind() المتجهات معًا أعمدةً، وتربطها rbind() صفوفًا. وتوسّع الدالتان نفساهما مصفوفة قائمة كذلك:
وتسافر الأسماء مع المتجهات: فقد استخدمت cbind() الاسمين heights وweights أسماءً للأعمدة تلقائيًا، وهو ما يبقي المصفوفة المطبوعة مقروءة. وتصر الدالتان على توافق الأطوال (مع إعادة التدوير للقيم بطول 1)؛ وربط متجه بطول 3 بمصفوفة ذات أربعة صفوف يطلق تحذيرًا.
ويمكن ضبط أسماء الأعمدة والصفوف مباشرة عبر colnames(m) <- ... وrownames(m) <- ...، وبعدها يمكنك الفهرسة بالاسم: people[, "weights"].
الضرب عنصرًا عنصرًا * مقابل ضرب المصفوفات الحقيقي %*%
هذا هو التمييز الأهم في هذا المقال كله. فـ R تملك معاملَي ضرب اثنين للمصفوفات، وهما يحسبان أشياء مختلفة تمامًا:
- التعبير
a * aهو ضرب عنصر بعنصر: كل خلية مضروبة في الخلية المقابلة. فتصير القيم1 2 3 4هي1 4 9 16، مرتبة في الشكل نفسه. وهذه ليست إلا عملية حسابية متجهية، أي المعامل*نفسه الذي تستخدمه مع المتجهات. - والتعبير
a %*% aهو ضرب المصفوفات من الجبر الخطي - إذ تكون كل خلية في النتيجة صفًا من المصفوفة الأولى مضروبًا في عمود من الثانية ثم مجموعًا. وتعطي المدخلات نفسها القيم7 10 15 22: أرقامًا مختلفة كليًا.
شغّل المقتطف وقارن المخرجين معًا؛ فرؤيتهما يختلفان على مدخلات متطابقة هي ما يرسّخ التمييز. وإذا كتبت * حيث تستدعي الرياضيات %*%، فلن تحذرك R - إذ تتوافق الأبعاد في الحالتين مع المصفوفات المربعة - وستحصل ببساطة على أرقام خاطئة. وفي شيفرة الإحصاء (مصفوفات التغاير، وجبر النماذج الخطية) تُعد هذه من العلل الصامتة الكلاسيكية.
وتنقل t() المصفوفة - أي تقلب الصفوف والأعمدة - وتظهر باستمرار بجوار %*% لأن ضرب المصفوفات يحتاج إلى تطابق الأبعاد الداخلية:
وللإكمال: تعكس solve(m) المصفوفة، ويعامل %*% المتجه بوصفه مصفوفة بعمود واحد. وهذا أقصى عمق يحتاجه معظم العمل مع البيانات.
ملخصات الصفوف والأعمدة
الجمع أو حساب المتوسط عبر الصفوف والأعمدة شائع إلى حد أن R تأتي بدوال مخصصة وسريعة له:
تطوي rowSums() كل صف إلى رقم واحد (6 15 هنا)، وتطوي colSums() كل عمود (5 7 9)، وتحسب صيغتا Means المتوسط بدلًا من الجمع. وفضّل هذه على الحلقات المصنوعة يدويًا بل وحتى على apply(m, 1, sum) - فهي أوضح وأسرع. أما للملخصات التي لا تغطيها هذه الأربع (كالقيمة العظمى لكل عمود مثلًا)، فإن عائلة apply هي الأداة العامة: apply(m, 2, max).
مصفوفة أم إطار بيانات؟
كلاهما مستطيل، فأيهما تختار؟
- المصفوفة: حين تكون كل خلية من النوع نفسه، وحين يهم الحساب. أي الحساب العددي، والجبر الخطي، وحسابات المسافات، والشبكات الشبيهة بالصور. والمصفوفات أخف وعملياتها أسرع بفضل ضمانة النوع الواحد تحديدًا.
- إطار البيانات: حين تكون الأعمدة من أنواع مختلفة - أسماء بجوار أعمار بجوار رايات منطقية. وهذه هي البيانات الجدولية الواقعية، وهي ما تتوقعه كل دوال تحليل البيانات تقريبًا.
وثمة قاعدة جيدة: إذا كنت ستفتحه طبيعيًا في جدول بيانات بأعمدة مسماة ومختلطة، فهو إطار بيانات. أما إذا كان شبكة أرقام تنوي إجراء الجبر عليها، فهو مصفوفة. والتحويل بينهما سهل (as.matrix() وas.data.frame()) - لكن as.matrix() على إطار بيانات يحتوي أي عمود نصي تكره كل شيء على النوع النصي، فحوّل الأعمدة العددية وحدها.
ما تخرج به
- المصفوفة متجه ذو أبعاد: نوع واحد في كل عناصرها، وتُبنى بـ
matrix(data, nrow, ncol)- وتُملأ عمودًا عمودًا ما لم تمررbyrow = TRUE. - افهرس بالصيغة
m[row, col]؛ والموضع الفارغ يعني "الكل"؛ وأضفdrop = FALSEعند اقتطاع صفوف أو أعمدة مفردة داخل الشيفرة. - تجمّع
cbind()وrbind()المصفوفات من متجهات أو توسّعان القائمة منها. - المعامل
*عنصر بعنصر، والمعامل%*%ضرب مصفوفات حقيقي - مدخلات واحدة وأجوبة مختلفة ودون أي تحذير. - تتولى
rowSums()وcolSums()وrowMeans()وcolMeans()الملخصات اليومية.
التالي: العوامل - كيف تمثل R البيانات الفئوية، والفخاخ المصاحبة لها.
الأسئلة الشائعة
كيف تنشئ مصفوفة في R؟
يعيد matrix(1:6, nrow = 2) تشكيل متجه إلى صفين وثلاثة أعمدة، ويملؤها عمودًا عمودًا. أضف byrow = TRUE لملئها صفًا صفًا بدلًا من ذلك. ويمكنك أيضًا تجميع مصفوفة من متجهات: فـ cbind() تلصقها أعمدةً، وrbind() تلصقها صفوفًا.
ما الفرق بين * و%*% في R؟
يضرب المعامل * عنصرًا عنصرًا - كل خلية في الخلية المقابلة، ويجب أن تتوافق الأبعاد. أما %*% فهو ضرب المصفوفات الحقيقي من الجبر الخطي (صفوف في أعمدة، فيجب أن تتطابق الأبعاد الداخلية). وهما يعطيان نتائج مختلفة تمامًا على المصفوفات نفسها، واستخدام * حيث قصدت %*% علة صامتة كلاسيكية.
كيف تحصل على صف واحد أو عمود واحد من مصفوفة في R؟
اترك الموضع الآخر فارغًا: فالتعبير m[1, ] هو الصف الأول، وm[, 2] هو العمود الثاني. ويعود كلاهما متجهًا عاديًا افتراضيًا. أضف drop = FALSE - كما في m[1, , drop = FALSE] - للإبقاء على النتيجة مصفوفة بصف واحد أو عمود واحد، وهو ما يهم حين تتوقع الشيفرة اللاحقة بعدين.