إنتقل إلى المحتوى الرئيسي

الأسبوع 8: التحليل الثنائي والمتعدد المتغيرات، الارتباط

🔗 الأسبوع الماضي: متغير واحد في كل مرة. هذا الأسبوع: كيف يتحرك متغيران (أو أكثر) معًا؟

🎯 أهداف التعلم

بنهاية هذا الأسبوع ستكون قادرًا على:

  • حساب معامل الارتباط بين متغيرين رقميين وتفسيره.
  • تصور العلاقة بين متغيرين رقميين بمخطط انتشار، وبين متغير رقمي ومتغير فئوي بمخططات صندوق مجمّعة.
  • استخدام متغير ثالث (اللون، أو عمود فئوي ثانٍ) لإضافة بُعد إلى مخطط ثنائي المتغيرات.
  • مقارنة متغيرين فئويين باستخدام جدول تقاطع.
  • شرح، بدقة، لماذا لا يعني الارتباط وجود سببية.

الدرس

الارتباط: كيف يتحرك متغيران رقميان معًا؟

يقيس معامل ارتباط بيرسون rr قوة واتجاه العلاقة الخطية بين متغيرين رقميين، ويتراوح من 1-1 (ارتباط سالب تام) عبر 00 (لا علاقة خطية) إلى +1+1 (ارتباط موجب تام):

df["math_score"].corr(df["reading_score"]) # رقم واحد بين -1 و 1

قاعدة تقريبية (ليست متفقًا عليها عالميًا، لكنها شائعة الاستخدام) لتفسير r|r|:

| r|r| | تفسير تقريبي | |---|---| | 0.0 – 0.2 | ضعيف جدًا / لا يُذكر | | 0.2 – 0.4 | ضعيف | | 0.4 – 0.6 | متوسط | | 0.6 – 0.8 | قوي | | 0.8 – 1.0 | قوي جدًا |

اعتبر هذا حدسًا أوليًا، لا حدًا صارمًا — فما يُعتبر "قويًا" قد يعتمد كثيرًا على المجال والسؤال المحدد المطروح.

بالنسبة لمجموعة الأعمدة الرقمية كلها دفعة واحدة، تنتج .corr() على DataFrame مصفوفة ارتباط كاملة — قيمة rr لكل زوج، بما في ذلك ارتباط كل متغير بنفسه بشكل بديهي بقيمة 1.01.0:

df[["math_score", "reading_score", "writing_score"]].corr()

تُصوِّر خريطة حرارية هذه المصفوفة بنظرة واحدة، مستخدمةً شدة اللون للدلالة على قوة الارتباط:

import seaborn as sns
import matplotlib.pyplot as plt

corr_matrix = df[["math_score", "reading_score", "writing_score"]].corr()
sns.heatmap(corr_matrix, annot=True, cmap="coolwarm", vmin=-1, vmax=1)
plt.title("Correlation between score types")
plt.show()

تطبع annot=True قيم rr الفعلية على كل خلية؛ يثبّت vmin=-1, vmax=1 مقياس اللون عند النطاق الحقيقي للمعامل، بحيث تكون شدة اللون قابلة للمقارنة بين خرائط حرارية مختلفة بدلاً من إعادة تحجيمها حسب النطاق الذي يصادف ظهوره في هذه المصفوفة تحديدًا.

مخططات الانتشار: تصور علاقة رقمية-رقمية

يُظهر مخطط الانتشار العلاقة الخام التي يلخصها معامل الارتباط برقم واحد فقط:

plt.scatter(df["reading_score"], df["writing_score"], alpha=0.5)
plt.xlabel("Reading score")
plt.ylabel("Writing score")
plt.title("Reading vs. writing scores")
plt.show()

تجعل alpha=0.5 النقاط شبه شفافة، بحيث تظهر النقاط المتداخلة (شائعة مع الدرجات الصحيحة المتكررة عبر عدد كبير من الطلاب) كمناطق أغمق بدلاً من أن تحجب بعضها البعض كليًا.

إضافة متغير ثالث باستخدام اللون

يُظهر مخطط الانتشار متغيرين فقط مباشرة، لكن معامل hue في seaborn يلوّن كل نقطة حسب عمود ثالث (فئوي عادةً)، مما يتيح لك التحقق مما إذا كانت العلاقة تصمد بنفس الشكل عبر المجموعات:

sns.scatterplot(data=df, x="reading_score", y="writing_score", hue="gender", alpha=0.6)
plt.title("Reading vs. writing scores, by gender")
plt.show()

إذا بدت سحابتا النقاط الملونتان وكأنهما تتبعان نفس الاتجاه العام، فمن المرجح أن علاقة القراءة/الكتابة لا تعتمد كثيرًا على الجنس؛ أما إذا كانت سحابة أحد الألوان مزاحة بوضوح أو ذات شكل مختلف، فهذا يستحق التحقيق أكثر — وهذا بالضبط نوع السؤال الذي بُنيت مخططات الأسبوع 9 المقسّمة للإجابة عنه بشكل أوفى.

مخططات الصندوق المجمّعة: متغير رقمي عبر الفئات

لمقارنة توزيع متغير رقمي عبر الفئات (وليس معدله فقط)، استخدم مخطط الصندوق المجمّع من seaborn — الامتداد متعدد المجموعات لمخطط الصندوق الفردي من الأسبوع الماضي:

sns.boxplot(data=df, x="test_preparation_course", y="math_score")
plt.title("Math score by test preparation status")
plt.show()

يُظهر هذا أكثر من مجرد متوسط groupby واحد: هل يختلف التشتت بين المجموعات أيضًا، وهل تتجمع القيم الشاذة في مجموعة أكثر من أخرى.

مقارنة متغيرين فئويين: جدول التقاطع

تعمل .corr() على الأعمدة الرقمية فقط — أما بالنسبة لمتغيرين فئويين، فإن pd.crosstab تبني جدولًا لعدد مرات حدوث كل مجموعة، وهو النظير الفئوي لفحص الارتباط:

pd.crosstab(df["gender"], df["test_preparation_course"])
pd.crosstab(df["gender"], df["test_preparation_course"], normalize="index") # نسب الصفوف بدلاً من الأعداد

تحوّل normalize="index" كل صف إلى نسب تجمع إلى 1 — مفيدة لطرح سؤال مثل "ضمن كل جنس، ما النسبة التي أكملت التحضير للاختبار؟" بدلاً من الأعداد الخام فقط، والتي يمكن أن تكون مضللة إذا اختلفت أحجام المجموعات نفسها.

الارتباط ليس سببية

إخبارك أن rr قريب من 11 أو 1-1 يعني أن متغيرين يتحركان معًا — لا يقول شيئًا عما إذا كان أحدهما يسبب الآخر، أو ما إذا كان كلاهما مدفوعًا بعامل ثالث. مثال كلاسيكي من الكتب المدرسية: مبيعات الآيس كريم وحوادث الغرق يرتبطان بقوة عبر السنة، لكن الآيس كريم لا يسبب الغرق — كلاهما يرتفع في الصيف، مدفوعًا بعامل ثالث (الطقس الدافئ، المزيد من السباحة). كلما وجدت ارتباطًا قويًا في هذه البيانات، اسأل صراحة: هل يوجد عامل ثالث معقول يمكن أن يفسر تحرك المتغيرين معًا؟

⚠️ أخطاء شائعة

  • اعتبار قيمة rr عالية دليلاً على السببية. يستحق هذا التكرار كلما ظهر — الارتباط وصف لعلاقة تلازمية، وليس دليلاً على آلية سببية بحد ذاته أبدًا.
  • تجاهل اختلاف حجم المجموعات عند قراءة جدول تقاطع. قد تبدو الأعداد الخام من pd.crosstab مثيرة فقط لأن مجموعة ما أكبر بكثير — يضبط normalize="index" (أو "columns") هذا الأثر.
  • إضافة عدد كبير جدًا من فئات hue إلى مخطط انتشار واحد. أكثر من 4-5 ألوان على مخطط واحد يصبح عادةً أصعب قراءةً لا أسهل — فكّر في تقسيم الأسبوع 9 (لوحات منفصلة) بدلاً من ذلك بمجرد أن يصبح مخطط ملوّن واحد مزدحمًا.
  • حساب .corr() على DataFrame يتضمن أعمدة غير رقمية دون اختيار مسبق. تتعامل إصدارات pandas الحديثة مع هذا بإسقاط الأعمدة غير الرقمية بصمت، لكن من الأوضح (والأكثر أمانًا عبر الإصدارات) اختيار الأعمدة الرقمية التي تريدها فعلاً بشكل صريح، كما هو موضح أعلاه.

🧩 التحديات

احسب معامل الارتباط بين math_score وwriting_score.

ابنِ مصفوفة الارتباط الكاملة 3×3 لأعمدة الدرجات الثلاثة وصوّرها كخريطة حرارية مع القيم الفعلية موضحة على كل خلية.

أنشئ مخطط صندوق مجمّع يقارن توزيعات math_score عبر فئتي lunch.

إذا أظهر نوع lunch وmath_score فرقًا ملحوظًا في مخطط الصندوق من التحدي السابق، اقترح عاملاً ثالثًا معقولاً واحدًا يمكن أن يفسر كليهما دون أن يكون نوع lunch سببًا مباشرًا في اختلاف الدرجات.

أنشئ مخطط انتشار لـ math_score مقابل reading_score، ملونًا (hue) حسب test_preparation_course. هل تبدو العلاقة متشابهة لكلتا المجموعتين، أم تبدو نقاط إحداهما مزاحة؟

باستخدام pd.crosstab مع normalize="index"، تحقق مما إذا كان نوع lunch وإكمال test_preparation_course يبدوان مرتبطين ببعضهما.

🤔 أسئلة سقراطية

  • من المرجح جدًا أن ترتبط أعمدة الدرجات الثلاثة (math، reading، writing) ارتباطًا قويًا ببعضها. ما التفسير المعقول لذلك — هل الأرجح أن كل مادة تحسّن الأخرى فعليًا، أم أن كليهما مدفوع بعامل كامن مشترك (مثل الاستعداد الأكاديمي العام، عادات الدراسة)؟ كيف تبدأ حتى في التمييز بين هذين الاحتمالين؟
  • معامل ارتباط قريب من 0 يعني عدم وجود علاقة خطية. هل يمكن أن يظل بين متغيرين علاقة قوية وحقيقية وغير خطية (مثل شكل حرف U) وتظهر مع ذلك r0r \approx 0؟ كيف سيبدو ذلك على مخطط انتشار قد يفوته رقم واحد تمامًا؟
  • لماذا تثبّت الخريطة الحرارية vmin=-1, vmax=1 صراحةً، بدلاً من ترك مقياس اللون يتكيف تلقائيًا مع أي نطاق من القيم يصادف ظهوره في هذه المصفوفة تحديدًا؟ ماذا يمكن أن يحدث خطأً عند مقارنة خريطتين حراريتين مختلفتين إذا لم تكن مقاييس ألوانهما مثبّتة بنفس الطريقة؟
  • تعطي pd.crosstab(..., normalize="index") وnormalize="columns" جدولين يبدوان مختلفين من نفس البيانات الخام. ما الفرق في السؤال الذي يجيب عنه كل منهما؟
  • إضافة hue إلى مخطط انتشار هي إحدى طرق جلب متغير ثالث. ماذا ستخسر، مقارنة بتقسيم الأسبوع 9 (لوحات جنبًا إلى جنب منفصلة)، إذا حاولت ترميز متغير رابع وخامس بالألوان على نفس المخطط الواحد؟

✅ اختبار الأسبوع

✅ اختبار الأسبوع

1. معامل ارتباط بيرسون بقيمة -0.9 يشير إلى:
2. مثال مبيعات الآيس كريم / حوادث الغرق يوضح:
3. مخطط الصندوق المجمّع (متغير رقمي مقسّم حسب فئة) يُظهر أكثر من متوسط groupby لأنه يكشف أيضًا:
4. ماذا يفعل alpha=0.5 في مخطط انتشار matplotlib؟
5. pd.crosstab هي الأداة المناسبة لمقارنة: