R言語 Advent Calendar 2025の9日目の記事です。
標準正規分布表を自前で作成したいというニッチな願望
標準正規分布表なんてググればいくらでも出てくるじゃん、という話ですが、そうはいっても自前で標準正規分布表をつくりたいひげめがねです、こんにちは。
そもそもなぜ、という話ですが、(文系の私がなぜか)統計学の講師をしているからです!!世は人材不足也。そして講師をすると試験問題を作らねばならない、ということは標準正規分布表を試験問題の末尾につけなければならないのです。試験は数式ばかりなので、LaTeXで作ります。
そんなの画像をLaTeXに貼ればいいじゃん、というご意見ごもっともですが、$z$値がどこまで必要か、とか、小数第何位まで表示するか、とか、いろいろ自前で作りたくなるものなのです。それよりなにより、他から引用したものを貼り付けるなんて美しくない!(笑)。というわけで自作しました。統計学の先生が使うのは当然Rでアール(???)
手順
- まず$z$値に対応する上側確率を算出する。
- その表をlong型からwide型にする。
- その表を基に表の部分のLaTeXコードを生成する。
- せっかくなので上側確率と受験者がわかるように可視化できるグラフもつくる。
標準正規分布の上側確率の算出
これはRの最も得意とするところですね。pnorm()関数を使いますが、上側確率の表を作成したいので、lower.tail = FALSEを記述します。なお、標準正規分布表は$3.0$の列まで作成したかったので、$z$値は$0$~$3.09$まで$0.01$刻みで設定しました。
そして、wide型に変える準備として、mutate()で、$z$値の小数第1位までの値と小数第2位の値を列追加します。また、数値が桁ずれしないように数値はchr型にしてしまいます。
# まずRを始めるときのおまじない
rm(list = ls())
library(conflicted)
library(tidyverse)
conflicts_prefer(dplyr::filter)
df_long <- dplyr::tibble(z = seq(0, 3.09, by = 0.01)) |> # z値は0~3.09まで0.01刻みで設定
mutate(p = pnorm(z, lower.tail = FALSE)) |> # 上側確率なので、"lower.tail = FALSE"
mutate(p = round(p, digits = 4), # pは小数第5位を四捨五入して小数第4位まで表示
first_decimal = floor(z * 10) / 10) |> # z値の小数第1位まで
mutate(second_decimal = z - first_decimal) |> # z値の小数第2位
mutate(p = sprintf("%.4f", p),
first_decimal = sprintf("%.1f", first_decimal),
second_decimal = sprintf("%.2f", second_decimal)) # 数値のchr化
head(df_long)
# A tibble: 6 × 4
z p first_decimal second_decimal
<dbl> <chr> <chr> <chr>
1 0 0.5000 0.0 0.00
2 0.01 0.4960 0.0 0.01
3 0.02 0.4920 0.0 0.02
4 0.03 0.4880 0.0 0.03
5 0.04 0.4840 0.0 0.04
6 0.05 0.4801 0.0 0.05
表のwide化
pivot_wider()関数で表をwide型にします。これにより普段お目にかかる標準正規分布表になりました。
df_wide <- df_long |>
select(-z) |>
pivot_wider(names_from = second_decimal, values_from = p) |> # wide型に変換
rename(z = first_decimal)
head(df_wide)
# A tibble: 6 × 11
z `0.00` `0.01` `0.02` `0.03` `0.04` `0.05` `0.06` `0.07` `0.08` `0.09`
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 0.0 0.5000 0.4960 0.4920 0.4880 0.4840 0.4801 0.4761 0.4721 0.4681 0.4641
2 0.1 0.4602 0.4562 0.4522 0.4483 0.4443 0.4404 0.4364 0.4325 0.4286 0.4247
3 0.2 0.4207 0.4168 0.4129 0.4090 0.4052 0.4013 0.3974 0.3936 0.3897 0.3859
4 0.3 0.3821 0.3783 0.3745 0.3707 0.3669 0.3632 0.3594 0.3557 0.3520 0.3483
5 0.4 0.3446 0.3409 0.3372 0.3336 0.3300 0.3264 0.3228 0.3192 0.3156 0.3121
6 0.5 0.3085 0.3050 0.3015 0.2981 0.2946 0.2912 0.2877 0.2843 0.2810 0.2776
表のLaTeXコード化
LaTeXの表にするには、列区切りに"&"を入れることと、改行の際に"\\ \hline"と記述することになります。
列区切りに"&"を入れるにはpaste(列名, collapse = " & ")でよいのですが、全列名を記述するのは美しくないので、unite()関数で全列結合しました。
そしてRで"\"(バックスラッシュ)を記述する場合には「正規表現のエスケープが絡む壁」があります。Rは基本的にエスケープできないので、それぞれにバックスラッシュを追加する必要があります。それ故
"\\ \hline"→"\\\\ \\hline"
とバックスラッシュ数が倍となります。
余談ですが、"\\\\ \\hline"をMarkdown記法で記載すると更に倍のバックスラッシュを記載することになるので、12本のバックスラッシュ"\\\\\\\\ \\\\hline"が必要です!しかし、Qiitaではバッククオート(`)で囲むことでエスケープが効くので、6本の記述のままで済みました。ありがとうQiita!
最後にヘッダー行を追加してコンソール上に表示させます。これをコピーしてLaTeXコードの該当部分に貼り付けます。
df_tex <- df_wide |>
unite(tex, everything(), sep = " & ") |> # 全列を結合
mutate(tex = paste(tex, "\\\\ \\hline")) # バックスラッシュを付加し、改行と罫線を追加
# ヘッダー行も生成
header <- paste(names(df_wide), collapse = " & ") |>
paste("\\\\ \\hline\\hline")
# ヘッダー行と上側確率全行をコンソール上に表示
cat(header, "\n", paste(df_tex$tex, collapse = "\n"))
# (動作確認時はhead() を挟むと便利。下記結果はhead()の結果)
# cat(header, "\n", paste(head(df_tex$tex), collapse = "\n"))
z & 0.00 & 0.01 & 0.02 & 0.03 & 0.04 & 0.05 & 0.06 & 0.07 & 0.08 & 0.09 \\ \hline\hline
0.0 & 0.5000 & 0.4960 & 0.4920 & 0.4880 & 0.4840 & 0.4801 & 0.4761 & 0.4721 & 0.4681 & 0.4641 \\ \hline
0.1 & 0.4602 & 0.4562 & 0.4522 & 0.4483 & 0.4443 & 0.4404 & 0.4364 & 0.4325 & 0.4286 & 0.4247 \\ \hline
0.2 & 0.4207 & 0.4168 & 0.4129 & 0.4090 & 0.4052 & 0.4013 & 0.3974 & 0.3936 & 0.3897 & 0.3859 \\ \hline
0.3 & 0.3821 & 0.3783 & 0.3745 & 0.3707 & 0.3669 & 0.3632 & 0.3594 & 0.3557 & 0.3520 & 0.3483 \\ \hline
0.4 & 0.3446 & 0.3409 & 0.3372 & 0.3336 & 0.3300 & 0.3264 & 0.3228 & 0.3192 & 0.3156 & 0.3121 \\ \hline
0.5 & 0.3085 & 0.3050 & 0.3015 & 0.2981 & 0.2946 & 0.2912 & 0.2877 & 0.2843 & 0.2810 & 0.2776 \\ \hline
標準正規分布の上側確率と知らせるためのグラフ描画
本来これで十分なのですが、受験者に「これは上側確率の表だよ」と知らせるためにグラフも描画します。どのへんで網掛けするか迷いましたが、私の美的センスで$z > 1.64$を網掛けしました
z_value <- c(1.64)
# 標準正規分布の線を描画するためのdf
df_z <- dplyr::tibble(x = seq(-4, 4, length.out = 1000), y = dnorm(x))
# 網掛け部分を描画するためのdf
df_shade <- dplyr::tibble(x = seq(z_value, 4, length.out = 1000),
y = dnorm(x))
# 描画
g <- ggplot(df_z, aes(x = x, y = y)) +
geom_line() +
geom_area(data = df_shade, fill = "black", alpha = 0.2) +
geom_vline(aes(xintercept = z_value), linetype = "dashed") +
theme_minimal() +
theme(
axis.text = element_blank(),
axis.title = element_blank(),
panel.grid = element_blank(),
panel.background = element_rect(fill = "white", color = NA),
plot.background = element_rect(fill = "white", color = NA))
g
# グラフをpdfファイルとして保存
ggsave("z_upper_p.pdf", plot = g,
width = 10, height = 5, units = "in")
以上でRコードは終了です。
Enjoy!
以下はLaTeXコードを貼り付けておきます。使用はご自由にどうぞ。
LaTeXコードと結果(画像)
\documentclass[dvipdfmx,11pt,a4j]{jarticle}
%--------- users packages ---------------
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[dvipdfmx]{graphicx}
\usepackage{enumerate}
\usepackage{qexam} %試験問題用パッケージ
\begin{document}
\begin{figure}
\centering
\includegraphics*[width=60mm]{z_upper_p.pdf}
\end{figure}
\begin{table}[h]
\centering
\caption{標準正規分布表(上側確率)}
\label{z_dist}
\begin{tabular}{|r||r|r|r|r|r|r|r|r|r|r|}
\hline
z & 0.00 & 0.01 & 0.02 & 0.03 & 0.04 & 0.05 & 0.06 & 0.07 & 0.08 & 0.09 \\ \hline \hline
0.0 & 0.5000 & 0.4960 & 0.4920 & 0.4880 & 0.4840 & 0.4801 & 0.4761 & 0.4721 & 0.4681 & 0.4641 \\ \hline
0.1 & 0.4602 & 0.4562 & 0.4522 & 0.4483 & 0.4443 & 0.4404 & 0.4364 & 0.4325 & 0.4286 & 0.4247 \\ \hline
0.2 & 0.4207 & 0.4168 & 0.4129 & 0.4090 & 0.4052 & 0.4013 & 0.3974 & 0.3936 & 0.3897 & 0.3859 \\ \hline
0.3 & 0.3821 & 0.3783 & 0.3745 & 0.3707 & 0.3669 & 0.3632 & 0.3594 & 0.3557 & 0.3520 & 0.3483 \\ \hline
0.4 & 0.3446 & 0.3409 & 0.3372 & 0.3336 & 0.3300 & 0.3264 & 0.3228 & 0.3192 & 0.3156 & 0.3121 \\ \hline
0.5 & 0.3085 & 0.3050 & 0.3015 & 0.2981 & 0.2946 & 0.2912 & 0.2877 & 0.2843 & 0.2810 & 0.2776 \\ \hline
0.6 & 0.2743 & 0.2709 & 0.2676 & 0.2643 & 0.2611 & 0.2578 & 0.2546 & 0.2514 & 0.2483 & 0.2451 \\ \hline
0.7 & 0.2420 & 0.2389 & 0.2358 & 0.2327 & 0.2296 & 0.2266 & 0.2236 & 0.2206 & 0.2177 & 0.2148 \\ \hline
0.8 & 0.2119 & 0.2090 & 0.2061 & 0.2033 & 0.2005 & 0.1977 & 0.1949 & 0.1922 & 0.1894 & 0.1867 \\ \hline
0.9 & 0.1841 & 0.1814 & 0.1788 & 0.1762 & 0.1736 & 0.1711 & 0.1685 & 0.1660 & 0.1635 & 0.1611 \\ \hline
1.0 & 0.1587 & 0.1562 & 0.1539 & 0.1515 & 0.1492 & 0.1469 & 0.1446 & 0.1423 & 0.1401 & 0.1379 \\ \hline
1.1 & 0.1357 & 0.1335 & 0.1314 & 0.1292 & 0.1271 & 0.1251 & 0.1230 & 0.1210 & 0.1190 & 0.1170 \\ \hline
1.2 & 0.1151 & 0.1131 & 0.1112 & 0.1093 & 0.1075 & 0.1056 & 0.1038 & 0.1020 & 0.1003 & 0.0985 \\ \hline
1.3 & 0.0968 & 0.0951 & 0.0934 & 0.0918 & 0.0901 & 0.0885 & 0.0869 & 0.0853 & 0.0838 & 0.0823 \\ \hline
1.4 & 0.0808 & 0.0793 & 0.0778 & 0.0764 & 0.0749 & 0.0735 & 0.0721 & 0.0708 & 0.0694 & 0.0681 \\ \hline
1.5 & 0.0668 & 0.0655 & 0.0643 & 0.0630 & 0.0618 & 0.0606 & 0.0594 & 0.0582 & 0.0571 & 0.0559 \\ \hline
1.6 & 0.0548 & 0.0537 & 0.0526 & 0.0516 & 0.0505 & 0.0495 & 0.0485 & 0.0475 & 0.0465 & 0.0455 \\ \hline
1.7 & 0.0446 & 0.0436 & 0.0427 & 0.0418 & 0.0409 & 0.0401 & 0.0392 & 0.0384 & 0.0375 & 0.0367 \\ \hline
1.8 & 0.0359 & 0.0351 & 0.0344 & 0.0336 & 0.0329 & 0.0322 & 0.0314 & 0.0307 & 0.0301 & 0.0294 \\ \hline
1.9 & 0.0287 & 0.0281 & 0.0274 & 0.0268 & 0.0262 & 0.0256 & 0.0250 & 0.0244 & 0.0239 & 0.0233 \\ \hline
2.0 & 0.0228 & 0.0222 & 0.0217 & 0.0212 & 0.0207 & 0.0202 & 0.0197 & 0.0192 & 0.0188 & 0.0183 \\ \hline
2.1 & 0.0179 & 0.0174 & 0.0170 & 0.0166 & 0.0162 & 0.0158 & 0.0154 & 0.0150 & 0.0146 & 0.0143 \\ \hline
2.2 & 0.0139 & 0.0136 & 0.0132 & 0.0129 & 0.0125 & 0.0122 & 0.0119 & 0.0116 & 0.0113 & 0.0110 \\ \hline
2.3 & 0.0107 & 0.0104 & 0.0102 & 0.0099 & 0.0096 & 0.0094 & 0.0091 & 0.0089 & 0.0087 & 0.0084 \\ \hline
2.4 & 0.0082 & 0.0080 & 0.0078 & 0.0075 & 0.0073 & 0.0071 & 0.0069 & 0.0068 & 0.0066 & 0.0064 \\ \hline
2.5 & 0.0062 & 0.0060 & 0.0059 & 0.0057 & 0.0055 & 0.0054 & 0.0052 & 0.0051 & 0.0049 & 0.0048 \\ \hline
2.6 & 0.0047 & 0.0045 & 0.0044 & 0.0043 & 0.0041 & 0.0040 & 0.0039 & 0.0038 & 0.0037 & 0.0036 \\ \hline
2.7 & 0.0035 & 0.0034 & 0.0033 & 0.0032 & 0.0031 & 0.0030 & 0.0029 & 0.0028 & 0.0027 & 0.0026 \\ \hline
2.8 & 0.0026 & 0.0025 & 0.0024 & 0.0023 & 0.0023 & 0.0022 & 0.0021 & 0.0021 & 0.0020 & 0.0019 \\ \hline
2.9 & 0.0019 & 0.0018 & 0.0018 & 0.0017 & 0.0016 & 0.0016 & 0.0015 & 0.0015 & 0.0014 & 0.0014 \\ \hline
3.0 & 0.0013 & 0.0013 & 0.0013 & 0.0012 & 0.0012 & 0.0011 & 0.0011 & 0.0011 & 0.0010 & 0.0010 \\ \hline
\end{tabular}
\end{table}
\end{document}
