المسارات
/
R
R
/
التمارين
/
الأشجار ومصائب tibble
الأشجار ومصائب tibble

الأشجار ومصائب tibble

تمرين تعلّمي

مقدمة

رأينا في أجزاء أخرى من المنهج أنواع بيانات متنوعة بخصائص مختلفة.

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

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

أنواع إطارات البيانات

على مدى العقود، أضافت R أنواع بيانات متعددة للتعامل مع البيانات الجدولية.

سيركّز هذا المنهج بشكل أساسي على tibbles، لكن من المفيد معرفة البدائل.

data.frame

في R الأساسية، data.frame هو list من vectors متساوية في الطول. يمكن تصوّره كجدول بيانات مستطيل، يكون فيه كل عمود متجانسًا، بينما يمكن لكل صف أن يحتوي (وهذا ما يحدث عادةً) على أنواع مختلفة من البيانات.

مثال يوضح ذلك:

# create the column vectors
languages <- c("Fortran", "R", "Python", "Julia")
created <- c(1957, 1993, 1991, 2012)
has.syllabus <- c(FALSE, TRUE, TRUE, TRUE)

# join columns to create the dataframe
df <- data.frame(languages, created, has.syllabus)
df
#>   languages created has.syllabus
#> 1   Fortran    1957        FALSE
#> 2         R    1993         TRUE
#> 3    Python    1991         TRUE
#> 4     Julia    2012         TRUE

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

tibble

تصميم data.frame قديم.

خبرة امتدّت عقودًا، إلى جانب أنماط متغيّرة في استخدام R، أدّت إلى إعادة تصميم أنتجت بديلًا حديثًا في Tidyverse: tibbles.

مقارنة بـ R الأساسية، تتميّز tibbles بما يلي:

  • قيم افتراضية مختلفة، للحدّ من المشكلات الشائعة.
  • ميل أقل لفرض تحويل أنواع البيانات أثناء الإدخال.
  • رسائل خطأ أكثر وأوضح.
  • صيغ عرض مختلفة، وعادةً أفضل.

باختصار، يهدف tibble إلى «أن يفعل أقل ويشكو أكثر»، ويوصف أيضًا بأنه «كسول وعبوس».

غالبًا ما يكون النوعان قابلين للتبادل: أي دالة تقبل data.frame ستقبل أيضًا tibble، والعكس صحيح.

التحويل بين النوعين سهل، عبر as_tibble(df) وas.data.frame(tbl).

في الأعمال الجديدة، سيساعدك استخدام tibbles على الأرجح في كتابة كود أكثر متانة. مع ذلك، الكود القديم والبيانات القديمة وفيرة جدًا في عالم R، لذا يُرجّح أن يبقى data.frame شائعًا لفترة طويلة.

# column vectors are same as for data.frame
library(tibble)
tbl <- tibble(languages, created, has.syllabus)
tbl
  # A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE      

لاحظ صيغة الطباعة الافتراضية: يُطبع سطر التعليق الذي يحمل الأبعاد تلقائيًا، وتُعرض أنواع الأعمدة أيضًا.

العمل مع tibbles

tibbles جزء أساسي من Tidyverse، لذا أضِفها عبر library(tibble) أو library(tidyverse).

إنشاء tibble

بأبسط طريقة، يمكننا استخدام الدالة tibble() لضم متجهات الأعمدة، كما في data.frame(). عُرض مثال على ذلك في قسم سابق.

إذا كان إدخال القيم صفًا صفًا أكثر ملاءمة، فالدالة المقابلة هي tribble().

عمليًا، هناك عشرات الطرق لإنشاء tibbles، لأنها صيغة الإخراج الافتراضية من مجموعة متنوعة من دوال Tidyverse.

التعامل مع tibble

تناول مفهوم البرمجة الوظيفية مكتبة purrr للتعامل مع المتجهات والقوائم (بنى بيانات أحادية البعد).

أما لإطارات البيانات (سواء التقليدية أو tibbles)، فالمكتبة المقابلة للاستخدام هي dplyr.

التقسيم الفرعي

يمكن التعامل مع إطارات البيانات، بما في ذلك tibbles، كقوائم من متجهات الأعمدة، لذا تسترجع فهرسة القوائم عمودًا محددًا.

tbl
# A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE    

tbl$created
#> [1] 1957 1993 1991 2012

يمكن أيضًا فهرسة إطار البيانات بفهرسة على نمط المصفوفات.

tbl[c(2, 4), 1:2]
  # A tibble: 2 × 2
#>   languages created
#>   <chr>       <dbl>
#> 1 R            1993
#> 2 Julia        2012

في R الحديثة مع منظومة Tidyverse، تكون دوال dplyr عمومًا أكثر مرونة وملاءمة، وستكون محور التركيز لبقية هذا المفهوم.

العمليات على مستوى الأعمدة

احصل على عمود واحد باستخدام pull() بالاسم أو بالرقم التسلسلي (الأرقام السالبة للعدّ من اليمين إلى اليسار).

tbl |> pull(created)
#> [1] 1957 1993 1991 2012

هذه هي النتيجة نفسها التي يعطيها tbl$created، لكن باستخدام دالة تناسب أسلوب الأنابيب.

للحصول على عدة أعمدة، فالدالة المناسبة هي select()، وهي متعددة الاستخدامات للغاية. احصل على الأعمدة (أو استبعدها) بناءً على خصائص أسمائها أو أنواعها.

# Range with position and/or name
tbl |> select(1:created)
  # A tibble: 4 × 2
#>   languages created
#>   <chr>       <dbl>
#> 1 Fortran      1957
#> 2 R            1993
#> 3 Python       1991
#> 4 Julia        2012      

# Use type of column
tbl |> select(where(is.numeric))
  # A tibble: 4 × 1
#>   created
#>     <dbl>
#> 1    1957
#> 2    1993
#> 3    1991
#> 4    2012

يُسمح بمعايير متعددة، باستخدام عوامل منطقية & و| و! (و، أو، ليس).

تبدو هذه القوة مبالغًا فيها بعض الشيء مع إطار البيانات البسيط الخاص بنا للغات. لحسن الحظ، يتضمن dplyr إطار tibble المسمّى starwars، مما يمنحنا شيئًا أكبر للتدرّب عليه.

# pick a subset of columns
starwars |> 
  select(name | ends_with("color")) |> 
  head(5)
  # A tibble: 5 × 4
#>   name           hair_color skin_color  eye_color
#>   <chr>          <chr>      <chr>       <chr>    
#> 1 Luke Skywalker blond      fair        blue     
#> 2 C-3PO          NA         gold        yellow   
#> 3 R2-D2          NA         white, blue red      
#> 4 Darth Vader    none       white       yellow   
#> 5 Leia Organa    brown      light       brown    

العمليات على مستوى الصفوف

احصل على الصفوف المطابقة لمعايير معينة باستخدام filter()، أو استبعدها باستخدام filter_out().

starwars |> 
  select(name:mass) |> 
  filter(between(height, 150, 165) & !is.na(mass))
  # A tibble: 4 × 3
#>   name               height  mass
#>   <chr>               <int> <dbl>
#> 1 Leia Organa           150    49
#> 2 Beru Whitesun Lars    165    75
#> 3 Nien Nunb             160    68
#> 4 Ben Quadinaros        163    65

يمكن أن تكون معايير الفلترة معقّدة كما نشاء، لكنها دائمًا مبنية على محتوى الصفوف.

إذا كانت أرقام الصفوف معروفة، يمكننا استخدام مجموعة متنوعة من دوال slice().

starwars |> 
  select(name | homeworld) |> 
  slice(20:25)
  # A tibble: 6 × 2
#>   name             homeworld
#>   <chr>            <chr>    
#> 1 Palpatine        Naboo    
#> 2 Boba Fett        Kamino   
#> 3 IG-88            NA       
#> 4 Bossk            Trandosha
#> 5 Lando Calrissian Socorro  
#> 6 Lobot            Bespin   

تعديل tibble

تحذير أول: السلوك الافتراضي copy-on-modify يعني أن tibble الأصلية تبقى عادة دون تغيير.

تُطبَّق معظم التعديلات على مستوى الأعمدة.

يمكن تغيير أسماء الأعمدة باستخدام rename(newname = oldname)، أو rename_with() لتطبيق دالة. لاحظ الصياغة داخل rename(). يُربَط محتوى العمود oldname بالاسم newname، ومن هنا يأتي الترتيب.

يمكن تغيير ترتيب الأعمدة باستخدام relocate(). تُنقل الأعمدة المحددة إلى أقصى المواضع اليسرى افتراضيًا، لكن يمكن استخدام الوسيط .before أو .after لضبط الموضع بدقة أكبر.

sw <- starwars |> select(name:species) |> slice(1:4)
sw
  # A tibble: 4 × 11
#>   name           height  mass hair_color skin_color  eye_color birth_year sex   gender    homeworld species
#>   <chr>           <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>     <chr>     <chr>  
#> 1 Luke Skywalker    172    77 blond      fair        blue            19   male  masculine Tatooine  Human  
#> 2 C-3PO             167    75 NA         gold        yellow         112   none  masculine Tatooine  Droid  
#> 3 R2-D2              96    32 NA         white, blue red             33   none  masculine Naboo     Droid  
#> 4 Darth Vader       202   136 none       white       yellow          41.9 male  masculine Tatooine  Human  

sw |> relocate(c(species, homeworld), .after = name)
  # A tibble: 4 × 11
#>   name           species homeworld height  mass hair_color skin_color  eye_color birth_year sex   gender   
#>   <chr>          <chr>   <chr>      <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>    
#> 1 Luke Skywalker Human   Tatooine     172    77 blond      fair        blue            19   male  masculine
#> 2 C-3PO          Droid   Tatooine     167    75 NA         gold        yellow         112   none  masculine
#> 3 R2-D2          Droid   Naboo         96    32 NA         white, blue red             33   none  masculine
#> 4 Darth Vader    Human   Tatooine     202   136 none       white       yellow          41.9 male  masculine

لإجراء تغييرات أكبر، تتيح لك mutate():

  • إنشاء أعمدة جديدة تكون دوالًا لأعمدة موجودة.
  • استبدال عمود موجود، بإنشاء عمود جديد بالاسم نفسه.
  • حذف عمود، بتعيين قيمته إلى NULL.

من الواضح أن mutate() قوية، وقد تكون مربكة، وهي سبب لامتناننا الشديد لسلوك copy-on-modify.

لا يوجد سبب واضح للاهتمام بمؤشر كتلة الجسم لشخصيات حرب النجوم، لكن من باب الاحتياط:

starwars |> 
  select(c(name, species, height, mass)) |> 
  mutate(BMI = mass / (height / 100)^2) |> 
  head(4)
  # A tibble: 4 × 5
#>   name           species height  mass   BMI
#>   <chr>          <chr>    <int> <dbl> <dbl>
#> 1 Luke Skywalker Human      172    77  26.0
#> 2 C-3PO          Droid      167    75  26.9
#> 3 R2-D2          Droid       96    32  34.7
#> 4 Darth Vader    Human      202   136  33.3

العمليات على مستوى الصفوف أقل شيوعًا لتعديل tibble واحدة (ستُناقش عملية دمج عدة tibbles في مفهوم لاحق).

استثناء واحد: تُرتّب arrange() الصفوف حسب القيم في عمود واحد أو أكثر.

tbl
#>       A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE  

tbl |> arrange(languages)
#>       A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 Julia        2012 TRUE        
#> 3 Python       1991 TRUE        
#> 4 R            1993 TRUE     

التعليمات

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

Note

تحتوي مكتبة datasets المضمّنة على العديد من مجموعات البيانات التجريبية، ويستخدم هذا التمرين مجموعة البيانات trees. لاستدعاء مجموعة البيانات، استخدم ببساطة الاسم trees.

library(datasets) # loading is optional

trees |> head(3)
#>    A data.frame: 3 × 3 	
#>   Girth	Height	Volume
#>	 <dbl>	 <dbl>	 <dbl>
#> 1   8.3	    70	  10.3  
#> 2   8.6	    65	  10.3
#> 3   8.8	    63	  10.2

لاحظ الأعمدة الثلاثة: Girth وHeight وVolume.

تكون مجموعة البيانات في البداية على شكل data.frame، لكن قد ترغب في العمل معها بصيغة tibble.

1. أعد تسمية عمود

لسوء الحظ، وقع خطأ كتابي لم يُصحَّح قط، وعمود واحد في مجموعة البيانات اسمه Girth، لكنه في الحقيقة Diameter.

عرّف كائن data.frame/tibble باسم tree_data الذي يصحّح اسم عمود Girth في trees إلى Diameter.

head(tree_data, 3)
#>     A tibble: 3 × 3 
#> Diameter	 Height	Volume
#>    <dbl>	  <dbl>	 <dbl>
#>      8.3	     70	  10.3
#>      8.6	     65	  10.3
#>      8.8	     63	  10.2

2. أضف عمودَي المحيط والوزن

لا يزال محيط الجذع (girth) قياسًا مفيدًا، وكذلك وزن الأخشاب. ويمكن حساب محيط الجذع بسهولة بالعلاقة πD، حيث D هو القطر. أما وزن كل الأخشاب فيمكن حسابه بالعلاقة ρV، حيث ρ = 35 هي كثافة خشب الكرز وV هو حجم الأخشاب.

عرّف الدالة girth_n_weight(data, rnd_digits) التي تأخذ إطار بيانات ووسائط عددية. ينبغي أن تُرجع الدالة إطار بيانات جديدًا يحتوي على العمودين الإضافيين: Girth وWeight مقرّبين إلى عدد الخانات المناسب الذي يحدده rnd_digits.

girth_n_weight(tree_data, 1) |> head(3)
#>           A tibble: 3 × 5 
#> Diameter  Height	 Volume	 Girth	Weight
#>    <dbl>	  <dbl>	  <dbl>	 <dbl>	 <dbl>
#>      8.3	     70	   10.3	  26.1	 360.5
#>      8.6	     65	   10.3	  27.0	 360.5
#>      8.8	     63	   10.2	  27.6	 357.0

3. نسخة البستان من مجموعة البيانات

يحتفظ البستان، لكل عميل محتمل، بنسخة خاصة من مجموعة البيانات تساعد في عملية البيع. تحتوي هذه المجموعة على العمودين الأكثر أهمية، Weight وHeight، وقد نُقلا إلى المقدمة، وهي مرتّبة حسب Weight.

عرّف الدالة orchard_copy(data, important_cols) التي تأخذ إطار بيانات كمدخل. ينبغي أن تُرجع إطار بيانات جديدًا نُقل فيه Weight وHeight إلى المقدمة وصُفّفت صفوفه حسب Weight.

tree_data |> girth_n_weight() |> orchard_copy() |> head(3)
#>            A tibble: 3 × 5 
#> Weight	Height	Diameter	Volume	Girth
#>  <dbl>	 <dbl>	   <dbl>	 <dbl>	<dbl>
#>    357	    63	       9	    10	   28
#>    360	    70	       8	    10	   26
#>    360	    65	       9	    10	   27

4. نسخة العميل من مجموعة البيانات

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

عرّف الدالة customer_copy(data, min_height, max_height, max_weight). أرجع نسخة للعميل من إطار البيانات تحتوي على الأعمدة Height وWeight وDiameter وGirth مع الصفوف التي تقع ضمن القيود.

tree_data |> 
    girth_n_weight(1) |> 
    orchard_copy() |>
    customer_copy(65, 75, 1500) |> 
    head(3)
#>        A tibble: 3 × 4 
#> Height	Weight	Diameter	Girth
#>  <dbl>	 <dbl>	   <dbl>	<dbl>
#>     70	 360.5	     8.3	 26.1
#>     65	 360.5	     8.6	 27.0
#>     66	 546.0	    11.0	 34.6
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
R Exercism

مستعد لبدء الأشجار ومصائب tibble؟

سجّل في Exercism لتتعلّم وتتقن R عبر 21 مفهومًا111 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.