الأسبوع 6: إطار عمل التحليل الاستكشافي
🎯 أهداف التعلّم
بنهاية هذا الأسبوع ستكون قادرًا على:
- شرح ما هو التحليل الاستكشافي للبيانات (EDA) ولماذا يسبق أي نمذجة أو استنتاج.
- تنميط مجموعة بيانات جديدة بشكل منهجي: الشكل، أنواع البيانات، القيم المفقودة، والتوزيعات الأساسية.
- صياغة مجموعة من الأسئلة التحليلية المحددة قبل الغوص في الرسوم البيانية.
- التعرّف على توزيع منحرف من الإحصاءات الملخّصة وحدها، قبل رسمه إطلاقًا.
- اكتشاف القيم المتطرفة المحتملة باستخدام المدى الربيعي (IQR).
الدرس
ما هو التحليل الاستكشافي؟
التحليل الاستكشافي هو ممارسة التحقيق في بنية مجموعة بيانات وجودتها وأنماطها قبل استخلاص استنتاجات منها — قائمة تحقق ضد القفز مباشرة إلى "هذا اكتشافي" دون التحقق أولًا أن البيانات تدعمه فعليًا. أطّره الإحصائي جون توكي، الذي أشاع المصطلح، كعمل تحرٍّ: ابحث عن أدلة، كوّن فرضيات، ثم تحقق منها، بدلًا من افتراض أن النمط الأول الذي تلاحظه حقيقي.
يبني هذا المسار ذو الأسابيع الخمسة نحو مُخرَج واحد: تقرير تحليل استكشافي كامل على مجموعة بيانات أداء الطلاب في الاختبارات في الأسبوع 10. يُهيّئ هذا الأسبوع الإطار الذي ستطبّقه كل أسبوع بعده.
الخطوة 1: تنميط مجموعة البيانات
قبل طرح أي سؤال مثير للاهتمام، أرسِ الأساسيات — نفس الخطوة الأولى للأسبوعين 6/10 من المسار العادي، لكن مُعالَجة هنا كخطوة أولى رسمية وقابلة للتكرار لأي مشروع:
import pandas as pd
df = pd.read_csv("students-performance.csv")
df.shape
df.dtypes
df.isna().sum()
df.describe()
df.head()
لكل عمود، اسأل: ما نوع هذا المتغيّر؟
- رقمي/مستمر (
math_score،reading_score،writing_score): يُلخَّص بالمتوسط، الوسيط، الانحراف المعياري، وشكل التوزيع. - فئوي (
gender،lunch،test_preparation_course،parental_level_of_education): يُلخَّص بعدّات/نسب لكل فئة، باستخدام.value_counts().
df["gender"].value_counts()
df["parental_level_of_education"].value_counts()
df["gender"].value_counts(normalize=True) # كنسب (0-1) بدلًا من عدّات خام
normalize=True هي النسخة المُوجَّهة لما كنت ستحسبه يدويًا كـcount / total — نفس فكرة احتمالات الكلمات في المسار الصعب لبايثون 101، مُطبَّقة على فئات بدلًا من كلمات.
الخطوة 2: صياغة الأسئلة قبل الرسم
خطأ شائع في التحليل الاستكشافي هو توليد عشرات الرسوم البيانية بلا سؤال موجِّه، ثم القرار بأثر رجعي أيها "يبدو مثيرًا للاهتمام" — هذا يدعو لرؤية أنماط ليست سوى ضوضاء. بدلًا من ذلك، اكتب مجموعة صغيرة من الأسئلة المحددة قبل التصور البصري، بناءً على ما ترتبط به الأعمدة على الأرجح:
- هل يرتبط إكمال
test_preparation_courseبدرجات أعلى؟ - هل يرتبط نوع
lunch(مؤشر تقريبي للوضع الاجتماعي-الاقتصادي في مجموعة البيانات هذه) بالدرجات؟ - هل ترتبط أعمدة الدرجات الثلاثة (
math،reading،writing) ببعضها؟
تبني الأسابيع 7-9 الأدوات للإجابة عن هذا النوع بالضبط من الأسئلة بدقة؛ يُجيب الأسبوع 10 عن مجموعة أوفى منها للتقرير النهائي.
الخطوة 3: قراءة الانحراف من الإحصاءات الملخّصة وحدها
قبل رسم أي توزيع، يُلمّح mean مقابل median بالفعل لشكله. لتوزيع متماثل تمامًا، يتساوى المتوسط والوسيط (أو قريبان جدًا). متوسط أعلى بشكل ملحوظ من الوسيط يوحي بانحراف يميني (موجب) — حفنة من القيم العالية غير المعتادة تسحب المتوسط للأعلى؛ متوسط أقل بشكل ملحوظ من الوسيط يوحي بانحراف يساري (سالب):
mean_score = df["math_score"].mean()
median_score = df["math_score"].median()
print(f"Mean: {mean_score:.1f}, Median: {median_score:.1f}")
ستتيح لك المدرجات التكرارية الأسبوع القادم رؤية هذا الشكل مباشرة، لكن القدرة على قراءته من رقمين وحدهما عادة مفيدة حتى قبل تحميل مكتبة رسم.
الخطوة 4: اكتشاف القيم المتطرفة بـIQR
المدى الربيعي (IQR) هو المسافة بين المئين الخامس والعشرين والخامس والسبعين — الوسط 50% من البيانات — ويُعطي طريقة قوية (لا تُشوّهها القيم المتطرفة) لتعليم القيم المتطرفة غير المعتادة:
q1 = df["math_score"].quantile(0.25)
q3 = df["math_score"].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
outliers = df[(df["math_score"] < lower_bound) | (df["math_score"] > upper_bound)]
len(outliers)
قاعدة عُرف مستخدَم على نطاق واسع (وهو أيضًا بالضبط ما تُعلّمه شوارب (whiskers) المخطط الصندوقي، الذي يغطيه الأسبوع 7) — وليس قانونًا طبيعيًا، فقط افتراض معقول وقياسي لـ"بعيد بشكل غير معتاد عن الوسط 50%".
الخطوة 5: التحقق المنطقي قبل الثقة بنمط
يستحق كل إحصاء ملخَّص لحظة من الشك: كم صفًا يستند إليه هذا فعليًا؟ هل يمكن أن تُشوّهه بيانات مفقودة؟ هل الفرق بين مجموعتين كبير بما يكفي ليكون مثيرًا للاهتمام، أم يمكن أن يكون ضوضاء من عيّنة صغيرة؟
df.groupby("test_preparation_course")["math_score"].agg(["mean", "count"])
فحص count جنبًا إلى جنب مع mean مهم: متوسط يبدو لافتًا مبني على 3 صفوف فقط يستحق ثقة أقل بكثير من نفس المتوسط المبني على 300.
⚠️ أخطاء شائعة
- معاملة قاعدة القيمة المتطرفة كحقيقة مطلقة. إنها عُرف، وليست قانونًا — "القيمة المتطرفة" المُعلَّمة قد تكون نقطة بيانات صحيحة فعليًا، وإن كانت غير معتادة، لا خطأً يجب إزالته.
- الخلط بين اتجاه الانحراف. الانحراف اليميني (الموجب) يعني ذيلًا أطول نحو القيم العالية، بمتوسط مسحوب فوق الوسيط — من السهل نسيان أي اتجاه هو أيهما؛ الارتكاز على "المتوسط مقابل الوسيط" بدلًا من محاولة حفظ "يسار/يمين" يتجنب الحيرة.
- الرسم قبل صياغة سؤال. من المغري القفز مباشرة إلى
df.plot()— قاوم ذلك حتى تكتب ما تحاول اكتشافه فعليًا، وفق الخطوة 2.
🧩 تحديات
حمّل students-performance.csv واستخدم .value_counts() لرؤية توزيع فئات عمود lunch.
قبل كتابة أي كود تحليل، اكتب (في خلية markdown، أو كتعليقات فقط) ثلاثة أسئلة محددة يمكن لمجموعة البيانات هذه الإجابة عنها على الأرجح، بناءً فقط على أسماء أعمدتها.
احسب متوسط writing_score مُجمَّعًا حسب parental_level_of_education، مع عدّ الطلاب في كل مجموعة. هل أي مجموعات صغيرة بما يكفي لتستحق متوسطاتها ثقة أقل؟
احسب المتوسط، الوسيط، والانحراف المعياري لـmath_score. بناءً على هذه الأرقام الثلاثة وحدها (بلا رسم بعد)، هل تتوقع أن يكون التوزيع متماثلًا تقريبًا، أم منحرفًا؟
كرّر فحص القيم المتطرفة بـIQR من الخطوة 4، لكن لعمود reading_score بدلًا من math_score. كم قيمة متطرفة تجد؟
باستخدام .value_counts(normalize=True)، ما نسبة الطلاب الذين أكملوا test_preparation_course؟
🤔 أسئلة سقراطية
- تأطير توكي للتحليل الاستكشافي كـ"عمل تحرٍّ" يعني أنك تُكوّن وتختبر فرضيات، لا فقط تصف بيانات. ما الفرق العملي بين "الطلاب الذين أكملوا التحضير للاختبار حصلوا على درجات أعلى في المتوسط" (وصف) ومعاملة ذلك كدليل أن التحضير للاختبار يسبّب درجات أعلى (ادعاء)؟ ماذا ستحتاج لتكون أكثر ثقة بالسببية؟
- لماذا يُصرّ الدرس على كتابة الأسئلة التحليلية قبل توليد الرسوم البيانية، لا بعدها؟ أي تحيّز يُقدّمه النظر للرسوم البيانية أولًا في الأسئلة التي ستطرحها في النهاية؟
- استُخدمت
df.groupby(...).agg(["mean", "count"])تحديدًا بحيث لا تُخطئ مجموعة عيّنة صغيرة كنمط قوي. هل يمكنك التفكير في مجموعة بمجموعة البيانات هذه حيث فرق متوسط كبير قد لا يزال غير ذي معنى كبير، حتى بعدّ كبير معقول؟ - تُعلّم قاعدة القيمة المتطرفة نقاطًا كـ"غير معتادة" بناءً فقط على موضعها بالتوزيع، بلا معرفة لماذا هي غير معتادة. هل يمكنك التفكير في سبب مشروع يجعل درجة طالب قيمة متطرفة حقيقية دون أن تكون خطأ إدخال بيانات؟
- مقارنة المتوسط والوسيط طريقة رخيصة لاكتشاف الانحراف دون رسم أي شيء. أي معلومات لا تُعطيك إياها هذه الحيلة والتي سيكشفها مدرج تكراري فعلي (الأسبوع القادم)؟