9  الطبقات (Layers)

9.1 مقدمة

في الفصل 1، تعلمت ما هو أكثر بكثير من مجرد كيفية إنشاء مخططات تشتت (scatterplots)، ومخططات أعمدة (bar charts)، ومخططات صندوقية (boxplots). لقد تعلمت الأساس الذي يمكنك استخدامه لإنشاء أي نوع من المخططات البيانية باستخدام حزمة ggplot2.

في هذا الفصل، ستتوسع في هذا الأساس حيث ستتعرف على القواعد الطبقية للرسومات (Layered grammar of graphics). سنبدأ بغوص أعمق في جماليات الربط (Aesthetic mappings)، والأشكال الهندسية (Geometric objects)، والأوجه (Facets). بعد ذلك، ستتعلم عن التحويلات الإحصائية (Statistical transformations) التي تقوم بها حزمة ggplot2 خلف الكواليس عند إنشاء المخطط البياني. تُستخدم هذه التحويلات لحساب قيم جديدة لرسمها، مثل أطوال الأعمدة في مخطط الأعمدة أو الخطوط المنصفة في المخطط الصندوقي. ستتعلم أيضاً عن تعديلات المواضع (Position adjustments)، والتي تعدل كيفية عرض الأشكال الهندسية (geoms) في مخططاتك. أخيراً، سنقدم مقدمة موجزة عن أنظمة الإحداثيات (Coordinate systems).

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

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

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

9.2 جماليات الربط (Aesthetic mappings)

“تكمن القيمة العظمى للصورة عندما تجبرنا على ملاحظة ما لم نكن نتوقع رؤيته قط.” — جون توكي (John Tukey)

تذكر أن إطار البيانات mpg المرفق مع حزمة ggplot2 يحتوي على 234 مشاهدة لـ 38 طرازاً من طرازات السيارات.

mpg
#> # A tibble: 234 × 11
#>   manufacturer model displ  year   cyl trans      drv     cty   hwy fl   
#>   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr>
#> 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p    
#> 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p    
#> 3 audi         a4      2    2008     4 manual(m6) f        20    31 p    
#> 4 audi         a4      2    2008     4 auto(av)   f        21    30 p    
#> 5 audi         a4      2.8  1999     6 auto(l5)   f        16    26 p    
#> 6 audi         a4      2.8  1999     6 manual(m5) f        18    26 p    
#> # ℹ 228 more rows
#> # ℹ 1 more variable: class <chr>

من بين المتغيرات الموجودة في mpg ما يلي:

  1. displ: سعة محرك السيارة، باللتر. وهو متغير عددي (Numerical variable).

  2. hwy: كفاءة السيارة في استهلاك الوقود على الطرق السريعة، بالميل لكل جالون (mpg). السيارة ذات الكفاءة المنخفضة تستهلك وقوداً أكثر من السيارة ذات الكفاءة العالية عندما تقطعان نفس المسافة. وهو متغير عددي.

  3. class: نوع السيارة. وهو متغير فئوي (Categorical variable).

دعنا نبدأ بالتمثيل البصري للعلاقة بين displ و hwy لمختلف فئات السيارات class. يمكننا القيام بذلك باستخدام مخطط تشتت (scatterplot) حيث يتم ربط المتغيرات الرقمية بالخصائص الجمالية للمحورين x و y ويتم ربط المتغير الفئوي بخصوصية جمالية مثل اللون color أو الشكل shape.

# Left
ggplot(mpg, aes(x = displ, y = hwy, color = class)) +
  geom_point()

# Right
ggplot(mpg, aes(x = displ, y = hwy, shape = class)) +
  geom_point()
#> Warning: The shape palette can deal with a maximum of 6 discrete values because more
#> than 6 becomes difficult to discriminate
#> ℹ you have requested 7 values. Consider specifying shapes manually if you
#>   need that many of them.
#> Warning: Removed 62 rows containing missing values or values outside the scale range
#> (`geom_point()`).

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

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

عندما يتم ربط المتغير class بجمالية الشكل shape، فإننا نحصل على تحذيرين:

1: يمكن لوحة أشكال الأشكال التعامل مع بحد أقصى 6 قيم منفصلة لأن أكثر من 6 يصبح من الصعب التمييز بينها؛ لديك 7. ضع في اعتبارك تحديد الأشكال يدوياً إذا كان لا بد من وجودها.

2: تم إزالة 62 صفاً تحتوي على قيم مفقودة (geom_point()).

نظراً لأن ggplot2 ستستخدم ستة أشكال فقط في المرة الواحدة بشكل افتراضي، فإن المجموعات الإضافية لن يتم رسمها عندما تستخدم جمالية الشكل. والتحذير الثاني مرتبط بذلك – حيث توجد 62 سيارة من فئة SUV في مجموعة البيانات ولم يتم رسمها.

وبالمثل، يمكننا ربط المتغير class بجماليات الحجم size أو الشفافية alpha أيضاً، واللذين يتحكمان في حجم النقاط ودرجة شفافيتها على التوالي.

# Left
ggplot(mpg, aes(x = displ, y = hwy, size = class)) +
  geom_point()
#> Warning: Using size for a discrete variable is not advised.

# Right
ggplot(mpg, aes(x = displ, y = hwy, alpha = class)) +
  geom_point()
#> Warning: Using alpha for a discrete variable is not advised.

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

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

وينتج عن كلاهما تحذيرات أيضاً:

لا ينصح باستخدام الشفافية (alpha) لمتغير منفصل.

إن ربط متغير منفصل (فئوي) غير مرتب (class) بجمالية مرتبة (size أو alpha) ليس فكرة جيدة عموماً لأنه يوحي بوجود ترتيب أو رتبة لا وجود لها في الواقع.

بمجرد قيامك بربط الجمالية، تتولى ggplot2 الباقي. فهي تختار مقياساً معقولاً لاستخدامه مع الجمالية، وتقوم ببناء مفتاح رسم (Legend) يشرح الربط بين المستويات والقيم. بالنسبة لجماليات المحورين x و y، لا تقوم ggplot2 بإنشاء مفتاح رسم، ولكنها تنشئ خط محور مع علامات تدريج وتسمية (Label). يوفر خط المحور نفس المعلومات التي يوفرها مفتاح الرسم؛ فهو يشرح الربط بين المواقع والقيم.

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

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point(color = "blue")

مخطط مبعثر لكفاءة استهلاك الوقود على الطرق السريعة مقابل سعة محرك السيارات يظهر ارتباطاً سالباً. كل النقاط زرقاء.

هنا، لا ينقل اللون معلومات حول متغير ما، ولكنه يغير فقط المظهر الخارجي للمخطط. ستحتاج إلى اختيار قيمة منطقية لتلك الجمالية:

  • اسم اللون كسلسلة نصية، على سبيل المثال، color = "blue"
  • حجم النقطة بالملليمتر، على سبيل المثال، size = 1
  • شكل النقطة كرقم، على سبيل المثال، shape = 1، كما هو موضح في الشكل 9.1.
الربط بين الأشكال والأرقام التي تمثلها: 0 - مربع مفتوح، 1 - دائرة مفتوحة، 2 - مثلث مفتوح، 3 - علامة زائد، 4 - علامة تقاطع، 5 - معين مفتوح،  6 - مثلث مقلوب مفتوح، 7 - مربع متقاطع، 8 - نجمة ثمانية، 9 - معين متقاطع، 10 - دائرة متقاطعة، 11 - نجمة خماسية، 12 - مربع متقاطع داخلي،  13 - دائرة متقاطعة داخلية، 14 - مربع ومثلث متداخل، 15 - مربع مصمت، 16 - دائرة صغيرة مصمتة، 17 - مثلث مصمت، 18 - معين مصمت،  19 - دائرة مصمتة، 20 - نقطة مقذوف، 21 - دائرة معبأة، 22 - مربع معبأ، 23 - معين معبأ، 24 - مثلث معبأ، 25 - مثلث مقلوب معبأ.
الشكل 9.1: تحتوي لغة R على 26 شكلاً مدمجاً يتم تحديدها بواسطة الأرقام. هناك بعض الأشكال التي تبدو مكررة: على سبيل المثال، 0 و 15 و 22 كلها مربعات. يأتي الاختلاف من التفاعل بين جماليات اللون color والتعبئة fill. الأشكال المجوفة (0-14) لها حدود يتم تحديدها بواسطة الجمالية color؛ الأشكال المصمتة (15-20) يتم تعبئتها بواسطة color؛ الأشكال المعبأة (21-25) لها حدود من color ويتم تعبئتها بواسطة الجمالية fill. تم ترتيب الأشكال للحفاظ على الأشكال المتشابهة متجاورة.

لقد ناقشنا حتى الآن الجماليات التي يمكننا ربطها أو تحديدها في مخطط التشتت، عند استخدام الشكل الهندسي للنقاط (point geom). يمكنك معرفة المزيد حول جميع جماليات الربط الممكنة في كتيب مواصفات الجماليات المتاح على الرابط https://ggplot2.tidyverse.org/articles/ggplot2-specs.html.

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

9.2.1 تمارين

  1. أنشئ مخططاً تشتتياً لـ hwy مقابل displ بحيث تكون النقاط عبارة عن مثلثات معبأة باللون الوردي.

  2. لماذا لم يؤدِ الكود التالي إلى ظهور مخطط بنقاط زرقاء؟

    ggplot(mpg) + 
      geom_point(aes(x = displ, y = hwy, color = "blue"))
  3. ماذا تفعل الجمالية stroke؟ ومع أي الأشكال تعمل؟ (إرشاد: استخدم الأمر ?geom_point).

  4. ماذا يحدث إذا قمت بربط جمالية بشيء آخر غير اسم المتغير، مثل aes(color = displ < 5)؟ لاحظ أنه سيتعين عليك أيضاً تحديد المحورين x و y.

9.3 الأشكال الهندسية

ما وجه الشبه بين هذين المخططين؟

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

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

يحتوي كلا المخططين على نفس متغير x، ونفس متغير y، وكلاهما يصفان نفس البيانات. لكن المخططين ليسا متطابقين؛ حيث يستخدم كل مخطط كائناً هندسياً (Geometric object) مختلفاً - يُعرف اختصاراً بـ geom - لتمثيل البيانات. يستخدم المخطط الأيسر الشكل الهندسي للنقاط (point geom)، بينما يستخدم المخطط الأيمن الشكل الهندسي للمنحنيات الناعمة (smooth geom)، وهو خط ناعم يتم مواءمته وتوزينه بناءً على البيانات.

لتغيير الشكل الهندسي في مخططك، قم بتغيير دالة الـ geom التي تضيفها إلى ggplot(). على سبيل المثال، لإنشاء المخططات أعلاه، يمكنك استخدام الكود التالي:

# Left
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point()

# Right
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_smooth()
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

تأخذ كل دالة geom في حزمة ggplot2 وسيطاً للربط mapping، إما أن يتم تعريفه محلياً (Locally) في طبقة الـ geom نفسها أو عالمياً (Globally) في طبقة ggplot(). ومع ذلك، لا تعمل كل جمالية مع كل geom؛ فقد تتمكن من تحديد شكل النقطة، لكنك لا تستطيع تحديد “شكل” الخط! إذا حاولت القيام بذلك، فإن ggplot2 ستتجاهل ربط تلك الجمالية بصمت دون إظهار خطأ. من ناحية أخرى، يمكنك تحديد نوع الخط (linetype) للشكل الهندسي الخطي. ستقوم الدالة geom_smooth() برسم خط مختلف، بنوع خط مختلف، لكل قيمة فريدة للمتغير الذي تربطه بنوع الخط.

# Left
ggplot(mpg, aes(x = displ, y = hwy, shape = drv)) + 
  geom_smooth()

# Right
ggplot(mpg, aes(x = displ, y = hwy, linetype = drv)) + 
  geom_smooth()

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

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

هنا، تقوم الدالة geom_smooth() بفصل السيارات إلى ثلاثة خطوط بناءً على قيمة المتغير drv الذي يصف نظام الدفع في السيارة. حيث يصف أحد الخطوط جميع النقاط التي لها القيمة 4 (دفع رباعي)، ويصف خط آخر جميع النقاط التي لها القيمة f (دفع أمامي)، ويصف الخط الأخير جميع النقاط التي لها القيمة r (دفع خلفي).

إذا كان هذا يبدو غريباً، فيمكننا جعله أكثر وضوحاً من خلال تراكب الخطوط فوق البيانات الخام ثم تلوين كل شيء وفقاً للمتغير drv.

ggplot(mpg, aes(x = displ, y = hwy, color = drv)) + 
  geom_point() +
  geom_smooth(aes(linetype = drv))

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

لاحظ أن هذا المخطط يحتوي على شكلين هندسيين (two geoms) في نفس الرسم البياني.

تستخدم العديد من الأشكال الهندسية، مثل geom_smooth()، كائناً هندسياً واحداً لعرض صفوف متعددة من البيانات. بالنسبة لهذه الأشكال الهندسية، يمكنك ضبط جمالية المجموعة group على متغير فئوي لرسم كائنات متعددة؛ حيث ستقوم ggplot2 برسم كائن منفصل لكل قيمة فريدة لمتغير التجميع. عملياً، ستقوم ggplot2 بتجميع البيانات تلقائياً لهذه الأشكال الهندسية كلما قمت بربط جمالية بمتغير منفصل (كما في مثال linetype). ومن المناسب الاعتماد على هذه الميزة لأن جمالية group بمفردها لا تضيف مفتاح رسم أو ميزات تمييزية للأشكال الهندسية.

# Left
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_smooth()

# Middle
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_smooth(aes(group = drv))

# Right
ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_smooth(aes(color = drv), show.legend = FALSE)

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

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

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

إذا وضعت روابط الجماليات داخل دالة geom معينة، فإن ggplot2 ستتعامل معها كروابط محلية (Local mappings) لتلك الطبقة فقط. وستستخدم هذه الروابط لتوسيع أو استبدال الروابط العالمية (Global mappings) لتلك الطبقة دون غيرها. وهذا يجعل من الممكن عرض جماليات مختلفة في طبقات مختلفة.

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point(aes(color = class)) + 
  geom_smooth()

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

يمكنك استخدام نفس الفكرة لتحديد بيانات data مختلفة لكل طبقة. هنا، نستخدم نقاطاً حمراء بالإضافة إلى دوائر مفتوحة لتسليط الضوء على السيارات ذات المقعدين (two-seater). يحل وسيط البيانات المحلي في دالة geom_point() محل وسيط البيانات العالمي في دالة ggplot() لتلك الطبقة فقط.

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point() + 
  geom_point(
    data = mpg |> filter(class == "2seater"), 
    color = "red"
  ) +
  geom_point(
    data = mpg |> filter(class == "2seater"), 
    shape = "circle open", size = 3, color = "red"
  )

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

الأشكال الهندسية هي لبنات البناء الأساسية في ggplot2. يمكنك تحويل مظهر مخططك بالكامل عن طريق تغيير الـ geom الخاص به، ويمكن للأشكال الهندسية المختلفة أن تكشف عن ميزات مختلفة لبياناتك. على سبيل المثال، يكشف المدرج التكراري (histogram) ومخطط الكثافة (density plot) أدناه أن توزيع مسافات الطرق السريعة ثنائي النمط (bimodal) وملتوي نحو اليمين، بينما يكشف المخطط الصندوقي (boxplot) عن وجود قيمتين متطرفتين محتملتين.

# Left
ggplot(mpg, aes(x = hwy)) +
  geom_histogram(binwidth = 2)

# Middle
ggplot(mpg, aes(x = hwy)) +
  geom_density()

# Right
ggplot(mpg, aes(x = hwy)) +
  geom_boxplot()

ثلاثة مخططات: مدرج تكراري، ومخطط كثافة، ومخطط صندوقي لكفاءة استهلاك الوقود على الطرق السريعة.

ثلاثة مخططات: مدرج تكراري، ومخطط كثافة، ومخطط صندوقي لكفاءة استهلاك الوقود على الطرق السريعة.

ثلاثة مخططات: مدرج تكراري، ومخطط كثافة، ومخطط صندوقي لكفاءة استهلاك الوقود على الطرق السريعة.

توفر حزمة ggplot2 أكثر من 40 شكلاً هندسياً (geom)، ولكنها لا تغطي جميع المخططات الممكنة التي يمكن للمرء إنشاؤها. إذا كنت بحاجة إلى geom مختلف، نوصي بالبحث في الحزم الامتدادية أولاً لمعرفة ما إذا كان شخص آخر قد قام بتنفيذه بالفعل (راجع الرابط https://exts.ggplot2.tidyverse.org/gallery/ للاطلاع على عينات منها). على سبيل المثال، تُعد حزمة (https://wilkelab.org/ggridges) ggridges مفيدة لإنشاء مخططات الكثافة المتراكبة (ridgeline plots)، والتي يمكن أن تكون مفيدة لتصور كثافة متغير عددي لمستويات مختلفة من متغير فئوي. في المخطط التالي، لم نستخدم فحسب شكلاً هندسياً جديداً (geom_density_ridges())، بل قمنا أيضاً بربط نفس المتغير بجماليات متعددة (ربط drv بالمحور y والتعبئة fill واللون color) بالإضافة إلى تحديد قيمة ثابتة للشفافية (alpha = 0.5) لجعل منحنيات الكثافة شفافة.

library(ggridges)

ggplot(mpg, aes(x = hwy, y = drv, fill = drv, color = drv)) +
  geom_density_ridges(alpha = 0.5, show.legend = FALSE)
#> Picking joint bandwidth of 1.28

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

إن أفضل مكان للحصول على نظرة شاملة لجميع الأشكال الهندسية التي تقدمها ggplot2، بالإضافة إلى جميع الدوال الموجودة في الحزمة، هو صفحة المراجع الرسمية: https://ggplot2.tidyverse.org/reference. ولمعرفة المزيد حول أي geom بمفرده، استخدم صفحة المساعدة الخاصة به (على سبيل المثال ?geom_smooth).

9.3.1 تمارين

  1. ما هو الشكل الهندسي (geom) الذي ستستخدمه لرسم مخطط خطي (line chart)؟ ومخطط صندوقي (boxplot)؟ ومدرج تكراري (histogram)؟ ومخطط مساحي (area chart)؟

  2. في مرحلة سابقة من هذا الفصل، استخدمنا الوسيط show.legend دون شرحه:

    ggplot(mpg, aes(x = displ, y = hwy)) +
      geom_smooth(aes(color = drv), show.legend = FALSE)

    ماذا يفعل الأمر show.legend = FALSE هنا؟ وماذا يحدث إذا قمت بحذفه؟ ولماذا تعتقد أننا استخدمناه في جزء سابق؟

  3. ماذا يفعل الوسيط se داخل الدالة geom_smooth()؟

  4. أعد كتابة كود R اللازم لتوليد المخططات البيانية التالية. لاحظ أنه أينما تم استخدام متغير فئوي في المخطط، فإنه يكون المتغير drv.

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

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

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

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

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

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

9.4 الأوجه (Facets)

في الفصل 1، تعلمت عن تقسيم المخططات إلى أوجه باستخدام الدالة facet_wrap()، والتي تقسم المخطط الرئيسي إلى مخططات فرعية تعرض كل منها جزءاً مستقطعاً من البيانات بناءً على متغير فئوي.

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point() + 
  facet_wrap(~cyl)

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

لتقسيم مخططك إلى أوجه بناءً على توليفة من متغيرين، انتقل من استخدام facet_wrap() إلى الدالة facet_grid(). الوسيط الأول للدالة facet_grid() هو أيضاً صيغة رياضية (formula)، ولكنه الآن عبارة عن صيغة ثنائية الجانب: rows ~ cols (الصفوف ~ الأعمدة).

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point() + 
  facet_grid(drv ~ cyl)

مخطط مبعثر لكفاءة استهلاك الوقود على الطرق السريعة مقابل سعة محرك السيارات، مقسم إلى أوجه حسب عدد الأسطوانات عبر الصفوف وحسب نوع نظام الدفع عبر الأعمدة.  ينتج عن هذا شبكة 4×3 مكونة من 12 وجهاً. بعض هذه الأوجه لا تحتوي على أي مشاهدات: 5 أسطوانات ودفع رباعي، أو 4 أو 5 أسطوانات ودفع أمامي.

بشكل افتراضي، تشترك جميع الأوجه في نفس المقياس والنطاق للمحورين الأفقي x والعمودي y. هذا الأمر مفيد جداً عندما تريد مقارنة البيانات عبر الأوجه المختلفة، ولكنه قد يكون قيداً عندما تريد تصور العلاقة داخل كل وجه بشكل أفضل. إن ضبط وسيط المقاييس scales في دالة تقسيم الأوجه على القيمة "free_x" سيسمح بمقاييس مختلفة لمحور x عبر الأعمدة، وتحديد "free_y" سيسمح بمقاييس مختلفة لمحور y عبر الصفوف، بينما تسمح القيمة "free" بالاثنين معاً.

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point() + 
  facet_grid(drv ~ cyl, scales = "free")

مخطط مبعثر لكفاءة استهلاك الوقود على الطرق السريعة مقابل سعة محرك السيارات، مقسم إلى أوجه حسب عدد الأسطوانات عبر الصفوف وحسب نوع نظام الدفع عبر الأعمدة.  ينتج عن هذا شبكة 4×3 مكونة من 12 وجهاً. بعض هذه الأوجه لا تحتوي على مشاهدات. تشترك الأوجه داخل الصف الواحد في نفس مقياس y، وتشترك الأوجه داخل العمود الواحد في نفس مقياس x.

9.4.1 تمارين

  1. ماذا يحدث إذا قمت بتقسيم الأوجه بناءً على متغير متصل (Continuous variable)؟

  2. ماذا تعني الخلايا الفارغة في المخطط أعلاه الذي استخدمنا فيه facet_grid(drv ~ cyl)؟ شغّل الكود التالي، وكيف ترتبط بالنتيجة في المخطط؟

    ggplot(mpg) + 
      geom_point(aes(x = drv, y = cyl))
  3. ما هي المخططات التي ينتجها الكود التالي؟ وماذا تفعل النقطة . هنا؟

    ggplot(mpg) + 
      geom_point(aes(x = displ, y = hwy)) +
      facet_grid(drv ~ .)
    
    ggplot(mpg) + 
      geom_point(aes(x = displ, y = hwy)) +
      facet_grid(. ~ cyl)
  4. خذ المخطط الأول المقسم إلى أوجه في هذا القسم:

    ggplot(mpg) + 
      geom_point(aes(x = displ, y = hwy)) + 
      facet_wrap(~ cyl, nrow = 2)

    ما هي مزايا استخدام تقسيم الأوجه بدلاً من جمالية اللون؟ وما هي العيوب؟ وكيف يمكن أن يتغير هذا التوازن إذا كان لديك مجموعة بيانات أكبر بكثير؟

  5. اقرأ صفحة المساعدة الخاصة بالدالة ?facet_wrap. ماذا يفعل الوسيط nrow؟ وماذا يفعل ncol؟ وما هي الخيارات الأخرى التي تتحكم في تخطيط اللوحات الفردية؟ ولماذا لا تحتوي الدالة facet_grid() على وسيطي nrow و ncol؟

  6. أي من المخططات التالية يسهل مقارنة سعة المحرك (displ) عبر السيارات ذات أنظمة الدفع المختلفة؟ وماذا يقول هذا عن متى يجب وضع متغير تقسيم الأوجه عبر الصفوف أو الأعمدة؟

    ggplot(mpg, aes(x = displ)) + 
      geom_histogram() + 
      facet_grid(drv ~ .)
    
    ggplot(mpg, aes(x = displ)) + 
      geom_histogram() +
      facet_grid(. ~ drv)
  7. أعد إنشاء المخطط التالي باستخدام الدالة facet_wrap() بدلاً من facet_grid(). كيف تتغير مواضع تسميات الأوجه؟

    ggplot(mpg) + 
      geom_point(aes(x = displ, y = hwy)) +
      facet_grid(drv ~ .)

9.5 التحويلات الإحصائية (Statistical transformations)

لنأخذ في الاعتبار مخططاً عمودياً أساسياً، يتم رسمه باستخدام الدالة geom_bar() أو geom_col(). يعرض المخطط التالي العدد الإجمالي للألماس في مجموعة بيانات diamonds، مقسمة حسب جودة القطع cut. مجموعة بيانات diamonds مدمجة في حزمة ggplot2 وتحتوي على معلومات حول ما يقرب من 54,000 قطعة ألماس، بما في ذلك السعر price، والوزن بالقيراط carat، واللون color، والنقاء clarity، وجودة القطع cut لكل ألماسة. يظهر المخطط أن هناك عدداً أكبر من الألماس المتاح بقطع ذي جودة عالية مقارنة بالقطع ذي الجودة المنخفضة.

ggplot(diamonds, aes(x = cut)) + 
  geom_bar()

مخطط أعمدة لعدد كل قطع من الألماس. هناك حوالي 1500 مقبول (Fair)، و5000 جيد (Good)، و12000 جيد جداً (Very Good)، و14000 ممتاز (Premium)، و22000 مثالي (Ideal).

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

  • تقوم مخططات الأعمدة (Bar charts)، والمدرجات التكرارية (histograms)، والمضلعات التكرارية (frequency polygons) بتجميع بياناتك في فئات ثم رسم تكرارات هذه الفئات، أي عدد النقاط التي تقع في كل فئة.

  • تقوم منحنيات المواءمة والتنعيم (Smoothers) بمواءمة نموذج إحصائي مع بياناتك ثم رسم التنبؤات الناتجة عن هذا النموذج.

  • تقوم المخططات الصندوقية (Boxplots) بحساب ملخص الأعداد الخمسة (five-number summary) للتوزيع ثم تعرض هذا الملخص في شكل صندوق منسق خصيصاً.

تُسمى الخوارزمية المستخدمة لحساب القيم الجديدة للمخطط البياني بـ stat، وهي اختصار للتحويل الإحصائي (statistical transformation). يوضح الشكل 9.2 كيفية عمل هذه العملية مع الدالة geom_bar().

شكل يوضح الخطوات الثلاث لإنشاء مخطط أعمدة.  الخطوة 1: تبدأ geom_bar() بمجموعة بيانات الألماس.  الخطوة 2: تحول geom_bar() البيانات باستخدام التحويل الإحصائي count، والذي يعيد مجموعة بيانات تحتوي على قيم القطع والتكرارات.  الخطوة 3: تستخدم geom_bar() البيانات المحولة لبناء المخطط، حيث يتم ربط cut بالمحور الأفقي وcount بالمحور العمودي.
الشكل 9.2: عند إنشاء مخطط أعمدة، نبدأ أولاً بالبيانات الخام، ثم نجمعها لحساب عدد المشاهدات في كل عمود، وأخيراً نربط تلك المتغيرات المحسوبة بجماليات المخطط البياني.

يمكنك معرفة التحويل الإحصائي (stat) الذي يستخدمه الشكل الهندسي (geom) من خلال فحص القيمة الافتراضية لوسيط stat. على سبيل المثال، توضح صفحة المساعدة ?geom_bar أن القيمة الافتراضية لـ stat هي "count"، مما يعني أن الدالة geom_bar() تستخدم الدالة stat_count(). تم توثيق الدالة stat_count() في نفس الصفحة الخاصة بـ geom_bar(). إذا قمت بالتمرير لأسفل، فإن القسم المسمى (المتغيرات المحسوبة) “Variables computed” يوضح أنها تحسب متغيرين جديدين: count (التكرار) و prop (النسبة التناسبية).

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

  1. قد ترغب في إلغاء التحويل الإحصائي الافتراضي واستبداله. في الكود أدناه، نقوم بتغيير الـ stat للدالة geom_bar() من count (الافتراضي) إلى identity. يتيح لنا هذا ربط ارتفاع الأعمدة بالقيم الخام لمتغير y مباشرة.

    diamonds |>
      count(cut) |>
      ggplot(aes(x = cut, y = n)) +
      geom_bar(stat = "identity")

    مخطط أعمدة لعدد كل قطع من الألماس. هناك حوالي 1500 مقبول، و5000 جيد، و12000 جيد جداً، و14000 ممتاز، و22000 مثالي.

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

    ggplot(diamonds, aes(x = cut, y = after_stat(prop), group = 1)) + 
      geom_bar()

    مخطط أعمدة للنسب التناسبية لكل قطع من الألماس. تقريباً، يشكل الألماس المقبول 0.03، والجيد 0.09، والجيد جداً 0.22، والممتاز 0.26، والمثالي 0.40.

    للعثور على المتغيرات المحتملة التي يمكن حسابها بواسطة التحويل الإحصائي، ابحث عن القسم المعنون “computed variables” في صفحة المساعدة للدالة geom_bar().

  3. قد ترغب في توجيه اهتمام أكبر إلى التحويل الإحصائي في كودك البرمجي. على سبيل المثال، يمكنك استخدام الدالة stat_summary()، والتي تلخص قيم y لكل قيمة فريدة من قيم x، لجلب الانتباه إلى الملخص الإحصائي الذي تقوم بحسابه:

    ggplot(diamonds) + 
      stat_summary(
        aes(x = cut, y = depth),
        fun.min = min,
        fun.max = max,
        fun = median
      )

    مخطط يوضح العمق (depth) على المحور العمودي والقطع (cut) على المحور الأفقي للألماس.  لكل مستوى من مستويات القطع، تمتد خطوط عمودية من الحد الأدنى إلى الحد الأقصى للعمق، ويتم الإشارة إلى وسيط العمق بنقطة على الخط.

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

9.5.1 تمارين

  1. ما هو الشكل الهندسي الافتراضي المرتبط بالدالة stat_summary()؟ كيف يمكنك إعادة كتابة المخطط السابق لاستخدام دالة الـ geom تلك بدلاً من دالة الـ stat؟

  2. ماذا تفعل الدالة geom_col()؟ وكيف تختلف عن الدالة geom_bar()؟

  3. تأتي معظم الأشكال الهندسية (geoms) والتحويلات الإحصائية (stats) في أزواج تُستخدم معاً تقريباً في كل الأوقات. أنشئ قائمة بجميع هذه الأزواج. ما الذي يجمع بينها؟ (إرشاد: اقرأ التوثيقات وصفحات المساعدة).

  4. ما هي المتغيرات التي تحسبها الدالة stat_smooth()؟ وما هي الوسائط التي تتحكم في سلوكها؟

  5. في مخطط أعمدة النسب التناسبية الخاص بنا، احتجنا إلى ضبط وسيط التجميع على group = 1. لماذا؟ وبعبارة أخرى، ما هي المشكلة في هذين المخططين البيانيين؟

    ggplot(diamonds, aes(x = cut, y = after_stat(prop))) + 
      geom_bar()
    ggplot(diamonds, aes(x = cut, fill = color, y = after_stat(prop))) + 
      geom_bar()

9.6 تعديلات المواضع (Position adjustments)

هناك سحر آخر يرتبط بمخططات الأعمدة. يمكنك تلوين مخطط الأعمدة باستخدام إما جمالية اللون color، أو، وهو الأكثر فائدة، جمالية التعبئة fill:

# Left
ggplot(mpg, aes(x = drv, color = drv)) + 
  geom_bar()

# Right
ggplot(mpg, aes(x = drv, fill = drv)) + 
  geom_bar()

مخططان عموديان لأنواع دفع السيارات. في المخطط الأول، للأعمدة حدود ملونة. في المخطط الثاني، تم تعبئتها بالألوان. تتوافق ارتفاعات الأعمدة مع عدد السيارات في كل فئة.

مخططان عموديان لأنواع دفع السيارات. في المخطط الأول، للأعمدة حدود ملونة. في المخطط الثاني، تم تعبئتها بالألوان. تتوافق ارتفاعات الأعمدة مع عدد السيارات في كل فئة.

لاحظ ماذا يحدث إذا قمت بربط جمالية التعبئة بمتغير آخر، مثل فئة السيارة class: يتم تكديس الأعمدة تلقائياً (stacked). ويمثل كل مستطيل ملون توليفة من متغير نظام الدفع drv وفئة السيارة class.

ggplot(mpg, aes(x = drv, fill = class)) + 
  geom_bar()

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

يتم إجراء هذا التكديس تلقائياً باستخدام تعديل الموضع (position adjustment) المحدد بواسطة وسيط الموضع position. إذا كنت لا تريد مخطط أعمدة مكدساً، يمكنك استخدام أحد الخيارات الثلاثة الأخرى: "identity" أو "dodge" أو "fill".

  • يضع الأمر position = "identity" كل كائن في المكان الذي يقع فيه بالضبط في سياق الرسم البياني. وهذا ليس مفيداً جداً للأعمدة، لأنه يؤدي إلى تداخلها وتغطية بعضها البعض. لرؤية هذا التداخل، نحتاج إما إلى جعل الأعمدة شفافة قليلاً عن طريق ضبط الشفافية alpha على قيمة صغيرة، أو جعلها شفافة تماماً عن طريق تعيين التعبئة كقيمة فارغة fill = NA.

    # Left
    ggplot(mpg, aes(x = drv, fill = class)) + 
      geom_bar(alpha = 1/5, position = "identity")
    # Right
    ggplot(mpg, aes(x = drv, color = class)) + 
      geom_bar(fill = NA, position = "identity")

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

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

    يُعد تعديل الموضع المتمثل في الهوية (identity position adjustment) أكثر فائدة للأشكال الهندسية ثنائية الأبعاد، مثل النقاط، حيث يكون هو الخيار الافتراضي لها.

  • يعمل الأمر position = "fill" بشكل يشبه التكديس، ولكنه يجعل كل مجموعة من الأعمدة المكدسة بنفس الارتفاع تماماً. وهذا يسهل من عملية مقارنة النسب التناسبية عبر المجموعات المختلفة.

  • يضع الأمر position = "dodge" الكائنات المتداخلة جنباً إلى جنب مباشرة. وهذا يسهل من عملية مقارنة القيم الفردية.

    # Left
    ggplot(mpg, aes(x = drv, fill = class)) + 
      geom_bar(position = "fill")
    
    # Right
    ggplot(mpg, aes(x = drv, fill = class)) + 
      geom_bar(position = "dodge")

    على اليسار، مخطط أعمدة مجزأ لأنواع دفع السيارات، حيث يتم تعبئة كل عمود بألوان مستويات الفئة (class). ارتفاع كل عمود هو 1 وارتفاعات القطاعات الملونة تمثل نسب السيارات.  على اليمين، مخطط أعمدة متجاور لأنواع دفع السيارات، حيث يتم تجميع الأعمدة المتجاورة حسب مستويات نوع الدفع. داخل كل مجموعة تمثل الأعمدة كل مستوى من مستويات الفئة.  بعض الفئات ممثلة في بعض أنواع الدفع وغير ممثلة في أخرى، مما يؤدي إلى أعداد غير متساوية من الأعمدة داخل كل مجموعة. تمثل ارتفاعات الأعمدة عدد السيارات.

    على اليسار، مخطط أعمدة مجزأ لأنواع دفع السيارات، حيث يتم تعبئة كل عمود بألوان مستويات الفئة (class). ارتفاع كل عمود هو 1 وارتفاعات القطاعات الملونة تمثل نسب السيارات.  على اليمين، مخطط أعمدة متجاور لأنواع دفع السيارات، حيث يتم تجميع الأعمدة المتجاورة حسب مستويات نوع الدفع. داخل كل مجموعة تمثل الأعمدة كل مستوى من مستويات الفئة.  بعض الفئات ممثلة في بعض أنواع الدفع وغير ممثلة في أخرى، مما يؤدي إلى أعداد غير متساوية من الأعمدة داخل كل مجموعة. تمثل ارتفاعات الأعمدة عدد السيارات.

هناك نوع آخر من التعديلات لا يفيد في مخططات الأعمدة، ولكنه قد يكون مفيداً جداً لمخططات التشتت. تذكر مخططنا التشتتي الأول؛ هل لاحظت أن المخطط يعرض 126 نقطة فقط، على الرغم من وجود 234 مشاهدة في مجموعة البيانات؟

مخطط مبعثر لكفاءة استهلاك الوقود على الطرق السريعة مقابل سعة محرك السيارات يظهر ارتباطاً سالباً.

القيم الكامنة لمتغيري hwy و displ مقربة، لذا تظهر النقاط على شبكة منتظمة ويتداخل العديد منها فوق بعضها البعض. تُعرف هذه المشكلة باسم التطابق المفرط للرسم (Overplotting). يجعل هذا الترتيب من الصعب رؤية توزيع البيانات؛ فهل نقاط البيانات موزعة بالتساوي في جميع أنحاء المخطط، أم أن هناك توليفة خاصة واحدة من hwy و displ تحتوي على 109 قيم؟

يمكنك تجنب هذا التراصف الشبكي عن طريق ضبط تعديل الموضع على القيمة "jitter" (الارتعاش أو التشتيت). يضيف الأمر position = "jitter" مقداراً صغيراً من الضوضاء العشوائية لكل نقطة؛ يؤدي هذا إلى تشتيت النقاط وتباعدها لأنه من غير المحتمل أن تحصل أي نقطتين على نفس المقدار من الضوضاء العشوائية تماماً.

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point(position = "jitter")

مخطط مبعثر مشتت (jittered) لكفاءة استهلاك الوقود على الطرق السريعة مقابل سعة محرك السيارات يظهر ارتباطاً سالباً.

قد يبدو إدخال العشوائية طريقة غريبة لتحسين مخططك البياني، ولكن بينما يجعل المخطط أقل دقة عند المقاييس الصغيرة، فإنه يجعله أكثر كشفاً وإيضاحاً عند المقاييس الكبيرة. ونظراً لأن هذه العملية مفيدة للغاية، فإن ggplot2 تأتي باختصار للدالة geom_point(position = "jitter") وهو: geom_jitter().

لمعرفة المزيد حول تعديلات المواضع، يمكنك البحث في صفحات المساعدة المرتبطة بكل تعديل: ?position_dodge و ?position_fill و ?position_identity و ?position_jitter و ?position_stack.

9.6.1 تمارين

  1. ما هي المشكلة في المخطط البياني التالي؟ وكيف يمكنك تحسينه؟

    ggplot(mpg, aes(x = cty, y = hwy)) + 
      geom_point()
  2. ما الفرق -إن وُجد- بين هذين المخططين البيانيين؟ ولماذا؟

    ggplot(mpg, aes(x = displ, y = hwy)) +
      geom_point()
    ggplot(mpg, aes(x = displ, y = hwy)) +
      geom_point(position = "identity")
  3. ما هي المعلمات (parameters) في دالة geom_jitter() التي تتحكم في مقدار التشتيت والارتعاش؟

  4. قارن وقابل بين الدالتين geom_jitter() و geom_count().

  5. ما هو تعديل الموضع الافتراضي للدالة geom_boxplot()؟ أنشئ تصوراً بيانياً لمجموعة بيانات mpg يوضح ذلك عملياً.

9.7 أنظمة الإحداثيات (Coordinate systems)

ربما تكون أنظمة الإحداثيات هي الجزء الأكثر تعقيداً في ggplot2. نظام الإحداثيات الافتراضي هو نظام الإحداثيات الديكارتية (Cartesian coordinate system) حيث يعمل موقعا x و y بشكل مستقل لتحديد موقع كل نقطة. وهناك نظاما إحداثيات آخران يفيدان في بعض الأحيان:

  • يقوم الأمر coord_quickmap() بضبط نسبة الأبعاد (aspect ratio) بشكل صحيح للخرائط الجغرافية. وهذا أمر مهم للغاية إذا كنت ترسم بيانات مكانية (spatial data) باستخدام ggplot2. ليس لدينا مساحة كافية لمناقشة الخرائط في هذا الكتاب، ولكن يمكنك معرفة المزيد في فصل الخرائط من كتاب ggplot2: Elegant graphics for data analysis.

    nz <- map_data("nz")
    
    ggplot(nz, aes(x = long, y = lat, group = group)) +
      geom_polygon(fill = "white", color = "black")
    
    ggplot(nz, aes(x = long, y = lat, group = group)) +
      geom_polygon(fill = "white", color = "black") +
      coord_quickmap()

    خريطتان لحدود نيوزيلندا. في المخطط الأول النسبة الباعية غير صحيحة، وفي المخطط الثاني صحيحة.

    خريطتان لحدود نيوزيلندا. في المخطط الأول النسبة الباعية غير صحيحة، وفي المخطط الثاني صحيحة.

  • تستخدم الدالة coord_polar() الإحداثيات القطبية (polar coordinates). تكشف الإحداثيات القطبية عن صلة ربط مثيرة للاهتمام بين مخطط الأعمدة ومخطط كوكسكومب (Coxcomb chart).

    bar <- ggplot(data = diamonds) + 
      geom_bar(
        mapping = aes(x = clarity, fill = clarity), 
        show.legend = FALSE,
        width = 1
      ) + 
      theme(aspect.ratio = 1)
    
    bar + coord_flip()
    bar + coord_polar()

    هناك مخططان؛ على اليسار مخطط أعمدة لنقاء الألماس، وعلى اليمين مخطط كوكسكومب لنفس البيانات.

    هناك مخططان؛ على اليسار مخطط أعمدة لنقاء الألماس، وعلى اليمين مخطط كوكسكومب لنفس البيانات.

9.7.1 تمارين

  1. حوّل مخطط أعمدة مكدس إلى مخطط دائري (pie chart) باستخدام الدالة coord_polar().

  2. ما الفرق بين coord_quickmap() و coord_map()؟

  3. ماذا يخبرك المخطط البياني التالي عن العلاقة بين استهلاك الوقود داخل المدينة وعلى الطرق السريعة؟ ولماذا يُعد الأمر coord_fixed() مهماً؟ وماذا تفعل الدالة geom_abline()؟

    ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +
      geom_point() + 
      geom_abline() +
      coord_fixed()

9.8 القواعد الطبقية للرسومات

يمكننا التوسع في قالب الرسم البياني الذي تعلمته في قسم 1.3 عن طريق إضافة تعديلات المواضع، والتحويلات الإحصائية، وأنظمة الإحداثيات، وتقسيم الأوجه:

ggplot(data = <DATA>) + 
  <GEOM_FUNCTION>(
     mapping = aes(<MAPPINGS>),
     stat = <STAT>, 
     position = <POSITION>
  ) +
  <COORDINATE_FUNCTION> +
  <FACET_FUNCTION>

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

تشكل المعلمات السبعة في القالب قواعد الرسومات (grammar of graphics)، وهو نظام رسمي لبناء المخططات البيانية. وتعتمد قواعد الرسومات على رؤية معمقة تفيد بأنه يمكنك وصف أي مخطط بياني بشكل فريد كمزيج من مجموعة بيانات، وشكل هندسي (geom)، ومجموعة من روابط الجماليات، وتحويل إحصائي (stat)، وتعديل للموضع، ونظام إحداثيات، ومخطط لتقسيم الأوجه، ومظهر عام (theme).

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

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

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

يمكنك استخدام هذه الطريقة لبناء أي مخطط بياني تتخيله. وبعبارة أخرى، يمكنك استخدام قالب الكود الذي تعلمته في هذا الفصل لبناء مئات الآلاف من المخططات البيانية الفريدة.

إذا كنت ترغب في معرفة المزيد عن الأسس النظرية الكامنة وراء حزمة ggplot2، فقد تستمتع بقراءة ورقة البحث العلمية “The Layered Grammar of Graphics”، والتي تصف نظرية ggplot2 بالتفصيل.

9.9 ملخص

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

هناك موردان مفيدان للغاية للحصول على نظرة عامة على وظائف ggplot2 الكاملة وهما الورقة المرجعية السريعة الخاصة بـ ggplot2 (والتي يمكنك العثور عليها على الرابط https://posit.co/resources/cheatsheets) والموقع الإلكتروني للحزمة (https://ggplot2.tidyverse.org).

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