أنواع البيانات في R
كل قيمة في R تنتمي إلى مجموعة صغيرة من الأنواع الذرية. وأربعة منها تؤدي العمل كله تقريبًا:
- double - الأعداد العشرية:
3.14و-2و1e6. وهذا ما تعنيه R بكلمة "numeric"، وهو الافتراضي لأي عدد تكتبه. - integer - الأعداد الصحيحة المخزنة بدقة، وتُكتب بلاحقة
L: 42L. - character - النص بين علامتي اقتباس:
"hello". - logical - قيمتا الصدق
TRUEوFALSE(إضافة إلىNA، القيمة المفقودة - راجع القيم المفقودة).
وهناك أيضًا النوع complex (2 + 3i) لبعض حسابات معالجة الإشارات العرضية؛ وقد تمضي سنوات دون أن تحتاج إليه. وتخبرك typeof() بنوع أي قيمة:
انتبه إلى السطر الأول: فالعدد العادي من النوع double حتى إن بدا صحيحًا. فالنتيجة typeof(42) هي "double"؛ و42L وحده عدد صحيح. وفي التحليل اليومي نادرًا ما يهم هذا الفرق - إذ تحوّل R بينهما بصمت - لكنه يفسر كثيرًا من المخرجات المحيرة من typeof().
typeof() مقابل class(): لماذا يختلفان
تملك R دالتين تبدوان معًا كأنهما تجيبان عن سؤال "ما هذا؟"، لكنهما تجيبان عن سؤالين مختلفين اختلافًا دقيقًا.
- تُبلغ
typeof()عن نوع التخزين: كيف تستقر القيمة في الذاكرة. - وتُبلغ
class()عن النوع السلوكي: التسمية التي يستخدمها إرسال الأساليب في R ليقرر مثلًا أي دالةprintأوsummaryينفّذ.
وبالنسبة إلى القيم البسيطة تختلف الإجابتان في المفردات فقط:
تقول typeof() إنه double (التخزين)، وتقول class() إنه numeric (السلوك). أما مع البنى فتتسع الفجوة:
المصفوفة مخزنة بوصفها متجهًا صحيحًا عاديًا، لكنها تتصرف بوصفها مصفوفة - وclass() هي ما يخبر R بطباعتها في هيئة شبكة. ويظهر الانقسام نفسه مع إطارات البيانات (تقول typeof إنها "list"، وتقول class إنها "data.frame") ومع التواريخ (تقول typeof إنها "double"، وتقول class إنها "Date").
والقاعدة العملية: استخدم class() حين تريد معرفة ما هو الشيء بالمعنى اليومي، وtypeof() حين تصحح ما تفعله R خلف الكواليس.
فحص الأنواع باستخدام is.*()
لكل نوع دالة فحص مقابلة تعيد TRUE أو FALSE - وهي مفيدة داخل الدوال التي تحتاج إلى التحقق من مدخلاتها:
وثمة فخ يستحق المعرفة: فالدالة is.numeric() تعطي TRUE للنوعين double وinteger معًا (فكلاهما أرقام)، لكن is.integer() تفحص التخزين، فتعطي FALSE مع العدد العادي 42:
إن فاجأك يومًا أن is.integer(7) تعطي FALSE لعدد هو صحيح بداهةً بالمعنى الرياضي - فهذا هو السبب. فالقيمة 7 من النوع double؛ والقيمة 7L من النوع integer.
تحويل الأنواع باستخدام as.*()
يستخدم التحويل الصريح عائلة as.*، بدالة واحدة لكل نوع هدف:
هناك سلوكان يجدر تذكرهما. تقوم as.integer() بـ البتر باتجاه الصفر بدل التقريب - فتصبح 7.9 هي 7. وحين يكون التحويل بلا معنى، لا ترمي R خطأ؛ بل تعيد NA وتصدر تحذيرًا:
شغّل ذلك وسترى NA مع تحذير "NAs introduced by coercion". وهذا مفيد فعلًا - إذ يمكنك تحويل عمود كامل من النص الفوضوي إلى أرقام فتصبح المدخلات غير القابلة للتحليل NA بدل أن تُعطِّل نصك البرمجي - لكنه يعني أيضًا أن قيمة NA صامتة قد تتسلل إلى بياناتك. وحين يكون التحويل مهمًا، تحقق من ظهور قيم NA جديدة بعده.
وتتحول القيم المنطقية إلى أرقام ومنها كما تتمنى: فـ as.numeric(TRUE) تساوي 1، وas.logical(0) تساوي FALSE. ولهذا تحسب sum() عدد قيم TRUE في متجه منطقي:
الإكراه الضمني: قاعدة النوع الواحد للمتجهات
لا يستطيع المتجه الذري في R أن يحمل إلا نوعًا واحدًا. فإذا جمعت قيمًا مختلطة بالدالة c() رقّت R كل شيء بصمت إلى أكثر الأنواع مرونة بين الموجودة، وفق سلّم ثابت:
logical ← integer ← double ← character
صارت القيم المنطقية 1 و0، وصارت الأعداد الصحيحة أعدادًا عشرية، وفي السطر الأخير صار كل شيء نصًا - فالنوع character ينتصر دائمًا، لأن أي قيمة يمكن كتابتها سلسلةً نصية. ويمكنك التأكد باستخدام typeof():
الإكراه هو المصدر الأول لأخطاء "لماذا صار عمودي نصًا؟": إذ تكفي سلسلة "N/A" شاردة في عمود من الأرقام لإكراه المتجه بأكمله على النوع النصي. وحين يحدث ذلك، أصلح القيمة المسيئة أولًا، ثم حوّل مجددًا باستخدام as.numeric().
ما تخرج به
- أربعة أنواع تؤدي العمل الأساسي: double ("numeric")، وinteger (
42L)، وcharacter، وlogical. -
typeof()= كيف يُخزَّن؛ وclass()= كيف يتصرف. فالقيمة3.14من النوعdoubleوتتصرف بوصفهاnumeric. - افحص بـ
is.numeric()وأخواتها؛ وحوّل بـas.numeric()وأخواتها - والتحويلات الفاشلة تعطيNAمع تحذير لا خطأ. - تحمل المتجهات نوعًا واحدًا فقط؛ والخلط يكره القيم وفق المسار logical ← integer ← double ← character.
التالي: نظرة أقرب على الأنواع العددية - التقريب، والدوال الرياضية، ومفاجآت الفاصلة العائمة التي تشترك فيها كل اللغات.
الأسئلة الشائعة
ما أنواع البيانات الأساسية في R؟
الأنواع الذرية هي double (الأعداد العشرية، وهو ما تسميه R "numeric")، وinteger (الأعداد الصحيحة المكتوبة بلاحقة L مثل 42L)، وcharacter (النص بين علامتي اقتباس)، وlogical (TRUE/FALSE)، إضافة إلى complex نادر الاستخدام. وكل ما هو أكبر - المتجهات وإطارات البيانات والقوائم - مبني من هذه الأنواع.
ما الفرق بين typeof() وclass() في R؟
تُبلغ typeof() عن كيفية تخزين القيمة في الذاكرة (نوعها منخفض المستوى)؛ بينما تُبلغ class() عما تتصرف القيمة على أنه، وهو ما يتحكم في الأساليب التي ترسلها R. وبالنسبة إلى عدد عشري بسيط يختلفان في التسمية فقط - فـ typeof(3.14) تعطي "double" بينما class(3.14) تعطي "numeric" - وبالنسبة إلى بنى مثل إطارات البيانات تقول typeof() إنها "list" بينما تقول class() إنها "data.frame".
كيف تحوّل الأنواع في R؟
استخدم عائلة as.*: فالدالة as.numeric("3.5") تعطي 3.5، وas.character(42) تعطي "42"، وas.integer(7.9) تبتر القيمة إلى 7. وحين يستحيل التحويل، تعيد R القيمة NA وتصدر تحذيرًا بدل خطأ - فـ as.numeric("abc") تساوي NA.
ما هو الإكراه في R؟
حين تجمع أنواعًا مختلفة في متجه واحد، تحوّل R كل شيء بصمت إلى النوع الأكثر مرونة، وفق الترتيب logical ← integer ← double ← character. لذا يصبح c(1, 2, "three") متجهًا نصيًا: "1" "2" "three".