1  التمثيل البصري للبيانات

1.1 مقدمة

“لقد جلب الرسم البياني البسيط من المعلومات إلى عقل مُحلل البيانات أكثر مما جلبه أي جهاز آخر.” — جون توكي (John Tukey)

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

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

1.1.1 المتطلبات الأساسية

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

library(tidyverse)
#> ── Attaching core tidyverse packages ───────────────────── tidyverse 2.0.0 ──
#> ✔ dplyr     1.2.1     ✔ readr     2.2.0
#> ✔ forcats   1.0.1     ✔ stringr   1.6.0
#> ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
#> ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
#> ✔ purrr     1.2.2     
#> ── Conflicts ─────────────────────────────────────── tidyverse_conflicts() ──
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ dplyr::lag()    masks stats::lag()
#> ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

يقوم هذا السطر البرمجي الواحد بتحميل الحزم الأساسية لمنظومة tidyverse، وهي الحزم التي ستستخدمها في كل عملية تحليل بيانات تقريباً. كما يخبرك أيضاً بأي الدوال من منظومة tidyverse تتعارض مع الدوال الموجودة في حزم R الأساسية (أو الحزم الأخرى التي قمت بتحميلها)1.

إذا قمت بتشغيل هذا الكود وظهرت لك رسالة الخطأ there is no package called tidyverse، فستحتاج أولاً إلى تثبيتها، ثم تشغيل الدالة library() مرة أخرى.

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

بالإضافة إلى منظومة tidyverse، سنستخدم أيضاً حزمة palmerpenguins، والتي تتضمن مجموعة بيانات penguins التي تحتوي على قياسات أجسام البطاريق في ثلاث جزر في أرخبيل بالمر (Palmer Archipelago)، وحزمة ggthemes التي توفر لوحة ألوان آمنة ومناسبة للمصابين بعمى الألوان.

library(palmerpenguins)
#> 
#> Attaching package: 'palmerpenguins'
#> The following objects are masked from 'package:datasets':
#> 
#>     penguins, penguins_raw
library(ggthemes)

1.2 الخطوات الأولى

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

1.2.1 إطار البيانات penguins

يمكنك اختبار إجاباتك على تلك الأسئلة باستخدام إطار البيانات (data frame) المسمى penguins والموجود في حزمة palmerpenguins (والذي يُعرف أيضاً بـ palmerpenguins::penguins). إطار البيانات هو عبارة عن مجموعة مستطيلة الشكل تتكون من متغيرات (في الأعمدة) ومشاهدات (في الأسطر). يحتوي إطار البيانات penguins على 344 مشاهدة تم جمعها وإتاحتها بواسطة الدكتورة كريستين جورمان (Dr. Kristen Gorman) ومحطة بالمر (Palmer Station)، التابعة لبرنامج الأبحاث البيئية طويلة الأجل في القارة القطبية الجنوبية (Antarctica LTER)2.

لتسهيل النقاش، دعنا نُعرّف بعض المصطلحات الإحصائية:

  • المتغير (Variable): هو كمية، أو صفة، أو خاصية يمكنك قياسها.
  • القيمة (Value): هي حالة المتغير عند قياسه. وقد تتغير قيمة المتغير من قياس إلى آخر.
  • المشاهدة (Observation): هي مجموعة من القياسات التي أُجريت في ظل ظروف مماثلة (عادةً ما تقوم بإجراء جميع القياسات في المشاهدة الواحدة في نفس الوقت وعلى نفس الكائن). وتحتوي المشاهدة الواحدة على عدة قيم، ترتبط كل قيمة منها بمتغير مختلف. وسنشير أحياناً إلى المشاهدة باسم “نقطة بيانات” (data point).
  • البيانات الجدولية (Tabular data): هي مجموعة من القيم، ترتبط كل قيمة منها بمتغير ومشاهدة. وتكون البيانات الجدولية منسقة (tidy) إذا وضعت كل قيمة في “الخلية” الخاصة بها، وكل متغير في العمود الخاص به، وكل مشاهدة في السطر الخاص بها.

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

اكتب اسم إطار البيانات في وحدة التحكم (console)، وستقوم بيئة R بطباعة معاينة لمحتوياته. لاحظ أنه مكتوب كلمة tibble في الجزء العلوي من هذه المعاينة. في منظومة tidyverse، نستخدم أطر بيانات خاصة تُسمى tibbles، والتي ستتعلم عنها المزيد قريباً.

penguins
#> # A tibble: 344 × 8
#>   species island    bill_length_mm bill_depth_mm flipper_length_mm
#>   <fct>   <fct>              <dbl>         <dbl>             <int>
#> 1 Adelie  Torgersen           39.1          18.7               181
#> 2 Adelie  Torgersen           39.5          17.4               186
#> 3 Adelie  Torgersen           40.3          18                 195
#> 4 Adelie  Torgersen           NA            NA                  NA
#> 5 Adelie  Torgersen           36.7          19.3               193
#> 6 Adelie  Torgersen           39.3          20.6               190
#> # ℹ 338 more rows
#> # ℹ 3 more variables: body_mass_g <int>, sex <fct>, year <int>

يحتوي إطار البيانات هذا على 8 أعمدة. وللحصول على عرض بديل يتيح لك رؤية جميع المتغيرات والمشاهدات القليلة الأولى لكل متغير، استخدم الدالة glimpse(). أما إذا كنت تعمل داخل بيئة RStudio، فيمكنك تشغيل الأمر View(penguins) لفتح نافذة تفاعلية لاستعراض البيانات.

glimpse(penguins)
#> Rows: 344
#> Columns: 8
#> $ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, A…
#> $ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torge…
#> $ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.…
#> $ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.…
#> $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, …
#> $ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 347…
#> $ sex               <fct> male, female, female, NA, female, male, female, m…
#> $ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2…

من بين المتغيرات الموجودة في إطار البيانات penguins ما يلي:

  1. species: فصيلة أو نوع البطريق (Adelie أو Chinstrap أو Gentoo).
  2. flipper_length_mm: طول زعنفة البطريق، بالملليمتر.
  3. body_mass_g: كتلة جسم البطريق، بالجرام.

لمعرفة المزيد عن مجموعة بيانات penguins، افتح صفحة المساعدة الخاصة بها عن طريق تشغيل الأمر ?penguins.

1.2.2 الهدف النهائي

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

A scatterplot of body mass vs. flipper length of penguins, with a best fit line of the relationship between these two variables  overlaid. The plot displays a positive, fairly linear, and relatively  strong relationship between these two variables. Species (Adelie,  Chinstrap, and Gentoo) are represented with different colors and  shapes. The relationship between body mass and flipper length is  roughly the same for these three species, and Gentoo penguins are  larger than penguins from the other two species.

1.2.3 إنشاء مخطط باستخدام ggplot

دعنا نُعيد إنشاء هذا المخطط خطوة بخطوة.

باستخدام حزمة ggplot2، تبدأ في بناء المخطط البياني باستخدام الدالة ggplot()، والتي تقوم بتعريف كائن المخطط (plot object) الذي ستضيف إليه الطبقات (layers) لاحقاً. ويتمثل المعامل الأول (first argument) في الدالة ggplot() في مجموعة البيانات المراد استخدامها في الرسم البياني؛ ولذلك فإن الأمر ggplot(data = penguins) ينشئ رسماً بيانياً فارغاً مهيأً لعرض بيانات penguins، ولكن نظراً لأننا لم نُخبره بعد بكيفية تمثيلها بصريًا، فإنه يظل فارغاً في الوقت الحالي. هذا ليس مخططاً مثيراً للاهتمام، ولكن يمكنك التفكير فيه كلوحة قماشية فارغة (canvas) ستقوم برسم بقية طبقات المخطط فوقها.

ggplot(data = penguins)

A blank, gray plot area.

بعد ذلك، نحتاج إلى إخبار الدالة ggplot() بكيفية التمثيل البصري للمعلومات المستمدة من بياناتنا. يُحدد معامل الربط mapping في الدالة ggplot() كيفية ربط المتغيرات الموجودة في مجموعة البيانات بالخصائص البصرية (الجماليات - aesthetics) للمخطط البياني الخاص بك. ويتم تعريف المعامل mapping دائماً داخل الدالة aes()، حيث يُحدد المعاملان x و y داخل الدالة aes() المتغيرات المراد إسقاطها على المحورين السيني والصادي. في الوقت الحالي، سنقوم فقط بربط طول الزعنفة بالخاصية الجمالية للمحور السيني x، وكتلة الجسم بالخاصية الجمالية للمحور الصادي y. وتبحث حزمة ggplot2 عن المتغيرات المربوطة هذه داخل معامل البيانات data، وهو في هذه الحالة إطار البيانات penguins.

ويوضح المخطط التالي النتيجة المترتبة على إضافة عمليات الربط هذه.

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
)

The plot shows flipper length on the x-axis, with values that range from  170 to 230, and body mass on the y-axis, with values that range from 3000  to 6000.

أصبح للوحتنا القماشية الفارغة الآن بنية أكثر وضوحاً — فقد بات من الجلي أين سيتم عرض أطوال الزعانف (على المحور السيني x-axis) وأين ستُعرض كتل الأجسام (على المحور الصادي y-axis). ولكن البيانات الخاصة بالبطاريق نفسها لم تظهر بعد على المخطط؛ ويعود السبب في ذلك إلى أننا لم نحدد بعد، في الكود البرمجي الخاص بنا، كيفية تمثيل المشاهدات المستمدة من إطار البيانات على المخطط.

وللقيام بذلك، نحتاج إلى تعريف العنصر الهندسي (geom): وهو الكائن الهندسي الذي يستخدمه المخطط لتمثيل البيانات. وتتوفر هذه الكائنات الهندسية في حزمة ggplot2 عبر دالّات تبدأ بـ geom_. وغالباً ما يصف الأشخاص المخططات البيانية بناءً على نوع العنصر الهندسي المستخدم فيها؛ على سبيل المثال، تستخدم المخططات الشريطية العناصر الشريطية (geom_bar())، وتستخدم المخططات الخطية العناصر الخطية (geom_line())، وتستخدم مخططات الصندوق العناصر الصندوقية (geom_boxplot())، بينما تستخدم مخططات التشتت عناصر النقاط (geom_point())، وهكذا.

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

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point()
#> Warning: Removed 2 rows containing missing values or values outside the scale range
#> (`geom_point()`).

A scatterplot of body mass vs. flipper length of penguins. The plot  displays a positive, linear, and relatively strong relationship between  these two variables.

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

وقبل أن نضيف المزيد من الطبقات إلى هذا المخطط، دعنا نتوقف للحظة ونراجع رسالة التحذير التي ظهرت لنا (تمت إزالة سطرين يحتويان على قيم مفقودة geom_point()):

Removed 2 rows containing missing values (geom_point()).

تظهر لنا هذه الرسالة لأن هناك بطريقين في مجموعة البيانات لدينا يفتقران إلى قيم كتلة الجسم و/أو طول الزعنفة، ولا تملك حزمة ggplot2 أي طريقة لتمثيلهما على المخطط دون توفر كلتا القيمتين. وتتبنى حزمة ggplot2 — مثلها مثل لغة R — الفلسفة التي ترى أن القيم المفقودة لا ينبغي أبداً أن تختفي بصمت دون تنبيه المستخدم. ويُعد هذا النوع من التحذيرات أحد أكثر التحذيرات شيوعاً التي ستراها عند التعامل مع بيانات حقيقية؛ فالقيم المفقودة مشكلة شائعة جداً وستتعلم عنها المزيد على مدار الكتاب، وتحديداً في الفصل الفصل 18. وسنقوم في بقية مخططات هذا الفصل بحجب هذا التحذير حتى لا يُطبع بجانب كل مخطط ننشئه.

1.2.4 إضافة الخصائص الجمالية والطبقات

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

ولتحقيق ذلك، هل سنحتاج إلى تعديل الخاصية الجمالية (aesthetic) أم العنصر الهندسي (geom)؟ إذا خمنت أن التعديل سيكون “في ربط الخصائص الجمالية، داخل الدالة aes()”، فقد بدأت بالفعل في استيعاب طريقة إنشاء التمثيلات البصرية للبيانات باستخدام ggplot2! وإذا لم يكن الأمر كذلك، فلا تقلق؛ فستقوم على مدار هذا الكتاب بإنشاء العديد من مخططات ggplot، وستتاح لك فرص أكثر لاختبار حدسك وتطويره أثناء بنائها.

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
  geom_point()

A scatterplot of body mass vs. flipper length of penguins. The plot  displays a positive, fairly linear, and relatively strong relationship  between these two variables. Species (Adelie, Chinstrap, and Gentoo)  are represented with different colors.

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

والآن، دعنا نضيف طبقة أخرى: منحنى ممهد (smooth curve) يعرض العلاقة بين كتلة الجسم وطول الزعنفة. وقبل أن تتابع، ارجع إلى الكود السابق وفكر في كيفية إضافة ذلك إلى مخططنا الحالي.

ونظراً لأن هذا يمثل كائناً هندسياً جديداً لتمثيل بياناتنا، فسنقوم بإضافة عنصر هندسي جديد كطبقة فوق عنصر النقاط لدينا، وهو: geom_smooth(). وسنحدد أننا نريد رسم خط الملاءمة الأفضل (line of best fit) بناءً على نموذج خطي (linear model) وذلك باستخدام المعامل method = "lm".

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
  geom_point() +
  geom_smooth(method = "lm")

A scatterplot of body mass vs. flipper length of penguins. Overlaid  on the scatterplot are three smooth curves displaying the  relationship between these variables for each species (Adelie,  Chinstrap, and Gentoo). Different penguin species are plotted in  different colors for the points and the smooth curves.

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

عندما يتم تعريف ربط الخصائص الجمالية (aesthetic mappings) داخل الدالة ggplot()، أي على المستوى العام (global level)، فإنه يُمرر تلقائيًا إلى كل طبقة من طبقات العناصر الهندسية (geom layers) اللاحقة في المخطط. ومع ذلك، يمكن لكل دالة هندسية في حزمة ggplot2 أن تأخذ أيضاً معامل ربط mapping خاصاً بها، مما يسمح بتعريف ربط الخصائص الجمالية على المستوى المحلي (local level)، حيث تُضاف هذه الخصائص المحلية إلى تلك الموروثة من المستوى العام. ونظراً لأننا نريد تلوين النقاط بناءً على الفصيلة، ولكننا لا نريد فصل الخطوط بناءً عليها، فينبغي لنا تحديد المعامل color = species داخل الدالة geom_point() فقط.

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point(mapping = aes(color = species)) +
  geom_smooth(method = "lm")

A scatterplot of body mass vs. flipper length of penguins. Overlaid  on the scatterplot is a single line of best fit displaying the  relationship between these variables for each species (Adelie,  Chinstrap, and Gentoo). Different penguin species are plotted in  different colors for the points only.

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

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

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point(mapping = aes(color = species, shape = species)) +
  geom_smooth(method = "lm")

A scatterplot of body mass vs. flipper length of penguins. Overlaid  on the scatterplot is a single line of best fit displaying the  relationship between these variables for each species (Adelie,  Chinstrap, and Gentoo). Different penguin species are plotted in  different colors and shapes for the points only.

لاحظ أن دليل المخطط (legend) يتم تحديثه تلقائياً ليعكس الأشكال المختلفة للنقاط أيضاً.

وأخيراً، يمكننا تحسين التسميات التوضيحية (labels) لمخططنا البياني باستخدام الدالة labs() في طبقة جديدة. وقد تبدو بعض المعاملات (arguments) في الدالة labs() غنية عن التعريف؛ حيث يضيف المعامل title عنواناً رئيسياً، ويضيف المعامل subtitle عنواناً فرعياً للمخطط. بينما تتطابق المعاملات الأخرى مع ربط الخصائص الجمالية؛ حيث يمثل المعامل x تسمية المحور السيني، ويمثل المعامل y تسمية المحور الصادي، في حين يحدد المعاملان color و shape التسمية التوضيحية الخاصة بدليل المخطط. بالإضافة إلى ذلك، يمكننا تحسين لوحة الألوان لتكون آمنة ومناسبة للمصابين بعمى الألوان باستخدام الدالة scale_color_colorblind() المستمدة من حزمة ggthemes.

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point(aes(color = species, shape = species)) +
  geom_smooth(method = "lm") +
  labs(
title = "كتلة الجسم وطول الزعنفة",
    subtitle = "أبعاد بطاريق أديلي، وشريطية الذقن، وجينتو",
    x = "طول الزعنفة (مم)", 
    y = "كتلة الجسم (جم)",
    color = "النوع", 
    shape = "النوع"
  ) +
  scale_color_colorblind()

A scatterplot of body mass vs. flipper length of penguins, with a  line of best fit displaying the relationship between these two variables  overlaid. The plot displays a positive, fairly linear, and relatively  strong relationship between these two variables. Species (Adelie,  Chinstrap, and Gentoo) are represented with different colors and  shapes. The relationship between body mass and flipper length is  roughly the same for these three species, and Gentoo penguins are  larger than penguins from the other two species.

أصبح لدينا أخيراً مخطط بياني يطابق “هدفنا النهائي” تماماً!

1.2.5 تمارين

  1. كم عدد الصفوف (المشاهدات) الموجودة في إطار البيانات penguins؟ وكم عدد الأعمدة (المتغيرات)؟

  2. ماذا يصف المتغير bill_depth_mm في إطار البيانات penguins؟ اقرأ صفحة المساعدة عبر الأمر ?penguins لمعرفة الإجابة.

  3. أنشئ مخطط تشتت يربط بين المتغيرين bill_depth_mm و bill_length_mm. بحيث يُمثَّل المتغير bill_depth_mm على المحور الصادي (y-axis) والمتغير bill_length_mm على المحور السيني (x-axis). صِف طبيعة العلاقة بين هذين المتغيرين.

  4. ماذا يحدث إذا أنشأت مخطط تشتت يربط بين المتغير الفئوي species والمتغير العددي bill_depth_mm؟ وماذا قد يكون البديل الأفضل كعنصر هندسي (geom) لتمثيل هذه البيانات؟

  5. لماذا يتسبب الكود التالي في ظهور خطأ (error)؟ وكيف يمكنك إصلاحه؟

ggplot(data = penguins) + 
  geom_point()
  1. ماذا يفعل المعامل na.rm داخل الدالة geom_point()؟ وما هي القيمة الافتراضية (default value) لهذا المعامل؟ أنشئ مخطط تشتت تستخدم فيه هذا المعامل بنجاح بعد ضبط قيمته لتكون TRUE.

  2. أضف التعليق التوضيحي (caption) التالي إلى المخطط البياني الذي أنشأته في التمرين السابق: “مصدر البيانات مستمد من حزمة palmerpenguins.” (تلميح: ألقِ نظرة على وثائق الدالة labs()).

  3. أعد إنشاء المخطط البياني التالي. ما هي الخاصية الجمالية (aesthetic) التي ينبغي ربط المتغير bill_depth_mm بها؟ وهل ينبغي ربطه على المستوى العام (global level) أم على مستوى العنصر الهندسي (geom level)؟

A scatterplot of body mass vs. flipper length of penguins, colored  by bill depth. A smooth curve of the relationship between body mass  and flipper length is overlaid. The relationship is positive,  fairly linear, and moderately strong.

  1. نفّذ هذا الكود ذهنياً وتوقع كيف سيبدو شكل المخرج (output). بعد ذلك، قم بتشغيل الكود في بيئة R للتحقق من صحة توقعاتك.
ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = island)
) +
  geom_point() +
  geom_smooth(se = FALSE)
  1. هل سيبدو هذان الرسمان البيانيان مختلفين؟ ولماذا / لم لا؟
ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point() +
  geom_smooth()

ggplot() +
  geom_point(
    data = penguins,
    mapping = aes(x = flipper_length_mm, y = body_mass_g)
  ) +
  geom_smooth(
    data = penguins,
    mapping = aes(x = flipper_length_mm, y = body_mass_g)
  )

1.3 استدعاءات دالة ggplot2

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

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
  geom_point()

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

وعند إعادة كتابة المخطط السابق بأسلوب أكثر إيجازاً واختصاراً نحصل على التالي:

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + 
  geom_point()

في المستقبل، ستتعلم أيضاً عن دالة الربط الأنبوبي (the pipe)؛ وهي الأداة |>، والتي ستتيح لك إنشاء ذلك المخطط البياني على النحو التالي:

penguins |> 
  ggplot(aes(x = flipper_length_mm, y = body_mass_g)) + 
  geom_point()

1.4 التمثيل البصري للتوزيعات التكرارية

تعتمد كيفية التمثيل البصري لتوزيع متغير ما على نوع هذا المتغير: سواء كان متغيراً فئوياً (categorical) أو متغيراً عدديّاً (numerical).

1.4.1 المتغير الفئوي (المتغير الوصفي)

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

ggplot(penguins, aes(x = species)) +
  geom_bar()

A bar chart of frequencies of species of penguins: Adelie  (approximately 150), Chinstrap (approximately 90), Gentoo  (approximately 125).

في المخططات الشريطية للمتغيرات الفئوية ذات المستويات غير المرتبة (الاسمية) — مثل فصائل البطاريق species الموضحة أعلاه — غالباً ما يكون من الأفضل إعادة ترتيب الأشرطة بناءً على تكراراتها (frequencies). ويتطلب القيام بذلك تحويل المتغير إلى عامل (factor) — وهو الأسلوب الذي تتعامل به بيئة R مع البيانات الفئوية — ومن ثمَّ إعادة ترتيب مستويات هذا العامل (levels of that factor).

ggplot(penguins, aes(x = fct_infreq(species))) +
  geom_bar()

A bar chart of frequencies of species of penguins, where the bars are  ordered in decreasing order of their heights (frequencies): Adelie  (approximately 150), Gentoo (approximately 125), Chinstrap  (approximately 90).

ستتعلم المزيد عن العوامل (factors) والدوال البرمجية المخصصة للتعامل معها — مثل الدالة fct_infreq() الموضحة أعلاه — في الفصل الفصل 16.

1.4.2 المتغير العددي (المتغير الكمي)

يكون المتغير عدديّاً (أو كميّاً) إذا كان بإمكانه اتخاذ نطاق واسع من القيم العددية، وكان من المنطقي إجراء عمليات الجمع، أو الطرح، أو حساب المتوسطات الحسابية باستخدام تلك القيم. ويمكن أن تكون المتغيرات العددية متصلة (continuous) أو منفصلة (discrete).

ويُعد المدرج التكراري (histogram) أحد الأساليب البصرية الأكثر شيوعاً واستخداماً لتمثيل توزيعات المتغيرات المتصلة.

ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 200)

A histogram of body masses of penguins. The distribution is unimodal  and right skewed, ranging between approximately 2500 to 6500 grams.

يقوم المدرج التكراري (histogram) بتقسيم المحور السيني إلى فئات أو فترات متساوية المساحة (bins)، ثم يستخدم ارتفاع الشريط لعرض عدد المشاهدات (التكرارات) التي تقع ضمن كل فئة. ويظهر الشريط الأكثر ارتفاعاً في الرسم البياني أعلاه أن هناك 39 مشاهدة تتراوح قيمة كتلة جسمها body_mass_g بين 3500 و 3700 غرام، وهما الحافتان اليسرى واليمنى للشريط.

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

ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 20)
ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 2000)

Two histograms of body masses of penguins, one with binwidth of 20  (left) and one with binwidth of 2000 (right). The histogram with binwidth  of 20 shows lots of ups and downs in the heights of the bins, creating a  jagged outline. The histogram  with binwidth of 2000 shows only three bins.

Two histograms of body masses of penguins, one with binwidth of 20  (left) and one with binwidth of 2000 (right). The histogram with binwidth  of 20 shows lots of ups and downs in the heights of the bins, creating a  jagged outline. The histogram  with binwidth of 2000 shows only three bins.

ويُعد منحنى الكثافة (density plot) أسلوبًا بصريًا بديلاً لتمثيل توزيعات المتغيرات العددية. ويمثل منحنى الكثافة نسخة ممهدة ومصقولة (smoothed-out) من المدرج التكراري، ويشكل بديلاً عمليًا له، لا سيما بالنسبة للبيانات المتصلة المستمدة من توزيع ممهد أصيل. ولن نخوض هنا في تفاصيل كيفية قيام الدالة geom_density() بتقدير الكثافة (إذ يمكنك قراءة المزيد حول ذلك في وثائق الدالة نفسها)، ولكن دعنا نوضح كيفية رسم منحنى الكثافة من خلال تشبيه تقريبي. تخيل مدرجاً تكرارياً مصنوعاً من مكعبات خشبية، ثم تخيل أنك أسقطت فوقه خيطاً من المعكرونة (السباغيتي) المطبوخة؛ إن الشكل الذي سيتخذه خيط المعكرونة وهو يتدلى وينساب فوق تلك المكعبات يمكن اعتباره بمثابة شكل منحنى الكثافة. وعلى الرغم من أن هذا المنحنى يظهر تفاصيل أقل مقارنة بالمدرج التكراري، إلا أنه يسهل عملية الاستقراء السريع لشكل التوزيع، وخاصة فيما يتعلق بالقمم المنوالية (modes) والالتواء (skewness).

ggplot(penguins, aes(x = body_mass_g)) +
  geom_density()
#> Warning: Removed 2 rows containing non-finite outside the scale range
#> (`stat_density()`).

A density plot of body masses of penguins. The distribution is unimodal  and right skewed, ranging between approximately 2500 to 6500 grams.

1.4.3 تمارين

  1. أنشئ مخططاً شريطياً (bar plot) لمتغير فصائل البطاريق species في إطار البيانات penguins، بحيث تقوم بتعيين المتغير species للخاصية الجمالية للمحور الصادي (y). كيف يختلف هذا المخطط البياني عما قبله؟

  2. ما وجه الاختلاف بين المخططين البيانيين التاليين؟ وأي الخصائص الجمالية، اللون الخارجي color أم لون التعبئة الداخلّي fill، تُعد أكثر فائدة وجدوى لتغيير ألوان الأشرطة؟

ggplot(penguins, aes(x = species)) +
  geom_bar(color = "red")

ggplot(penguins, aes(x = species)) +
  geom_bar(fill = "red")
  1. ماذا يفعل معامل عدد الفئات bins في الدالة geom_histogram()؟

  2. أنشئ مدرجاً تكرارياً لمتغير الوزن بالقيراط carat في مجموعة بيانات الألماس diamonds (والتي تتاح تلقائياً عند تحميل حزمة tidyverse). قم بتجريب قيم مختلفة لعرض الفئة (binwidths)؛ فما هي قيمة عرض الفئة التي تكشف عن الأنماط الأكثر إثارة للاهتمام في البيانات؟

1.5 التمثيل البصري للعلاقات

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

1.5.1 متغير عددي وآخَر فئوي

لتمثيل العلاقة بين متغير عددي ومتغير فئوي بصريًا، يمكننا استخدام مخططات الصندوق والطرفين المتجاورة (side-by-side box plots). ويُعد مخطط الصندوق (boxplot) بمثابة اختزال بصري لمقاييس النزعة المركزية والموقع (المئينات) التي تصف التوزيع التكراري، كما أنه مفيد جدًا في تحديد القيم المتطرفة (outliers) المحتملة. وكما هو موضح في الشكل 1.1، يتكون كل مخطط صندوقي مما يلي:

  • صندوق يشير إلى مدى النصف الأوسط من البيانات، وهي المسافة الإحصائية المعروفة باسم المدى الإرباعي (interquartile range - IQR)، والتي تمتد من الإرباعي الأول (المئين 25) للتوزيع إلى الإرباعي الثالث (المئين 75). ويوجد في منتصف هذا الصندوق خط يمثل الوسيط (أي المئين 50) للتوزيع. وتمنحك هذه الخطوط الثلاثة تصوراً واضحاً عن مدى تشتت التوزيع، وما إذا كان متماثلاً حول الوسيط أم ملتويّاً نحو أحد الجانبين.

  • نقاط بصرية تعرض المشاهدات التي تبعد عن حافتي الصندوق بمسافة تزيد عن 1.5 ضعف المدى الإرباعي (1.5 × IQR). ونظراً لأن هذه النقاط المتطرفة تُعد غير عادية أو شاذة، فإنه يتم تمثيلها بشكل فردي ومستقل.

  • خط (أو طرف/شارب) يمتد من كل طرف من طرفي الصندوق ليصل إلى أبعد نقطة في التوزيع لا تُصنف كقيمة متطرفة.

A diagram depicting how a boxplot is created following the steps outlined  above.
الشكل 1.1: مخطط توضيحي يبين كيفية إنشاء الصندوق الصندوقي (boxplot).

دعونا نلقي نظرة على التوزيع التكراري لكتلة الجسم مصنفاً حسب الفصائل باستخدام دالة geom_boxplot():

ggplot(penguins, aes(x = species, y = body_mass_g)) +
  geom_boxplot()

Side-by-side box plots of distributions of body masses of Adelie,  Chinstrap, and Gentoo penguins. The distribution of Adelie and  Chinstrap penguins' body masses appear to be symmetric with  medians around 3750 grams. The median body mass of Gentoo penguins  is much higher, around 5000 grams, and the distribution of the  body masses of these penguins appears to be somewhat right skewed.

وبدلاً من ذلك، يمكننا إنشاء منحنيات الكثافة باستخدام الدالة geom_density().

ggplot(penguins, aes(x = body_mass_g, color = species)) +
  geom_density(linewidth = 0.75)

A density plot of body masses of penguins by species of penguins. Each  species (Adelie, Chinstrap, and Gentoo) is represented with different  colored outlines for the density curves.

لقد قمنا أيضاً بتخصيص سمك الخطوط باستخدام معامل عرض الخط linewidth لكي تبرز وتتضح بشكل أفضل قليلاً مقارنة بالخلفية.

بالإضافة إلى ذلك، يمكننا ربط متغير الفصائل species بكلتا الخاصيتين الجماليتين: اللون الخارجي color ولون التعبئة الداخلي fill معاً، مع استخدام الخاصية الجمالية الخاصة بدرجة الشفافية alpha لإضفاء نوع من الشفافية على منحنيات الكثافة المعبأة بالكامل. وتأخذ هذه الخاصية الجمالية قيماً تتراوح بين 0 (شفاف تماماً) و 1 (معتم تماماً). وفي المخطط البياني التالي، تم تعيين قيمتها عند 0.5.

ggplot(penguins, aes(x = body_mass_g, color = species, fill = species)) +
  geom_density(alpha = 0.5)

A density plot of body masses of penguins by species of penguins. Each  species (Adelie, Chinstrap, and Gentoo) is represented in different  colored outlines for the density curves. The density curves are also  filled with the same colors, with some transparency added.

لاحظ المصطلحات التي استخدمناها هنا:

  • نحن نقوم بـ ربط/مطابقة (map) المتغيرات بالخصائص الجمالية إذا أردنا أن تختلف السمة البصرية التي تمثلها تلك الخاصية الجمالية بناءً على قيم ذلك المتغير.
  • وفيما عدا ذلك، فإننا نقوم بـ تعيين/تحديد (set) قيمة ثابتة للخاصية الجمالية.

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

يمكننا استخدام المخططات الشريطية المكدسة (stacked bar plots) للتمثيل البصري للعلاقة بين متغيرين فئويين. على سبيل المثال، يعرض المخططان الشريطيتان المكدسان التاليان العلاقة بين متغير الجزيرة island ومتغير الفصيلة species، أو على وجه التحديد، يمثلان توزيع الفصائل species داخل كل جزيرة.

ويوضح المخطط البياني الأول التكرارات (frequencies) الخاصة بكل فصيلة من فصائل البطاريق في كل جزيرة. ويُظهر مخطط التكرارات هذا أن هناك أعداداً متساوية من فصيلة “أديلي” (Adelies) في كل جزيرة، ولكننا لا نحصل منه على تصور دقيق ونظرة واضحة حول التوازن النسبي (النسب المئوية) للفصائل داخل كل جزيرة على حدة.

ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar()

Bar plots of penguin species by island (Biscoe, Dream, and Torgersen)

ويُعد المخطط البياني الثاني — وهو مخطط التكرار النسبي (relative frequency plot) الذي تم إنشاؤه عبر تعيين المعامل position = "fill" داخل العنصر الهندسي (geom) — أكثر فائدة وجدوى لمقارنة توزيعات الفصائل عبر الجزر المختلفة؛ وذلك لأنه لا يتأثر بعدم تساوِي أعداد البطاريق الكلية بين تلك الجزر. وباستخدام هذا المخطط، يمكننا أن نلاحظ بوضوح أن بطاريق الجنتو (Gentoo) تعيش جميعها في جزيرة بيسكو (Biscoe) وتشكل ما يقرب من 75% من إجمالي البطاريق في تلك الجزيرة، بينما تعيش بطاريق الشينستراب (Chinstrap) بأكملها في جزيرة دريم (Dream) وتشكل حوالي 50% من بطاريقها، في حين تنتشر بطاريق الأديلي (Adelie) عبر الجزر الثلاث وتشكل 100% من مجتمع البطاريق في جزيرة تورجيرسن (Torgersen).

ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(position = "fill")

Bar plots of penguin species by island (Biscoe, Dream, and Torgersen) the bars are scaled to the same height, making it a relative frequencies  plot. The y-axis is labeled "count" by default.

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

ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(position = "fill") +
  labs(y = "النسبة")

Bar plots of penguin species by island (Biscoe, Dream, and Torgersen) the bars are scaled to the same height, making it a relative frequencies  plot. The y-axis is labeled "proportion".

1.5.3 متغيران عدديان

لقد تعرفت حتى الآن على مخططات التشتت (scatterplots) — والتي يتم إنشاؤها باستخدام الدالة geom_point() — والمنحنيات الممهدة (smooth curves) — والتي يتم إنشاؤها باستخدام الدالة geom_smooth() — لتمثيل العلاقة بين متغيرين عدديين بصريًا. ويُعد مخطط التشتت على الأرجح الأسلوب البصري الأكثر شيوعًا واستخدامًا لاستكشاف وتوضيح طبيعة العلاقة بين متغيرين كميين (عدديين).

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point()

A scatterplot of body mass vs. flipper length of penguins. The plot  displays a positive, linear, relatively strong relationship between  these two variables.

1.5.4 ثلاثة متغيرات أو أكثر

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

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(aes(color = species, shape = island))

A scatterplot of body mass vs. flipper length of penguins. The plot  displays a positive, linear, relatively strong relationship between  these two variables. The points are colored based on the species of the  penguins and the shapes of the points represent islands (round points are  Biscoe island, triangles are Dream island, and squared are Torgersen  island). The plot is very busy and it's difficult to distinguish the shapes of the points.

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

ولتقسيم المخطط البياني إلى نوافذ بناءً على متغير واحد، يمكنك استخدام الدالة facet_wrap(). والمعامل الأول (first argument) لهذه الدالة عبارة عن صيغة (formula)3، والتي يتم إنشاؤها باستخدام الرمز ~ متبوعاً باسم المتغير. وينبغي أن يكون المتغير الذي يتم تمريره إلى الدالة facet_wrap() متغيراً فئويّاً.

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(aes(color = species, shape = species)) +
  facet_wrap(~island)

A scatterplot of body mass vs. flipper length of penguins. The shapes and  colors of points represent species. Penguins from each island are on a  separate facet. Within each facet, the relationship between body mass and  flipper length is positive, linear, relatively strong.

ستتعرف على العديد من العناصر الهندسية (geoms) الأخرى لتمثيل توزيعات المتغيرات والعلاقات بينها بصريًا في الفصل 9.

1.5.5 تمارين

  1. يحتوي إطار البيانات mpg المُضمّن في حزمة ggplot2 على 234 مشاهدة جمعتها وكالة حماية البيئة الأمريكية لـ 38 طرازاً من طرازات السيارات. أي المتغيرات في mpg تُعد فئوية؟ وأيها تُعد عددية؟ (إرشاد: اكتب ?mpg في وحدة التحكم لقراءة وثائق مجموعة البيانات). وكيف يمكنك استعراض هذه المعلومات عند تشغيل واستدعاء mpg؟

  2. أنشئ مخططاً للتشتت (scatterplot) لمتغير كفاءة استهلاك الوقود على الطرق السريعة hwy مقابل سعة المحرك displ باستخدام إطار البيانات mpg. بعد ذلك، قم بربط متغير عددي ثالث بالخاصية الجمالية للون color، ثم الحجم size، ثم بكل من اللون والحجم معاً، ثم بالشكل shape. كيف تختلف سلوكيات هذه الخصائص الجمالية عند التعامل مع المتغيرات الفئوية مقارنة بالمتغيرات العددية؟

  3. في مخطط التشتت لـ hwy مقابل displ، ماذا يحدث إذا قمت بربط متغير ثالث بخاصية عرض الخط linewidth؟

  4. ماذا يحدث إذا قمت بربط المتغير نفسه بأكثر من خاصية جمالية في المخطط البياني الواحد؟

  5. أنشئ مخططاً للتشتت لمتغير عمق المنقار bill_depth_mm مقابل طول المنقار bill_length_mm، وقم بتلوين النقاط بناءً على متغير الفصيلة species. ماذا يكشف إدخال التلوين حسب الفصيلة عن طبيعة العلاقة بين هذين المتغيرين؟ وماذا عن تقسيم المخطط إلى نوافذ فرعية (faceting) بناءً على الفصيلة species؟

  6. لماذا ينتج عن الكود التالي مفتاحان منفصلان للرسم (two separate legends)؟ وكيف يمكنك معالجة ذلك لدمج المفتاحين معاً في مفتاح موحد؟

ggplot(
  data = penguins,
  mapping = aes(
    x = bill_length_mm, y = bill_depth_mm, 
    color = species, shape = species
  )
) +
  geom_point() +
  labs(color = "النوع")
  1. أنشئ المخططين الشريطين المكدسين التاليين (two stacked bar plots). ما هو السؤال الإحصائي الذي يمكنك الإجابة عنه باستخدام المخطط الأول؟ وما هو السؤال الذي يمكنك الإجابة عنه باستخدام المخطط الثاني؟
ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(position = "fill")
ggplot(penguins, aes(x = species, fill = island)) +
  geom_bar(position = "fill")

1.6 حفظ المخططات البيانية

بمجرد انتهائك من إنشاء المخطط البياني، قد ترغب في إخراجه من بيئة R عبر حفظه كملف صورة لتتمكن من استخدامه في مكان آخر. وتلك هي المهمة المنوطة بالدالة ggsave()، والتي تقوم بحفظ آخر مخطط بياني تم إنشاؤه مباشرة على القرص الصلب:

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point()
ggsave(filename = "penguin-plot.png")

سيؤدي هذا إلى حفظ المخطط البياني في دليل العمل الخاص بك (working directory)، وهو مفهوم ستتعرف عليه بمزيد من التفصيل في القسم الفصل 6.

وإذا لم تقم بتحديد أبعاد العرض width والارتفاع height، فسيتم أخذها تلقائياً من أبعاد شاشة العرض الحالية للرسم (plotting device). ولضمان قابلية إعادة إنتاج الكود (reproducible code)، يُفضّل دائماً تحديد هذه الأبعاد برمجياً. ويمكنك الاطلاع على مزيد من التفاصيل حول الدالة ggsave() عبر قراءة الوثائق الخاصة بها (documentation).

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

1.6.1 تمارين

  1. قم بتشغيل أسطر الكود التالية. أي من المخططين البيانيين تم حفظه باسم mpg-plot.png؟ ولماذا؟
ggplot(mpg, aes(x = class)) +
  geom_bar()
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()
ggsave("mpg-plot.png")
  1. ما الذي تحتاج إلى تغييره في الكود البرمجي السابق لحفظ المخطط البياني بصيغة ملف PDF بدلاً من PNG؟ وكيف يمكنك معرفة أنواع ملفات الصور المدعومة والتي يمكن استخدامها مع الدالة ggsave()؟

1.7 المشكلات الشائعة

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

ابدأ دائماً بمقارنة الكود الذي تقوم بتشغيله بدقة شديدة مع الكود المعروض في الكتاب. لغة R دقيقة وحساسة للغاية (extremely picky)، ووجود رمز واحد في غير موضعه يمكن أن يحدث فارقاً كبيراً. تأكد من أن كل قوس فتح ( يقابله قوس إغلاق )، وأن كل علامة تنصيص " مزدوجة مقترنة بعلامة تنصيص أخرى. وفي بعض الأحيان، قد تقوم بتشغيل الكود دون أن يحدث أي شيء؛ في هذه الحالة، تحقق من الجانب الأيسر لوحدة التحكم (console): إذا وجدت رمز الزائد +، فهذا يعني أن بيئة R ترى أنك لم تكتب تعبيراً برمجياً مكتملاً، وهي بانتظارك لإنهائه. وفي مثل هذه الحالة، يكون من السهل عادةً البدء من جديد عبر الضغط على زر ESCAPE لإلغاء معالجة الأمر الحالي.

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

ggplot(data = mpg) 
+ geom_point(mapping = aes(x = displ, y = hwy))

إذا ما زلت عالقاً، فجرّب استخدام نظام المساعدة (help). يمكنك الحصول على الدعم والمعلومات بشأن أي دالة في بيئة R عن طريق تشغيل الأمر ?function_name في وحدة التحكم (console)، أو تظليل اسم الدالة والضغط على مفتاح F1 في بيئة RStudio. ولا تقلق إذا لم تبدُ لك صفحات المساعدة مفيدة بشكل واضح في البداية؛ بل تجاوزها وانتقل مباشرة إلى الأسفل لاستعراض الأمثلة التطبيقية (examples) وابحث عن كود برمجي يشابه ويطابق ما تحاول القيام به.

وإذا لم يساعدك ذلك، فاقرأ رسالة الخطأ (error message) بعناية شديدة؛ ففي بعض الأحيان تكون الإجابة كامنة ومخفية في تفاصيلها! ولكن عندما تكون مبتدئاً في التعامل مع بيئة R، فحتى لو كانت الإجابة متضمنة داخل رسالة الخطأ، فقد لا تمتلك بعد المهارة الكافية لتفسيرها وفهم مغزاها الإحصائي أو البرمجي. وتظل الأداة البديلة الرائعة الأخرى المتاحة لك هي محرك البحث Google: جرّب البحث عن نص رسالة الخطأ عبر الإنترنت، فمن المرجح جداً أن يكون مستخدم آخر قد واجه المشكلة البرمجية نفسها وحصل على الدعم والحل رقمياً.

1.8 ملخص

في هذا الفصل، تعلمت أساسيات التمثيل البصري للبيانات باستخدام حزمة ggplot2. لقد بدأنا بالفكرة المحورية التي ترتكز عليها هذه الحزمة كلياً: وهي أن التمثيل البصري ما هو إلا عملية مطابقة/ربط (mapping) للمتغيرات المستخرجة من بياناتك بالخصائص البصرية والجمالية (aesthetic properties) للمخطط؛ مثل الموضع، واللون، والحجم، والشكل. ثم تعرفت بعد ذلك على كيفية التدرج في بناء المخطط البياني وتطوير درجة تعقيده وتحسين طريقة عرضه وإخراجه طبقة تلو الأخرى (layer-by-layer). وتطرقنا كذلك إلى المخططات البيانية الأكثر شيوعاً واستخداماً لتمثيل التوزيع التكراري لمتغير فردي واحد، فضلاً عن تمثيل واستكشاف طبيعة العلاقات بين متغيرين أو أكثر بصريًا، وذلك عبر توظيف مطابقات الخصائص الجمالية الإضافية و/أو تقسيم المخطط الكلي إلى مصغرات متعددة مكررة (small multiples) باستخدام النوافذ الفرعية (faceting).

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

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


  1. يمكنك إخفاء هذه الرسالة وإجبار النظام على حل التعارضات عند الطلب باستخدام حزمة conflicted، والتي تزداد أهميتها كلما قمت بتحميل المزيد من الحزم. يمكنك معرفة المزيد عن حزمة conflicted عبر الرابط https://conflicted.r-lib.org.↩︎

  2. Horst AM, Hill AP, Gorman KB (2020). palmerpenguins: Palmer Archipelago (Antarctica) penguin data. R package version 0.1.0. https://allisonhorst.github.io/palmerpenguins/. doi: 10.5281/zenodo.3960218.↩︎

  3. المقصود بكلمة “صيغة” (formula) هنا هو الاسم البرمجي للبنية الناتجة عن استخدام الرمز ~ في بيئة R، وليست مرادفاً لكلمة “معادلة رياضية” (equation).↩︎