read.csv() تحوّل ملف CSV إلى data frame
قراءة ملف CSV في R استدعاء دالة واحد مدمج في اللغة، ولا يحتاج إلى حزمة:
sales <- read.csv("sales.csv")
تقرأ read.csv() الملف، وتستخدم السطر الأول أسماءً للأعمدة، وتخمّن نوعًا لكل عمود، وتُرجع إطار بيانات. وهذه هي القصة كلها مع ملف حسن السلوك.
ويمكنك رؤيتها وهي تعمل دون أي ملف على الإطلاق، لأن read.csv() تقبل أيضًا نص CSV خامًا عبر الوسيط text، وهو المحلّل نفسه لكن يتغذى على سلسلة نصية بدل اسم ملف:
str() هي أول ما ينبغي تشغيله على أي شيء تستورده، فهي تعرض كل عمود مع النوع المخمَّن له. وهنا وصل name نصًا، وscore عددًا صحيحًا، وpassed منطقيًا، وكلها مخمَّنة من القيم. وللفحص البصري السريع، تعرض head(students) الصفوف الأولى وتعدّها nrow(students). وإذا ظهر عمود توقعته عدديًا بالنوع chr، فثمة شيء في ذلك العمود ليس رقمًا، كتعليق شارد أو "N/A" أو فاصل آلاف، وتلك هي اللحظة المناسبة لإصلاحه، لا بعد ثلاثة رسوم بيانية.
الوسائط المهمة
تملك read.csv() عشرات المعاملات، لكنك ستستخدم نحو خمسة منها.
header: هل يحمل السطر الأول أسماء الأعمدة. القيمة الافتراضية TRUE. وإذا كان ملفك يبدأ بالبيانات مباشرة، مرّر header = FALSE فيسمي R الأعمدة V1 وV2 وهكذا.
sep: فاصل الحقول، وقيمته الافتراضية ",". ويكتب جزء كبير من أوروبا ملفات CSV بفواصل منقوطة لأن الفاصلة هناك علامة عشرية. ويوفر R صيغة مضبوطة مسبقًا لذلك تحديدًا: تستخدم read.csv2() القيمتين sep = ";" وdec = ",":
تخرج قيم الطول أعدادًا سليمة، فقد عرفت read.csv2 أن 1,63 تعني واحدًا وثلاثة وستين من المئة. ولو استخدمت read.csv العادية على هذا الملف لحصلت على عمود واحد ممسوخ، لأن لا شيء يقسم بشكل صحيح عند فاصلة هي في الحقيقة علامة عشرية.
na.strings: أي السلاسل النصية تُعد قيمًا مفقودة. افتراضيًا تتحول "NA" وحدها إلى قيمة مفقودة. أما التصديرات الحقيقية فتكتب البيانات المفقودة خلايا فارغة أو "missing" أو "-" أو "NULL"، وما لم تصرّح بها فإنها تصل نصًا وتجرّ العمود كله إلى النوع النصي:
ومع na.strings الصحيحة تصبح الخلايا الفارغة قيم NA حقيقية ويبقى score عدديًا. وبدونها تكون "missing" مجرد كلمة، ويصبح العمود نصًا.
skip: عدد الأسطر التي تُتجاهل قبل بدء البيانات. فالتصديرات تحب وضع سطر عنوان أو سطرين فوق الجدول الفعلي، وskip = 2 تقفز فوقهما.
colClasses: فرض أنواع الأعمدة بدل ترك R يخمّن. والضحية الكلاسيكية هي كل ما يحمل أصفارًا بادئة، كالرموز البريدية وأرقام الهواتف ومعرّفات المنتجات، إذ يقرأها مخمّن R أعدادًا فيتلف الأصفار:
تحولت 00501 إلى 501. كانت البيانات صحيحة في الملف وخاطئة في R، بصمت. وتخبر colClasses = c("integer", "character") المحلّل بنوع كل عمود بالترتيب، فتنجو الأصفار.
وثمة وسيط لم تعد بحاجة إليه: stringsAsFactors. فطوال معظم تاريخ R كانت read.csv() تحوّل كل عمود نصي إلى factor ما لم تقل غير ذلك، وهو ما تسبب في قدر هائل من الالتباس. ومنذ R 4.0 صارت القيمة الافتراضية FALSE، فيبقى النص نصًا. وستظل ترى stringsAsFactors = FALSE منثورة في الدروس الأقدم، وهي على إصدار حديث من R غير ضارة لكنها زائدة.
مسارات الملفات: السبب الأول لفشل read.csv
الخطأ الذي يصادفه كل متعلم لـ R في أسبوعه الأول:
Error in file(file, "rt") : cannot open the connection
In addition: Warning message:
cannot open file 'sales.csv': No such file or directory
الملف موجود، فأنت تنظر إليه في مدير الملفات. لكن R لا ينظر هناك. فاسم الملف المجرد مثل "sales.csv" يُحل نسبةً إلى دليل العمل الخاص بـ R، وهو عادةً ليس المجلد الذي يوجد فيه ملفك. وسطران يشخّصان المشكلة:
getwd() # where R is actually looking
file.exists("sales.csv") # FALSE means the path is wrong, full stop
إذا أعادت file.exists() القيمة FALSE فلن ينفع أي قدر من إعادة التشغيل، بل أصلح المسار. فإما أن تعطي المسار الكامل (read.csv("C:/Users/ada/data/sales.csv")، والشرطات المائلة الأمامية تعمل على Windows أيضًا وهي أأمن من المائلة الخلفية)، وإما أن تنقل دليل عمل R إلى مجلد البيانات بـ setwd(). أما ماهية دليل العمل وكيف تديره المشاريع ولماذا يُستهجن استخدام setwd() داخل السكربتات فيغطيه مستند دليل العمل.
اجعل file.exists() رد فعل تلقائيًا لديك، فهي تحوّل "فشل استيراد غامض" إلى "خطأ إملائي في مسار" خلال ثانية واحدة.
قراءة ملف CSV من رابط مباشرةً
تقبل read.csv() رابطًا في كل موضع تقبل فيه اسم ملف، وتنزّل الملف نيابة عنك:
url <- "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/penguins.csv"
penguins <- read.csv(url)
str(penguins)
وهذا مفيد للدروس ومجموعات البيانات العامة، فلا خطوة تنزيل ولا مشكلات مسارات. والمقابل أن سكربتك صار يعتمد على الشبكة وعلى بقاء ذلك الرابط حيًّا، لذا نزّل مرة واحدة واقرأ النسخة المحلية لأي شيء ستعيد تشغيله كثيرًا.
readr::read_csv: البديل الأسرع والأكثر إفصاحًا
توفر tidyverse قارئ CSV خاصًا بها ضمن حزمة readr: وهو read_csv() (بشرطة سفلية لا نقطة). ويستحق المعرفة لأن معظم شيفرة R الحديثة التي ستقرأها تستخدمه:
install.packages("readr") # once
library(readr) # every session
flights <- read_csv("flights.csv")
والفروق العملية عن read.csv():
- السرعة: أسرع بوضوح مع الملفات التي تحوي مئات الآلاف من الصفوف.
- تُرجع tibble: وهو data frame محدّث يطبع معاينة مضغوطة بدل إغراق الطرفية.
- تُبلغ عن تخميناتها: إذ تطبع بعد القراءة النوع المكتشف لكل عمود، فيظهر فورًا أي عمود حُلِّل نصًا بينما توقعت أعدادًا، بدل أن يكون مفاجأة لاحقة.
- لا تمسخ أسماء الأعمدة أبدًا: فـ
read.csv()تعيد كتابة ترويسة مثلfirst nameلتصبحfirst.name، بينما تُبقيهاread_csv()كما هي.
وللملف الصغير أي من الدالتين يفي بالغرض. والجأ إلى read_csv() حين تكبر الملفات أو حين تكون بقية سكربتك من tidyverse أصلًا. وكل ما سبق عن المسارات والفواصل (توجد read_csv2() أيضًا) وسلاسل القيم المفقودة (na =) ينطبق هنا بأسماء وسائط مختلفة قليلًا.
وحين تنتهي من التحليل، فإن رحلة العودة، أي تصدير نتائجك إلى ملف، يغطيها مستند write.csv.
الخلاصة
- تقرأ
df <- read.csv("file.csv")ملف CSV إلى data frame، فافحصه فورًا بـstr()وhead(). - تحلّل
read.csv(text = "...")سلسلة CSV مباشرة، وهي ممتازة للتجارب والأمثلة الصغيرة. - الوسائط التي تستحق مكانها:
headerوsep(أوread.csv2للفواصل المنقوطة) وna.stringsوskipوcolClasses. - تعني "Cannot open file" أن دليل العمل ليس حيث تظن، وتحسم
getwd()وfile.exists()الأمر فورًا. -
readr::read_csv()هي نسخة tidyverse الأسرع: tibbles، وأنواع أعمدة مُبلَّغ عنها، وأسماء بلا مساس.
في المستند التالي: الملفات التي ليست نصًا صرفًا، أي قراءة جداول Excel بحزمة readxl.
الأسئلة الشائعة
كيف تقرأ ملف CSV في R؟
باستخدام read.csv("file.csv")، وهي مدمجة ولا تحتاج إلى أي حزمة. تُرجع data frame بعمود لكل عمود في الملف، وتستخدم السطر الأول أسماءً للأعمدة. أسنِد النتيجة إلى متغير: df <- read.csv("sales.csv")، ثم افحصها بـ str(df) وhead(df).
لماذا تقول read.csv الرسالة 'cannot open file: No such file or directory'؟
لأن R يبحث عن الملف نسبةً إلى دليل عمله، وهو على الأرجح ليس المجلد الذي يوجد فيه ملفك. شغّل getwd() لترى أين يبحث R، وfile.exists("file.csv") لتأكيد عدم العثور عليه. وأصلح ذلك بمسار كامل، أو بضبط دليل العمل على مجلد الملف.
ما الفرق بين read.csv وread_csv في R؟
read.csv() من base R، متاحة دائمًا وتُرجع data frame عاديًا. أما read_csv() فتأتي من حزمة readr، وهي أسرع مع الملفات الكبيرة، وتُرجع tibble، ولا تمس أسماء أعمدتك أبدًا، وتطبع أنواع الأعمدة التي خمّنتها فتلتقط التحليل الخاطئ فورًا. وللملفات الصغيرة أي منهما يفي، أما للملفات الكبيرة أو سلاسل tidyverse فاستخدم read_csv().
كيف تقرأ ملف CSV مفصولًا بفاصلة منقوطة في R؟
استخدم read.csv2("file.csv")، فهي read.csv مضبوطة مسبقًا على العرف الأوروبي: الفاصلة المنقوطة فاصلًا للحقول والفاصلة علامةً عشرية. أو مرّر sep = ";" (وdec = "," عند الحاجة) إلى read.csv العادية.