R(数値計算)

2026年3月 6日 (金)

[R]重み付き非線形最小二乗法を行う

nls関数を使う。weightsオプションに重みを指定すると、その重みを考慮した計算を行う。

以下の例では、与えられた8組の測定値を使用して、非線形最小二乗法により回帰モデルy = b1 + b2 * exp(b3 * x)(b1, b2, b3は回帰係数)を作成した例。重み無し、重み付きでそれぞれ計算している。図を見てのとおり、重みを考慮しないモデル(青実線)は全点の近くをまんべんなく通っているが、重みを考慮したモデル(x = 3, 6のみ重みが他の100倍、赤実線)は、x = 3, 6の測定値を無理矢理通るようなモデル(回帰係数)が求められていることがわかる。

R_nls

> # 計算の用意
> x <- c(1, 2, 3, 5, 6, 7, 8, 9)
> y <- c(1, 4, 10, 25, 32, 49, 64, 75) # 測定値(図の黒丸)
> w <- rep(1, length(x))
> w[c(3, 5)] <- 100
> dtf <- data.frame(x, y, w)
> print(dtf)
x y w
1 1 1 1
2 2 4 1
3 3 10 100
4 5 25 1
5 6 32 100
6 7 49 1
7 8 64 1
8 9 75 1
> f <- function(x, b) b[1] + b[2] * exp(b[3] * x)
> b0 <- c(1, 1, 1)
>
> # 重みを考慮しない計算(図の青実線)
> r1 <- nls(y ~ f(x, b), start = list(b = b0))
> sm <- summary(r1)
> print(r1) # nls関数による計算結果の概要
Nonlinear regression model
model: y ~ f(x, b)
data: parent.frame()
b1 b2 b3
-25.4828 21.1533 0.1756
residual sum-of-squares: 32.98
Number of iterations to convergence: 21
Achieved convergence tolerance: 3.185e-06
> print(sm$coef) # 推定した回帰係数,その標準誤差,t値,p値
Estimate Std. Error t value Pr(>|t|)
b1 -25.4827638 10.22340189 -2.492591 0.054986347
b2 21.1532994 8.08590380 2.616071 0.047321602
b3 0.1755871 0.03299953 5.320898 0.003137863
> print(sum(sm$resi ^ 2)) # 残差平方和
[1] 32.98248
> print(sum((y - fitted(r1)) ^ 2))
[1] 32.98248
> print(sm$df) # 推定した回帰係数の数と自由度
[1] 3 5
> print(sum(sm$resi ^ 2) / sm$df[2]) # 残差分散
[1] 6.596497
> print(sm$sigma) # 回帰値の標準誤差
[1] 2.568365
> print(sm$sigma ^ 2 * sm$cov.unscaled) # 推定した回帰係数の分散共分散行列
b1 b2 b3
b1 104.5179462 -81.8524151 0.329318514
b2 -81.8524151 65.3818402 -0.265714306
b3 0.3293185 -0.2657143 0.001088969
>
> # 重みを考慮した計算(図の赤実線)
> r2 <- nls(y ~ f(x, b), weights = w, start = list(b = b0))
> sm <- summary(r2)
> print(r2) # nls関数による計算結果の概要
Nonlinear regression model
model: y ~ f(x, b)
data: parent.frame()
b1 b2 b3
-10.9272 10.1219 0.2413
weighted residual sum-of-squares: 70.58
Number of iterations to convergence: 16
Achieved convergence tolerance: 5.171e-07
> print(sm$coef) # 推定した回帰係数,その標準誤差,t値,p値
Estimate Std. Error t value Pr(>|t|)
b1 -10.927232 3.53132490 -3.094372 0.0270250777
b2 10.121862 2.44947466 4.132258 0.0090650345
b3 0.241281 0.02632775 9.164514 0.0002593529
> print(sum(sm$resi ^ 2)) # 残差平方和
[1] 70.57934
> print(sum(w * (y - fitted(r2)) ^ 2))
[1] 70.57934
> print(sm$df) # 推定した回帰係数の数と自由度
[1] 3 5
> print(sum(sm$resi ^ 2) / sm$df[2]) # 残差分散
[1] 14.11587
> print(sm$sigma) # 回帰値の標準誤差
[1] 3.757109
> print(sm$sigma ^ 2 * sm$cov.unscaled) # 推定した回帰係数の分散共分散行列
b1 b2 b3
b1 12.47025554 -8.57361848 0.0908751718
b2 -8.57361848 5.99992612 -0.0642324913
b3 0.09087517 -0.06423249 0.0006931503
>
> # 図の描画
> plot(x, fitted(r1), type = "l", col = "blue", xlab = "x", ylab = "y")
> lines(x, fitted(r2), col = "red")
> points(x, y, pch = 20)

2025年10月29日 (水)

[R]計算機イプシロンを求める

計算機イプシロンとは、不等式「1 + e > 1」が成り立つ最小の正数eのこと。これを手計算で求めてみる。

> R.Version()$platform
[1] "x86_64-w64-mingw32"
> e <- 1.0; while (1.0 + e > 1.0) {e <- e / 2}; e <- e * 2
> print(e)
[1] 2.220446e-16

結果は環境によって異なるが、この環境下では約2.2×10-16であることがわかった。

なお、Rにはあらかじめ稼働環境の計算能力の値を格納したリスト.Machineがグローバル変数として定義されており、これに含まれるdouble.epsが計算機イプシロンである。

> .Machine$double.eps
[1] 2.220446e-16

手計算の計算結果と矛盾がないことがわかる。

2024年12月 7日 (土)

[R]複数の引数を持つ関数の値の最小値(最大値)を求める

optim関数を使う。使用時には適切な初期値を与える必要がある。数値的に求めるため、必ずしも期待通りの結果が得られるわけではないので注意。

以下はf1とf2というそれぞれ2つずつ引数を持つ関数の最小値と最大値を求めた例。f1の値が最小となるのはf1(5, 7)、f2の値が最大値となるのはf2(5, 7)であることは明らか。optim関数はデフォルトでは関数の値が最小値となる引数を求めるが、最大値となる引数を求めたい場合はcontrolオプションにlist(fnscale = -1)を指定すること。

> f1 <- function(x) {(x[1] - 5) ^ 2 + (x[2] - 7) ^ 2}
> f2 <- function(x) {-((x[1] - 5) ^ 2 + (x[2] - 7) ^ 2)}
> r <- optim(c(0, 0), f1, method = "BFGS")
> print(r)
$par
[1] 5 7
$value
[1] 1.31369e-22
$counts
function gradient
12 3
$convergence
[1] 0
$message
NULL

f2関数の最小値は負の無限大であり、fnscaleに-1を指定しない状態では求めた引数(以下の$par)がまともに求まっていないことが明らか。fnscaleに-1を指定することで、期待通りの結果が得られる。

> r <- optim(c(0, 0), f2, method = "BFGS")
> print(r)
$par
[1] -4.758163e+13 -1.858368e+13
$value
[1] -2.609365e+27
$counts
function gradient
28 28
$convergence
[1] 0
$message
NULL
> r <- optim(c(0, 0), f2, method = "BFGS", control = list(fnscale = -1))
> print(r)
$par
[1] 5 7
$value
[1] -1.31369e-22
$counts
function gradient
12 3
$convergence
[1] 0
$message
NULL

2023年5月31日 (水)

[R]回帰モデルの信頼区間と予測区間

lm関数により求めた回帰モデルの信頼区間と予測区間はpredict関数で求めることができる。以下の計算により、赤実線が求めた回帰直線。桃破線がその95%信頼区間、橙点線がその95%予測区間。

> # 説明変数xと目的変数y
> x <- c(1, 2, 3, 5, 6, 7, 8, 10, 11, 12)
> y <- c(0, 1, 1, 2, 4, 4, 6, 5, 8, 8)
> # 回帰直線を求める
> r <- lm(y ~ x)
> print(r)
Call:
lm(formula = y ~ x)
Coefficients:
(Intercept) x
-0.8567 0.7318
> # yの推定値
> yest <- fitted(r)
> # 信頼区間と予測区間の計算(後続の手順のためdata.frame化)
> dtf <- data.frame(x)
> rcon <- predict(r, dtf, interval = "confidence", level = 0.95)
> rpre <- predict(r, dtf, interval = "prediction", level = 0.95)
> rcon <- data.frame(rcon)
> rpre <- data.frame(rpre)
> # 図の作成(黒丸:観測値,赤実線:回帰直線,桃破線:信頼区間,橙点線:予測区間)
> plot(x, y, type = "n", asp = 1.0)
> lines(x, rpre$lwr, lty = "dotted", col = "orange", lwd = 2.0)
> lines(x, rpre$upr, lty = "dotted", col = "orange", lwd = 2.0)
> lines(x, rcon$lwr, lty = "dashed", col = "pink", lwd = 2.0)
> lines(x, rcon$upr, lty = "dashed", col = "pink", lwd = 2.0)
> lines(x, yest, col = "red", lwd = 2.0)
> points(x, y, pch = 20, col = "black")

R_conpre

> # 以下はpredict関数と手計算の計算結果の比較
> ah <- 0.95
> n <- length(x)
> k <- 2
> mxxx <- matrix(c(rep(1, n), x), ncol = 2)
> mxy <- matrix(y, ncol = 1)
> # 最小二乗推定量
> mxb <- solve(t(mxxx) %*% mxxx) %*% (t(mxxx) %*% mxy)
> print(mxb)
[,1]
[1,] -0.8567050
[2,] 0.7318008
> # yの推定値
> yest <- mxxx %*% mxb
> # 残差分散
> s <- sqrt(sum((y - yest) ^ 2) / (n - k))
> #
> s0 <- sh0 <- double(n)
> for (i in 1:n) {
+ mxx0 <- matrix(c(1, x[i]), ncol = 1)
+ s0[i] <- s * sqrt(t(mxx0) %*% solve(t(mxxx) %*% mxxx) %*% mxx0)
+ sh0[i] <- s * sqrt(1 + t(mxx0) %*% solve(t(mxxx) %*% mxxx) %*% mxx0)
+ }
> # 95%信頼区間
> ycon1 <- yest - s0 * qt((1. - ah) / 2, n - k, lower.tail = FALSE)
> ycon2 <- yest + s0 * qt((1. - ah) / 2, n - k, lower.tail = FALSE)
> # 95%予測区間
> ypre1 <- yest - sh0 * qt((1. - ah) / 2, n - k, lower.tail = FALSE)
> ypre2 <- yest + sh0 * qt((1. - ah) / 2, n - k, lower.tail = FALSE)
> # predict関数による信頼区間と予測区間
> print(data.frame(rcon$lwr, rcon$upr, rpre$lwr, rpre$upr))
rcon.lwr rcon.upr rpre.lwr rpre.upr
1 -1.1763909 0.9265825 -2.2315171 1.981709
2 -0.3152118 1.5290049 -1.4382140 2.652007
3 0.5349489 2.1424457 -0.6558465 3.333241
4 2.1772621 3.4273356 0.8728263 4.731771
5 2.9513451 4.1168542 1.6179064 5.450293
6 3.6831458 4.8486549 2.3497072 6.182094
7 4.3726644 5.6227379 3.0682286 6.927174
8 5.6575543 7.2650511 4.4667589 8.455846
9 6.2709951 8.1152118 5.1479929 9.238214
10 6.8734175 8.9763909 5.8182913 10.031517
> # 手計算による信頼区間と予測区間
> print(data.frame(ycon1, ycon2, ypre1, ypre2))
ycon1 ycon2 ypre1 ypre2
1 -1.1763909 0.9265825 -2.2315171 1.981709
2 -0.3152118 1.5290049 -1.4382140 2.652007
3 0.5349489 2.1424457 -0.6558465 3.333241
4 2.1772621 3.4273356 0.8728263 4.731771
5 2.9513451 4.1168542 1.6179064 5.450293
6 3.6831458 4.8486549 2.3497072 6.182094
7 4.3726644 5.6227379 3.0682286 6.927174
8 5.6575543 7.2650511 4.4667589 8.455846
9 6.2709951 8.1152118 5.1479929 9.238214
10 6.8734175 8.9763909 5.8182913 10.031517

2023年1月 3日 (火)

[R]回帰分析におけるAICを簡単に求める

回帰分析をlm関数で行い、その戻り値を用いてAIC関数を使う。以下は「情報量規準」(朝倉書店)に掲載の計算例(pp.59-61)を再現した結果。

観測値はpp.59-60に掲載されており、以下のとおり。これをtable_p059.csvと保存する。

 i,    x,     y
1, 0.00, 0.854
2, 0.05, 0.786
3, 0.10, 0.706
4, 0.15, 0.763
5, 0.20, 0.772
6, 0.25, 0.693
7, 0.30, 0.805
8, 0.35, 0.739
9, 0.40, 0.760
10, 0.45, 0.764
11, 0.50, 0.810
12, 0.55, 0.791
13, 0.60, 0.798
14, 0.65, 0.841
15, 0.70, 0.882
16, 0.75, 0.879
17, 0.80, 0.863
18, 0.85, 0.934
19, 0.90, 0.971
20, 0.95, 0.985

以下、計算結果。当該書籍では、上記の観測値を用いた多項式回帰モデルの残差分散とAICの一覧を表にまとめて掲載している(p.61)。

> dtf <- read.csv("table_p059.csv", header = TRUE)
> n <- nrow(dtf)
> for (k in 1:9) {
+ r <- lm(y ~ poly(x, k), data = dtf)
+ rss <- sum(r$re ^ 2)
+ cat(sprintf("次数:%d, 残差分散:%8.6f, AIC:%5.2f\n", k, rss / n, AIC(r)))
+ }
次数:1, 残差分散:0.002587, AIC:-56.38
次数:2, 残差分散:0.000922, AIC:-75.03
次数:3, 残差分散:0.000833, AIC:-75.04
次数:4, 残差分散:0.000737, AIC:-75.50
次数:5, 残差分散:0.000688, AIC:-74.89
次数:6, 残差分散:0.000650, AIC:-74.00
次数:7, 残差分散:0.000622, AIC:-72.89
次数:8, 残差分散:0.000607, AIC:-71.38
次数:9, 残差分散:0.000599, AIC:-69.66

2023年1月 1日 (日)

[R]回帰分析における対数尤度を簡単に求める

回帰分析をlm関数で行い、その戻り値を用いてlogLik関数を使う。以下は「情報量規準」(朝倉書店)に掲載の計算例(pp.59-61)を再現した結果。

観測値はpp.59-60に掲載されており、以下のとおり。これをtable_p059.csvと保存する。

 i,    x,     y
1, 0.00, 0.854
2, 0.05, 0.786
3, 0.10, 0.706
4, 0.15, 0.763
5, 0.20, 0.772
6, 0.25, 0.693
7, 0.30, 0.805
8, 0.35, 0.739
9, 0.40, 0.760
10, 0.45, 0.764
11, 0.50, 0.810
12, 0.55, 0.791
13, 0.60, 0.798
14, 0.65, 0.841
15, 0.70, 0.882
16, 0.75, 0.879
17, 0.80, 0.863
18, 0.85, 0.934
19, 0.90, 0.971
20, 0.95, 0.985

以下、計算結果。当該書籍では、上記の観測値を用いた多項式回帰モデルの残差分散と対数尤度の一覧を表にまとめて掲載している(p.61)。

> dtf <- read.csv("table_p059.csv", header = TRUE)
> n <- nrow(dtf)
> for (k in 1:9) {
+ r <- lm(y ~ poly(x, k), data = dtf)
+ rss <- sum(r$re ^ 2)
+ cat(sprintf("次数:%d, 残差分散:%8.6f, 対数尤度:%5.2f\n", k, rss / n, logLik(r)))
+ }
次数:1, 残差分散:0.002587, 対数尤度:31.19
次数:2, 残差分散:0.000922, 対数尤度:41.51
次数:3, 残差分散:0.000833, 対数尤度:42.52
次数:4, 残差分散:0.000737, 対数尤度:43.75
次数:5, 残差分散:0.000688, 対数尤度:44.44
次数:6, 残差分散:0.000650, 対数尤度:45.00
次数:7, 残差分散:0.000622, 対数尤度:45.44
次数:8, 残差分散:0.000607, 対数尤度:45.69
次数:9, 残差分散:0.000599, 対数尤度:45.83

2022年12月19日 (月)

[R]パスカルの三角形を求める

以下のスクリプトを実行する。1~12のパスカルの三角形を求めている。

for (i in 1:12) {
cat(sprintf("%2d: ", i))
for (j in 0:i) {
cat(sprintf("%3d ", choose(i, j)))
}
cat("\n")
}

出力

 1:   1   1 
2: 1 2 1
3: 1 3 3 1
4: 1 4 6 4 1
5: 1 5 10 10 5 1
6: 1 6 15 20 15 6 1
7: 1 7 21 35 35 21 7 1
8: 1 8 28 56 70 56 28 8 1
9: 1 9 36 84 126 126 84 36 9 1
10: 1 10 45 120 210 252 210 120 45 10 1
11: 1 11 55 165 330 462 462 330 165 55 11 1
12: 1 12 66 220 495 792 924 792 495 220 66 12 1

2022年9月 8日 (木)

[R]丸め誤差を考慮して数値の比較を行う

コンピューターは内部では浮動小数点演算を行っている都合上、丸め誤差は避けられない。それは実数同士の演算でよく見られる。

> (1 + 2) == 3
[1] TRUE
> (0.1 + 0.2) == 0.3
[1] FALSE
> print(sprintf("%.20f", 0.1 + 0.2))
[1] "0.30000000000000004441"
> print(sprintf("%.20f", 0.3))
[1] "0.29999999999999998890"

Rには標準でVisual BasicのDecimal型に相当するベクトルの型は存在せず、計算時に工夫する方法がある。

簡単に回避する方法としてsignif関数を使う方法がある。この関数は指定した有効数字を指定した桁まで丸めることができるため(デフォルトは6桁)、比較時にこの関数を使用すればよい。

> signif(0.1 + 0.2) == 0.3
[1] TRUE
> signif(0.1 + 0.2, digits = 16) == 0.3
[1] TRUE
> signif(0.1 + 0.2, digits = 17) == 0.3
[1] FALSE
> print(sprintf("%.20f", signif(0.1 + 0.2, digits = c(15, 16, 17, 18))))
[1] "0.29999999999999998890" "0.29999999999999998890" "0.30000000000000004441" "0.30000000000000004441"
> print(sprintf("%.20f", signif(0.3, digits = c(15, 16, 17, 18))))
[1] "0.29999999999999998890" "0.29999999999999998890" "0.29999999999999998890" "0.29999999999999998890"

ちなみに、round関数でも同様のことができるが、round関数とsignif関数ではdigitsオプションの意味が違うため注意。この手の処理でround関数を使うことは推奨しない。

> round(0.1 + 0.2, digits = 15) == 0.3
[1] TRUE
> round(0.1 + 0.2, digits = 16) == 0.3
[1] FALSE
> print(sprintf("%.20f", round(0.1 + 0.2, digits = 14:17)))
[1] "0.29999999999999998890" "0.29999999999999998890" "0.30000000000000004441" "0.30000000000000004441"
> print(sprintf("%.20f", round(0.3, digits = 14:17)))
[1] "0.29999999999999998890" "0.29999999999999998890" "0.29999999999999998890" "0.29999999999999998890"

丸め誤差を完全に自動でうまく処理できる方法はなく、その計算で求められる精度を基に、その都度工夫をする必要がある。

2022年5月15日 (日)

[R]単回帰分析(入門 統計解析 医学・自然科学編、東京書籍、pp.236-237)

本計算に使用するデータは、以下のとおり(p.236)。

 i,  x,  y
1, 7, 6
2, 7, 9
3, 12, 10
4, 11, 13
5, 10, 13
6, 5, 7
7, 9, 11
8, 11, 14
9, 11, 15
10, 12, 7
11, 11, 13
12, 15, 14
13, 14, 10
14, 16, 16
15, 8, 8
16, 2, 8
17, 14, 8
18, 8, 12
19, 12, 16
20, 16, 12

これをメモ帳に貼り付けてファイル「table10-1.csv」で保存し、カレントディレクトリに置いておく。

> dtf <- read.csv("table10-1.csv", header = TRUE)
> xi <- dtf$x
> yi <- dtf$y
> n <- nrow(dtf)
> p <- 2
> xm <- mean(xi)
> ym <- mean(yi)
> ssx2 <- sum((xi - xm) ^ 2)
> ssy2 <- sum((yi - ym) ^ 2)
> ssxy <- sum((xi - xm) * (yi - ym))
> b1 <- ssxy / ssx2
> b0 <- ym - b1 * xm
> yt <- b0 + b1 * xi
> sse <- sum((yi - yt) ^ 2)
> sh2 <- sse / (n - p)
> se <- sqrt(sh2)
> seb1 <- sqrt(sh2 / sum((xi - xm) ^ 2))
> tb1 <- b1 / seb1
> cat(sprintf("回帰式 y~ = %.4fx + %.3f\n", b1, b0))
回帰式 y~ = 0.4468x + 6.387
> cat(sprintf("誤差分散σ2の不偏推定量 σ2^ = %.2f\n", sh2))
誤差分散σ2の不偏推定量 σ2^ = 7.61
> cat(sprintf("回帰値の標準誤差 s.e. = %.2f\n", se))
回帰値の標準誤差 s.e. = 2.76
> cat(sprintf("β1の推定値の標準誤差 s.e.(b1) = %.3f\n", seb1))
β1の推定値の標準誤差 s.e.(b1) = 0.173
> cat(sprintf("β1の推定値のt値 t = %.2f\n", tb1))
β1の推定値のt値 t = 2.59
> cat(sprintf("t0.025(18) = %.2f\n", qt(1 - 0.025, n - p)))
t0.025(18) = 2.10

2022年2月 3日 (木)

[R]毎年の太陽黒点数(Wolfer sunspot number)のデータ

パッケージTSSSに格納されている。データ名はSunspot。1749年から1979年までの毎年の値で、データ数は231個。

> library(TSSS)
> Sunspot
エラー: オブジェクト 'Sunspot' がありません
> data(Sunspot)
> Sunspot
Time Series:
Start = 1749
End = 1979
Frequency = 1
[1] 80.9 83.4 47.7 47.8 30.7 12.2 9.6 10.2 32.4 47.6 54.0 62.9 85.9 61.2 45.1
[16] 36.4 20.9 11.4 37.8 69.8 106.1 100.8 81.6 66.5 34.8 30.6 7.0 19.8 92.5 154.4
[31] 125.9 84.8 68.1 38.5 22.8 10.2 24.1 82.9 132.0 130.9 118.1 89.9 66.6 60.0 46.9
[46] 41.0 21.3 16.0 6.4 4.1 6.8 14.5 34.0 45.0 43.1 47.5 42.2 28.1 10.1 8.1
[61] 2.5 0.1 1.4 5.0 12.2 13.9 35.4 45.8 41.1 30.1 23.9 15.6 6.6 4.0 1.8
[76] 8.5 16.6 36.3 49.6 64.2 67.0 70.9 47.8 27.5 8.5 13.2 56.9 121.5 138.3 103.2
[91] 85.7 64.6 36.7 24.2 10.7 15.0 40.1 61.5 98.5 124.7 96.3 66.6 64.5 54.1 39.0
[106] 20.6 6.7 4.3 22.7 54.8 93.8 95.8 77.2 59.1 44.0 47.0 30.5 16.3 7.3 37.6
[121] 74.0 139.0 111.2 101.6 66.2 44.7 17.0 11.3 12.4 3.4 6.0 32.3 54.3 59.7 63.7
[136] 63.5 52.2 25.4 13.1 6.8 6.3 7.1 35.6 73.0 85.1 78.0 64.0 41.8 26.2 26.7
[151] 12.1 9.5 2.7 5.0 24.4 42.0 63.5 53.8 62.0 48.5 43.9 18.6 5.7 3.6 1.4
[166] 9.6 47.4 57.1 103.9 80.6 63.6 37.6 26.1 14.2 5.8 16.7 44.3 63.9 69.0 77.8
[181] 64.9 35.7 21.2 11.1 5.7 8.7 36.1 79.7 114.4 109.6 88.8 67.8 47.5 30.6 16.3
[196] 9.6 33.2 92.6 151.6 136.3 134.7 83.9 69.4 31.5 13.9 4.4 38.0 141.7 190.2 184.8
[211] 159.0 112.3 53.9 37.5 27.9 10.2 15.1 47.0 93.8 105.9 105.5 104.5 66.6 68.9 38.0
[226] 34.5 15.5 12.6 27.5 92.5 155.4
> plot(Sunspot)


Sunspot

より以前の記事一覧

無料ブログはココログ

■■

■■■