Menu
flag Ar iconالعربيةdown icon

القوائم في R: الإنشاء والوصول (‏[[ مقابل $ مقابل [) والتعديل

القوائم هي حاوية R التي تقبل كل شيء: أنواع مختلطة، وبنى متداخلة، وإطارات بيانات كاملة. والمهارة الأساسية هي معرفة متى يعيد [ قائمة أصغر ومتى يعيد [[ العنصر نفسه.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

القائمة تحمل أي شيء

لدى المتجه قاعدة صارمة واحدة: أن يكون كل عنصر من النوع نفسه. أما القائمة فلا قاعدة كهذه لديها. فكل عنصر في القائمة يمكن أن يكون من نوع مختلف، وبطول مختلف، بل يمكن أن يكون قائمة أخرى. وهي حاوية R عامة الغرض:

تُبلغ length() عن القيمة 4 - أي أربعة عناصر، رغم أن أحدها (وهو scores) يحتوي ثلاثة أرقام بذاته. فالقائمة تعدّ حجيراتها لا ما بداخلها. والدالة str() (أي البنية) هي أسرع وسيلة لرؤية ما تحمله القائمة فعلًا - سطر لكل عنصر مع اسمه ونوعه ومعاينة له. اجعل str() انعكاسًا تلقائيًا: فكلما سلّمتك دالة شيئًا ولم تكن متأكدًا مما هو، طبّق عليه str().

والأسماء اختيارية (فالتعبير list(1, "a", TRUE) قانوني) لكنها تستحق الوجود دائمًا تقريبًا - إذ توثّق القائمة المسماة نفسها بنفسها.

الطرق الثلاث للدخول: ‏[ و[[ و$

هذا هو الجزء من القوائم الذي يخطئ فيه الجميع مرة واحدة، فلنضبطه تمامًا. هناك ثلاثة معاملات وصول، وهي تؤدي مهمتين مختلفتين:

  • يعيد [ قائمة أصغر - حاوية من النوع نفسه تحمل ما اخترته.
  • ويعيد [[ العنصر نفسه - أي القيمة الفعلية داخل الحجيرة.
  • والمعامل $ اختصار لـ [[ مع اسم حرفي: فالتعبير person$age هو person[["age"]].

والفرق غير مرئي حين تطبع بلا تدقيق، وشديد الوضوح لحظة محاولتك الحساب:

فالتعبير person["scores"] صنفه "list" - أي قائمة بطول 1 داخلها متجه الدرجات. أما person[["scores"]] فصنفه "numeric" - أي المتجه نفسه، جاهزًا لـ mean(). واستدعِ mean() على نسخة القوس المفرد فتحصل على خطأ يقول إن الوسيط ليس عدديًا؛ وتلك الرسالة هي دائمًا تقريبًا علامة على أنك استخدمت [ حيث قصدت [[.

وثمة استعارة عالقة في الذهن: القائمة قطار. فالمعامل [ يعطيك قطارًا أقصر - وهو ما يزال قطارًا. أما [[ فيفتح عربة ويسلّمك الحمولة.

فمتى يكون [ الأداة الصحيحة إذًا؟ حين تريد عدة عناصر وتريد إبقاءها معبأة معًا:

فلا يمكنك سحب عنصرين "بذاتيهما" دفعة واحدة - إذ تحتاج قيمتان إلى حاوية - ولذا فإن اختيار عدة عناصر يكون دائمًا بالقوس المفرد ويعطي دائمًا قائمة.

وثمة فرق إضافي: يستخدم $ المطابقة الجزئية (فالتعبير person$sc يجد scores إن كان بلا لبس)، وهو أمر مريح في الطرفية وعبء في النصوص البرمجية. وفي الشيفرة التي يجب أن تستمر في العمل، فضّل [[ مع الاسم الكامل - كما أنه يقبل اسمًا مخزنًا في متغير، وهو ما لا يستطيعه $.

الإضافة والتغيير والحذف

تنمو القوائم وتنكمش بالإسناد البسيط - دون حاجة إلى أسلوب إضافة خاص:

  • الإسناد إلى اسم غير موجود بعد يضيف العنصر.
  • والإسناد إلى اسم موجود يستبدله.
  • والإسناد إلى الموضع length(x) + 1 يلحق عنصرًا غير مسمى.
  • وإسناد NULL يحذف العنصر كليًا - فتصير القائمة أقصر. (وهذا هو الموضع الوحيد الذي تعمل فيه NULL أمر حذف؛ فإذا احتجت فعلًا إلى تخزين "لا شيء" في حجيرة، فاستخدم x["k"] <- list(NULL).)

ولإلصاق قائمتين تباعًا، تعمل c() على القوائم كما تعمل على المتجهات: فالتعبير c(list_a, list_b) يعيد قائمة واحدة أطول.

القوائم المتداخلة

لأن عنصر القائمة يمكن أن يكون قائمة أخرى، تتداخل القوائم إلى أي عمق - وهو ما يجعلها الشكل الطبيعي في R للبيانات المهيكلة مثل JSON المحلَّل أو النتائج المجمّعة:

والتنقيب في الداخل ليس إلا تسلسلًا لمعاملات الوصول - إذ ينزل كل $ أو [[ مستوى واحدًا. وحين تصبح بنية متداخلة محيرة، تعرض str(company) الشجرة كلها دفعة واحدة، وتعرض str(company, max.level = 1) الطبقة العليا فقط.

التسطيح باستخدام unlist()

تطوي unlist() القائمة - مهما بلغ عمق تداخلها - في متجه واحد:

وثمة أمران جديران بالانتباه. أولًا، تبني unlist() أسماء نيابة عنك (مثل math1 وmath2 وart) كي تظل قادرًا على معرفة مصدر كل قيمة. وثانيًا - وهذا هو الفخ - المتجه يحمل نوعًا واحدًا فقط، لذا فإن تسطيح قائمة مختلطة يكره كل شيء على أكثر الأنواع مرونة بين الموجودة. فالعدد 1 عاد سلسلةً نصية "1". وإذا سطّحت فتحولت أرقامك إلى نص، فالقائمة لم تكن متجانسة كما ظننت.

لماذا تهم القوائم

قد تبدو القوائم موضوعًا للمبتدئين ستتجاوزه. والحقيقة عكس ذلك - فهي عنصر حامل في اللغة كلها:

  • الدوال التي تعيد عدة أشياء تعيد قائمة. فدوال R تعيد كائنًا واحدًا؛ والقائمة تجعل ذلك الكائن الواحد يحمل قيمة مقدَّرة هنا وجدول معاملات هناك. وحين تجري انحدارًا، يكون كائن النموذج الذي تستعيده قائمة (كبيرة ومصنّفة) - وتطبيق str() عليه يثبت ذلك.
  • إطار البيانات قائمة من الأعمدة - متجهات متساوية الطول مع بعض السلوك الإضافي. وكل ما تعلمته للتو ($ و[[ وإسناد NULL لحذف عنصر) ينطبق حرفيًا على أعمدة إطار البيانات.
  • عائلة apply - أي lapply() وأخواتها - تأخذ قائمة، وتطبق دالة على كل عنصر، وتعيد قائمة. فقوائم داخلة وقوائم خارجة هي الصيغة القياسية للحساب المتكرر في R.

ما تخرج به

  • تحمل القوائم أي شيء: أنواعًا مختلطة، وأطوالًا غير متساوية، وقوائم أخرى، وإطارات بيانات كاملة.
  • يعيد [ قائمة أصغر؛ ويعيد [[ العنصر نفسه؛ والمعامل $ هو [[ مع اسم حرفي. تنوي الحساب؟ إذًا أنت تريد [[ أو $.
  • أضف بالإسناد إلى اسم جديد، واحذف بإسناد NULL، واجمع بـ c().
  • تسطّح unlist() القائمة إلى متجه وتكره الأنواع المختلطة أثناء ذلك - فتحقق من صنف النتيجة.
  • إطارات البيانات قوائم من الأعمدة، لذا تنتقل هذه المعرفة مباشرة إليها.

التالي: المصفوفات - نظير القائمة بنوع واحد وبصفوف وأعمدة.

الأسئلة الشائعة

ما الفرق بين [ و[[ في R؟

تعيد الأقواس المفردة [ دائمًا قائمة تحتوي العناصر المحددة - أي حاوية أصغر من النوع نفسه. أما الأقواس المزدوجة [[ فتصل إلى الداخل وتعيد العنصر نفسه. فإذا كان x$scores يحمل متجهًا عدديًا، فإن x["scores"] قائمة بطول 1 بينما x[["scores"]] هو المتجه العددي. استخدم [[ (أو $) حين تريد العمل بالقيمة فعلًا.

كيف تضيف عنصرًا إلى قائمة في R؟

أسند إلى اسم غير موجود بعد: ‏x$email <- "rosa@example.com" أو x[["email"]] <- .... فتنمو القائمة تلقائيًا. وللإضافة دون اسم، أسند إلى الموضع التالي: ‏x[[length(x) + 1]] <- value. ولحذف عنصر، أسند إليه NULL: ‏x$email <- NULL.

كيف تحوّل قائمة إلى متجه في R؟

تسطّح unlist(x) القائمة (بما فيها المتداخلة) إلى متجه واحد. ولأن المتجه يحمل نوعًا واحدًا فقط، يُكره كل شيء على أكثر الأنواع مرونة بين الموجودة - فقائمة من الأرقام والنصوص تصبح متجهًا نصيًا بالكامل. تحقق من class() النتيجة بعد التسطيح.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن