20  جداول البيانات

20.1 مقدمة

في الفصل 7، تعلمت كيفية استيراد البيانات من الملفات النصية العادية مثل .csv و .tsv. حان الوقت الآن لتعلم كيفية استخراج البيانات من جداول البيانات، سواء كانت جدول بيانات Excel أو جدول بيانات جوجل (Google Sheet). سيعتمد هذا على الكثير مما تعلمته في الفصل 7، ولكننا سنناقش أيضاً اعتبارات وتعقيدات إضافية عند العمل مع البيانات القادمة من جداول البيانات.

إذا كنت أنت أو زملاؤك تستخدمون جداول البيانات لتنظيم البيانات، فإننا نوصي بشدة بقراءة ورقة “تنظيم البيانات في جداول البيانات” (Data Organization in Spreadsheets) للكاتبين كارل برومان وكارا وو: https://doi.org/10.1080/00031305.2017.1375989. إن الممارسات الأفضل المعروضة في هذه الورقة ستوفر عليك الكثير من المتاعب عندما تقوم باستيراد البيانات من جدول بيانات إلى R لتحليلها وتمثيلها بصرياً.

20.2 Excel

برنامج Microsoft Excel هو برنامج جداول بيانات واسع الاستخدام حيث تُنظم البيانات في أوراق عمل (worksheets) داخل ملفات جداول البيانات.

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

في هذا القسم، ستتعلم كيفية تحميل البيانات من جداول بيانات Excel في R باستخدام حزمة readxl. هذه الحزمة ليست جزءاً من النواة الأساسية لـ tidyverse، لذا ستحتاج إلى تحميلها بشكل صريح، ولكن يتم تثبيتها تلقائياً عند تثبيت حزمة tidyverse. لاحقاً، سنستخدم أيضاً حزمة writexl، والتي تسمح لنا بإنشاء جداول بيانات Excel.

20.2.2 البداية

تسمح لك معظم دوال readxl بتحميل جداول بيانات Excel إلى R:

  • read_xls() تقرأ ملفات Excel بتنسيق xls.
  • read_xlsx() تقرأ ملفات Excel بتنسيق xlsx.
  • read_excel() يمكنها قراءة الملفات بكلتا الصيغتين xls و xlsx. حيث تخمن نوع الملف بناءً على المدخلات.

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

20.2.3 قراءة جداول بيانات Excel

يُظهر الشكل 20.1 كيف يبدو جدول البيانات الذي سنقرؤه في R داخل برنامج Excel. يمكن تنزيل جدول البيانات هذا كملف Excel من الرابط https://docs.google.com/spreadsheets/d/1V1nPp1tzOuutXFLb3G9Eyxi3qxeEhnOXUzL5_BcCQ0w/.

نظرة على جدول بيانات الطلاب في Excel. يحتوي جدول البيانات على معلومات عن 6 طلاب، ورقمهم التعريفي، والاسم الكامل، والطعام المفضل، وخطة الوجبات، والعمر.
الشكل 20.1: جدول بيانات يسمى students.xlsx في Excel.

المعامل الأول لـ read_excel() هو مسار الملف المراد قراءته.

students <- read_excel("data/students.xlsx")

ستقرأ read_excel() الملف كجدول من نوع tibble.

students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE  
#>          <dbl> <chr>            <chr>              <chr>               <chr>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2            2 Barclay Lynn     French fries       Lunch only          5    
#> 3            3 Jayendra Lyne    N/A                Breakfast and lunch 7    
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          6

لدينا ستة طلاب في البيانات وخمسة متغيرة لكل طالب. ومع ذلك، هناك بعض الأمور التي قد نسترعي معالجتها في مجموعة البيانات هذه:

  1. أسماء الأعمدة غير منظمة في كل مكان. يمكنك تقديم أسماء أعمدة تتبع تنسيقاً متسقاً؛ نوصي بتنسيق snake_case باستخدام المعامل col_names.

    read_excel(
      "data/students.xlsx",
      col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age")
    )
    #> # A tibble: 7 × 5
    #>   student_id full_name        favourite_food     meal_plan           age  
    #>   <chr>      <chr>            <chr>              <chr>               <chr>
    #> 1 Student ID Full Name        favourite.food     mealPlan            AGE  
    #> 2 1          Sunil Huffmann   Strawberry yoghurt Lunch only          4    
    #> 3 2          Barclay Lynn     French fries       Lunch only          5    
    #> 4 3          Jayendra Lyne    N/A                Breakfast and lunch 7    
    #> 5 4          Leon Rossini     Anchovies          Lunch only          <NA> 
    #> 6 5          Chidiegwu Dunkel Pizza              Breakfast and lunch five 
    #> 7 6          Güvenç Attila    Ice cream          Lunch only          6

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

    read_excel(
      "data/students.xlsx",
      col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age"),
      skip = 1
    )
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan           age  
    #>        <dbl> <chr>            <chr>              <chr>               <chr>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
    #> 2          2 Barclay Lynn     French fries       Lunch only          5    
    #> 3          3 Jayendra Lyne    N/A                Breakfast and lunch 7    
    #> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
    #> 6          6 Güvenç Attila    Ice cream          Lunch only          6
  2. في عمود favourite_food، إحدى المشاهدات هي N/A، والتي تعني “غير متاح” ولكن لا يتم التعرف عليها حالياً كـ NA (لاحظ التباين بين N/A هذه وعمر الطالب الرابع في القائمة). يمكنك تحديد السلاسل النصية التي يجب التعرف عليها كـ NA باستخدام المعامل na. افتراضياً، فقط "" (سلسلة نصية فارغة، أو في حالة القراءة من جدول بيانات، خلية فارغة أو خلية تحتوى الصيغة =NA()) يتم التعرف عليها كـ NA.

    read_excel(
      "data/students.xlsx",
      col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age"),
      skip = 1,
      na = c("", "N/A")
    )
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan           age  
    #>        <dbl> <chr>            <chr>              <chr>               <chr>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
    #> 2          2 Barclay Lynn     French fries       Lunch only          5    
    #> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
    #> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
    #> 6          6 Güvenç Attila    Ice cream          Lunch only          6
  3. هناك مشكلة أخرى متبقية وهي أن age يُقرأ كمتغير نصي، ولكن يجب أن يكون عددياً في الحقيقة. تماماً كما هو الحال مع read_csv() وأخواتها لقراءة البيانات من الملفات المسطحة، يمكنك تزويد المعامل col_types لـ read_excel() وتحديد أنواع الأعمدة للمتغيرات التي تقرؤها. بناء الجملة مختلف قليلاً، على أي حال. خياراتك هي "skip" أو "guess" أو "logical" أو "numeric" أو "date" أو "text" أو "list".

    read_excel(
      "data/students.xlsx",
      col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age"),
      skip = 1,
      na = c("", "N/A"),
      col_types = c("numeric", "text", "text", "text", "numeric")
    )
    #> Warning: Expecting numeric in E6 / R6C5: got 'five'
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <chr>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch    NA
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6

    ومع ذلك، لم ينتج هذا أيضاً النتيجة المرجوة تماماً. من خلال تحديد أن age يجب أن يكون عددياً، قمنا بتحويل الخلية الواحدة ذات الإدخال غير العددي (والتي كانت قيمتها five) إلى NA. في هذه الحالة، يجب أن نقرأ العمر كـ "text" ثم ننتقل للتعديل بمجرد تحميل البيانات في R.

    students <- read_excel(
      "data/students.xlsx",
      col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age"),
      skip = 1,
      na = c("", "N/A"),
      col_types = c("numeric", "text", "text", "text", "text")
    )
    
    students <- students |>
      mutate(
        age = if_else(age == "five", "5", age),
        age = parse_number(age)
      )
    
    students
    #> # A tibble: 6 × 5
    #>   student_id full_name        favourite_food     meal_plan             age
    #>        <dbl> <chr>            <chr>              <chr>               <dbl>
    #> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only              4
    #> 2          2 Barclay Lynn     French fries       Lunch only              5
    #> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch     7
    #> 4          4 Leon Rossini     Anchovies          Lunch only             NA
    #> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch     5
    #> 6          6 Güvenç Attila    Ice cream          Lunch only              6

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

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

20.2.4 قراءة أوراق العمل

من الميزات المهمة التي تميز جداول البيانات عن الملفات المسطحة هي مفهوم أوراق العمل (worksheets) المتعددة. يُظهر الشكل 20.2 جدول بيانات Excel يحتوي على أوراق عمل متعددة. تأتي البيانات من حزمة palmerpenguins، ويمكنك تنزيل جدول البيانات هذا كملف Excel من الرابط https://docs.google.com/spreadsheets/d/1aFu8lnD_g0yjF5O-K6SFgSEWiHPpgvFCF0NY9D6LXnY/. تحتوي كل ورقة عمل على معلومات عن طيور البطريق من جزيرة مختلفة تم جمع البيانات منها.

نظرة على جدول بيانات penguins في Excel. يحتوي جدول البيانات على ثلاث أوراق عمل: Torgersen Island و Biscoe Island و Dream Island.
الشكل 20.2: جدول بيانات يسمى penguins.xlsx في Excel يحتوي على ثلاث أوراق عمل.

يمكنك قراءة ورقة عمل واحدة من جدول بيانات باستخدام المعامل sheet في read_excel(). الخيار الافتراضي، الذي كنا نعتمد عليه حتى الآن، هو الورقة الأولى.

read_excel("data/penguins.xlsx", sheet = "Torgersen Island")
#> # A tibble: 52 × 8
#>   species island    bill_length_mm     bill_depth_mm      flipper_length_mm
#>   <chr>   <chr>     <chr>              <chr>              <chr>            
#> 1 Adelie  Torgersen 39.1               18.7               181              
#> 2 Adelie  Torgersen 39.5               17.399999999999999 186              
#> 3 Adelie  Torgersen 40.299999999999997 18                 195              
#> 4 Adelie  Torgersen NA                 NA                 NA               
#> 5 Adelie  Torgersen 36.700000000000003 19.3               193              
#> 6 Adelie  Torgersen 39.299999999999997 20.6               190              
#> # ℹ 46 more rows
#> # ℹ 3 more variables: body_mass_g <chr>, sex <chr>, year <dbl>

يتم قراءة بعض المتغيرات التي يبدو أنها تحتوي على بيانات عددية كمتغيرات نصية بسبب عدم التعرف على السلسلة النصية "NA" كـ NA حقيقية.

penguins_torgersen <- read_excel("data/penguins.xlsx", sheet = "Torgersen Island", na = "NA")

penguins_torgersen
#> # A tibble: 52 × 8
#>   species island    bill_length_mm bill_depth_mm flipper_length_mm
#>   <chr>   <chr>              <dbl>         <dbl>             <dbl>
#> 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
#> # ℹ 46 more rows
#> # ℹ 3 more variables: body_mass_g <dbl>, sex <chr>, year <dbl>

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

excel_sheets("data/penguins.xlsx")
#> [1] "Torgersen Island" "Biscoe Island"    "Dream Island"

بمجرد معرفة أسماء أوراق العمل، يمكنك قراءتها بشكل فردي باستخدام read_excel().

penguins_biscoe <- read_excel("data/penguins.xlsx", sheet = "Biscoe Island", na = "NA")
penguins_dream  <- read_excel("data/penguins.xlsx", sheet = "Dream Island", na = "NA")

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

dim(penguins_torgersen)
#> [1] 52  8
dim(penguins_biscoe)
#> [1] 168   8
dim(penguins_dream)
#> [1] 124   8

يمكننا تجميعها معاً باستخدام bind_rows().

penguins <- bind_rows(penguins_torgersen, penguins_biscoe, penguins_dream)
penguins
#> # A tibble: 344 × 8
#>   species island    bill_length_mm bill_depth_mm flipper_length_mm
#>   <chr>   <chr>              <dbl>         <dbl>             <dbl>
#> 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 <dbl>, sex <chr>, year <dbl>

في الفصل 26 سنتحدث عن طرق للقيام بهذا النوع من المهام دون كود مكرر.

20.2.5 قراءة جزء من الورقة

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

نظرة على جدول بيانات الوفيات deaths في Excel. يحتوي جدول البيانات على أربعة صفوف في الأعلى تحتوي على معلومات ليست بيانات؛ النص 'من أجل الاتساق في تخطيط البيانات، وهو أمر جميل حقاً، وسأستمر في تدوين الملاحظات هنا.' يتوزع عبر الخلايا في هذه الصفوف الأربعة الأولى. بعد ذلك، يوجد إطار بيانات يتضمن معلومات عن وفيات 10 أشخاص مشهورين، بما في ذلك أسماؤهم ومهنهم وأعمارهم وما إذا كان لديهم أطفال أم لا وتاريخ الميلاد والوفاة. في الأسفل، هناك أربعة صفوف أخرى من المعلومات غير المتعلقة بالبيانات؛ النص 'لقد كان هذا ممتعاً حقاً، ولكننا ننهي الآن!' يتوزع عبر الخلايا في هذه الصفوف الأربعة السفلية.
الشكل 20.3: جدول بيانات يسمى deaths.xlsx في Excel.

جدول البيانات هذا هو أحد الأمثلة المرفقة في حزمة readxl. يمكنك استخدام الدالة readxl_example() لتحديد موقع جدول البيانات على نظامك في المجلد الذي تم تثبيت الحزمة فيه. تعيد هذه الدالة مسار جدول البيانات، والذي يمكنك استخدامه في read_excel() كالمعتاد.

deaths_path <- readxl_example("deaths.xlsx")
deaths <- read_excel(deaths_path)
#> New names:
#> • `` -> `...2`
#> • `` -> `...3`
#> • `` -> `...4`
#> • `` -> `...5`
#> • `` -> `...6`
deaths
#> # A tibble: 18 × 6
#>   `Lots of people`    ...2       ...3  ...4     ...5          ...6           
#>   <chr>               <chr>      <chr> <chr>    <chr>         <chr>          
#> 1 simply cannot resi… <NA>       <NA>  <NA>     <NA>          some notes     
#> 2 at                  the        top   <NA>     of            their spreadsh…
#> 3 or                  merging    <NA>  <NA>     <NA>          cells          
#> 4 Name                Profession Age   Has kids Date of birth Date of death  
#> 5 David Bowie         musician   69    TRUE     17175         42379          
#> 6 Carrie Fisher       actor      60    TRUE     20749         42731          
#> # ℹ 12 more rows

الصفوف الثلاثة الأولى والصفوف الأربعة السفلية ليست جزءاً من إطار البيانات. من الممكن التخلص من هذه الصفوف الزائدة باستخدام المعاملين skip و n_max، ولكننا نوصي باستخدام نطاقات الخلايا (cell ranges). في Excel، الخلية العلوية اليسرى هي A1. كلما تحركت عبر الأعمدة إلى اليمين، يتحرك مسمى الخلية أبجدياً، أي B1 و C1 إلخ. وكلما تحركت إلى الأسفل في العمود، يزداد الرقم في مسمى الخلية، أي A2 و A3 إلخ.

هنا البيانات التي نريد قراءتها تبدأ في الخلية A5 وتنتهي في الخلية F15. في تدوين جداول البيانات، هذا هو A5:F15، والذي نزوده للمعامل range:

read_excel(deaths_path, range = "A5:F15")
#> # A tibble: 10 × 6
#>   Name          Profession   Age `Has kids` `Date of birth`    
#>   <chr>         <chr>      <dbl> <lgl>      <dttm>             
#> 1 David Bowie   musician      69 TRUE       1947-01-08 00:00:00
#> 2 Carrie Fisher actor         60 TRUE       1956-10-21 00:00:00
#> 3 Chuck Berry   musician      90 TRUE       1926-10-18 00:00:00
#> 4 Bill Paxton   actor         61 TRUE       1955-05-17 00:00:00
#> 5 Prince        musician      57 TRUE       1958-06-07 00:00:00
#> 6 Alan Rickman  actor         69 FALSE      1946-02-21 00:00:00
#> # ℹ 4 more rows
#> # ℹ 1 more variable: `Date of death` <dttm>

20.2.6 أنواع البيانات

في ملفات CSV، جميع القيم هي سلاسل نصية. هذا ليس دقيقاً تماماً للبيانات، ولكنه بسيط: كل شيء هو سلسلة نصية.

البيانات الأساسية في جداول بيانات Excel أكثر تعقيداً. يمكن أن تكون الخلية واحدة من أربعة أشياء:

  • قيمة منطقية (Boolean)، مثل TRUE أو FALSE أو NA.

  • رقم، مثل “10” أو “10.5”.

  • تاريخ ووقت (datetime)، والتي يمكن أن تتضمن أيضاً الوقت مثل “11/1/21” أو “11/1/21 3:00 PM”.

  • سلسلة نصية، مثل “ten”.

عند العمل مع بيانات جداول البيانات، من المهم أن تضع في اعتبارك أن البيانات الأساسية يمكن أن تكون مختلفة جداً عما تراه في الخلية. على سبيل المثال، ليس لدى Excel مفهوم للعدد الصحيح (integer). تتم معالجة جميع الأرقام كأعداد عشرية (floating points)، ولكن يمكنك اختيار عرض البيانات بعدد قابل للتخصيص من الخانات العشرية. وبالمثل، يتم تخزين التواريخ في الواقع كأرقام، وتحديداً عدد الأيام منذ 1 يناير 1900. يمكنك تخصيص كيفية عرض التاريخ عن طريق تطبيق التنسيق في Excel. ومن المربك أيضاً أنه من الممكن الحصول على شيء يبدو كأنه رقم ولكنه في الواقع سلسلة نصية (على سبيل المثال، كتابة '10 في خلية في Excel).

هذه الاختلافات بين كيفية تخزين البيانات الأساسية مقابل كيفية عرضها يمكن أن تسبب مفاجآت عند تحميل البيانات إلى R. افتراضياً، ستخمن readxl نوع البيانات في عمود معين. إن سير العمل الموصى به هو ترك readxl تخمن أنواع الأعمدة، والتأكد من أنك راضٍ عن أنواع الأعمدة المخمنة، وإذا لم تكن كذلك، ارجع وعدّل الاستيراد مع تحديد col_types كما هو موضح في قسم 20.2.3.

تحدٍ آخر هو عندما يكون لديك عمود في جدول بيانات Excel يحتوي على مزيج من هذه الأنواع، على سبيل المثال، بعض الخلايا عددية، والأخرى نصية، والأخرى تواريخ. عند استيراد البيانات إلى R، يتعين على readxl اتخاذ بعض القرارات. في هذه الحالات، يمكنك ضبط نوع هذا العمود على "list"، والذي سيحمل العمود كقائمة من المتجهات ذات الطول 1، حيث يتم تخمين نوع كل عنصر في المتجه.

في بعض الأحيان يتم تخزين البيانات بطرق أكثر غرابة، مثل لون خلفية الخلية، أو ما إذا كان النص عريضاً أم لا. في مثل هذه الحالات، قد تجد حزمة tidyxl مفيدة. راجع الرابط https://nacnudus.github.io/spreadsheet-munging-strategies/ للمزيد حول استراتيجيات العمل مع البيانات غير الجدولية من Excel.

20.2.7 الكتابة إلى ملف Excel

دعنا ننشئ إطار بيانات صغيراً يمكننا كتابته وتصديره لاحقاً. لاحظ أن item عبارة عن عامل (factor) و quantity رقم صحيح (integer).

bake_sale <- tibble(
  item     = factor(c("brownie", "cupcake", "cookie")),
  quantity = c(10, 5, 8)
)

bake_sale
#> # A tibble: 3 × 2
#>   item    quantity
#>   <fct>      <dbl>
#> 1 brownie       10
#> 2 cupcake        5
#> 3 cookie         8

يمكنك إعادة كتابة البيانات وحفظها على القرص كملف Excel باستخدام الدالة write_xlsx() من حزمة writexl:

write_xlsx(bake_sale, path = "data/bake-sale.xlsx")

يُظهر الشكل 20.4 كيف تبدو البيانات في Excel. لاحظ أن أسماء الأعمدة متضمنة ومكتوبة بخط عريض (bold). يمكن إيقاف تشغيل ذلك عن طريق ضبط المعاملين col_names و format_headers على القيمة FALSE.

إطار بيانات مبيعات المخبوزات الذي تم إنشاؤه سابقاً مدمجاً في Excel.
الشكل 20.4: جدول بيانات يسمى bake-sale.xlsx في Excel.

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

read_excel("data/bake-sale.xlsx")
#> # A tibble: 3 × 2
#>   item    quantity
#>   <chr>      <dbl>
#> 1 brownie       10
#> 2 cupcake        5
#> 3 cookie         8

20.2.8 المخرجات المنسقة

تُعد حزمة writexl حلًا خفيف الوزن لكتابة جدول بيانات Excel بسيط، ولكن إذا كنت مهتماً بخصائص إضافية مثل الكتابة إلى أوراق متعددة داخل جدول البيانات وإضافة التنسيقات والأنماط (styling)، فستحتاج إلى استخدام حزمة openxlsx. لن ندخل في تفاصيل استخدام هذه الحزمة هنا، ولكننا نوصي بقراءة https://ycphs.github.io/openxlsx/articles/Formatting.html للحصول على مناقشة موسعة حول وظائف التنسيق الإضافية للبيانات المكتوبة من R إلى Excel باستخدام openxlsx.

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

20.2.9 تمارين

  1. في ملف Excel، أنشئ مجموعة البيانات التالية واحفظها باسم survey.xlsx. بدلاً من ذلك، يمكنك تنزيلها كملف Excel من هنا.

    جدول بيانات يحتوي على 3 أعمدة (group و subgroup و id) و 12 صفاً. حتوي عمود group على قيمتين: 1 (يمتد عبر 7 صفوف مدمجة) و 2 (يمتد عبر 5 صفوف مدمجة). يحتوي عمود subgroup على أربع قيم: A (تمتد عبر 3 صفوف مدمجة)، B (تمتد عبر 4 صفوف مدمجة)، A (تمتد عبر صفين مدمجين)، و B (تمتد عبر 3 صفوف مدمجة). يحتوي عمود id على اثنتي عشرة قيمة، الأرقام من 1 إلى 12.

    ثم اقرأها في R، بحيث يكون survey_id كمتغير نصي (character) و n_pets كمتغير عددي (numeric).

    #> # A tibble: 6 × 2
    #>   survey_id n_pets
    #>   <chr>      <dbl>
    #> 1 1              0
    #> 2 2              1
    #> 3 3             NA
    #> 4 4              2
    #> 5 5              2
    #> 6 6             NA
  2. في ملف Excel آخر، أنشئ مجموعة البيانات التالية واحفظها باسم roster.xlsx. بدلاً من ذلك، يمكنك تنزيلها كملف Excel من هنا.

    جدول بيانات يحتوي على 3 أعمدة (group و subgroup و id) و 12 صفاً. يحتوي عمود group على قيمتين: 1 (يمتد عبر 7 صفوف مدمجة) و 2 (يمتد عبر 5 صفوف مدمجة). يحتوي عمود subgroup على أربع قيم: A (تمتد عبر 3 صفوف مدمجة)، B (تمتد عبر 4 صفوف مدمجة)، A (تمتد عبر صفين مدمجين)، و B (تمتد عبر 3 صفوف مدمجة). يحتوي عمود id على اثنتي عشرة قيمة، الأرقام من 1 إلى 12.

    ثم اقرأها إلى R. ينبغي أن يُسمى إطار البيانات الناتج roster وأن يبدو بالشكل التالي.

    #> # A tibble: 12 × 3
    #>    group subgroup    id
    #>    <dbl> <chr>    <dbl>
    #>  1     1 A            1
    #>  2     1 A            2
    #>  3     1 A            3
    #>  4     1 B            4
    #>  5     1 B            5
    #>  6     1 B            6
    #>  7     1 B            7
    #>  8     2 A            8
    #>  9     2 A            9
    #> 10     2 B           10
    #> 11     2 B           11
    #> 12     2 B           12
  3. في ملف Excel جديد، أنشئ مجموعة البيانات التالية واحفظها باسم sales.xlsx. بدلاً من ذلك، يمكنك تنزيلها كملف Excel من هنا.

    جدول بيانات يحتوي على عمودين و 13 صفاً. الصفان الأولان يحتويان على نص يتضمن معلومات حول الورقة. يقول الصف 1 "يحتوي هذا الملف على معلومات حول المبيعات". يقول الصف 2 "تنظم البيانات حسب اسم العلامة التجارية، ولكل علامة تجارية لدينا الرقم التعريفي للعنصر المباع، وكم تم بيعه.". ثم هناك صفان فارغان، ثم 9 صفوف من البيانات.

    أ. اقرأ ملف sales.xlsx واحفظه باسم sales. ينبغي أن يبدو إطار البيانات كما يلي، بحيث يكون id و n أسماء للأعمدة وبحيث يحتوي على 9 صفوف.

    #> # A tibble: 9 × 2
    #>   id      n    
    #>   <chr>   <chr>
    #> 1 Brand 1 n    
    #> 2 1234    8    
    #> 3 8721    2    
    #> 4 1822    3    
    #> 5 Brand 2 n    
    #> 6 3333    1    
    #> 7 2156    3    
    #> 8 3987    6    
    #> 9 3216    5

    ب. قم بتعديل sales بشكل أكبر للوصول به إلى الشكل التنسيقي النظيف (tidy) التالي الذي يحتوي على ثلاثة أعمدة (brand و id و n) و 7 صفوف من البيانات. لاحظ أن id و n عدديان، بينما brand متغير نصي.

    #> # A tibble: 7 × 3
    #>   brand      id     n
    #>   <chr>   <dbl> <dbl>
    #> 1 Brand 1  1234     8
    #> 2 Brand 1  8721     2
    #> 3 Brand 1  1822     3
    #> 4 Brand 2  3333     1
    #> 5 Brand 2  2156     3
    #> 6 Brand 2  3987     6
    #> 7 Brand 2  3216     5
  4. أعد إنشاء إطار بيانات bake_sale ثم قُم بتصديره إلى ملف Excel باستخدام الدالة write.xlsx() من حزمة openxlsx.

  5. في الفصل 7، تعلمت عن الدالة janitor::clean_names() لتحويل أسماء الأعمدة إلى صيغة snake_case. اقرأ ملف students.xlsx الذي قدمناه سابقاً في هذا القسم واستخدم هذه الدالة لتنظيف أسماء الأعمدة.

  6. ماذا يحدث إذا حاولت قراءة ملف بالامتداد .xlsx باستخدام الدالة read_xls()؟

20.3 مستندات جوجل (Google Sheets)

تُعد جداول بيانات جوجل (Google Sheets) برنامج جداول بيانات آخر شائع الاستخدام. وهو مجاني ويعتمد على الويب. تماماً كما هو الحال مع Excel، يتم تنظيم البيانات في Google Sheets في أوراق عمل (تسمى أيضاً أوراق أو sheets) داخل ملفات جداول البيانات.

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

سيركز هذا القسم أيضاً على جداول البيانات، ولكن هذه المرة ستقوم بتحميل البيانات من Google Sheet باستخدام حزمة googlesheets4. هذه الحزمة ليست ضمن النواة الأساسية لـ tidyverse أيضاً، لذا يتوجب عليك تحميلها بشكل صريح.

ملاحظة سريعة حول اسم الحزمة: تستخدم googlesheets4 الإصدار الرابع من واجهة برمجة التطبيقات Sheets API v4 لتوفير واجهة R مع Google Sheets، ومن هنا جاء الاسم.

20.3.2 البداية

الدالة الرئيسية لحزمة googlesheets4 هي read_sheet()، والتي تقرأ Google Sheet من رابط URL أو معرّف ملف (file id). تُعرف هذه الدالة أيضاً باسم range_read().

يمكنك أيضاً إنشاء ورقة جديدة تماماً باستخدام gs4_create() أو الكتابة إلى ورقة موجودة باستخدام sheet_write() وأخواتها.

في هذا القسم سنعمل مع نفس مجموعات البيانات التي استخدمناها في قسم Excel لإبراز أوجه التشابه والاختلاف بين سير العمل لقراءة البيانات من Excel و Google Sheets. تم تصميم حزمتي readxl و googlesheets4 لمحاكاة وظائف حزمة readr، والتي توفر الدالة read_csv() التي رأيتها في الفصل 7. لذلك، يمكن إنجاز العديد من المهام ببساطة عن طريق استبدال read_excel() بـ read_sheet(). ومع ذلك، ستلاحظ أيضاً أن Excel و Google Sheets لا يتصرفان بنفس الطريقة تماماً، وبالتالي قد تطلب مهام أخرى إجراء تحديثات إضافية على استدعاءات الدوال.

20.3.3 قراءة Google Sheets

يُظهر الشكل 20.5 كيف يبدو جدول البيانات الذي سنقرؤه في R داخل Google Sheets. هذه هي نفس مجموعة البيانات الموجودة في الشكل 20.1، باستثناء أنها مخزنة في Google Sheet بدلاً من Excel.

نظرة على جدول بيانات الطلاب في Google Sheets. يحتوي جدول البيانات على معلومات عن 6 طلاب، ورقمهم التعريفي، والاسم الكامل، والطعام المفضل، وخطة الوجبات، والعمر.
الشكل 20.5: جدول بيانات Google يسمى students في نافذة المتصفح.

المعامل الأول لـ read_sheet() هو رابط URL للملف المراد قراءته، وتعيد جدولاً من نوع tibble: https://docs.google.com/spreadsheets/d/1V1nPp1tzOuutXFLb3G9Eyxi3qxeEhnOXUzL5_BcCQ0w. روابط URL هذه ليست مريحة للعمل معها، لذا غالباً ما سترغب في تحديد الورقة بواسطة مُعرّفها (ID).

students_sheet_id <- "1V1nPp1tzOuutXFLb3G9Eyxi3qxeEhnOXUzL5_BcCQ0w"
students <- read_sheet(students_sheet_id)
#> ✔ Reading from students.
#> ✔ Range Sheet1.
students
#> # A tibble: 6 × 5
#>   `Student ID` `Full Name`      favourite.food     mealPlan            AGE   
#>          <dbl> <chr>            <chr>              <chr>               <list>
#> 1            1 Sunil Huffmann   Strawberry yoghurt Lunch only          <dbl> 
#> 2            2 Barclay Lynn     French fries       Lunch only          <dbl> 
#> 3            3 Jayendra Lyne    N/A                Breakfast and lunch <dbl> 
#> 4            4 Leon Rossini     Anchovies          Lunch only          <NULL>
#> 5            5 Chidiegwu Dunkel Pizza              Breakfast and lunch <chr> 
#> 6            6 Güvenç Attila    Ice cream          Lunch only          <dbl>

تماماً كما فعلنا مع read_excel()، يمكننا تزويد read_sheet() بأسماء الأعمدة والسلاسل النصية لـ NA وأنواع الأعمدة.

students <- read_sheet(
  students_sheet_id,
  col_names = c("student_id", "full_name", "favourite_food", "meal_plan", "age"),
  skip = 1,
  na = c("", "N/A"),
  col_types = "dcccc"
)
#> ✔ Reading from students.
#> ✔ Range 2:10000000.

students
#> # A tibble: 6 × 5
#>   student_id full_name        favourite_food     meal_plan           age  
#>        <dbl> <chr>            <chr>              <chr>               <chr>
#> 1          1 Sunil Huffmann   Strawberry yoghurt Lunch only          4    
#> 2          2 Barclay Lynn     French fries       Lunch only          5    
#> 3          3 Jayendra Lyne    <NA>               Breakfast and lunch 7    
#> 4          4 Leon Rossini     Anchovies          Lunch only          <NA> 
#> 5          5 Chidiegwu Dunkel Pizza              Breakfast and lunch five 
#> 6          6 Güvenç Attila    Ice cream          Lunch only          6

لاحظ أننا حددنا أنواع الأعمدة بشكل مختلف قليلاً هنا باستخدام رموز قصيرة. على سبيل المثال، “dcccc” تعني “double, character, character, character, character”.

من الممكن أيضاً قراءة الأوراق الفردية من Google Sheets. دعنا نقرأ ورقة “Torgersen Island” من Google Sheet لطيور البطريق:

penguins_sheet_id <- "1aFu8lnD_g0yjF5O-K6SFgSEWiHPpgvFCF0NY9D6LXnY"
read_sheet(penguins_sheet_id, sheet = "Torgersen Island")
#> ✔ Reading from penguins.
#> ✔ Range ''Torgersen Island''.
#> # A tibble: 52 × 8
#>   species island    bill_length_mm bill_depth_mm flipper_length_mm
#>   <chr>   <chr>     <list>         <list>        <list>           
#> 1 Adelie  Torgersen <dbl [1]>      <dbl [1]>     <dbl [1]>        
#> 2 Adelie  Torgersen <dbl [1]>      <dbl [1]>     <dbl [1]>        
#> 3 Adelie  Torgersen <dbl [1]>      <dbl [1]>     <dbl [1]>        
#> 4 Adelie  Torgersen <chr [1]>      <chr [1]>     <chr [1]>        
#> 5 Adelie  Torgersen <dbl [1]>      <dbl [1]>     <dbl [1]>        
#> 6 Adelie  Torgersen <dbl [1]>      <dbl [1]>     <dbl [1]>        
#> # ℹ 46 more rows
#> # ℹ 3 more variables: body_mass_g <list>, sex <chr>, year <dbl>

يمكنك الحصول على قائمة بجميع الأوراق داخل Google Sheet باستخدام sheet_names():

sheet_names(penguins_sheet_id)
#> [1] "Torgersen Island" "Biscoe Island"    "Dream Island"

أخيراً، تماماً كما هو الحال مع read_excel()، يمكننا قراءة جزء من Google Sheet عن طريق تحديد نطاق range في read_sheet(). لاحظ أننا نستخدم أيضاً الدالة gs4_example() أدناه لتحديد موقع مثال Google Sheet المرفق مع حزمة googlesheets4.

deaths_url <- gs4_example("deaths")
deaths <- read_sheet(deaths_url, range = "A5:F15")
#> ✔ Reading from deaths.
#> ✔ Range A5:F15.
deaths
#> # A tibble: 10 × 6
#>   Name          Profession   Age `Has kids` `Date of birth`    
#>   <chr>         <chr>      <dbl> <lgl>      <dttm>             
#> 1 David Bowie   musician      69 TRUE       1947-01-08 00:00:00
#> 2 Carrie Fisher actor         60 TRUE       1956-10-21 00:00:00
#> 3 Chuck Berry   musician      90 TRUE       1926-10-18 00:00:00
#> 4 Bill Paxton   actor         61 TRUE       1955-05-17 00:00:00
#> 5 Prince        musician      57 TRUE       1958-06-07 00:00:00
#> 6 Alan Rickman  actor         69 FALSE      1946-02-21 00:00:00
#> # ℹ 4 more rows
#> # ℹ 1 more variable: `Date of death` <dttm>

20.3.4 الكتابة إلى Google Sheets

يمكنك الكتابة من R إلى Google Sheets باستخدام write_sheet(). المعامل الأول هو إطار البيانات المراد كتابته، والمعامل الثاني هو اسم (أو معرّف آخر) لـ Google Sheet المراد الكتابة إليها:

write_sheet(bake_sale, ss = "bake-sale")

إذا كنت ترغب في كتابة بياناتك إلى ورقة عمل (worksheet) محددة داخل Google Sheet، يمكنك تحديد ذلك باستخدام المعامل sheet أيضاً.

write_sheet(bake_sale, ss = "bake-sale", sheet = "Sales")

20.3.5 المصادقة (Authentication)

بينما يمكنك القراءة من Google Sheet عامة دون المصادقة بحساب Google الخاص بك ومع gs4_deauth()، فإن قراءة ورقة خاصة أو الكتابة إلى ورقة يتطلب مصادقة حتى تتمكن googlesheets4 من عرض وإدارة جداول Google Sheets الخاصة بك.

عندما تحاول قراءة ورقة تتطلب المصادقة، ستوجهك googlesheets4 إلى متصفح الويب مع مطالبة لتسجيل الدخول إلى حساب Google الخاص بك ومنح الإذن للعمل نيابة عنك في Google Sheets. ومع ذلك، إذا كنت ترغب في تحديد حساب Google معين، أو نطاق المصادقة (authentication scope)، إلخ، فيمكنك القيام بذلك باستخدام gs4_auth()، على سبيل المثال gs4_auth(email = "mine@example.com")، مما سيفرض استخدام رمز بريد إلكتروني محدد. لمزيد من تفاصيل المصادقة، نوصي بقراءة دليل توثيق المصادقة لحزمة googlesheets4: https://googlesheets4.tidyverse.org/articles/auth.html.

20.3.6 تمارين

  1. اقرأ مجموعة بيانات students التي وردت سابقاً في الفصل من Excel وأيضاً من Google Sheets، دون تقديم أي معاملات إضافية لدوال read_excel() و read_sheet(). هل أطر البيانات الناتجة في R متطابقة تماماً؟ إذا لم تكن كذلك، فكيف تختلف؟

  2. اقرأ Google Sheet المعنونة survey من الرابط https://pos.it/r4ds-survey، بحيث يكون survey_id كمتغير نصي و n_pets كمتغير عددي.

  3. اقرأ Google Sheet المعنونة roster من الرابط https://pos.it/r4ds-roster. ينبغي أن يُسمى إطار البيانات الناتج roster وأن يبدو بالشكل التالي.

    #> # A tibble: 12 × 3
    #>    group subgroup    id
    #>    <dbl> <chr>    <dbl>
    #>  1     1 A            1
    #>  2     1 A            2
    #>  3     1 A            3
    #>  4     1 B            4
    #>  5     1 B            5
    #>  6     1 B            6
    #>  7     1 B            7
    #>  8     2 A            8
    #>  9     2 A            9
    #> 10     2 B           10
    #> 11     2 B           11
    #> 12     2 B           12

20.4 ملخص

يُعد برنامج Microsoft Excel و Google Sheets من أكثر أنظمة جداول البيانات شعبية. إن القدرة على التفاعل مع البيانات المخزنة في ملفات Excel و Google Sheets مباشرة من R هي قوة خارقة! في هذا الفصل تعلمت كيفية قراءة البيانات إلى R من جداول البيانات من Excel باستخدام read_excel() من حزمة readxl ومن Google Sheets باستخدام read_sheet() من حزمة googlesheets4. تعمل هذه الدوال بشكل متشابه جداً ولها معاملات متشابهة لتحديد أسماء الأعمدة، وسلاسل قيم NA، والصفوف المراد تجاوزها أعلى الملف الذي تقرؤه، إلخ. بالإضافة إلى ذلك، تجعل كلتا الدالتين من الممكن قراءة ورقة واحدة فقط من جدول البيانات.

من ناحية أخرى، تتطلب الكتابة إلى ملف Excel حزمة ودالة مختلفتين (writexl::write_xlsx()) بينما يمكنك الكتابة إلى Google Sheet باستخدام حزمة googlesheets4، عن طريق الدالة write_sheet().

في الفصل التالي، ستتعلم عن مصدر بيانات مختلف وكيفية قراءة البيانات من ذلك المصدر إلى R: قواعد البيانات (databases).