Menu
flag Ar iconالعربيةdown icon

كتابة البيانات إلى ملفات في R (write.csv وsaveRDS)

التصدير من R: ‏write.csv مع row.names = FALSE (دائمًا)، وwrite.table لفواصل أخرى، وsaveRDS للرحلات الكاملة التي لا تفقد أنواع بياناتك.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

‏write.csv() تصدّر data frame

إخراج إطار بيانات من R إلى ملف تستطيع برامج أخرى فتحه هو استدعاء واحد:

write.csv(df, "results.csv", row.names = FALSE)

هذا هو السطر الذي ينبغي حفظه، بما فيه row.names = FALSE الذي سنبرره بعد قليل. والوسيط الأول هو إطار البيانات، والثاني اسم الملف، ولا تُرجع الدالة شيئًا: فمهمتها كلها هي الأثر الجانبي المتمثل في إنشاء الملف.

ويمكنك أن ترى بالضبط ما تنتجه write.csv() دون المساس بالقرص، لأنها تقبل أي اتصال في الموضع الذي تتوقع فيه اسم ملف، بما في ذلك stdout() الذي يطبع فحسب:

وها هو ملف CSV: سطر ترويسة، وسطر لكل صف، والقيم النصية محاطة بعلامات اقتباس والأرقام مجردة. (والاقتباس ناتج عن القيمة الافتراضية quote = TRUE، وكل قارئ CSV سائد يتعامل معه، فاتركه كما هو.) والكتابة إلى ملف حقيقي هي الاستدعاء نفسه مع وضع "results.csv" محل stdout().

لماذا ينبغي أن تكون row.names = FALSE إعدادك الافتراضي

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

اكتسب التصدير عمودًا أول بلا اسم يحمل "1" و"2"، وعند إعادة الاستيراد اضطرت read.csv() إلى تسميته بشيء ما، فاخترعت X. وهذا هو عمود X الغامض الذي يحيّر كل مبتدئ: فهو ليس بيانات، بل ترقيم صفوف R يتسرب إلى ملفك. وكرر ذلك بضع مرات عبر دورة تصدير واستيراد وستتراكم لديك X وX.1 وX.2 وهكذا.

ونادرًا جدًا ما تحمل أسماء الصفوف معلومات حقيقية، وحين تحملها فإن تلك المعلومات تنتمي إلى عمود سليم أصلًا. لذا اجعل row.names = FALSE رد فعل تلقائيًا. وكان ينبغي على الأرجح أن تكون القيمة الافتراضية، لكنها ليست كذلك لأسباب تتعلق بالتوافق الخلفي، فالعادة مسؤوليتك أنت.

أين يحط الملف

تكتب write.csv(df, "results.csv") داخل دليل العمل الخاص بـ R، وإذا لم يكن ذلك حيث توقعت فسيبدو أن الملف اختفى. لم تفشل العملية، بل الملف موجود في المجلد الذي تطبعه getwd(). وهذه المصيدة نفسها الموجودة في القراءة لكن معكوسة، وتحلها الأداتان نفساهما:

getwd()                          # the folder your file went to
write.csv(df, "C:/Users/ada/results/out.csv", row.names = FALSE)   # or: remove all doubt

تجعل المسارات الكاملة (والشرطات المائلة الأمامية تعمل على كل المنصات بما فيها Windows) السكربتات بلا لبس. وكيفية ضبط دليل العمل وإدارته يغطيها مستند دليل العمل.

‏write.table() لفواصل أخرى ومسألة الإلحاق

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

مفصول بعلامات جدولة وبلا اقتباس، وهي الصيغة التي تفضلها كثير من الأدوات اللاحقة. ولاحظ أنك مع write.table() تضبط كل شيء صراحةً، فهي لا ترث الفاصلة ولا إعدادات الاقتباس الافتراضية من write.csv.

وثمة أمر واحد ترفض write.csv() فعله وهو الإلحاق: فتمرير append = TRUE يعطيك تحذيرًا بأن الإعداد قد تُجوهل، لأن الإلحاق سيعيد كتابة سطر الترويسة في وسط الملف. وإذا كنت تحتاج فعلًا إلى إضافة صفوف إلى ملف CSV موجود، كتسجيل النتائج عبر عدة تشغيلات، فانزل إلى write.table() واكبح الترويسة بنفسك:

write.table(new_rows, "log.csv", sep = ",",
            append = TRUE, col.names = FALSE, row.names = FALSE)

وإذا كنت ضمن tidyverse، فإن readr::write_csv(df, "out.csv") هي نظيرة read_csv(): المخرجات نفسها، وأسرع مع الأطر الكبيرة، ولا تكتب أسماء الصفوف من الأساس، أي وسيط أقل تتذكره.

‏saveRDS() وreadRDS(): رحلات ذهاب وإياب دقيقة

صيغة CSV صيغة فاقدة. فهي تخزّن نصًا لا غير، لذا يصبح عمود التاريخ السلسلة "2026-08-07"، ويصبح العامل تسمياته مع ضياع ترتيب المستويات، وتختفي السمات، ويجب إعادة تخمين كل نوع عند الاستيراد (بشكل صحيح غالبًا وخاطئ أحيانًا، والأصفار البادئة خير مثال). كما لا تستطيع CSV حمل أكثر من جدول مستطيل واحد: فالنموذج المعاير والقائمة والمصفوفة ذات أسماء الأبعاد ليس لها تمثيل في CSV إطلاقًا.

وحين يكون الملف مخصصًا لـ R، كحفظ بيانات اليوم النظيفة لجلسة الغد، فتخطَّ الصيغ النصية تمامًا:

saveRDS(df, "clean_data.rds")        # today

df <- readRDS("clean_data.rds")      # tomorrow: identical object, types intact

تسلسل saveRDS() كائن R واحدًا، أي كائن، بدقة تامة. وتعيده readRDS() بايتًا ببايت: فتحتفظ العوامل بمستوياتها، وتبقى التواريخ تواريخ، ولا يُعاد تخمين شيء. وتعمل مع أي شيء، من إطار بيانات إلى قائمة أطر بيانات إلى نموذج انحدار معاير. وملف .rds ثنائي مضغوط، لذا يكون عادةً أصغر من CSV أيضًا. والتكلفة الوحيدة أن لا شيء سوى R يستطيع فتحه، وهو بالضبط سبب القاعدة العملية: ‏CSV للمشاركة مع البشر والأدوات الأخرى، وRDS للحفظ لنفسك.

‏save() وload() ولماذا تفوز RDS عادةً

ستصادف أيضًا زوجًا أقدم. تكتب save() عدة كائنات مسمّاة في ملف .RData واحد، وتستعيدها load() إلى مساحة عملك بأسمائها الأصلية:

save(df, model, params, file = "session.RData")
load("session.RData")    # df, model, params silently appear

وهذه السهولة هي المشكلة نفسها. فـ load() هي التي تقرر أسماء المتغيرات لا أنت، إذ تزرع الكائنات في بيئتك دون أن تراها، وتكتب بصمت فوق أي شيء يحمل تلك الأسماء أصلًا. وبعد ستة أشهر لن يتذكر أحد ما يحتويه session.RData أصلًا دون تحميله لاكتشاف ذلك.

أما readRDS() فواجهتها صادقة: أنت تختار الاسم (df <- readRDS(...))، ولا يُكتب فوق شيء من وراء ظهرك، والملف الواحد يعني كائنًا واحدًا. وما لم تكن بحاجة تحديدًا إلى تجميع عدة كائنات في ملف واحد، فضّل RDS. (وإذا احتجت إلى تجميعة فعلًا، فوضع الكائنات في قائمة مسمّاة وحفظها بـ saveRDS() يعطيك الأثر نفسه بلا أي مفاجآت.)

الخلاصة

  • الصيغة write.csv(df, "out.csv", row.names = FALSE) هي التصدير كله، ونعم، دائمًا مع row.names = FALSE.
  • عمود X الغامض عند إعادة الاستيراد هو تسرب أسماء الصفوف إلى الملف، فالقيمة الافتراضية كتبتها وأنت لا ينبغي أن تفعل.
  • تحط الملفات في دليل العمل، وتعثر getwd() على التصديرات "الضائعة"، وتمنعها المسارات الكاملة.
  • تتعامل write.table() مع فواصل أخرى ومع الإلحاق الحقيقي (بـ col.names = FALSE)، بينما لا تستطيع write.csv() الإلحاق عمدًا.
  • تعيد saveRDS() / readRDS() أي كائن R بدقة تامة مع أنواعه وعوامله وسماته. فـ CSV للمشاركة وRDS لنفسك.
  • فضّل RDS على save()/load(): أسماء صريحة وبلا كتابة صامتة فوق البيانات.

في المستند التالي: نوع بيانات يستحق صفحة خاصة قبل أن يلدغك في استيراد، وهو التواريخ.

الأسئلة الشائعة

كيف تصدّر data frame إلى ملف CSV في R؟

بالصيغة write.csv(df, "output.csv", row.names = FALSE). والجزء row.names = FALSE مهم، فبدونه يكتب R أرقام صفوفه الداخلية عمودًا أول إضافيًا، يظهر عمودًا غامضًا اسمه X عند إعادة استيراد الملف.

لماذا يحتوي ملف CSV المصدَّر على عمود X عند إعادة قراءته؟

لأن القيمة الافتراضية في write.csv هي row.names = TRUE، فقد كتبت أرقام الصفوف عمودًا أول بلا اسم، ثم سمّته read.csv بالاسم X عند إعادة القراءة. صدّر باستخدام row.names = FALSE فيختفي العمود الشبح.

ما الفرق بين write.csv وsaveRDS في R؟

تُنتج write.csv جدولًا نصيًا يستطيع أي أحد فتحه، لكنها تخزّن نصًا فقط، فمستويات العوامل وأنواع التواريخ والسمات والكائنات غير الجدولية تُسطَّح أو تُفقد، ويجب إعادة تخمين الأنواع عند الاستيراد. أما saveRDS فتخزّن كائن R واحدًا بايتًا ببايت، وتُرجعه readRDS كما كان تمامًا. فاستخدم CSV للمشاركة مع الآخرين، وRDS للحفظ لجلسات R الخاصة بك.

أين تحفظ write.csv الملف؟

في دليل العمل الحالي لـ R، ما لم تعطِ مسارًا كاملًا. شغّل getwd() لترى أين هو، فإذا بدا أن ملفك اختفى فهو موجود في المجلد الذي تطبعه getwd(). وتمرير مسار كامل مثل "C:/Users/ada/results/out.csv" يزيل أي التباس.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن