10  تحليل البيانات الاستكشافي

10.1 مقدمة

سيوضح لك هذا الفصل كيفية استخدام أدوات التمثيل البصري (Visualization) والتحويل (Transformation) لاستكشاف بياناتك بطريقة منهجية؛ وهي المهمة التي يُطلق عليها علماء الإحصاء اسم تحليل البيانات الاستكشافي exploratory data analysis، أو EDA اختصاراً. يُعد تحليل البيانات الاستكشافي دورة تكرارية (Iterative cycle) تتضمن الخطوات التالية:

  1. توليد أسئلة حول بياناتك.

  2. البحث عن إجابات لهذه الأسئلة عبر تمثيل البيانات بصرياً، وتحويلها، ونمذجتها (Modelling).

  3. استخدام ما تعلمته لتعديل أسئلتك و/أو توليد أسئلة جديدة.

لا يُعد تحليل البيانات الاستكشافي (EDA) عملية رسمية ذات قواعد صارمة، بل هو — قبل كل شيء — “سمة عقلية” ومنهجية تفكير. فخلال المراحل الأولية من هذا التحليل، يجب أن تمنح نفسك الحرية الكاملة لاستقصاء كل فكرة تخطر ببالك؛ فبعض هذه الأفكار سيسفر عن نتائج مثمرة، وبعضها الآخر سيكون طريقاً مسدوداً. ومع استمرار استكشافك، ستصل في النهاية إلى عدد قليل من الرؤى الثاقبة والمنتجة التي ستعتمد عليها لاحقاً لكتابتها ومشاركتها مع الآخرين.

ويمثل الـ EDA جزءاً أساسياً من أي عملية لتحليل البيانات، حتى وإن كانت أسئلة البحث الرئيسة محددة لك مسبقاً وبوضوح؛ وذلك لأنك بحاجة دائمًا إلى التحقق من جودة بياناتك. وتطهير البيانات (Data cleaning) ليس سوى أحد التطبيقات العملية لتحليل البيانات الاستكشافي؛ حيث تطرح أسئلة حيال مدى مطابقة البيانات لتوقعاتك من عدمه. ولإجراء عملية التطهير بكفاءة، ستحتاج إلى توظيف جميع أدوات الـ EDA: التمثيل البصري، والتحويل، والنمذجة.

10.1.1 المتطلبات المسبقة

سنقوم في هذا الفصل بالدمج بين ما تعلمته حول حزمتي dplyr و ggplot2 لطرح الأسئلة تفاعلياً، والإجابة عنها باستخدام البيانات، ثم توليد أسئلة جديدة بناءً على تلك الإجابات.

10.2 الأسئلة

“لا توجد أسئلة إحصائية روتينية، بل توجد إجراءات إحصائية مشكوك في صحتها.” — السير ديفيد كوكس

“إن الحصول على إجابة تقريبية للسؤال الصحيح — والذي غالباً ما يكون غامضاً — خيرٌ بكثير من الحصول على إجابة دقيقة للسؤال الخطأ — والذي يمكن دائماً جعله محدداً بدقة.” — جون توكي

إن هدفك الأساسي خلال مرحلة تحليل البيانات الاستكشافي (EDA) هو بناء فهم عميق لبياناتك. وأسهل طريقة لتحقيق ذلك هي استخدام الأسئلة كأدوات لتوجيه عملية الاستقصاء. فعندما تطرح سؤالاً، فإنه يوجه انتباهك إلى جزء محدد من مجموعة البيانات، ويساعدك على تحديد الرسوم البيانية، أو النماذج، أو التحويلات الإحصائية التي يتعين عليك إجراؤها.

يُعد الـ EDA في جوهره عملية إبداعية. وكما هو الحال في معظم العمليات الإبداعية، فإن المفتاح لطرح أسئلة ذات جودة عالية (Quality) هو توليد كمية كبيرة (Quantity) من الأسئلة في البداية. إذ يصعب طرح أسئلة كاشفة وعميقة عند نقطة انطلاق التحليل لأنك لا تعرف بعد الأسرار والرؤى الكامنة في مجموعة بياناتك. ومن ناحية أخرى، فإن كل سؤال جديد تطرحه سيكشف لك جانباً جديداً من البيانات ويزيد من فرصك في تحقيق اكتشاف علمي. وبذلك يمكنك النفاذ سريعاً إلى أجزاء البيانات الأكثر أهمية — وتطوير مجموعة من الأسئلة المثيرة للتفكير — إذا ما اتبعت كل إجابة تصل إليها بسؤال جديد يبنى عليها.

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

  1. ما نوع التباين (Variation) الذي يحدث داخل متغيراتي ؟

  2. ما نوع التباين المشترك (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)

مدرج تكراري لقراريط الماس، حيث يتراوح المحور السيني من 0 إلى 4.5  والمحور الصادي من 0 إلى 30000. التوزيع ملتوٍ نحو اليمين (موجب الالتواء)  مع وجود عدد قليل جداً من الماس في الفئة الممركزة عند 0، وحوالي 30000 ماسة  في الفئة الممركزة عند 0.5، وتقريباً 15000 ماسة في الفئة الممركزة عند 1،  وأقل بكثير (حوالي 5000 ماسة) في الفئة الممركزة عند 1.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)

مدرج تكراري لقراريط الماس، حيث يتراوح المحور السيني من 0 إلى 3 والمحور  الصادي من 0 إلى ما يقارب 2500. عرض الفئة ضيق للغاية (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 إلى 60  والمحور الصادي من 0 إلى 12000. توجد قمة واضحة حول القيمة 5،  وتبدو البيانات متكتلة بالكامل حول هذه القمة.

توجد مشاهدات كثيرة جداً في الفئات التكرارية الشائعة، مما يجعل الفئات النادرة قصيرة للغاية ويصعب رؤيتها (على الرغم من أنه لو حدقت بتركيز شديد عند القيمة 0 فقد تلمح شيئاً ما). ولتسهيل رؤية هذه القيم غير المعتادة، نحتاج إلى تقريب الرؤية (Zoom in) نحو القيم الصغيرة للمحور الصادي باستخدام الدالة coord_cartesian():

ggplot(diamonds, aes(x = y)) + 
  geom_histogram(binwidth = 0.5) +
  coord_cartesian(ylim = c(0, 50))

مدرج تكراري لعرض الماس. يتراوح المحور السيني من 0 إلى 60 والمحور  الصادي من 0 إلى 50. توجد قمة حول القيمة 5، وتبدو البيانات متجمعة حولها.  بخلاف تلك البيانات، توجد فئة واحدة عند 0 بارتفاع يقارب 8، وفئة أخرى  فوق 30 بقليل بارتفاع 1، وأخرى تحت 60 بقليل بارتفاع 1 أيضاً.

تقبل الدالة 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 تمارين

  1. استكشف توزيع كل من المتغيرات الثلاثة x و y و z في مجموعة بيانات diamonds. ما الذي تعلمته؟ فكر في بنية قطعة الماس وكيف يمكنك بناءً عليها تحديد أي الأبعاد يمثل الطول، وأيها العرض، وأيها العمق.

  2. استكشف توزيع متغير السعر price. هل اكتشفت أي شيء غير معتاد أو مفاجئ؟ (تلميح: فكر بعناية في عرض الفئة binwidth وتأكد من تجريب نطاق واسع من القيم).

  3. كم عدد الماسات التي تبلغ كتلتها 0.99 قيراط؟ وكم عددها عند 1 قيراط؟ ما الذي تظن أنه السبب وراء هذا التفاوت؟

  4. قارن ووازن بين استخدام الدالة coord_cartesian() في مقابل دالتي xlim() أو ylim() عند تقريب الرؤية في المدرج التكراري. ما الذي يحدث إذا تركت وسيط عرض الفئة binwidth دون تحديد؟ وما الذي يحدث إذا حاولت تقريب الرؤية بحيث لا يظهر سوى نصف عمود تكراري فقط؟

10.4 القيم غير المعتادة

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

  1. حذف الصف (المشاهدة) بالكامل الذي يحتوي على تلك القيم الغريبة:

    diamonds2 <- diamonds |> 
      filter(between(y, 3, 20))

    لا نوصي بهذا الخيار؛ لأن وجود قيمة واحدة غير صالحة لا يعني بالضرورة أن جميع القيم الأخرى المقاسة لنفس المشاهدة غير صالحة أيضاً. بالإضافة إلى ذلك، إذا كانت بياناتك ذات جودة منخفضة، فقد تجد نفسك في النهاية دون أي بيانات متبقية بعد تطبيق هذا الأسلوب على كل متغير!

  2. بدلاً من ذلك، نوصي باستبدال القيم غير المعتادة بقيم مفقودة. والطريقة الأسهل للقيام بذلك هي استخدام الدالة mutate() لاستبدال المتغير بنسخة معدلة منه، حيث يمكنك توظيف دالة الشرط if_else() لتحويل القيم غير المعتادة إلى NA:

    diamonds2 <- diamonds |> 
      mutate(y = if_else(y < 3 | y > 20, NA, y))

نظراً لأنه ليس من الواضح أين يجب تضمين القيم المفقودة على الرسم البياني، فإن حزمة 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()`).

مخطط تشتت يوضح العرض مقابل الطول للماس. توجد علاقة خطية قوية  بين المتغيرين. جميع الماسات باستثناء واحدة لها طول أكبر من 3.  والقيمة الشاذة الوحيدة لها طول يساوي 0 وعرض يقارب 6.5.

لإخفاء هذا التحذير ومنع ظهوره، قم بضبط الوسيط na.rm = TRUE داخل دالة الهندسة البيانية:

ggplot(diamonds2, aes(x = x, y = y)) + 
  geom_point(na.rm = TRUE)

وفي أوقات أخرى، قد ترغب في فهم ما الذي يجعل المشاهدات ذات القيم المفقودة تختلف عن المشاهدات ذات القيم المسجلة فعلياً. على سبيل المثال، في مجموعة بيانات الرحلات الجوية nycflights13::flights1، تشير القيم المفقودة في متغير وقت الإقلاع dep_time إلى أن الرحلة قد أُلغيت. لذا، قد ترغب في مقارنة أوقات الإقلاع المجدولة للرحلات الملغاة وغير الملغاة. يمكنك القيام بذلك عن طريق إنشاء متغير جديد، والاستعانة بالدالة is.na() للتحقق مما إذا كان وقت الإقلاع dep_time مفقوداً أم لا.

nycflights13::flights |> 
  mutate(
    cancelled = is.na(dep_time),
    sched_hour = sched_dep_time %/% 100,
    sched_min = sched_dep_time %% 100,
    sched_dep_time = sched_hour + (sched_min / 60)
  ) |> 
  ggplot(aes(x = sched_dep_time)) + 
  geom_freqpoly(aes(color = cancelled), binwidth = 1/4)

مضلع تكراري لأوقات الإقلاع المجدولة للرحلات الجوية. يمثل الخطان الرحلات  الملغاة وغير الملغاة. يتراوح المحور السيني من 0 إلى 25 والمحور الصادي  من 0 إلى 10000. عدد الرحلات غير الملغاة أعلى بكثير من الرحلات الملغاة.

ومع ذلك، فإن هذا الرسم البياني ليس مثالياً؛ لأن عدد الرحلات غير الملغاة يفوق بكثير عدد الرحلات الملغاة. وفي القسم التالي، سوف نستكشف بعض التقنيات المتقدمة لتحسين هذه المقارنة الإحصائية بصرياً.

10.4.1 تمارين

  1. ما الذي يحدث للقيم المفقودة في المدرج التكراري (Histogram)؟ وما الذي يحدث لها في المخطط الشريطي (Bar chart)؟ ولماذا يوجد هذا الاختلاف في كيفية معالجة القيم المفقودة بينهما؟

  2. ما الوظيفة التي يقوم بها الأمر na.rm = TRUE في دالتي المتوسط الحسابي mean() والمجموع sum()؟

  3. أعد إنشاء المضلع التكراري لمتغير 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)

مضلع تكراري لأسعار الماس حيث يتم تمثيل كل جودة قطع (Fair، Good،  Very Good، Premium، و Ideal) بخط ملون مختلف. يتراوح المحور السيني  من 0 إلى 30000 والمحور الصادي من 0 إلى 5000. تتداخل الخطوط بشكل كبير،  مما يشير إلى توزيعات تكرارية متشابهة لأسعار الماس. إحدى الميزات البارزة  هي أن الماس من فئة Ideal يمتلك أعلى قمة حول القيمة 1500.

لاحظ أن حزمة 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)

مضلع تكراري لكثافة أسعار الماس حيث يتم تمثيل كل جودة قطع بخط ملون مختلف.  يتراوح المحور السيني من 0 إلى 20000. تتداخل الخطوط بكثرة، مما يشير إلى  توزيعات كثافة متقاربة لأسعار الماس. ومما يثير الانتباه أن جميع الفئات  باستثناء فئة Fair تمتلك قمماً عالية حول السعر 1500، وأن فئة Fair لها متوسط أعلى من البقية.

لاحظ أننا قمنا بربط الكثافة بالمحور y؛ وبما أن الكثافة density ليست متغيراً أصلياً في مجموعة بيانات diamonds، فإننا بحاجة إلى حسابها أولاً، ولذلك استخدمنا الدالة المساعدة after_stat() لإجراء هذا الحساب الإحصائي الفوري.

هناك شيء مفاجئ وصادم للغاية في هذا الرسم البياني - إذ يبدو أن الماس ذو القطع التجاري السيئ (Fair - وهو أدنى مستويات الجودة) يمتلك أعلى متوسط سعر! ولكن ربما يرجع ذلك إلى أن المضلعات التكرارية تكون معقدة وصعبة التفسير في بعض الأحيان نظراً لكثرة التفاصيل المتداخلة في الرسم.

ويُعد مخططات الصندوق المتجاورة (Side-by-side boxplots) رسماً بيانياً أبسط بصرياً لاستكشاف هذه العلاقة.

ggplot(diamonds, aes(x = cut, y = price)) +
  geom_boxplot()

مخططات صندوق وطرفين متجاورة لأسعار الماس حسب جودة القطع. توزيع الأسعار  ملتوٍ نحو اليمين لكل فئة قطع. قيم الوسيط متقاربة جداً من بعضها البعض،  مع ملاحظة أن وسيط الماس المثالي Ideal هو الأدنى ووسيط القطع المقبول Fair هو الأعلى.

من خلال هذا المخطط، نحصل على تفاصيل أقل حول التوزيع الإحصائي الدقيق، ولكن رسوم الصندوق والطرفين تكون أكثر إحكاماً وتركيزاً بحيث يمكننا مقارنتها بسهولة فائقة (واستيعاب المزيد منها في رسم واحد). وهي تدعم بقوة تلك النتيجة المنافية للحدس الظاهري بأن الماس الأعلى جودة يكون أرخص ثمناً في العادة! وفي التمارين، ستكون متحمساً لاكتشاف التفسير العلمي الكامن وراء هذه الظاهرة.

يُعد متغير القطع 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 تمارين

  1. استخدم ما تعلمته لتحسين التمثيل البصري لأوقات إقلاع الرحلات الملغاة في مقابل الرحلات غير الملغاة.

  2. بناءً على تحليل البيانات الاستكشافي (EDA)، ما هو المتغير الذي يبدو أكثر أهمية للتنبؤ بسعر الماسة في مجموعة بيانات diamonds ؟ وكيف يرتبط هذا المتغير بجودة القطع cut ؟ ولماذا يؤدي تضافر هاتين العلاقتين معاً إلى جعل الماس ذي الجودة المنخفضة أغلى ثمناً في المتوسط؟

  3. بدلاً من تبديل تعيين المتغيرات بين المحورين السيني والصادي، أضف طبقة الدالة coord_flip() كطبقة جديدة إلى مخطط الصندوق الرأسي لتحويله إلى مخطط أفقي. كيف تقارن هذه النتيجة بأسلوب تبديل المتغيرات مباشرة؟

  4. إحدى المشكلات التي تواجه مخططات الصندوق والطرفين (Boxplots) هي أنها طُوِّرت في عصر كانت فيه مجموعات البيانات أصغر بكثير، ولذا فإنها تميل إلى عرض عدد هائل وصادم من “القيم المتطرفة الشاذة” عند تطبيقها على البيانات الضخمة. أحد الحلول البديلة لمعالجة هذه المشكلة هو مخطط قيم الحروف (Letter value plot). قم بتثبيت حزمة lvplot، وجرب استخدام الدالة geom_lv() لعرض توزيع السعر في مقابل جودة القطع. ما الذي تعلمته؟ وكيف تفسر هذه المخططات سيكومترياً وإحصائياً؟

  5. أنشئ تمثيلاً بصرياً لأسعار الماس في مقابل متغير فئوي ونوعي من مجموعة بيانات diamonds باستخدام الدالة geom_violin() أولاً، ثم باستخدام المدرج التكراري المقسم إلى لوحات geom_histogram()، ثم باستخدام المضلع التكراري الملون geom_freqpoly()، وأخيراً باستخدام مخطط الكثافة الملون geom_density(). قارن ووازن بين المخططات الأربعة. ما هي المزايا والعيوب لكل طريقة من طرق التمثيل البصري لتوزيع متغير عددي بناءً على مستويات متغير فئوي؟

  6. إذا كانت لديك مجموعة بيانات صغيرة، فمن المفيد أحياناً استخدام الدالة geom_jitter() لتجنب مشكلة تطابق النقاط وتراكمها (Overplotting) لرصد العلاقة بين متغير متصل ومتغير فئوي بسهولة أكبر. توفر حزمة ggbeeswarm عدداً من الطرق المشابهة للدالة geom_jitter(). اذكرها واشرح باختصار وظيفة كل منها.

10.5.2 متغيران فئويان

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

ggplot(diamonds, aes(x = cut, y = color)) +
  geom_count()

مخطط تشتت يوضح اللون مقابل جودة قطع الماس. توجد نقطة واحدة لكل  توليفة من مستويات القطع (Fair، Good، Very Good، Premium، و Ideal)  واللون (D، E، F، G، H، I، و J). تمثل أحجام النقاط عدد المشاهدات  لتلك التوليفة، ويشير مفتاح الرسم إلى أن هذه الأحجام تتراوح بين 1000 و 4000.

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

وثمة أسلوب آخر لاستكشاف العلاقة بين هذه المتغيرات الفئوية يكمن في حساب التكرارات أولاً باستخدام حزمة 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):

diamonds |> 
  count(color, cut) |>  
  ggplot(aes(x = color, y = cut)) +
  geom_tile(aes(fill = n))

مخطط قرميدي يوضح جودة القطع مقابل لون الماس. تمثل كل قرميدة توليفة  من القطع/اللون، وتتلون القراميد وفقاً لعدد المشاهدات في كل منها.  يوجد عدد من الماسات ذات القطع المثالي Ideal أكثر من القطوع الأخرى،  مع تسجيل أعلى تكرار للماس المثالي ذي اللون G. بينما تعد فئة القطع المقبول Fair  والماس ذو اللون I الأقل في التكرار.

وإذا كانت المتغيرات الفئوية غير مرتبة (Unordered)، فقد ترغب في استخدام حزمة seriation لإعادة ترتيب الصفوف والأعمدة في آن واحد، مما يساعد على كشف الأنماط والبنى الكامنة المثيرة للاهتمام بشكل أكثر وضوحاً. أما بالنسبة للمخططات الأكبر حجماً، فيمكنك تجربة حزمة heatmaply التي تنشئ خرائط حرارية تفاعلية وديناميكية.

10.5.2.1 تمارين

  1. كيف يمكنك إعادة قياس (Rescale) مجموعة بيانات التكرارات أعلاه لإظهار توزيع جودة القطع cut داخل كل لون color بشكل أكثر وضوحاً، أو توزيع اللون داخل كل جودة قطع؟

  2. ما هي الرؤى والاستبصارات المختلفة التي يمكنك الحصول عليها من واقع البيانات باستخدام المخطط الأعمدي المجزأ (Segmented bar chart) إذا تم تعيين اللون على المحور السيني x وتعيين جودة القطع على خاصية التعبئة fill؟ قم بحساب التكرارات الإحصائية التي تقع في كل جزء من هذه الأجزاء.

  3. استخدم الدالة 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)

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

ولكن استخدام الشفافية قد يظل تحدياً صعباً ومحدود الفائدة مع مجموعات البيانات الضخمة للغاية. ويكمن الحل البديل الآخر في تقسيم المساحة إلى فئات أو صناديق ثنائية الأبعاد (2D binning). في السابق، استخدمت دالتي geom_histogram() و geom_freqpoly() للتقسيم الفئوي في بعد واحد، والآن ستتعلم كيفية استخدام دالتي geom_bin2d() و geom_hex() للتقسيم في بعدين.

تقوم دالتا geom_bin2d() و geom_hex() بتقسيم المستوى الإحداثي إلى فئات ثنائية الأبعاد، ثم تستخدمان ألوان التعبئة لإظهار عدد النقاط التي تقع داخل كل فئة. تنشئ الدالة geom_bin2d() فئات مستطيلة الشكل، بينما تنشئ الدالة geom_hex() فئات سداسية الأضلاع (Hexagonal bins). وسوف تحتاج إلى تثبيت حزمة hexbin لتتمكن من تشغيل واستخدام الدالة geom_hex().

ggplot(smaller, aes(x = carat, y = price)) +
  geom_bin2d()
#> `stat_bin2d()` using `bins = 30`. Pick better value `binwidth`.

# install.packages("hexbin")
ggplot(smaller, aes(x = carat, y = price)) +
  geom_hex()

المخطط 1: مخطط كثافة مقسم إلى فئات مستطيلة للسعر مقابل القيراط.  المخطط 2: مخطط فئات سداسية للسعر مقابل القيراط. يظهر كلا المخططين  أن الكثافة الأعلى للماس تتركز في القراريط المنخفضة والأسعار المنخفضة.

المخطط 1: مخطط كثافة مقسم إلى فئات مستطيلة للسعر مقابل القيراط.  المخطط 2: مخطط فئات سداسية للسعر مقابل القيراط. يظهر كلا المخططين  أن الكثافة الأعلى للماس تتركز في القراريط المنخفضة والأسعار المنخفضة.

وهناك خيار آخر يتمثل في تحويل المتغير العددي المتصل إلى فئات متقطعة ليعامل معاملة المتغير النوعي. وعندها يمكنك تطبيق إحدى التقنيات التي تعلمتها سابقاً لتمثيل التوليفة المكونة من متغير فئوي ومتغير متصل. على سبيل المثال، يمكنك تقسيم متغير القيراط 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'.

مخططات صندوق وطرفين متجاورة للسعر حسب القيراط. يمثل كل مخطط صندوقي الماسات  التي تقع ضمن مدى تفصله 0.1 قيراط في الوزن. تظهر المخططات أنه كلما زاد القيراط  ارتفع وسيط السعر أيضاً. بالإضافة إلى ذلك، فإن الماسات التي تبلغ كتلها 1.5 قيراط  أو أقل لها توزيعات أسعار ملتوية نحو اليمين، ومن 1.5 إلى 2 تكون التوزيعات متماثلة تقريباً،  والماسات الأثقل وزناً تكون توزيعاتها ملتوية نحو اليسار. الماسات الأصغر والأرخص ثمناً  تحتوي على قيم شاذة في الطرف الأعلى، والماسات الأكبر والأغلى تحتوي على قيم شاذة في الطرف الأدنى.

تقوم الدالة cut_width(x, width)، كما تم توظيفها أعلاه، بتقسيم المتغير x إلى فئات يبلغ عرض كل منها القيمة المحددة في width. وبشكل افتراضي، تبدو مخططات الصندوق متطابقة تقريباً في عرضها البصري (بغض النظر عن عدد القيم الشاذة) دون مراعاة لعدد المشاهدات الفعلي في كل فئة، مما يجعل من الصعب معرفة أن كل صندوق يلخص عدداً مختلفاً تماماً من نقاط البيانات. ولإظهار هذا التباين وتعديل عرض الصندوق ليتناسب طردياً مع حجم عينة الفئة، يمكنك ضبط الوسيط varwidth = TRUE.

10.5.3.1 تمارين

  1. بدلاً من تلخيص التوزيع الشرطي باستخدام مخطط الصندوق، يمكنك استخدام المضلع التكراري. ما الذي يتعين عليك مراعاته وتوقعه عند المفاضلة بين استخدام الدالة cut_width() والدالة cut_number()؟ وكيف يؤثر ذلك على التمثيل البصري للتوزيع ثنائي الأبعاد للمتغيرين carat و price؟

  2. قم بتمثيل توزيع متغير القيراط carat بصرياً، على أن يكون مقسماً ومجزأً بناءً على فئات متغير السعر price.

  3. كيف يقارن توزيع أسعار الماسات الضخمة للغاية بتوزيع أسعار الماسات الصغيرة؟ وهل تتوافق هذه النتيجة مع توقعاتك النظرية، أم أنها فاجأتك؟

  4. ادمج بين تقنيتين من التقنيات التي تعلمتها لتمثيل التوزيع المشترك لثلاثة متغيرات معاً وهي: جودة القطع cut، والقيراط carat، والسعر price.

  5. تكشف المخططات ثنائية الأبعاد عن قيم متطرفة شاذة قد لا تكون مرئية على الإطلاق في المخططات أحادية البعد. على سبيل المثال، تمتلك بعض النقاط في المخطط التالي توليفة غير معتادة وغريبة من قيم 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))
  6. بدلاً من إنشاء صناديق ذات عرض متساوٍ باستخدام الدالة 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()

مخطط تشتت يوضح البواقي مقابل القيراط للماس. يتراوح المحور السيني من 0  إلى 5، والمحور الصادي من 0 إلى ما يقارب 4. تتكتل معظم البيانات حول  القيم المنخفضة للقيراط والبواقي. ويوجد نمط منحني واضح يظهر انخفاضاً  في البواقي مع زيادة حجم القيراط.

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

ggplot(diamonds_aug, aes(x = cut, y = .resid)) + 
  geom_boxplot()

مخططات صندوق وطرفين متجاورة للبواقي حسب جودة القطع. يعرض المحور السيني  جودات القطع المختلفة (من Fair إلى Ideal)، ويتراوح المحور الصادي من 0 إلى ما يقارب 5.  الوسائط متشابهة تماماً، وتقع تقريباً بين 0.75 و 1.25. كل توزيع من توزيعات  البواقي ملتوٍ نحو اليمين، مع وجود العديد من القيم الشاذة في الطرف الأعلى.

ونحن لا نتوسع في مناقشة وبناء النماذج الرياضية في هذا الكتاب؛ لأن فهم ماهية النماذج الإحصائية وكيفية عملها يكون أكثر سهولة ويسراً بمجرد أن تتقن أولاً أدوات معالجة البيانات (Data wrangling) ومبادئ البرمجة الأساسية.

10.7 ملخص

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

وفي الفصل القادم، سوف نركز بشكل كامل على الأدوات والوسائل البرمجية التي تمكننا من مشاركة ونشر نتائجنا البحثية وتوصيلها للآخرين بكفاءة عالية.


  1. تذكّر أنه عندما نحتاج إلى إظهار المصدر الصريح لدالة (أو مجموعة بيانات)، سنستخدم الصيغة الخاصة package::function() أو package::dataset.↩︎