ما هو دليل العمل
تعمل كل جلسة R "داخل" مجلد واحد بالضبط: هو دليل العمل. وكلما استخدمت مسارًا نسبيًا - أي مسارًا لا يبدأ من جذر نظام الملفات - حلّته R بالنسبة إلى ذلك المجلد. فالتعبير read.csv("data.csv") لا يعني "ابحث عن data.csv في مكان ما على حاسوبي"؛ بل يعني "افتح الملف المسمى data.csv في دليل العمل".
وهذه الحقيقة وحدها تفسر أشيع خطأ لدى المبتدئين في R:
read.csv("data.csv")
# Error in file(file, "rt") : cannot open the connection
# In addition: Warning message: cannot open file 'data.csv': No such file or directory
فالملف موجود - لكن ليس في المجلد الذي تنظر إليه R حاليًا. ولا خطأ في استدعاء read.csv لديك؛ بل إن دليل عمل الجلسة وموقع الملف غير متفقين. والدالتان التاليتان تحوّلان ذلك من لغز إلى تشخيص في عشر ثوانٍ.
الدالتان getwd() وsetwd()
تخبرك getwd() (أي احصل على دليل العمل) بالمكان الذي تبحث فيه R الآن:
نفّذ ذلك فترى مسارًا مطلقًا - وهو المجلد الذي ترسو عنده هذه الجلسة. وتغيّره setwd() (أي اضبط دليل العمل):
setwd("C:/Users/rosa/projects/sales-analysis") # Windows: use / not \
setwd("/Users/rosa/projects/sales-analysis") # macOS / Linux
getwd() # confirm it took
وثمة ملاحظتان تقنيتان. على Windows، اكتب المسارات بالرمز / (أو بالرمز \\ المضاعف) - فالرمز \ المفرد محرف هروب داخل سلاسل R النصية، ولذا فإن "C:\Users" خطأ نحوي. كما تطلق setwd() خطأً فوريًا إذا لم يكن المجلد موجودًا، وهو ألطف إخفاق ستحصل عليه طوال يومك: إذ لا يمكنه أن يتركك مشيرًا بصمت إلى الموضع الخطأ.
ولذا فإن روتين التصحيح لأي خطأ "cannot open file" هو: getwd() لترى أين تبحث R، ثم قارن ذلك بالمكان الذي يقيم فيه الملف فعلًا.
list.files(): انظر إلى ما تراه R
الأداة الثالثة في العدة تسرد محتويات دليل العمل - أي حرفيًا مجموعة الأسماء التي يستطيع مسار نسبي بلوغها:
فإذا ظهر الملف الذي تحاول قراءته في تلك المخرجات، فسيجده read.csv("thatfile.csv"). وإن لم يظهر، فلن ينفع أي قدر من إعادة كتابة سطر read.csv - فدليل العمل هو ما ينبغي إصلاحه. ويلقي list.files("data") نظرة داخل مجلد فرعي، ويرشّح list.files(pattern = "\\.csv$") النتائج إلى ملفات CSV وحدها. وحين يبدو اسم ملف صحيحًا لكنه لا يُوجد، تكشف list.files() أيضًا عن المتهمين الكلاسيكيين: امتداد .txt غير مرئي ألحقه محرر نصوص، أو الفرق بين Data.csv وdata.csv على نظام حساس لحالة الأحرف.
لماذا ينكسر setwd() المكتوب يدويًا
والآن الجزء ذو الرأي. سترى نصوصًا برمجية تفتتح بالسطر:
setwd("C:/Users/me/Desktop/stuff/project3/final_FINAL")
ذلك السطر يعمل على جهاز واحد بالضبط: جهاز مؤلفه. أما على حاسوبك المحمول، أو جهاز Mac الخاص بزميلك، أو خادم القسم، أو حاسوبك أنت بعد إعادة تنظيم، فسيطلق خطأً في السطر الأول. فكتابة مسار مطلق يدويًا تخبز تخطيط مجلدات شخص واحد داخل شيفرة يُفترض أن تعيش بعده.
والحل هو قلب المسؤولية: لا تجعل النص البرمجي يبحث عن المجلد - بل ابدأ R في المجلد الصحيح، واستخدم مسارات نسبية داخل النص البرمجي. وعمليًا:
- استخدم مشاريع RStudio. ففتح ملف
.Rprojيضبط دليل العمل على مجلد المشروع تلقائيًا، على أي جهاز. وداخل النص البرمجي، يعملread.csv("data/sales.csv")عندئذ لكل من يملك المشروع. - والتشغيل من الطرفية يؤدي المهمة نفسها: فالأمر
Rscript analysis.Rيستخدم المجلد الذي تطلقه منه دليلَ عمل (راجع تشغيل نصوص R البرمجية). - وتمضي حزمة here خطوة أبعد لمن يريد ذلك - إذ يبني
here::here("data", "sales.csv")المسارات انطلاقًا من جذر المشروع بغض النظر عن المكان الذي تاه إليه دليل العمل - لكن المشاريع مع المسارات النسبية تغطي معظم الاحتياجات دونها.
والدالة setwd() نفسها ليست شرًا - فلا بأس بها في العمل التفاعلي أثناء الاستكشاف. والرائحة الكريهة تحديدًا هي استدعاء setwd() مطلق وخاص بجهاز بعينه مودع في مستهل نص برمجي مشترك.
مساحة العمل: ls() وrm()
بجوار سؤال "في أي مجلد أنا" يقع الجزء الثاني من حالة الجلسة: مساحة العمل (البيئة العامة) - أي كل كائن أنشأته في هذه الجلسة. وتسردها ls()، وتزيلها rm():
يطبع استدعاء ls() الأخير القيمة character(0) - أي مساحة عمل فارغة. والتعويذة rm(list = ls()) تبدو غريبة حتى تحللها: فـ ls() تعيد المتجه النصي لكل أسماء الكائنات، وrm(list = ...) تحذف كل اسم في ذلك المتجه. وهي المكافئ البرمجي لأيقونة المكنسة في لوحة البيئة في RStudio.
واعرف ما لا تفعله: فالحزم المحمّلة بـ library() تبقى محمّلة، والخيارات تبقى مضبوطة، ودليل العمل يبقى في مكانه. فهي تنظّف الكائنات ولا شيء آخر - ولذا فهي ليست بديلًا عن إعادة تشغيل R حين تريد صفحة نظيفة فعلًا.
مسح الطرفية، وفخ ملف .RData
مسح الطرفية - أي سجل الأوامر والمخرجات - أمر تجميلي ومنفصل عن تنظيف مساحة العمل. وفي RStudio يتم بالاختصار Ctrl+L؛ وفي الشيفرة، يرسل التعبير الغريب cat("\014") محرف تغذية الصفحة الذي تفسره معظم الطرفيات بمعنى "امسح الشاشة". ولا يمس أي منهما كائناتك.
وأخيرًا، الفخ. فحين تخرج من R تعرض عليك "save workspace image?" - أي كتابة كل كائن إلى ملف .RData مخفي يُعاد تحميله بصمت في المرة التالية التي تبدأ فيها R في ذلك المجلد. ارفض ذلك، وعطّله نهائيًا (في RStudio: Tools ← Global Options ← ألغِ تحديد "Restore .RData" واضبط "Save workspace" على Never). فمساحة العمل المستعادة تعني أن جلستك تبدأ ملوثة بكائنات من أيام مضت، أنشأتها شيفرة ربما عدّلتها أو حذفتها منذ ذلك الحين. فيبدو نصك البرمجي ناجحًا - لأنه يتكئ على model_v2 بائت لم يعد أي شيء في الملف ينشئه - حتى اللحظة التي يعمل فيها على جهاز نظيف فينهار.
والعادة المهنية عكس ذلك: ابدأ نظيفًا، وأعد تشغيل النص البرمجي. فإذا كان تحليلك نصًا برمجيًا يعمل من بدايته حتى نهايته في جلسة جديدة، فهو قابل لإعادة الإنتاج بحكم البناء؛ ومساحة العمل مخرجات قابلة للتخلص لا حالة ثمينة. أعد تشغيل R كثيرًا (في RStudio: Session ← Restart R) تحديدًا لتثبت أن نصك البرمجي ما يزال قائمًا بذاته.
ما تخرج به
- تُحل المسارات النسبية بالنسبة إلى دليل العمل؛ وتعرضه
getwd()، وتغيّرهsetwd()، وتعرضlist.files()ما يمكن بلوغه. - الرسالة "cannot open file" تعني دائمًا تقريبًا أن دليل العمل ومجلد الملف غير متفقين - شخّص بـ
getwd()معlist.files(). - لا تودع أبدًا استدعاء
setwd()مطلقًا في نص برمجي مشترك - استخدم مشاريع RStudio (أو شغّل النص البرمجي من مجلده) مع مسارات نسبية. - تسرد
ls()كائنات مساحة العمل؛ وتزيلrm(x)واحدًا؛ وينظّفهاrm(list = ls())كلها - وتبقى الحزم والخيارات. - الاختصار Ctrl+L (أو
cat("\014")) يمسح نص الطرفية وحده. - لا تحفظ ملف
.RDataأو تستعده تلقائيًا: ابدأ نظيفًا وأعد تشغيل النص البرمجي - فذلك ما يجعل عملك قابلًا لإعادة الإنتاج.
التالي: قراءة البيانات الحقيقية - الدالة read.csv() وأخواتها، بعد أن صارت R تبحث في المجلد الصحيح لتجدها.
الأسئلة الشائعة
كيف أضبط دليل العمل في R؟
استدعِ setwd("path/to/folder") لتغييره وgetwd() لرؤية الحالي. وفي RStudio يمكنك استخدام القائمة أيضًا: Session ← Set Working Directory. لكن لأي شيء ستعيد تشغيله أو تشاركه، فضّل مشروع RStudio (الذي يضبط دليل العمل تلقائيًا) على سطر setwd() مكتوب يدويًا - فالمسارات المطلقة تنكسر على كل جهاز آخر.
كيف أنظّف البيئة في R؟
يزيل rm(list = ls()) كل كائن من البيئة العامة: إذ تسرد ls() كل أسماء الكائنات وتحذفها rm(). ولإزالة كائن واحد فقط، استخدم rm(x). ولاحظ أن هذا ينظّف الكائنات وحدها - فالحزم المحمّلة تبقى محمّلة، ونص الطرفية لا يُمس (وذلك هو Ctrl+L في RStudio).
لماذا تقول read.csv إنها 'cannot open file' مع أن الملف موجود؟
لأن R تحل المسارات النسبية بالنسبة إلى دليل عملها، ودليلك يشير إلى مكان غير المجلد الذي يحوي الملف. نفّذ getwd() لترى أين تبحث R وlist.files() لترى ما تراه هناك. وأصلح الأمر بفتح المشروع عند المجلد الصحيح (أو باستخدام setwd())، لا بلصق مسار مطلق في النص البرمجي.