16  العوامل

16.1 مقدمة

تُستخدم العوامل (Factors) للمتغيرات الفئوية (categorical variables)، وهي المتغيرات التي تحتوي على مجموعة محددة ومعروفة من القيم المحتملة. كما أنها مفيدة جداً عندما ترغب في عرض المتجهات النصية بترتيب غير أبجدي.

سنبدأ بتوضيح سبب أهمية العوامل في تحليل البيانات1 وكيفية إنشائها باستخدام الدالة factor(). ثم سنقدم لك مجموعة بيانات gss_cat التي تحتوي على العديد من المتغيرات الفئوية للتجريب عليها. ستستخدم بعد ذلك مجموعة البيانات تلك للممارسة على تعديل ترتيب وقيم العوامل، قبل أن نختم بمناقشة حول العوامل المرتبة (ordered factors).

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

توفر R الأساسية (Base R) بعض الأدوات الأساسية لإنشاء العوامل والتحكم فيها. سنكمل هذه الأدوات باستخدام حزمة forcats، والتي تُعد جزءاً من نواة tidyverse. توفر هذه الحزمة أدوات للتعامل مع المتغيرات الفئوية (وهي كلمة مرادفة لـ categorical، كما أن اسمها جناس تصحيفي لـ factors!) باستخدام مجموعة واسعة من الدوال المساعدة.

16.2 أساسيات العوامل

تخيل أن لديك متغيراً يسجل الأشهر:

x1 <- c("Dec", "Apr", "Jan", "Mar")

استخدام سلسلة نصية لتسجيل هذا المتغير يواجه مشكلتين:

  1. هناك اثنا عشر شهراً محتملاً فقط، ولا يوجد ما يحميك من الأخطاء الإملائية:
x2 <- c("Dec", "Apr", "Jam", "Mar")
  1. لا يتم فرزها وترتيبها بطريقة مفيدة إحصائياً:
sort(x1)
#> [1] "Apr" "Dec" "Jan" "Mar"

يمكنك حل كلتا المشكلتين باستخدام عامل (factor). لإنشاء عامل، يجب أن تبدأ بإنشاء قائمة بالمستويات المعتمدة والمالوفة (levels):

month_levels <- c(
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)

الآن يمكنك إنشاء العامل:

y1 <- factor(x1, levels = month_levels)
y1
#> [1] Dec Apr Jan Mar
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

sort(y1)
#> [1] Jan Mar Apr Dec
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

وأي قيم غير موجودة في المستويات المحددة سيتم تحويلها تلقائياً وبصمت إلى NA:

y2 <- factor(x2, levels = month_levels)
y2
#> [1] Dec  Apr  <NA> Mar 
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

يبدو هذا محفوفاً بالمخاطر، لذا قد ترغب في استخدام forcats::fct() بدلاً من ذلك:

y2 <- fct(x2, levels = month_levels)
#> Error in `fct()`:
#> ! All values of `x` must appear in `levels` or `na`
#> ℹ Missing level: "Jam"

إذا قمت بحذف المستويات، فسيتم أخذها من البيانات بترتيب أبجدي:

factor(x1)
#> [1] Dec Apr Jan Mar
#> Levels: Apr Dec Jan Mar

الفرز الأبجدي فيه نوع من المخاطرة لأن ليس كل جهاز كمبيوتر يرتب النصوص بنفس الطريقة. لذلك تقوم forcats::fct() بالترتيب حسب أول ظهور للقيم في البيانات:

fct(x1)
#> [1] Dec Apr Jan Mar
#> Levels: Dec Apr Jan Mar

إذا احتجت في أي وقت إلى الوصول إلى مجموعة المستويات الصالحة مباشرة، يمكنك القيام بذلك باستخدام levels():

levels(y2)
#>  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"

يمكنك أيضاً إنشاء عامل عند قراءة بياناتك باستخدام حزمة readr عبر col_factor():

csv <- "
month,value
Jan,12
Feb,56
Mar,12"

df <- read_csv(csv, col_types = cols(month = col_factor(month_levels)))
#> Warning: The `file` argument of `read_csv()` should use `I()` for literal data as of
#> readr 2.2.0.
#>   
#>   # Bad (for example):
#>   read_csv("x,y\n1,2")
#>   
#>   # Good:
#>   read_csv(I("x,y\n1,2"))
df$month
#> [1] Jan Feb Mar
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

16.3 المسح الاجتماعي العام (General Social Survey)

في بقية هذا الفصل، سنستخدم مجموعة البيانات forcats::gss_cat. وهي عينة بيانات من المسح الاجتماعي العام، وهو مسح أمريكي طويل الأمد يجريه مركز البحث المستقل NORC في جامعة شيكاغو. يحتوي المسح على آلاف الأسئلة، لذلك اختار هادلي في gss_cat حفنة منها لتوضيح بعض التحديات الشائعة التي ستواجهها عند العمل مع العوامل.

gss_cat
#> # A tibble: 21,483 × 9
#>    year marital         age race  rincome        partyid           
#>   <int> <fct>         <int> <fct> <fct>          <fct>             
#> 1  2000 Never married    26 White $8000 to 9999  Ind,near rep      
#> 2  2000 Divorced         48 White $8000 to 9999  Not str republican
#> 3  2000 Widowed          67 White Not applicable Independent       
#> 4  2000 Never married    39 White Not applicable Ind,near rep      
#> 5  2000 Divorced         25 White Not applicable Not str democrat  
#> 6  2000 Married          25 White $20000 - 24999 Strong democrat   
#> # ℹ 21,477 more rows
#> # ℹ 3 more variables: relig <fct>, denom <fct>, tvhours <int>

(تذكر، بما أن مجموعة البيانات هذه توفرها الحزمة، يمكنك الحصول على مزيد من المعلومات حول المتغيرات باستخدام ?gss_cat.)

عندما يتم تخزين العوامل في جدول بيانات من نوع tibble، لا يمكنك رؤية مستوياتها بسهولة. إحدى الطرق لعرضها هي استخدام count():

gss_cat |>
  count(race)
#> # A tibble: 3 × 2
#>   race      n
#>   <fct> <int>
#> 1 Other  1959
#> 2 Black  3129
#> 3 White 16395

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

16.3.1 تمارين

  1. استكشف توزيع متغير rincome (الدخل المعلن عنه). ما الذي يجعل المخطط الشريطِي الافتراضي صعب الفهم؟ كيف يمكنك تحسين المخطط البياني؟

  2. ما هي الديانة الأكثر شيوعاً relig في هذا المسح؟ ما هو الانتماء الحزبي الأكثر شيوعاً partyid؟

  3. ما هي الديانة relig التي ينطبق عليها الطائفة/المذهب denom؟ كيف يمكنك معرفة ذلك باستخدام جدول؟ كيف يمكنك معرفة ذلك باستخدام التمثيل البياني؟

16.4 تعديل ترتيب العوامل

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

relig_summary <- gss_cat |>
  group_by(relig) |>
  summarize(
    tvhours = mean(tvhours, na.rm = TRUE),
    n = n()
  )

ggplot(relig_summary, aes(x = tvhours, y = relig)) +
  geom_point()

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

من الصعب قراءة هذا المخطط لأنه لا يوجد نمط عام واضح. يمكننا تحسينه بإعادة ترتيب مستويات relig باستخدام fct_reorder(). تأخذ fct_reorder() ثلاثة وسائط:

  • .f: العامل الذي تريد تعديل مستوياته.
  • .x: متجه عددي تريد استخدامه لإعادة ترتيب المستويات.
  • اختيارياً .fun: دالة تُستخدم إذا كانت هناك قيم متعددة لـ .x لكل قيمة من .f. القيمة الافتراضية هي median (الوسيط).
ggplot(relig_summary, aes(x = tvhours, y = fct_reorder(relig, tvhours))) +
  geom_point()

نفس مخطط الانتشار أعلاه، ولكن الآن يتم عرض الديانة بترتيب تصاعدي لساعات التلفزيون. تأتي "شرقية أخرى" في أدنى ساعات التلفزيون (أقل من 2)، وتأتي "لا أعرف" في الأعلى (أكثر من 5).

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

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

relig_summary |>
  mutate(
    relig = fct_reorder(relig, tvhours)
  ) |>
  ggplot(aes(x = tvhours, y = relig)) +
  geom_point()

ماذا لو أنشأنا مخططاً مشابهاً يوضح كيف يتغير متوسط العمر عبر مستويات الدخل المعلن عنها؟

rincome_summary <- gss_cat |>
  group_by(rincome) |>
  summarize(
    age = mean(age, na.rm = TRUE),
    n = n()
  )

ggplot(rincome_summary, aes(x = age, y = fct_reorder(rincome, age))) +
  geom_point()

مخطط انتشار يظهر العمر على المحور السيني والدخل على المحور الصادي. تمت إعادة ترتيب الدخل حسب متوسط العمر، وهو ما لا يبدو منطقياً. أحد أجزاء المحور الصادي ينتقل من 6000-6999$، ثم <1000$، ثم 8000-9999$.

هنا، إعادة ترتيب المستويات بشكل عشوائي ليست فكرة صائبة! ذلك لأن rincome لديه بالفعل ترتيب منطقي ومبدئي لا ينبغي لنا الإخلال به. احفظ دالة fct_reorder() للعوامل التي تكون مستوياتها مرتبة بشكل عشوائي.

ومع ذلك، من المنطقي نقل فئة “غير ذي صلة” (Not applicable) إلى المقدمة مع المستويات الخاصة الأخرى. يمكنك استخدام fct_relevel(). تأخذ هذه الدالة عاملاً .f، ثم أي عدد من المستويات التي تريد نقلها إلى بداية الصف.

ggplot(rincome_summary, aes(x = age, y = fct_relevel(rincome, "Not applicable"))) +
  geom_point()

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

لماذا تعتقد أن متوسط العمر لفئة “غير ذي صلة” (Not applicable) مرتفع للغاية؟

نوع آخر من إعادة الترتيب يكون مفيداً عندما تقوم بتلوين الخطوط في المخطط البياني. تقوم fct_reorder2(.f, .x, .y) بإعادة ترتيب العامل .f بناءً على قيم .y المرتبطة بأكبر قيم لـ .x. هذا يجعل المخطط أسهل في القراءة لأن ألوان الخطوط في أقصى يمين الرسم البياني ستتطابق مع مفتاح الرسم (legend).

by_age <- gss_cat |>
  filter(!is.na(age)) |>
  count(age, marital) |>
  group_by(age) |>
  mutate(
    prop = n / sum(n)
  )

ggplot(by_age, aes(x = age, y = prop, color = marital)) +
  geom_line(linewidth = 1) +
  scale_color_brewer(palette = "Set1")

ggplot(by_age, aes(x = age, y = prop, color = fct_reorder2(marital, age, prop))) +
  geom_line(linewidth = 1) +
  scale_color_brewer(palette = "Set1") +
  labs(color = "marital")

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

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

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

gss_cat |>
  mutate(marital = marital |> fct_infreq() |> fct_rev()) |>
  ggplot(aes(x = marital)) +
  geom_bar()

مخطط شريطي للحالة الاجتماعية مرتب من الأقل إلى الأكثر شيوعاً: لا إجابة (~0)، منفصل (~1,000)، أرمل (~2,000)، مطلق (~3,000)، لم يتزوج قط (~5,000)، متزوج (~10,000).

16.4.1 تمارين

  1. هناك بعض الأرقام المرتفعة بشكل مريب في tvhours. هل المتوسط الحسابي يلخص البيانات بشكل جيد هنا؟

  2. لكل عامل في gss_cat حدد ما إذا كان ترتيب المستويات عشوائياً أم مبنياً على ترتيب منطقي/مبدئي.

  3. لماذا أدى نقل فئة “Not applicable” إلى مقدمة المستويات إلى نقلها إلى أسفل المخطط البياني؟

16.5 تعديل قيم المستويات

الأكثر قوة من تغيير ترتيب المستويات هو تغيير قيمها ذاتها. يتيح لك ذلك توضيح المسميات والنصوص لأغراض النشر، وتجميع المستويات للعروض التقديمية عالية المستوى. الأداة الأكثر شمولاً وقوة هي fct_recode(). تتيح لك إعادة ترميز أو تغيير قيمة كل مستوى. على سبيل المثال، خذ متغير partyid من جدول بيانات gss_cat:

gss_cat |> count(partyid)
#> # A tibble: 10 × 2
#>   partyid                n
#>   <fct>              <int>
#> 1 No answer            154
#> 2 Don't know             1
#> 3 Other party          393
#> 4 Strong republican   2314
#> 5 Not str republican  3032
#> 6 Ind,near rep        1791
#> # ℹ 4 more rows

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

gss_cat |>
  mutate(
    partyid = fct_recode(partyid,
      "Republican, strong"     = "Strong republican",
      "Republican, weak"       = "Not str republican",
      "Independent, near rep" = "Ind,near rep",
      "Independent, near dem" = "Ind,near dem",
      "Democrat, weak"        = "Not str democrat",
      "Democrat, strong"      = "Strong democrat"
    )
  ) |>
  count(partyid)
#> # A tibble: 10 × 2
#>   partyid                   n
#>   <fct>                 <int>
#> 1 No answer               154
#> 2 Don't know                1
#> 3 Other party             393
#> 4 Republican, strong     2314
#> 5 Republican, weak       3032
#> 6 Independent, near rep  1791
#> # ℹ 4 more rows

ستترك fct_recode() المستويات التي لم تذكرها صراحة كما هي، وستحذرك إذا أشرت بطريق الخطأ إلى مستوى غير موجود.

لتدمج المجموعات، يمكنك إسناد مستويات قديمة متعددة إلى نفس القيمة الجديدة:

gss_cat |>
  mutate(
    partyid = fct_recode(partyid,
      "Republican, strong"     = "Strong republican",
      "Republican, weak"       = "Not str republican",
      "Independent, near rep" = "Ind,near rep",
      "Independent, near dem" = "Ind,near dem",
      "Democrat, weak"        = "Not str democrat",
      "Democrat, strong"      = "Strong democrat",
      "Other"                  = "No answer",
      "Other"                  = "Don't know",
      "Other"                  = "Other party"
    )
  )

استخدم هذه التقنية بحذر: إذا قمت بتجميع فئات مختلفة تماماً معاً، فستنتهي بنتائج مضللة.

إذا كنت تريد دمج وتقليص الكثير من المستويات، فإن fct_collapse() تُعدّ خياراً بديلًا ومفيداً للدالة fct_recode(). لكل متغير جديد، يمكنك تقديم متجه من المستويات القديمة:

gss_cat |>
  mutate(
    partyid = fct_collapse(partyid,
      "other" = c("No answer", "Don't know", "Other party"),
      "rep"   = c("Strong republican", "Not str republican"),
      "ind"   = c("Ind,near rep", "Independent", "Ind,near dem"),
      "dem"   = c("Not str democrat", "Strong democrat")
    )
  ) |>
  count(partyid)
#> # A tibble: 4 × 2
#>   partyid     n
#>   <fct>   <int>
#> 1 other     548
#> 2 rep      5346
#> 3 ind      8409
#> 4 dem      7180

في بعض الأحيان ترغب فقط في تجميع المجموعات الصغيرة معاً لجعل الرسم البياني أو الجدول أسهل وأبسط. هذه هي مهمة عائلة دوال fct_lump_*(). تُعد fct_lump_lowfreq() نقطة بداية بسيطة تقوم بتجميع الفئات ذات التكرار الأقل تدريجياً في فئة “Other”، مع الحفاظ دائماً على “Other” كأصغر فئة.

gss_cat |>
  mutate(relig = fct_lump_lowfreq(relig)) |>
  count(relig)
#> # A tibble: 2 × 2
#>   relig          n
#>   <fct>      <int>
#> 1 Protestant 10846
#> 2 Other      10637

في هذه الحالة ليس الأمر مفيداً جداً: صحيح أن غالبية الأمريكيين في هذا المسح من البروتستانت، لكننا نود على الأرجح رؤية المزيد من التفاصيل! بدلاً من ذلك، يمكننا استخدام fct_lump_n() لتحديد أننا نريد بالضبط 10 مجموعات:

gss_cat |>
  mutate(relig = fct_lump_n(relig, n = 10)) |>
  count(relig, sort = TRUE)
#> # A tibble: 10 × 2
#>   relig          n
#>   <fct>      <int>
#> 1 Protestant 10846
#> 2 Catholic    5124
#> 3 None        3523
#> 4 Christian    689
#> 5 Other        458
#> 6 Jewish       388
#> # ℹ 4 more rows

اقرأ وثائق الحزمة للتعرف على fct_lump_min() و fct_lump_prop() المفيدتين في حالات أخرى.

16.5.1 تمارين

  1. كيف تغيرت نسب الأشخاص الذين يعرّفون أنفسهم كديمقراطيين، وجمهوريين، ومستقلين عبر الزمن؟

  2. كيف يمكنك دمج rincome في مجموعة صغيرة من الفئات؟

  3. لاحظ وجود 9 مجموعات (باستثناء Other) في مثال fct_lump أعلاه. لماذا ليست 10؟ (تلميح: اكتب ?fct_lump وستجد أن القيمة الافتراضية للوسيط other_level هي “Other”.)

16.6 العوامل المرتبة

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

يمكنك التعرف على العامل المرتب عند طباعته لأنه يستخدم رمز < بين مستويات العامل:

ordered(c("a", "b", "c"))
#> [1] a b c
#> Levels: a < b < c

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

  • إذا قمت بربط عامل مرتب باللون (color) أو التعبئة (fill) في ggplot2، فسيتم استخدام السلم اللونِي scale_color_viridis()/scale_fill_viridis() كخيار افتراضي، وهو سلم ألوان يرمز إلى الرتب والتدرج.
  • إذا استخدمت متغيراً تنبؤياً مرتباً في نموذج خطي (linear model)، فسوف يستخدم “المقارنات متعددة الحدود” (polynomial contrasts). هذه مفيدة نوعاً ما، لكن من غير المرجح أن تكون قد سمعت عنها ما لم تكن حاصلاً على الدكتوراه في الإحصاء، وحتى ذلك الحين ربما لا تقوم بتفسيرها بشكل روتيني. إذا كنت تريد معرفة المزيد، نوصي بقراءة vignette("contrasts", package = "faux") بواسطة ليزا ديبروين (Lisa DeBruine).

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

16.7 ملخص

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

إذا كنت تريد معرفة المزيد عن العوامل بعد قراءة هذا الفصل، نوصي بقراءة ورقة أميليا مكنامارا ونيكولاس هورتون، Wrangling categorical data in R. تستعرض هذه الورقة بعض التاريخ المناقش في stringsAsFactors: An unauthorized biography و stringsAsFactors = <sigh>، وتقارن نهج tidyverse للبيانات الفئوية الموضح في هذا الكتاب مع طرق R الأساسية. ساعدت نسخة مبكرة من الورقة في تحفيز وتحديد نطاق حزمة forcats؛ فشكراً لأميليا ونيك!

في الفصل التالي، سنغير الاتجاه لنبدأ في التعلم عن التواريخ والأوقات في R. تبدو التواريخ والأوقات بسيطة بشكل مخادع، ولكن كما ستكتشف قريباً، كلما تعلمت المزيد عنها، كلما بدت أكثر تعقيداً!


  1. كما أنها مهمة للغاية في النمذجة الإحصائية (modelling).↩︎