numericとinteger:Rの2つの数値型
Rは数値を2通りの方法で保存します。numeric(倍精度浮動小数点として保存)が既定で、明示的に指定しない限り、入力した数値はすべてdoubleになります。integer は正確な整数のための別の型で、L の接尾辞で要求します。
実務ではあまり気にする必要はありません。Rが静かに変換してくれますし、is.numeric() は両方で TRUE になります(型システム全体はデータ型を参照)。この違いが表に出る唯一の場所が除算です。/ は整数どうしであっても常に doubleを返します。
integer は主に、数を数える関数(length()、seq_len()、nrow())の出力や、添字の値として現れます。整数の除算が必要なときのために、Rには専用の演算子があります(後述)。
丸めの関数群
Rには数値を切り詰める方法が5つあり、それぞれ意味が異なります。
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() はちょうど0.5のときIEEE 754規格に従って偶数への丸め(銀行家の丸め)を行います。
出力は 0 2 2 4 です。0.5はそれぞれ最も近い偶数へ丸められます。これはバグではなく、丸めた値をたくさん合計するときに系統的な上振れを防ぐためのものです。レポートで学校式の四捨五入が必要なら、ごくわずかに値を足すか、表示層でフォーマットしましょう。
日常的な数学関数
基本は予想どおりに動きます。
sqrt() は平方根、abs() は絶対値、^ はべき乗、exp(x) は e の x 乗です。したがって exp(1) はネイピア数、約2.718282になります。
つまずきやすい関数がひとつ、log() です。Rの log() は底10ではなく自然対数(底 e)です。
1行目はおよそ 4.60517 と表示されます。底10のつもりで見ていた人が期待する 2 ではありません。その底が必要なら log10() や log2() を使うか、base = を明示的に渡しましょう。(この慣習は自然対数を既定とする統計の分野では標準的です。)
剰余 %% と整数除算 %/%
余りの計算は2つの演算子でカバーできます。
%% は剰余(余り)で、17を5で割ると商3、余り2です。%/% は整数除算で、17の中に5がいくつ丸ごと入るかを返します。両者は x == (x %/% y) * y + (x %% y) を満たします。
%% の定番の用途は割り切れるかの判定です。
負の数について微妙な点がひとつ。Rの %% は除数の符号を取ります(Pythonと同じ、Cとは異なる)。
答えは -1 ではなく 2 です。Rは「3の倍数に何を足せば-7になるか」と答えるので、除数が正なら結果は 0..2 に収まります。添字の巡回に便利ですが、CやJavaから来た人には意外に映ります。
特殊値:Inf、-Inf、NaN
Rの数値はIEEE 754に従うので、一部の演算はエラーではなく特殊値を生みます。
1/0 は Inf(無限大)、-1/0 は -Inf、そして本当に未定義な量である 0/0 は NaN(not a number)です。この区別は重要です。Inf は答え(「何よりも大きい」)ですが、NaN は答えの不在です。== NaN は決して機能しないので、専用の関数で判定します。
最後の行に注目してください。NaN は NA としても数えられるので is.na() が捕まえます。is.na() が「この値は使えないか?」の標準的なチェックである理由がまたひとつ増えました(詳しくは欠損値)。
Rは指数表記もそのまま読み書きします。2.5e3 は2500で、非常に小さい数や大きい数は既定でe表記で表示されます。
レポートで普通の小数表記が必要なときは format(x, scientific = FALSE)(または scipen オプション)を使いましょう。
浮動小数点の意外な挙動
小数を2進数で保存するすべての言語が共有する現象で、Rも例外ではありません。
結果は FALSE です。0.1 + 0.2 は実際には 0.30000000000000004 だからです。0.1も0.2も正確な2進表現を持たず、ごく小さな誤差が積み重なります。Rの既定の表示は有効数字7桁を見せることでこれを隠しているので、== の比較が失敗するまで問題が見えないのです。
ルールはこうです。計算した小数を == で比較してはいけません。 妥当な許容誤差の範囲で比較する all.equal() を使いましょう。
isTRUE() で包むのは、値が異なるとき all.equal() が FALSE ではなく差の説明を返すからです。正確さが重要な整数の処理では、integer は約21億まで厳密に表せます。数を数えるコードが integer 型を使う理由がまたひとつあるわけです。
この記事のまとめ
- 入力した数値はすべてdoubleです。
42Lでintegerになりますが、/はいずれにせよdoubleを返します。 round()は0.5を偶数へ丸めます。floor/ceiling/trunc/signifは切り詰め方がそれぞれ違うので、自分がどれを意図しているか把握しましょう。log()は自然対数です。他の底にはlog10()、log2()、またはbase =を使います。%%は余り(符号は除数に従う)、%/%は整数の商を返します。1/0はInf、0/0はNaN、そして0.1 + 0.2 != 0.3。小数の比較には==ではなくall.equal()を。
次は日々のデータのもう半分——文字列と、それを作り、整形し、検索するためにRが用意している関数群です。
よくある質問
Rのnumericとintegerの違いは何ですか?
numeric(double)は入力した数値に対するRの既定の型です。42 は整数に見えてもdoubleです。integerは別の保存型で、42L のように L を付けて要求します。通常の除算は整数どうしでも常にdoubleを返すので、整数除算には %/% を使います。
Rのlog()は自然対数ですか?
はい。Rの log(x) は底10ではなく自然対数(底 e)です。底10には log10()、底2には log2()、任意の底には log(x, base = b) を使います。log(100) は2ではなく約4.605です。
Rのround()はどう動きますか?
round(x, digits) は指定した小数点以下の桁数に丸めますが、ちょうど0.5の場合は「偶数への丸め」(銀行家の丸め)になります。round(2.5) は 2、round(3.5) は 4 です。これはIEEE 754規格に従うもので、丸めた値を合計するときの偏りを減らしますが、学校で習った四捨五入を期待していると驚かされます。
Rで 0.1 + 0.2 が 0.3 と等しくならないのはなぜ?
doubleは2進数で保存され、0.1・0.2・0.3には正確な2進表現が存在しないため、0.1 + 0.2 は実際には0.30000000000000004になります。計算した小数を == で比較してはいけません。isTRUE(all.equal(x, y)) を使うか、abs(x - y) < 1e-9 を確認しましょう。