10 تحليل البيانات الاستكشافي
10.1 مقدمة
سيوضح لك هذا الفصل كيفية استخدام أدوات التمثيل البصري (Visualization) والتحويل (Transformation) لاستكشاف بياناتك بطريقة منهجية؛ وهي المهمة التي يُطلق عليها علماء الإحصاء اسم تحليل البيانات الاستكشافي exploratory data analysis، أو EDA اختصاراً. يُعد تحليل البيانات الاستكشافي دورة تكرارية (Iterative cycle) تتضمن الخطوات التالية:
توليد أسئلة حول بياناتك.
البحث عن إجابات لهذه الأسئلة عبر تمثيل البيانات بصرياً، وتحويلها، ونمذجتها (Modelling).
استخدام ما تعلمته لتعديل أسئلتك و/أو توليد أسئلة جديدة.
لا يُعد تحليل البيانات الاستكشافي (EDA) عملية رسمية ذات قواعد صارمة، بل هو — قبل كل شيء — “سمة عقلية” ومنهجية تفكير. فخلال المراحل الأولية من هذا التحليل، يجب أن تمنح نفسك الحرية الكاملة لاستقصاء كل فكرة تخطر ببالك؛ فبعض هذه الأفكار سيسفر عن نتائج مثمرة، وبعضها الآخر سيكون طريقاً مسدوداً. ومع استمرار استكشافك، ستصل في النهاية إلى عدد قليل من الرؤى الثاقبة والمنتجة التي ستعتمد عليها لاحقاً لكتابتها ومشاركتها مع الآخرين.
ويمثل الـ EDA جزءاً أساسياً من أي عملية لتحليل البيانات، حتى وإن كانت أسئلة البحث الرئيسة محددة لك مسبقاً وبوضوح؛ وذلك لأنك بحاجة دائمًا إلى التحقق من جودة بياناتك. وتطهير البيانات (Data cleaning) ليس سوى أحد التطبيقات العملية لتحليل البيانات الاستكشافي؛ حيث تطرح أسئلة حيال مدى مطابقة البيانات لتوقعاتك من عدمه. ولإجراء عملية التطهير بكفاءة، ستحتاج إلى توظيف جميع أدوات الـ EDA: التمثيل البصري، والتحويل، والنمذجة.
10.1.1 المتطلبات المسبقة
سنقوم في هذا الفصل بالدمج بين ما تعلمته حول حزمتي dplyr و ggplot2 لطرح الأسئلة تفاعلياً، والإجابة عنها باستخدام البيانات، ثم توليد أسئلة جديدة بناءً على تلك الإجابات.
10.2 الأسئلة
“لا توجد أسئلة إحصائية روتينية، بل توجد إجراءات إحصائية مشكوك في صحتها.” — السير ديفيد كوكس
“إن الحصول على إجابة تقريبية للسؤال الصحيح — والذي غالباً ما يكون غامضاً — خيرٌ بكثير من الحصول على إجابة دقيقة للسؤال الخطأ — والذي يمكن دائماً جعله محدداً بدقة.” — جون توكي
إن هدفك الأساسي خلال مرحلة تحليل البيانات الاستكشافي (EDA) هو بناء فهم عميق لبياناتك. وأسهل طريقة لتحقيق ذلك هي استخدام الأسئلة كأدوات لتوجيه عملية الاستقصاء. فعندما تطرح سؤالاً، فإنه يوجه انتباهك إلى جزء محدد من مجموعة البيانات، ويساعدك على تحديد الرسوم البيانية، أو النماذج، أو التحويلات الإحصائية التي يتعين عليك إجراؤها.
يُعد الـ EDA في جوهره عملية إبداعية. وكما هو الحال في معظم العمليات الإبداعية، فإن المفتاح لطرح أسئلة ذات جودة عالية (Quality) هو توليد كمية كبيرة (Quantity) من الأسئلة في البداية. إذ يصعب طرح أسئلة كاشفة وعميقة عند نقطة انطلاق التحليل لأنك لا تعرف بعد الأسرار والرؤى الكامنة في مجموعة بياناتك. ومن ناحية أخرى، فإن كل سؤال جديد تطرحه سيكشف لك جانباً جديداً من البيانات ويزيد من فرصك في تحقيق اكتشاف علمي. وبذلك يمكنك النفاذ سريعاً إلى أجزاء البيانات الأكثر أهمية — وتطوير مجموعة من الأسئلة المثيرة للتفكير — إذا ما اتبعت كل إجابة تصل إليها بسؤال جديد يبنى عليها.
لا توجد قاعدة جامدة تحدد الأسئلة التي يجب عليك طرحها لتوجيه بحثك. ومع ذلك، هناك نوعان من الأسئلة يظلان مفيدين دائماً لتحقيق اكتشافات جوهرية داخل بياناتك، ويمكن صياغتهما بشكل عام كالتالي:
ما نوع التباين (Variation) الذي يحدث داخل متغيراتي ؟
ما نوع التباين المشترك (Covariation) الذي يحدث بين متغيراتي؟
سيركز الجزء المتبقي من هذا الفصل على استعراض هذين السؤالين؛ حيث سنشرح مفهوم كل من التباين والتباين المشترك، ونبين لك طرقاً عديدة للإجابة عن كل منهما.
10.3 التباين (Variation)
التباين هو ميل قيم المتغير الإحصائي إلى التغير من قياس إلى آخر. ويمكنك رصد التباين بسهولة في الحياة الواقعية؛ فلو قمت بقياس أي متغير متصل (Continuous variable) مرتين متتاليتين، فستحصل على نتيجتين مختلفتين. ويصدق هذا الأمر حتى وإن كنت تقيس كميات ثابتة في الفيزياء مثل سرعة الضوء؛ إذ سيتضمن كل قياس من قياساتك نسبة خطأ ضئيلة تتفاوت من محاولة لأخرى. كما يمكن للمتغيرات أن تتباين أيضاً عند قياسها عبر أفراد مختلفين (مثل: ألوان العيون لأشخاص مختلفين) أو في أزمنة مختلفة (مثل: مستويات طاقة الإلكترون في لحظات متفاوتة). ولكل متغير نمط تباين خاص به (Pattern of variation)، يمكنه كشف معلومات قيمة حول كيفية تغيره بين القياسات المختلفة للمفردة الواحدة، وكذلك عبر المشاهدات والمفردات المختلفة ككل. وأفضل طريقة لفهم هذا النمط هي تمثيل توزيع قيم المتغير بصرياً، وهو ما تعلمته بالتفصيل في الفصل الخاص بتمثيل البيانات بصرياً الفصل 1.
سنبدأ استكشافنا بتمثيل توزيع أوزان الماس (carat) القيراط لحوالي 54,000 ماسة من مجموعة بيانات diamonds. وبما أن متغير القيراط carat هو متغير عددي (Numerical variable)، فإنه يمكننا استخدام المدرج التكراري (Histogram):
ggplot(diamonds, aes(x = carat)) +
geom_histogram(binwidth = 0.5)
والآن بعد أن تمكنت من تمثيل التباين بصرياً، ما الذي ينبغي عليك البحث عنه في رسومك البيانية؟ وما نوع أسئلة المتابعة التي يجب أن تطرحها؟ لقد قمنا بإعداد قائمة أدناه تشمل أكثر أنواع المعلومات فائدة والتي ستجدها في رسوماتك، مصحوبة ببعض أسئلة المتابعة المقترحة لكل نوع. إن المفتاح لصياغة أسئلة متابعة جيدة يكمن في الاعتماد على فضولك العلمي (ما الذي ترغب في معرفة المزيد عنه؟) وكذلك على حسك النقدي والتشكيكي (كيف يمكن لهذا الرسم أن يكون مضللاً؟).
10.3.1 القيم الشائعة (Typical values)
في كل من المخططات الشريطية (Bar charts) والمدرجات التكرارية (Histograms)، تشير الأعمدة الطويلة إلى القيم الشائعة والأكثر تكراراً للمتغير، بينما تشير الأعمدة القصيرة إلى القيم الأقل شيوعاً. أما المساحات الخالية من الأعمدة فتفصح عن قيم لم تظهر مطلقاً في بياناتك. ولتحويل هذه الملاحظات البصرية إلى أسئلة بحثية مفيدة، ابحث عن أي شيء غير متوقع:
ما هي القيم الأكثر شيوعاً؟ ولماذا؟
ما هي القيم النادرة؟ ولماذا؟ وهل يتطابق ذلك مع توقعاتك النظرية؟
هل يمكنك رصد أي أنماط غير معتادة أو شاذة؟ وما الذي يمكنه تفسيرها؟
دعنا نلقي نظرة على توزيع متغير القيراط carat للماس الأصغر حجماً.
smaller <- diamonds |>
filter(carat < 3)
ggplot(smaller, aes(x = carat)) +
geom_histogram(binwidth = 0.01)
يثير هذا المدرج التكراري عدة أسئلة موضوعية ومثيرة للاهتمام:
لماذا يتركز وجود الماس بكثافة أكبر عند أرقام القراريط الصحيحة (مثل 1، 2) والكسور الشائعة (مثل نصف قيراط، ثلاثة أرباع القيراط)؟
لماذا نجد عدد الماس الواقع على يمين كل قمة (أعلى قليلاً) أكثر من الماس الواقع على يسارها (أقل قليلاً)؟
يمكن للتمثيل البصري أيضاً أن يكشف عن وجود تكتلات أو عناقيد (Clusters)، مما يشير إلى وجود مجموعات فرعية (Subgroups) داخل بياناتك. ولفهم هذه المجموعات الفرعية، اطرح الأسئلة التالية:
ما وجه الشبه بين المشاهدات والمفردات داخل كل مجموعة فرعية؟
كيف تختلف المشاهدات الموجودة في التكتلات المنفصلة عن بعضها البعض؟
كيف يمكنك تفسير أو وصف هذه التكتلات؟
لماذا قد يكون مظهر هذه التكتلات مضللاً في بعض الأحيان؟
يمكن الإجابة عن بعض هذه الأسئلة من واقع البيانات المتاحة مباشرة، بينما سيتطلب بعضها الآخر استشارة خبراء المجال (Domain expertise) ذوي العلاقة بطبيعة البيانات. والعديد من هذه الأسئلة سيدفعك حتماً إلى استكشاف العلاقات بين المتغيرات؛ لمعرفة ما إذا كانت قيم متغير معين قادرة على تفسير سلوك متغير آخر والتنبؤ به، وهو ما سننتقل إليه بعد قليل.
10.3.2 القيم غير المعتادة (Unusual values)
القيم الشاذة أو المتطرفة (Outliers) هي مشاهدات غير معتادة؛ وهي نقاط بيانات لا يبدو أنها تتوافق مع النمط العام للتوزيع. وفي بعض الأحيان، تكون هذه القيم الشاذة ناتجة عن أخطاء في إدخال البيانات (Data entry errors)، وفي أحيان أخرى تكون مجرد قيم قصوى تصادف رصدها أثناء عملية جمع البيانات، بينما قد تشير في أوقات أخرى إلى اكتشافات علمية جديدة ومهمة. وعندما تمتلك كمية كبيرة من البيانات، يكون من الصعب أحياناً رصد القيم الشاذة باستخدام المدرج التكراري (Histogram). على سبيل المثال، لنأخذ توزيع المتغير y (الذي يمثل عرض الماس بالملليمتر) من مجموعة بيانات diamonds؛ إذ إن الدليل الوحيد على وجود قيم شاذة هنا هو الحدود الواسعة وغير المعتادة للمحور السيني.
ggplot(diamonds, aes(x = y)) +
geom_histogram(binwidth = 0.5)
توجد مشاهدات كثيرة جداً في الفئات التكرارية الشائعة، مما يجعل الفئات النادرة قصيرة للغاية ويصعب رؤيتها (على الرغم من أنه لو حدقت بتركيز شديد عند القيمة 0 فقد تلمح شيئاً ما). ولتسهيل رؤية هذه القيم غير المعتادة، نحتاج إلى تقريب الرؤية (Zoom in) نحو القيم الصغيرة للمحور الصادي باستخدام الدالة coord_cartesian():
ggplot(diamonds, aes(x = y)) +
geom_histogram(binwidth = 0.5) +
coord_cartesian(ylim = c(0, 50))
تقبل الدالة coord_cartesian() أيضاً وسيطاً للمحور السيني xlim() عندما تحتاج إلى تقريب الرؤية أفقياً. وتجدر الإشارة إلى أن حزمة ggplot2 تحتوي كذلك على دالتي xlim() و ylim() منفصلتين، لكنهما تعملان بطريقة مختلفة قليلاً؛ إذ تقومان باستبعاد وحذف البيانات التي تقع خارج الحدود المحددة تماماً بدلاً من مجرد تقريب الرؤية بصرياً.
يتيح لنا هذا التقريب البصري رصد ثلاث قيم غير معتادة: 0، وتقريباً 30، وتقريباً 60. ويمكننا استخلاص هذه القيم وتحديدها بدقة باستخدام حزمة dplyr:
unusual <- diamonds |>
filter(y < 3 | y > 20) |>
select(price, x, y, z) |>
arrange(y)
unusual
#> # A tibble: 9 × 4
#> price x y z
#> <int> <dbl> <dbl> <dbl>
#> 1 5139 0 0 0
#> 2 6381 0 0 0
#> 3 12800 0 0 0
#> 4 15686 0 0 0
#> 5 18034 0 0 0
#> 6 2130 0 0 0
#> 7 2130 0 0 0
#> 8 2075 5.15 31.8 5.12
#> 9 12210 8.09 58.9 8.06يقيس المتغير y أحد الأبعاد الثلاثة للماس بالملليمتر (mm). ونحن نعلم يقيناً من الناحية المنطقية والفيزيائية أنه لا يمكن لماسة أن يكون عرضها 0 مم، ولذا يجب أن تكون هذه القيم خاطئة تماماً. ومن خلال إجراء تحليل البيانات الاستكشافي (EDA)، اكتشفنا بيانات مفقودة (Missing data) تم ترميزها بشكل خاطئ كقيمة صفرية، وهو أمر لم نكن لنعثر عليه أبداً لو اكتفينا بمجرد البحث التقليدي عن القيم المفقودة صراحة NA. وبناءً على ذلك، قد نختار مستقبلاً إعادة ترميز هذه القيم وتحويلها إلى NA لتجنب الحسابات الإحصائية المضللة. كما قد نشك أيضاً في أن القياسات البالغة 32 مم و 59 مم هي قياسات غير معقولة؛ إذ إن هذه الماسات سيزيد طولها عن بوصة كاملة، ومع ذلك فإن أسعارها لم تبلغ مئات الآلاف من الدولارات!
ومن الممارسات البحثية الجيدة والموصى بها تكرار تحليلك الإحصائي مرتين: مرة بوجود القيم الشاذة، ومرة بدونها. فإذا كان لها تأثير ضئيل جدًا على النتائج، ولم تتمكن من معرفة سبب وجودها، فمن المقبول تماماً حذفها والمضي قدماً في التحليل. ومع ذلك، إذا كان لها تأثير جوهري وكبير على نتائجك، فلا ينبغي لك إسقاطها دون مبرر علمي؛ بل سيتعين عليك تقصي السبب الكامن وراءها (خطأ في إدخال البيانات مثلاً)، والإفصاح بوضوح في تقريرك البحثي عن قيامك بحذفها.
10.3.3 تمارين
استكشف توزيع كل من المتغيرات الثلاثة
xوyوzفي مجموعة بياناتdiamonds. ما الذي تعلمته؟ فكر في بنية قطعة الماس وكيف يمكنك بناءً عليها تحديد أي الأبعاد يمثل الطول، وأيها العرض، وأيها العمق.استكشف توزيع متغير السعر
price. هل اكتشفت أي شيء غير معتاد أو مفاجئ؟ (تلميح: فكر بعناية في عرض الفئةbinwidthوتأكد من تجريب نطاق واسع من القيم).كم عدد الماسات التي تبلغ كتلتها 0.99 قيراط؟ وكم عددها عند 1 قيراط؟ ما الذي تظن أنه السبب وراء هذا التفاوت؟
قارن ووازن بين استخدام الدالة
coord_cartesian()في مقابل دالتيxlim()أوylim()عند تقريب الرؤية في المدرج التكراري. ما الذي يحدث إذا تركت وسيط عرض الفئةbinwidthدون تحديد؟ وما الذي يحدث إذا حاولت تقريب الرؤية بحيث لا يظهر سوى نصف عمود تكراري فقط؟
10.4 القيم غير المعتادة
إذا واجهت قيماً غير معتادة في مجموعة بياناتك، وأردت ببساطة تجاوزها للانتقال إلى بقية تحليلاتك الإحصائية، فإنك أمام خيارين:
-
حذف الصف (المشاهدة) بالكامل الذي يحتوي على تلك القيم الغريبة:
لا نوصي بهذا الخيار؛ لأن وجود قيمة واحدة غير صالحة لا يعني بالضرورة أن جميع القيم الأخرى المقاسة لنفس المشاهدة غير صالحة أيضاً. بالإضافة إلى ذلك، إذا كانت بياناتك ذات جودة منخفضة، فقد تجد نفسك في النهاية دون أي بيانات متبقية بعد تطبيق هذا الأسلوب على كل متغير!
-
بدلاً من ذلك، نوصي باستبدال القيم غير المعتادة بقيم مفقودة. والطريقة الأسهل للقيام بذلك هي استخدام الدالة
mutate()لاستبدال المتغير بنسخة معدلة منه، حيث يمكنك توظيف دالة الشرطif_else()لتحويل القيم غير المعتادة إلىNA:
نظراً لأنه ليس من الواضح أين يجب تضمين القيم المفقودة على الرسم البياني، فإن حزمة ggplot2 لا تدرجها في الرسم، لكنها تظهر لك تحذيراً يفيد بأنه قد تم إزالتها واستبعادها:
ggplot(diamonds2, aes(x = x, y = y)) +
geom_point()
#> Warning: Removed 9 rows containing missing values or values outside the scale range
#> (`geom_point()`).
لإخفاء هذا التحذير ومنع ظهوره، قم بضبط الوسيط na.rm = TRUE داخل دالة الهندسة البيانية:
ggplot(diamonds2, aes(x = x, y = y)) +
geom_point(na.rm = TRUE)وفي أوقات أخرى، قد ترغب في فهم ما الذي يجعل المشاهدات ذات القيم المفقودة تختلف عن المشاهدات ذات القيم المسجلة فعلياً. على سبيل المثال، في مجموعة بيانات الرحلات الجوية nycflights13::flights1، تشير القيم المفقودة في متغير وقت الإقلاع dep_time إلى أن الرحلة قد أُلغيت. لذا، قد ترغب في مقارنة أوقات الإقلاع المجدولة للرحلات الملغاة وغير الملغاة. يمكنك القيام بذلك عن طريق إنشاء متغير جديد، والاستعانة بالدالة is.na() للتحقق مما إذا كان وقت الإقلاع dep_time مفقوداً أم لا.

ومع ذلك، فإن هذا الرسم البياني ليس مثالياً؛ لأن عدد الرحلات غير الملغاة يفوق بكثير عدد الرحلات الملغاة. وفي القسم التالي، سوف نستكشف بعض التقنيات المتقدمة لتحسين هذه المقارنة الإحصائية بصرياً.
10.4.1 تمارين
ما الذي يحدث للقيم المفقودة في المدرج التكراري (Histogram)؟ وما الذي يحدث لها في المخطط الشريطي (Bar chart)؟ ولماذا يوجد هذا الاختلاف في كيفية معالجة القيم المفقودة بينهما؟
ما الوظيفة التي يقوم بها الأمر
na.rm = TRUEفي دالتي المتوسط الحسابيmean()والمجموعsum()؟أعد إنشاء المضلع التكراري لمتغير
scheduled_dep_timeملوناً حسب حالة إلغاء الرحلة من عدمه. وقم أيضاً بإنشاء لوحات منفصلة (Facet) بناءً على متغيرcancelled. جرب قيماً مختلفة لوسيط المقاييسscalesفي دالة التقسيم اللوحي للتخفيف من أثر طغيان عدد الرحلات غير الملغاة على الرحلات الملغاة.
10.5 التباين المشترك (Covariation)
إذا كان التباين يصف السلوك الإحصائي داخل المتغير الواحد، فإن التباين المشترك يصف السلوك بين المتغيرات. والتباين المشترك هو ميل قيم متغيرين أو أكثر إلى التغير معاً بطريقة ارتباطية متبادلة. وأفضل طريقة لرصد التباين المشترك وتحديده هي تمثيل العلاقة بين متغيرين أو أكثر بصرياً.
10.5.1 متغير فئوي ومتغير عددي
على سبيل المثال، دعنا نستكشف كيف يتغير سعر الماس (price) بتغير جودة قطعه (والتي يقيسها المتغير الفئوي cut) باستخدام دالة المضلع التكراري geom_freqpoly():
ggplot(diamonds, aes(x = price)) +
geom_freqpoly(aes(color = cut), binwidth = 500, linewidth = 0.75)
لاحظ أن حزمة ggplot2 تستخدم مقياس ألوان مرتباً لمتغير القطع cut لأنه مُعرَّف كمتغير عاملي مرتب (Ordered factor variable) في البيانات. وسوف تتعلم المزيد عن هذه المتغيرات في الفصل الخاص بالعوامل المرتبة قسم 16.6.
إن المظهر الافتراضي لـ geom_freqpoly() ليس مفيداً كثيراً هنا؛ لأن الارتفاع — الذي يتحدد بالعدد الإجمالي للمشاهدات (Overall count) — يتفاوت تفاوتًا هائلاً عبر مستويات الجودة المختلفة عبر cut، مما يجعل من الصعب رؤية الاختلافات في أشكال التوزيعات الإحصائية نفسها.
ولتسهيل عملية المقارنة، نحتاج إلى تغيير ما يتم عرضه على المحور الصادي. فبدلاً من عرض التكرار الخام (Count)، سنقوم بنمذجة وعرض الكثافة (Density)، وهي التكرار المقنن بحيث تصبح المساحة الإجمالية تحت كل مضلع تكراري مساوية للواحد الصحيح.
ggplot(diamonds, aes(x = price, y = after_stat(density))) +
geom_freqpoly(aes(color = cut), binwidth = 500, linewidth = 0.75)
لاحظ أننا قمنا بربط الكثافة بالمحور y؛ وبما أن الكثافة density ليست متغيراً أصلياً في مجموعة بيانات diamonds، فإننا بحاجة إلى حسابها أولاً، ولذلك استخدمنا الدالة المساعدة after_stat() لإجراء هذا الحساب الإحصائي الفوري.
هناك شيء مفاجئ وصادم للغاية في هذا الرسم البياني - إذ يبدو أن الماس ذو القطع التجاري السيئ (Fair - وهو أدنى مستويات الجودة) يمتلك أعلى متوسط سعر! ولكن ربما يرجع ذلك إلى أن المضلعات التكرارية تكون معقدة وصعبة التفسير في بعض الأحيان نظراً لكثرة التفاصيل المتداخلة في الرسم.
ويُعد مخططات الصندوق المتجاورة (Side-by-side boxplots) رسماً بيانياً أبسط بصرياً لاستكشاف هذه العلاقة.
ggplot(diamonds, aes(x = cut, y = price)) +
geom_boxplot()
من خلال هذا المخطط، نحصل على تفاصيل أقل حول التوزيع الإحصائي الدقيق، ولكن رسوم الصندوق والطرفين تكون أكثر إحكاماً وتركيزاً بحيث يمكننا مقارنتها بسهولة فائقة (واستيعاب المزيد منها في رسم واحد). وهي تدعم بقوة تلك النتيجة المنافية للحدس الظاهري بأن الماس الأعلى جودة يكون أرخص ثمناً في العادة! وفي التمارين، ستكون متحمساً لاكتشاف التفسير العلمي الكامن وراء هذه الظاهرة.
يُعد متغير القطع cut عاملاً مرتباً: فالقطع المقبول (Fair) أسوأ من الجيد (Good)، والآخر أسوأ من الجيد جداً (Very Good)، وهكذا دواليك. ومع ذلك، فإن العديد من المتغيرات النوعية والفئوية لا تمتلك مثل هذا الترتيب الضمني الأصيل، ولذلك قد ترغب في إعادة ترتيبها لتقديم عرض بياني أكثر كشفاً للمعلومات. وإحدى الطرق الفعالة لتحقيق ذلك هي استخدام دالة إعادة الترتيب العاملية fct_reorder(). وسوف تتعلم المزيد عن هذه الدالة وتطبيقاتها السيكومترية والعملية في الفصل الخاص بتعديل ترتيب العوامل قسم 16.4، ولكننا نريد أن نقدم لك لمحة سريعة عنها هنا نظراً لأهميتها البالغة وفائدتها العالية. لنأخذ على سبيل المثال متغير فئة السيارة class في مجموعة بيانات كفاءة استهلاك الوقود mpg؛ حيث قد يهمك معرفة كيف تختلف كفاءة استهلاك الوقود على الطرق السريعة عبر فئات السيارات المختلفة:
ggplot(mpg, aes(x = class, y = hwy)) +
geom_boxplot()
ولجعل النمط العام والاتجاه أسهل في الرصد والمقارنة البصرية، يمكننا إعادة ترتيب فئات السيارات class بناءً على القيمة الوسيطة لكفاءة الوقود hwy لكل فئة:
ggplot(mpg, aes(x = fct_reorder(class, hwy, median), y = hwy)) +
geom_boxplot()
وإذا كانت لديك أسماء متغيرات فئوية طويلة تتداخل عند الكتابة، فإن الدالة geom_boxplot() ستعمل بشكل أفضل بكثير إذا قمت بتدوير الرسم بمقدار 90 درجة؛ ويمكنك تحقيق ذلك بسهولة عبر تبديل تعيينات الخصائص البيانية (Aesthetic mappings) بين المحورين السيني والصادي:
ggplot(mpg, aes(x = hwy, y = fct_reorder(class, hwy, median))) +
geom_boxplot()
10.5.1.1 تمارين
استخدم ما تعلمته لتحسين التمثيل البصري لأوقات إقلاع الرحلات الملغاة في مقابل الرحلات غير الملغاة.
بناءً على تحليل البيانات الاستكشافي (EDA)، ما هو المتغير الذي يبدو أكثر أهمية للتنبؤ بسعر الماسة في مجموعة بيانات diamonds ؟ وكيف يرتبط هذا المتغير بجودة القطع cut ؟ ولماذا يؤدي تضافر هاتين العلاقتين معاً إلى جعل الماس ذي الجودة المنخفضة أغلى ثمناً في المتوسط؟
بدلاً من تبديل تعيين المتغيرات بين المحورين السيني والصادي، أضف طبقة الدالة
coord_flip()كطبقة جديدة إلى مخطط الصندوق الرأسي لتحويله إلى مخطط أفقي. كيف تقارن هذه النتيجة بأسلوب تبديل المتغيرات مباشرة؟إحدى المشكلات التي تواجه مخططات الصندوق والطرفين (Boxplots) هي أنها طُوِّرت في عصر كانت فيه مجموعات البيانات أصغر بكثير، ولذا فإنها تميل إلى عرض عدد هائل وصادم من “القيم المتطرفة الشاذة” عند تطبيقها على البيانات الضخمة. أحد الحلول البديلة لمعالجة هذه المشكلة هو مخطط قيم الحروف (Letter value plot). قم بتثبيت حزمة lvplot، وجرب استخدام الدالة
geom_lv()لعرض توزيع السعر في مقابل جودة القطع. ما الذي تعلمته؟ وكيف تفسر هذه المخططات سيكومترياً وإحصائياً؟أنشئ تمثيلاً بصرياً لأسعار الماس في مقابل متغير فئوي ونوعي من مجموعة بيانات
diamondsباستخدام الدالةgeom_violin()أولاً، ثم باستخدام المدرج التكراري المقسم إلى لوحاتgeom_histogram()، ثم باستخدام المضلع التكراري الملونgeom_freqpoly()، وأخيراً باستخدام مخطط الكثافة الملونgeom_density(). قارن ووازن بين المخططات الأربعة. ما هي المزايا والعيوب لكل طريقة من طرق التمثيل البصري لتوزيع متغير عددي بناءً على مستويات متغير فئوي؟إذا كانت لديك مجموعة بيانات صغيرة، فمن المفيد أحياناً استخدام الدالة
geom_jitter()لتجنب مشكلة تطابق النقاط وتراكمها (Overplotting) لرصد العلاقة بين متغير متصل ومتغير فئوي بسهولة أكبر. توفر حزمة ggbeeswarm عدداً من الطرق المشابهة للدالةgeom_jitter(). اذكرها واشرح باختصار وظيفة كل منها.
10.5.2 متغيران فئويان
لتمثيل التباين المشترك بين متغيرين فئويين بصرياً، ستحتاج أولاً إلى حساب عدد المشاهدات لكل تركيبة توليفية من مستويات هذين المتغيرين. وإحدى الطرق المباشرة لتحقيق ذلك هي الاعتماد على الدالة المدمجة geom_count():
ggplot(diamonds, aes(x = cut, y = color)) +
geom_count()
يعبر حجم كل دائرة في الرسم البياني عن عدد المشاهدات ونقاط البيانات الواقعة ضمن هذه التوليفة المحددة من القيم. ويظهر التباين المشترك في هذا المخطط على شكل ارتباط قوي بين قيم محددة على المحور السيني وقيم أخرى على المحور الصادي.
وثمة أسلوب آخر لاستكشاف العلاقة بين هذه المتغيرات الفئوية يكمن في حساب التكرارات أولاً باستخدام حزمة dplyr:
diamonds |>
count(color, cut)
#> # A tibble: 35 × 3
#> color cut n
#> <ord> <ord> <int>
#> 1 D Fair 163
#> 2 D Good 662
#> 3 D Very Good 1513
#> 4 D Premium 1603
#> 5 D Ideal 2834
#> 6 E Fair 224
#> # ℹ 29 more rowsومن ثم تمثيلها بصرياً باستخدام دالة geom_tile() وربطها بخاصية التعبئة اللونية (Fill aesthetic):
وإذا كانت المتغيرات الفئوية غير مرتبة (Unordered)، فقد ترغب في استخدام حزمة seriation لإعادة ترتيب الصفوف والأعمدة في آن واحد، مما يساعد على كشف الأنماط والبنى الكامنة المثيرة للاهتمام بشكل أكثر وضوحاً. أما بالنسبة للمخططات الأكبر حجماً، فيمكنك تجربة حزمة heatmaply التي تنشئ خرائط حرارية تفاعلية وديناميكية.
10.5.2.1 تمارين
كيف يمكنك إعادة قياس (Rescale) مجموعة بيانات التكرارات أعلاه لإظهار توزيع جودة القطع
cutداخل كل لونcolorبشكل أكثر وضوحاً، أو توزيع اللون داخل كل جودة قطع؟ما هي الرؤى والاستبصارات المختلفة التي يمكنك الحصول عليها من واقع البيانات باستخدام المخطط الأعمدي المجزأ (Segmented bar chart) إذا تم تعيين اللون على المحور السيني
xوتعيين جودة القطع على خاصية التعبئةfill؟ قم بحساب التكرارات الإحصائية التي تقع في كل جزء من هذه الأجزاء.استخدم الدالة
geom_tile()بالتعاون مع حزمةdplyrلاستكشاف كيف يتغير متوسط تأخر إقلاع الرحلات الجوية حسب وجهة السفر وشهر السنة. ما الذي يجعل هذا الرسم البياني صعب القراءة؟ وكيف يمكنك تحسينه وتطويره إحصائياً؟
10.5.3 متغيران عدديان
لقد رأيت بالفعل طريقة ممتازة لتمثيل التباين المشترك بين متغيرين عدديين متصلين: رسم مخطط التشتت باستخدام الدالة geom_point(). حيث يمكنك رصد التباين المشترك كنمط متسق تتوزع بناءً عليه النقاط. على سبيل المثال، يمكنك بوضوح رؤية علاقة إيجابية طردية بين حجم القيراط وسعر الماسة؛ فالماسات ذات القراريط الأعلى تكون أغلى سعراً، وتأخذ هذه العلاقة نمطاً أسياً (Exponential).
ggplot(smaller, aes(x = carat, y = price)) +
geom_point()
(سنعتمد في هذا القسم على مجموعة البيانات المختصرة smaller لنبقى مركزين على الكتلة الأساسية من الماس التي يقل حجمها عن 3 قراريط).
تصبح مخططات التشتت أقل فائدة وكفاءة كلما كبر حجم مجموعة البيانات لديك؛ وذلك لأن النقاط تبدأ في التطابق والتراكم فوق بعضها البعض (Overplotting)، وتتكدس في مساحات سوداء مصمتة ومتجانسة، مما يجعل من الصعب تقييم الفروق في كثافة البيانات عبر الفضاء ثنائي الأبعاد، فضلاً عن صعوبة رصد الاتجاه العام (Trend). ولقد رأيت سابقاً طريقة لمعالجة هذه المشكلة: وهي استخدام خاصية الشفافية alpha لإضفاء شفافية نسبية على النقاط.
ggplot(smaller, aes(x = carat, y = price)) +
geom_point(alpha = 1 / 100)
ولكن استخدام الشفافية قد يظل تحدياً صعباً ومحدود الفائدة مع مجموعات البيانات الضخمة للغاية. ويكمن الحل البديل الآخر في تقسيم المساحة إلى فئات أو صناديق ثنائية الأبعاد (2D binning). في السابق، استخدمت دالتي geom_histogram() و geom_freqpoly() للتقسيم الفئوي في بعد واحد، والآن ستتعلم كيفية استخدام دالتي geom_bin2d() و geom_hex() للتقسيم في بعدين.
تقوم دالتا geom_bin2d() و geom_hex() بتقسيم المستوى الإحداثي إلى فئات ثنائية الأبعاد، ثم تستخدمان ألوان التعبئة لإظهار عدد النقاط التي تقع داخل كل فئة. تنشئ الدالة geom_bin2d() فئات مستطيلة الشكل، بينما تنشئ الدالة geom_hex() فئات سداسية الأضلاع (Hexagonal bins). وسوف تحتاج إلى تثبيت حزمة hexbin لتتمكن من تشغيل واستخدام الدالة geom_hex().


وهناك خيار آخر يتمثل في تحويل المتغير العددي المتصل إلى فئات متقطعة ليعامل معاملة المتغير النوعي. وعندها يمكنك تطبيق إحدى التقنيات التي تعلمتها سابقاً لتمثيل التوليفة المكونة من متغير فئوي ومتغير متصل. على سبيل المثال، يمكنك تقسيم متغير القيراط carat إلى فئات، ثم عرض مخطط صندوق وطرفين لكل مجموعة وفئة منها:
ggplot(smaller, aes(x = carat, y = price)) +
geom_boxplot(aes(group = cut_width(carat, 0.1)))
#> Warning: Orientation is not uniquely specified when both the x and y aesthetics are
#> continuous. Picking default orientation 'x'.
تقوم الدالة cut_width(x, width)، كما تم توظيفها أعلاه، بتقسيم المتغير x إلى فئات يبلغ عرض كل منها القيمة المحددة في width. وبشكل افتراضي، تبدو مخططات الصندوق متطابقة تقريباً في عرضها البصري (بغض النظر عن عدد القيم الشاذة) دون مراعاة لعدد المشاهدات الفعلي في كل فئة، مما يجعل من الصعب معرفة أن كل صندوق يلخص عدداً مختلفاً تماماً من نقاط البيانات. ولإظهار هذا التباين وتعديل عرض الصندوق ليتناسب طردياً مع حجم عينة الفئة، يمكنك ضبط الوسيط varwidth = TRUE.
10.5.3.1 تمارين
بدلاً من تلخيص التوزيع الشرطي باستخدام مخطط الصندوق، يمكنك استخدام المضلع التكراري. ما الذي يتعين عليك مراعاته وتوقعه عند المفاضلة بين استخدام الدالة
cut_width()والدالةcut_number()؟ وكيف يؤثر ذلك على التمثيل البصري للتوزيع ثنائي الأبعاد للمتغيرينcaratوprice؟قم بتمثيل توزيع متغير القيراط
caratبصرياً، على أن يكون مقسماً ومجزأً بناءً على فئات متغير السعرprice.كيف يقارن توزيع أسعار الماسات الضخمة للغاية بتوزيع أسعار الماسات الصغيرة؟ وهل تتوافق هذه النتيجة مع توقعاتك النظرية، أم أنها فاجأتك؟
ادمج بين تقنيتين من التقنيات التي تعلمتها لتمثيل التوزيع المشترك لثلاثة متغيرات معاً وهي: جودة القطع cut، والقيراط carat، والسعر price.
-
تكشف المخططات ثنائية الأبعاد عن قيم متطرفة شاذة قد لا تكون مرئية على الإطلاق في المخططات أحادية البعد. على سبيل المثال، تمتلك بعض النقاط في المخطط التالي توليفة غير معتادة وغريبة من قيم
xوyمعاً، مما يجعلها قيماً شاذة على الرغم من أن قيمxمنفردة وقيمyمنفردة تبدو طبيعية تماماً عند فحصها بشكل مستقل. لماذا يُعد مخطط التشتت (Scatterplot) خياراً وعرضاً أفضل بكثير من المخطط المقسم إلى فئات (Binned plot) في هذه الحالة الخاصة؟diamonds |> filter(x >= 4) |> ggplot(aes(x = x, y = y)) + geom_point() + coord_cartesian(xlim = c(4, 11), ylim = c(4, 11)) -
بدلاً من إنشاء صناديق ذات عرض متساوٍ باستخدام الدالة
cut_width()، يمكننا إنشاء صناديق تحتوي على أعداد متساوية تقريباً من المشاهدات والنقاط باستخدام الدالةcut_number(). ما هي مزايا وعيوب هذا الأسلوب البديل؟ggplot(smaller, aes(x = carat, y = price)) + geom_boxplot(aes(group = cut_number(carat, 20)))
10.6 الأنماط والنماذج الإحصائية
في حال وجود علاقة منتظمة بين متغيرين، فإنها ستظهر على شكل نمط محدد في البيانات. وإذا رصدت نمطاً ما، فاطرح على نفسك الأسئلة المعرفية التالية:
هل يمكن أن يكون هذا النمط ناتجاً عن محض الصدفة العشوائية؟
كيف يمكنك وصف وتوصيف العلاقة الرياضية التي ينطوي عليها هذا النمط؟
ما مدى قوة العلاقة الارتباطية التي يشير إليها هذا النمط؟
ما هي المتغيرات الأخرى التي قد تؤثر في هذه العلاقة؟
هل تتغير طبيعة هذه العلاقة إذا نظرنا إلى مجموعات فرعية مستقلة من البيانات؟
تقدم الأنماط في بياناتك مؤشرات ودلائل قوية حول طبيعة العلاقات، أي أنها تكشف عن التباين المشترك. وإذا كنت تنظر إلى التباين كظاهرة إحصائية تولد حالة من عدم اليقين (Uncertainty)، فإن التباين المشترك هو الظاهرة المقابلة التي تقلل من هذا اليقين المفقود؛ فإذا كان هناك تباين مشترك بين متغيرين، يمكنك استخدام قيم المتغير الأول لإجراء تنبؤات إحصائية أكثر دقة حول قيم المتغير الثاني. وإذا كان التباين المشترك ناتجاً عن علاقة سببية (Causal relationship — وهي حالة خاصة)، فيمكنك حينها استخدام قيمة متغير واحد للتحكم في قيمة المتغير الثاني وتوجيهها.
وتُعد النماذج الإحصائية (Models) أداة بالغة القوة لاستخلاص الأنماط وعزلها من البيانات. على سبيل المثال، لننظر إلى بيانات الماس؛ حيث يصعب فهم العلاقة النقية بين جودة القطع cut والسعر price مباشرة، لأن جودة القطع والقيراط يرتبطان برباط وثيق، كما أن القيراط والسعر يرتبطان بعلاقة قوية جداً. ومن الممكن هنا استخدام نموذج إحصائي لإزالة وتحييد أثر العلاقة القوية جداً بين السعر والقيراط، حتى نتمكن من استكشاف الفروق الدقيقة والمؤثرات المتبقية. يقوم الكود التالي بملاءمة نموذج خطي يتنبأ بالسعر price بناءً على حجم القيراط carat ثم يحسب البواقي (Residuals — وهي الفارق بين القيمة التنبؤية والقيمة الفعلية المشاهدة). وتمنحنا هذه البواقي رؤية دقيقة لأسعار الماس بمجرد إزالة وتحييد تأثير وزن القيراط تماماً. لاحظ أننا بدلاً من استخدام القيم الخام للمتغيرين price و carat، قمنا بتحويلهما لوغاريتمياً أولاً (Log transform)، ثم واءمنا النموذج على القيم المحولة، وبعد ذلك قمنا برفع المخرجات إلى الدالة الأسية لإعادة البواقي إلى مقياس الأسعار الخام الأصلي:
library(tidymodels)
diamonds <- diamonds |>
mutate(
log_price = log(price),
log_carat = log(carat)
)
diamonds_fit <- linear_reg() |>
fit(log_price ~ log_carat, data = diamonds)
diamonds_aug <- augment(diamonds_fit, new_data = diamonds) |>
mutate(.resid = exp(.resid))
ggplot(diamonds_aug, aes(x = carat, y = .resid)) +
geom_point()
وبمجرد قيامك بعزل وإزالة العلاقة الطاغية بين القيراط والسعر، يمكنك أخيراً رؤية النمط المنطقي والمتوقع للعلاقة بين جودة القطع والسعر: فبالنظر إلى حجم الماسة النسبي، نجد أن الماس ذي جودة القطع الأعلى والأفضل يكون أغلى ثمناً بشكل متسق، وهو ما يثبت الفرضية النظرية بوضوح.
ggplot(diamonds_aug, aes(x = cut, y = .resid)) +
geom_boxplot()
ونحن لا نتوسع في مناقشة وبناء النماذج الرياضية في هذا الكتاب؛ لأن فهم ماهية النماذج الإحصائية وكيفية عملها يكون أكثر سهولة ويسراً بمجرد أن تتقن أولاً أدوات معالجة البيانات (Data wrangling) ومبادئ البرمجة الأساسية.
10.7 ملخص
تعلمت في هذا الفصل مجموعة متنوعة من الأدوات الإحصائية والبيانية التي تساعدك على فهم طبيعة التباين (Variation) الكامن داخل بياناتك. ورأيت تقنيات ومنهجيات تتعامل مع متغير واحد في كل مرة (أحادية البعد)، وأخرى تتعامل مع زوج من المتغيرات معاً (ثنائية البعد). وقد يبدو هذا مقيداً للوهلة الأولى إذا كانت لديك عشرات أو مئات المتغيرات في بياناتك، ولكن هذه التقنيات هي الحجر الأساس والركيزة المتينة التي تُبنى عليها كافة التقنيات الأخرى.
وفي الفصل القادم، سوف نركز بشكل كامل على الأدوات والوسائل البرمجية التي تمكننا من مشاركة ونشر نتائجنا البحثية وتوصيلها للآخرين بكفاءة عالية.
تذكّر أنه عندما نحتاج إلى إظهار المصدر الصريح لدالة (أو مجموعة بيانات)، سنستخدم الصيغة الخاصة
package::function()أوpackage::dataset.↩︎
