numeric وinteger: نوعا الأرقام في R
تخزّن R الأرقام بطريقتين. النوع numeric (المخزن بوصفه عددًا عائمًا مزدوج الدقة) هو الافتراضي - فكل عدد تكتبه من النوع double ما لم تقل غير ذلك. والنوع integer نوع منفصل للأعداد الصحيحة الدقيقة تطلبه بلاحقة L:
وعمليًا نادرًا ما تحتاج إلى الاهتمام: فـ R تحوّل بينهما بصمت، وis.numeric() تعطي TRUE لكليهما (راجع أنواع البيانات لنظام الأنواع الكامل). والموضع الوحيد الذي يظهر فيه الفرق هو القسمة - فالمعامل / يعيد دائمًا قيمة من النوع double، حتى بين عددين صحيحين:
وتظهر الأعداد الصحيحة غالبًا بوصفها مخرجات دوال العد (length() وseq_len() وnrow()) وقيمًا للفهرسة. وحين تحتاج إلى قسمة صحيحة، تملك R معاملًا مخصصًا - وسنتناوله بعد قليل.
عائلة التقريب
تمنحك R خمس طرق لتشذيب عدد، ولكل منها معنى مميز:
round(x, digits)- إلى عدد من المنازل العشرية (والافتراضي 0).floor(x)- إلى أقرب عدد صحيح أصغر، دائمًا باتجاه سالب اللانهاية.ceiling(x)- إلى أقرب عدد صحيح أكبر، دائمًا باتجاه موجب اللانهاية.trunc(x)- تقطع الكسور، دائمًا باتجاه الصفر. ولاحظ الفرق مع الأعداد السالبة: فـtrunc(-2.7)تعطي-2، بينماfloor(-2.7)تعطي-3.signif(x, digits)- إلى عدد من الأرقام المعنوية، لا المنازل العشرية: فـsignif(123456, 2)تعطي120000.
ومفاجأة شهيرة واحدة: تستخدم round() قاعدة النصف إلى الزوجي (تقريب المصرفيين) مع الأنصاف الدقيقة، وفق معيار IEEE 754:
يطبع ذلك 0 2 2 4 - إذ يُقرَّب كل نصف إلى أقرب عدد زوجي. وهذه ليست علة؛ بل تمنع الانحياز المنهجي نحو الأعلى حين تجمع كثيرًا من القيم المقرّبة. وإذا احتاج تقرير ما إلى التقريب المدرسي، فأضف دفعة صغيرة أو نسّق القيمة في طبقة العرض بدلًا من ذلك.
الدوال الرياضية اليومية
تعمل الأساسيات تمامًا كما تتوقع:
فـ sqrt() هي الجذر التربيعي، وabs() القيمة المطلقة، والمعامل ^ هو الأس، وexp(x) هي e مرفوعة للأس x - لذا فإن exp(1) هي عدد أويلر، نحو 2.718282.
والدالة الوحيدة التي تربك الناس هي log(). في R، الدالة log() هي اللوغاريتم الطبيعي (بالأساس e) لا بالأساس 10:
يطبع السطر الأول نحو 4.60517 - لا القيمة 2 التي يتوقعها من يفكر بالأساس 10. فالجأ إلى log10() وlog2() حين تقصد هذين الأساسين، أو مرر base = صراحة. (وهذا العرف قياسي في الإحصاء، حيث اللوغاريتم الطبيعي هو الافتراضي.)
معامل الباقي %% والقسمة الصحيحة %/%
يغطي معاملان حساب الباقي:
المعامل %% هو باقي القسمة: فقسمة 17 على 5 تعطي 3 والباقي 2. والمعامل %/% هو القسمة الصحيحة: كم عدد الخمسات الكاملة التي تدخل في 17. ويحقق المعاملان معًا العلاقة x == (x %/% y) * y + (x %% y).
والاستخدام الكلاسيكي للمعامل %% هو اختبار قابلية القسمة:
وثمة دقة مع الأعداد السالبة: فالمعامل %% في R يأخذ إشارة المقسوم عليه (مثل Python، وبخلاف C):
النتيجة 2 لا -1 - إذ تجيب R عن سؤال "ماذا أضيف إلى مضاعف للعدد 3 لأبلغ -7؟"، وهو ما يبقي النتائج ضمن المجال 0..2 عند مقسوم عليه موجب. مفيد في تدوير الفهارس؛ ومفاجئ إن كنت قادمًا من C أو Java.
القيم الخاصة: Inf و-Inf وNaN
تتبع أرقام R معيار IEEE 754، لذا تنتج بعض العمليات قيمًا خاصة بدل أخطاء:
فـ 1/0 تعطي Inf (اللانهاية)، و-1/0 تعطي -Inf، و0/0 - وهي كمية غير معرّفة فعلًا - تعطي NaN أي "ليس عددًا". والتمييز مهم: فـ Inf جواب ("أكبر من أي شيء")، بينما NaN غياب للجواب. وتختبرهما بدوال مخصصة، لأن المقارنة == NaN لا تعمل أبدًا:
انتبه إلى السطر الأخير: فالقيمة NaN تُعد أيضًا NA، لذا تلتقطها is.na() - وهذا سبب إضافي لكون is.na() هي الفحص القياسي لسؤال "هل هذه القيمة غير صالحة للاستعمال؟" (المزيد في القيم المفقودة).
كما تقرأ R وتكتب الترميز العلمي أصلًا - فالقيمة 2.5e3 تساوي 2500، وتُطبع الأعداد الصغيرة جدًا أو الكبيرة جدًا بالترميز الأسي افتراضيًا:
استخدم format(x, scientific = FALSE) (أو الخيار scipen) حين يحتاج تقرير ما إلى أعداد عشرية عادية.
مفاجأة الفاصلة العائمة
تشترك في هذه المفاجأة كل لغة تخزّن الكسور بالنظام الثنائي، وR ليست استثناء:
النتيجة FALSE - لأن 0.1 + 0.2 تساوي فعليًا 0.30000000000000004. فلا القيمة 0.1 ولا 0.2 لها تمثيل ثنائي دقيق، والأخطاء الضئيلة تتراكم. وطباعة R الافتراضية تخفي ذلك بعرض سبعة أرقام معنوية، ولهذا تبدو المشكلة غير مرئية حتى تفشل مقارنة بالمعامل ==.
والقاعدة: لا تقارن الأعداد العشرية المحسوبة بالمعامل == أبدًا. استخدم all.equal() التي تقارن ضمن هامش تسامح معقول:
غلّفها بـ isTRUE() لأن all.equal() تعيد وصفًا للفرق (لا FALSE) حين تختلف القيم. وللعمل بالأعداد الصحيحة حيث تهم الدقة، تبقى الأعداد الصحيحة دقيقة حتى نحو 2.1 مليار - وهو سبب آخر لاستخدام شيفرة العد للنوع integer.
ما تخرج به
- كل عدد تكتبه من النوع double؛ واللاحقة
42Lتنشئ عددًا صحيحًا، والمعامل/يعيد قيمة double في الحالتين. - تقرّب
round()النصف إلى الزوجي؛ وكل منfloorوceilingوtruncوsignifيشذّب بطريقة مختلفة - فاعرف أيها تقصد. - الدالة
log()هي اللوغاريتم الطبيعي؛ استخدمlog10()أوlog2()أوbase =لأساسات أخرى. - يعطي
%%الباقي (وإشارته تتبع المقسوم عليه)، ويعطي%/%خارج القسمة الصحيح. - القيمة
1/0هيInf، و0/0هيNaN، و0.1 + 0.2 != 0.3- فقارن الأعداد العشرية بـall.equal()لا بـ==أبدًا.
التالي: النصف الآخر من البيانات اليومية - السلاسل النصية، والدوال التي تمنحك إياها R لبنائها وتنسيقها والبحث فيها.
الأسئلة الشائعة
ما الفرق بين numeric وinteger في R؟
النوع numeric (double) هو الافتراضي في R لأي عدد تكتبه - فالقيمة 42 من النوع double وإن بدت صحيحة. أما integer فهو نوع تخزين منفصل تطلبه بلاحقة L: 42L. والقسمة العادية تعيد دائمًا قيمة double حتى بين عددين صحيحين؛ استخدم %/% للقسمة الصحيحة.
هل الدالة log() في R هي اللوغاريتم الطبيعي؟
نعم - فالدالة log(x) في R هي اللوغاريتم الطبيعي (بالأساس e) لا بالأساس 10. استخدم log10() للأساس 10، وlog2() للأساس 2، أو log(x, base = b) لأي أساس. فالقيمة log(100) تساوي نحو 4.605 لا 2.
كيف تعمل الدالة round() في R؟
تقرّب round(x, digits) إلى عدد المنازل العشرية المحدد، لكن الأنصاف الدقيقة تُقرَّب وفق قاعدة "النصف إلى الزوجي" (تقريب المصرفيين): فـ round(2.5) تعطي 2 وround(3.5) تعطي 4. وهذا يتبع معيار IEEE 754 ويقلل الانحياز عند جمع قيم مقرّبة، لكنه يفاجئ من يتوقع التقريب المدرسي.
لماذا لا يساوي 0.1 + 0.2 القيمة 0.3 في R؟
تُخزَّن أعداد double بالنظام الثنائي، وليس للقيم 0.1 و0.2 و0.3 تمثيل ثنائي دقيق، لذا فإن 0.1 + 0.2 تساوي فعليًا 0.30000000000000004. لا تقارن الأعداد العشرية المحسوبة بالمعامل == أبدًا؛ استخدم isTRUE(all.equal(x, y)) أو تحقق من abs(x - y) < 1e-9.