10.1 knitr::kable() 関数

knitr パッケージの kable() 関数はとてもシンプルな表生成用の関数で, その設計もシンプルです. 行列やデータフレームのように厳密に矩形状のデータに対してのみ表を生成します. 表のセルを細かく整形したりセルを結合したりはできません. しかしこの関数は表の外見をカスタマイズする多くの引数を持っています.

kable(x, format, digits = getOption("digits"), row.names = NA,
  col.names = NA, align, caption = NULL, label = NULL, format.args = list(),
  escape = TRUE, ...)

10.1.1 サポートする表形式

データオブジェクト x を単純な表で表すことだけが必要ならば, ほとんどの場合, knitr::kable(x) で十分でしょう. format 引数は knitr のソース文書フォーマットに従って自動的に設定されます. 引数が取り得る値は列をパイプで区切った pipe, Pandoc 式の単純な表である simple, LaTeX の表 latex, HTML の表 html, reStructuredText (rst) 形式の rst です. R Markdown 文書に対して kable() はデフォルトで pipe フォーマットを使用し, このような外見になります.

knitr::kable(head(mtcars[, 1:4]), "pipe")
|                  |  mpg| cyl| disp|  hp|
|:-----------------|----:|---:|----:|---:|
|Mazda RX4         | 21.0|   6|  160| 110|
|Mazda RX4 Wag     | 21.0|   6|  160| 110|
|Datsun 710        | 22.8|   4|  108|  93|
|Hornet 4 Drive    | 21.4|   6|  258| 110|
|Hornet Sportabout | 18.7|   8|  360| 175|
|Valiant           | 18.1|   6|  225| 105|

単純な表, そして HTML, LaTeX, reStructuredText での表を生成できます.

knitr::kable(head(mtcars[, 1:4]), "simple")
                      mpg   cyl   disp    hp
------------------  -----  ----  -----  ----
Mazda RX4            21.0     6    160   110
Mazda RX4 Wag        21.0     6    160   110
Datsun 710           22.8     4    108    93
Hornet 4 Drive       21.4     6    258   110
Hornet Sportabout    18.7     8    360   175
Valiant              18.1     6    225   105
knitr::kable(mtcars[1:2, 1:2], "html")
<table>
 <thead>
  <tr>
   <th style="text-align:left;">   </th>
   <th style="text-align:right;"> mpg </th>
   <th style="text-align:right;"> cyl </th>
  </tr>
 </thead>
<tbody>
  <tr>
   <td style="text-align:left;"> Mazda RX4 </td>
   <td style="text-align:right;"> 21 </td>
   <td style="text-align:right;"> 6 </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Mazda RX4 Wag </td>
   <td style="text-align:right;"> 21 </td>
   <td style="text-align:right;"> 6 </td>
  </tr>
</tbody>
</table>
knitr::kable(head(mtcars[, 1:4]), "latex")
\begin{tabular}{l|r|r|r|r}
\hline
  & mpg & cyl & disp & hp\\
\hline
Mazda RX4 & 21.0 & 6 & 160 & 110\\
\hline
Mazda RX4 Wag & 21.0 & 6 & 160 & 110\\
\hline
Datsun 710 & 22.8 & 4 & 108 & 93\\
\hline
Hornet 4 Drive & 21.4 & 6 & 258 & 110\\
\hline
Hornet Sportabout & 18.7 & 8 & 360 & 175\\
\hline
Valiant & 18.1 & 6 & 225 & 105\\
\hline
\end{tabular}
knitr::kable(head(mtcars[, 1:4]), "rst")
=================  ====  ===  ====  ===
\                   mpg  cyl  disp   hp
=================  ====  ===  ====  ===
Mazda RX4          21.0    6   160  110
Mazda RX4 Wag      21.0    6   160  110
Datsun 710         22.8    4   108   93
Hornet 4 Drive     21.4    6   258  110
Hornet Sportabout  18.7    8   360  175
Valiant            18.1    6   225  105
=================  ====  ===  ====  ===

pipesimple のフォーマットのみが移植可能だと覚えておいてください. つまり, これだけがどの出力文書フォーマットでも動作します. それ以外の表形式は特定のフォーマットに対してのみ, 例えば format = 'latex' は LaTeX 出力に対してのみの動作です. 特定の表形式を使うことでより細かい操作ができますが, 代わりに移植性を犠牲にします.

特定の1つの表形式だけが必要で, それが文書のデフォルト形式でないなら, knitr.table.format という R のグローバルオプションで一括設定できます. 例えばこのように.

options(knitr.table.format = "latex")

このオプションには, 表形式を表す文字列か NULL を返す関数を与えることもできます. NULL の場合は knitr は適切な表形式を自動的に決定しようとします. 例えば出力フォーマットが LaTeX の場合のみ latex を使用できます.

options(knitr.table.format = function() {
  if (knitr::is_latex_output())
    "latex" else "pipe"
})

10.1.2 列名を変更する

データフレームの列の名前と読者に見せたいものとが一致するとは限りません. R のデータの列名でよくあるのは, 単語を区切るのにスペースを使わずドットやアンダースコアで代用します. これは表を読む上で不自然に感じるでしょう. col.names 引数を使うと列名を新しい名前のベクトルで置き換えることができます. 例えば iris データの列名のドットをスペースに置換します.

iris2 <- head(iris)
knitr::kable(iris2, col.names = gsub("[.]", " ", names(iris)))
Sepal Length Sepal Width Petal Length Petal Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

col.names 引数には必ずしも gsub() ような関数で列を与える必要はなく, 元のデータオブジェクトの列数と同じ長さであれば, 以下の例のように好きな文字列ベクトルを与えることができます.

knitr::kable(
  iris,
  col.names = c('ここ', 'には', '5つの', '名前が', '必要')
)

10.1.3 列のアラインメントを指定する

表の各列のアラインメントを変更するには, 左揃え l, 中央揃え c, 右揃え r のどれかと一致する1文字づつの文字ベクトルまたは, 1つの文字列で指定できます. よって kable(..., align = c('c', 'l'))kable(..., align = 'cl') に省略できます. デフォルトでは, 数値列は右揃えで, それ以外は左揃えになります. これが使用例です.

# 左, 中央, 中央, 中央, 右, 右揃え
knitr::kable(iris2, align = "lccrr")
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

10.1.4 表にキャプションを追加する

caption 引数で表にキャプションを追加できます. 以下が例です (表10.1参照).

knitr::kable(iris2, caption = "表のキャプションの例")
表 10.1: 表のキャプションの例
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

4.7節で言及したように, 出力フォーマットが bookdown パッケージ由来のものであれば, キャプションのある表を相互参照することができます.

10.1.5 数値列を整形する

小数点以下の最大表示桁数を digits 引数で指定できます. 値は round() 関数に与えられるものと同じです. それ以外の整形用の引数は base R の format() 関数に与えられるものを format.args に与えられます. まず round()format() を使ったいくつかの簡単な例をお見せすれば, この後の kable() 引数がどう動作するか理解できることでしょう.

round(1.234567, 0)
## [1] 1
round(1.234567, digits = 1)
## [1] 1.2
round(1.234567, digits = 3)
## [1] 1.235
format(1000, scientific = TRUE)
## [1] "1e+03"
format(10000.123, big.mark = ",")
## [1] "10,000"

それでは表の数値を丸め整形します.

d <- cbind(X1 = runif(3), X2 = 10^c(3, 5, 7), X3 = rnorm(3, 0,
  1000))
# 最大で4桁表示
knitr::kable(d, digits = 4)
X1 X2 X3
0.0698 1e+03 -571.9407
0.7653 1e+05 -2161.6849
0.0049 1e+07 -1705.9400
# 列ごとにそれぞれ丸める
knitr::kable(d, digits = c(5, 0, 2))
X1 X2 X3
0.06975 1e+03 -571.94
0.76528 1e+05 -2161.68
0.00491 1e+07 -1705.94
# 指数表記を使わせない
knitr::kable(d, digits = 3, format.args = list(scientific = FALSE))
X1 X2 X3
0.070 1000 -571.941
0.765 100000 -2161.685
0.005 10000000 -1705.940
# 大きな数に対してカンマ区切りする
knitr::kable(d, digits = 3, format.args = list(big.mark = ",",
  scientific = FALSE))
X1 X2 X3
0.070 1,000 -571.941
0.765 100,000 -2,161.685
0.005 10,000,000 -1,705.940

10.1.6 欠損値を表示する

デフォルトでは欠損値 (NA) は表の上で NA という文字で表示されます. これを R のグローバルオプション knitr.kable.NA で他の値に置き換えたり何も表示させない, つまり NA を空白にする, といったことができます. 例えば以下の2つ目の表では NA を空白に置き換え, 3つ目の表で ** で表示しています.

d[rbind(c(1, 1), c(2, 3), c(3, 2))] <- NA
knitr::kable(d)  # デフォルトでは NA は表示される
X1 X2 X3
NA 1e+03 -571.9
0.7653 1e+05 NA
0.0049 NA -1705.9
# NA を空白に置き換え
opts <- options(knitr.kable.NA = "")
knitr::kable(d)
X1 X2 X3
1e+03 -571.9
0.7653 1e+05
0.0049 -1705.9
options(knitr.kable.NA = "**")
knitr::kable(d)
X1 X2 X3
** 1e+03 -571.9
0.7653 1e+05 **
0.0049 ** -1705.9
options(opts)  # グローバルオプションを元に戻す

10.1.7 特殊文字をエスケープする

あなたがもし HTML や LaTeX に詳しいなら, これらの言語にいくつかの特殊文字があることを知っているでしょう. 安全に出力するために, kable() はデフォルトでは escape = TRUE 引数によって特殊文字をエスケープし, これは全ての文字がそのまま表示され, 特殊文字はその特別な意味を失います. 例えば > は HTML の表に対しては &gt; に置き換えられ, LaTeX の表に対しては _\_ としてエスケープされます. あなたが専門家で, 特殊文字を適切に扱う方法を知っているなら, escape = FALSE 引数によってこれを無効化することもできます. 以下の2つ目の表では, 特殊文字である $, \, _ を含む LaTeX の数式表現を与えています.

m <- lm(dist ~ speed, data = cars)
d <- coef(summary(m))
knitr::kable(d)
Estimate Std. Error t value Pr(>|t|)
(Intercept) -17.579 6.7584 -2.601 0.0123
speed 3.932 0.4155 9.464 0.0000
# 行と列の名前に数式表現を与える
rownames(d) <- c("$\\beta_0$", "$\\beta_1$")
colnames(d)[4] <- "$P(T > |t|)$"
knitr::kable(d, escape = FALSE)
Estimate Std. Error t value \(P(T > &#124;t&#124;)\)
\(\beta_0\) -17.579 6.7584 -2.601 0.0123
\(\beta_1\) 3.932 0.4155 9.464 0.0000

escape = FALSE なしでは特殊文字はエスケープされるか置き換えられます. 例えば $\$ に, _\_ に, \\textbackslash{} にエスケープされます.

knitr::kable(d, format = "latex", escape = TRUE)
\begin{tabular}{l|r|r|r|r}
\hline
  & Estimate & Std. Error & t value & \$P(T > |t|)\$\\
\hline
\$\textbackslash{}beta\_0\$ & -17.579 & 6.7584 & -2.601 & 0.0123\\
\hline
\$\textbackslash{}beta\_1\$ & 3.932 & 0.4155 & 9.464 & 0.0000\\
\hline
\end{tabular}

LaTeX で他によく知られた特殊文字として, #, %, &, {, } があります. HTML のよく知られた特殊文字は &, <, > そして " です. escape = FALSE で表を生成する際には, 正しい方法で特殊文字を使うよう注意深くなるべきです. とてもよくある失敗として, LaTeX で escape = FALSE を使いつつ, %_ が特殊文字であると気づかずに表の列名やキャプションに含んでしまうというものがあります.

特殊文字のエスケープの方法を正しく知っている自信がないなら knitr には2つのヘルパー内部関数があります. 以下はその例です.

knitr:::escape_latex(c("100%", "# コメント", "列名"))
## [1] "100\\%"       "\\# コメント" "列名"
knitr:::escape_html(c("<アドレス>", "x = \"文字列\"",
  "a & b"))
## [1] "&lt;アドレス&gt;"       "x = &quot;文字列&quot;"
## [3] "a &amp; b"

10.1.8 複数の表を横に並べる

データフレームや行列のリストを kable() に与えて, 複数の表を並べて生成することができます. 例えば表10.2は以下のコードから生成された2つの表を含んでいます.

d1 <- head(cars, 3)
d2 <- head(mtcars[, 1:3], 5)
knitr::kable(
  list(d1, d2),
  caption = '横に並べられた2つの表',
  booktabs = TRUE, valign = 't'
)
表 10.2: 横に並べられた2つの表
speed dist
4 2
4 10
7 4
mpg cyl disp
Mazda RX4 21.0 6 160
Mazda RX4 Wag 21.0 6 160
Datsun 710 22.8 4 108
Hornet 4 Drive 21.4 6 258
Hornet Sportabout 18.7 8 360

この機能は HTML と PDF 出力でのみ機能することに注意してください.

表を横に並べて個別の表をカスタマイズできるようにしたいと考えているなら, kables() 関数 (つまり, kable() の複数形を意味しています) を使い, kable() オブジェクトのリストを与えることもできます. 例えば, 表10.3の左の表の列名を変更し, かつ右の表の表示桁数をゼロに変更します.

# データオブジェクト d1, d2 は以前のコードチャンクのもの
knitr::kables(
  list(
    # 第1の kable() は列名を変更する
    knitr::kable(
      d1, col.names = c('速さ', '距離'), valign = 't'
    ),
    # 第2の kable() は表示桁数を設定する
    knitr::kable(d2, digits = 0, valign = 't')
  ),
  caption = 'knitr::kables() によって作成された2つの表.'
)
表 10.3: knitr::kables() によって作成された2つの表.
速さ 距離
4 2
4 10
7 4
mpg cyl disp
Mazda RX4 21 6 160
Mazda RX4 Wag 21 6 160
Datsun 710 23 4 108
Hornet 4 Drive 21 6 258
Hornet Sportabout 19 8 360

10.1.9 for ループから複数の表を作成する (*)

kable() に関してよく混乱することの1つは, for ループ内では動作しないということです. この問題は kable() に限らず他のパッケージにも存在します. 原因は少々複雑です. 技術的な話に関心があるなら, “The Ghost Printer behind Top-level R Expressions.” というブログ記事で解説されています.

以下のコードチャンクは3つの表を生成する, とあなたは予想するかもしれませんが, そうはなりません.

```{r}
for (i in 1:3) {
  knitr::kable(head(iris))
}
```

明示的に kable() の結果をプリントし, チャンクオプション results = 'asis' を適用しなければなりません. 例えばこのように.

```{r, results='asis'}
for (i in 1:3) {
  print(knitr::kable(head(iris)))
}
```

一般に, for ループ内で出力を生成するときは, 出力する要素をそれぞれ区別するためにそれぞれの直後に改行コード (\n) または HTML のコメント行 (<!-- -->) を加えることをおすすめします. これが例です.

```{r, results='asis'}
for (i in 1:3) {
  print(knitr::kable(head(iris), caption = 'A caption.'))
  cat('\n\n<!-- -->\n\n')
}
```

セパレータがないと Pandoc は個別の要素を検出するのに失敗します. 例えばグラフのすぐ後に表を続けて書いたとき, 表が認識されなくなります.

![](logo.png)
                      mpg   cyl   disp    hp
------------------  -----  ----  -----  ----
Mazda RX4            21.0     6    160   110
Mazda RX4 Wag        21.0     6    160   110

しかし明示的に分離した場合はこうなります. 以下では画像の直後に空白行を挟んでいることに気をつけてください.

![](logo.png)

                      mpg   cyl   disp    hp
------------------  -----  ----  -----  ----
Mazda RX4            21.0     6    160   110
Mazda RX4 Wag        21.0     6    160   110

あるいはこのように.

![](logo.png)

<!-- -->

                      mpg   cyl   disp    hp
------------------  -----  ----  -----  ----
Mazda RX4            21.0     6    160   110
Mazda RX4 Wag        21.0     6    160   110

10.1.10 LaTeX の表をカスタマイズする (*)

必要なのが LaTeX の出力のみなら, さらにいくつか kable() のオプションがあります. これらは HTML 等, 他の種類のフォーマットでは無視されることに注意してください. 表のフォーマットオプションをグローバルに設定 (10.1.1節参照) していない限り, この節の例では kable()format 引数を明示的に使わなければなりません.

knitr::kable(iris2, format = "latex", booktabs = TRUE)

表のキャプションを設定 (10.1.4節参照) している場合, kable() は表を table 環境で囲みます. つまりこうなります.

\begin{table}
% the table body (usually the tabular environment)
\end{table}

この環境は table.envir 引数で次のように変更できます.

knitr::kable(cars[1:2, ], format = "latex", table.envir = "figure")
\begin{figure}
\begin{tabular}{r|r}
\hline
speed & dist\\
\hline
4 & 2\\
\hline
4 & 10\\
\hline
\end{tabular}
\end{figure}

表のフロート位置は position 引数によって制御されます. 例えば position = "!b" によって表のフロートをページ下部に置くことを強制できます.

knitr::kable(cars[1:2, ], format = "latex", table.envir = "table",
  position = "!b")
\begin{table}[!b]
\begin{tabular}{r|r}
\hline
speed & dist\\
\hline
4 & 2\\
\hline
4 & 10\\
\hline
\end{tabular}
\end{table}

表にキャプションがある場合, caption.short 引数でこの例のようにキャプションの短縮形を与えることもできます.

knitr::kable(iris2, caption = "長い長いキャプション",
  caption.short = "短いキャプション")

キャプションの短縮形は LaTeX 上では \caption[]{} コマンドのブラケット ([]) 内に与えられ, ほとんどの場合は出力された PDF の表一覧で使用されます. 短縮形がない場合は, キャプション全文が表示されます.

出版物レベルのクオリティで作表するための LaTeX パッケージ booktabs に詳しいなら, この例のように booktabs = TRUE を設定できます.

iris3 <- head(iris, 10)
knitr::kable(iris3, format = "latex", booktabs = TRUE)
\begin{tabular}{rrrrl}
\toprule
Sepal.Length & Sepal.Width & Petal.Length & Petal.Width & Species\\
\midrule
5.1 & 3.5 & 1.4 & 0.2 & setosa\\
4.9 & 3.0 & 1.4 & 0.2 & setosa\\
4.7 & 3.2 & 1.3 & 0.2 & setosa\\
4.6 & 3.1 & 1.5 & 0.2 & setosa\\
5.0 & 3.6 & 1.4 & 0.2 & setosa\\
\addlinespace
5.4 & 3.9 & 1.7 & 0.4 & setosa\\
4.6 & 3.4 & 1.4 & 0.3 & setosa\\
5.0 & 3.4 & 1.5 & 0.2 & setosa\\
4.4 & 2.9 & 1.4 & 0.2 & setosa\\
4.9 & 3.1 & 1.5 & 0.1 & setosa\\
\bottomrule
\end{tabular}

R Markdown で booktabs のような LaTeX パッケージが追加で必要なら, YAML で宣言しなければならないことを忘れないでください (やり方は6.4節参照).

引数 booktabsTRUEFALSE (デフォルト) であるかに依存して表の外見は変わります.

booktabs = FALSE の場合

  • 表の列が垂直線で区切られます. vline 引数を使って垂直線を削除することができます. 例えば knitr::kable(iris, vline = "") と言うふうにします. デフォルトは vline = "|" です.このオプションをグローバルに設定することもでき, 表ごとに指定する必要はありません. 例えば options(knitr.table.vline = "") とします.
  • 水平線を toprule, midrule, linesep, bottomrule 引数で定義できます. これらのデフォルト値は \hline です.

booktabs = TRUE の場合

  • 表に垂線はありませんが, vline 引数で追加することができます.

  • テーブルのヘッダと末尾にのみ水平線が描かれます. デフォルトの引数の値は toprule = "\\toprule", midrule = "\\midrule", bottomrule = "\\bottomrule" です. デフォルトでは1行分の空きが5行ごとに挿入されます. これは linesep 引数で制御でき, このデフォルトは c("", "", "", "", "\\addlinespace") となっています. 3行ごとに空白を与えたいなら, このようにできます.

    knitr::kable(iris3, format = "latex", linesep = c("", "", "\\addlinespace"),
      booktabs = TRUE)
    \begin{tabular}{rrrrl}
    \toprule
    Sepal.Length & Sepal.Width & Petal.Length & Petal.Width & Species\\
    \midrule
    5.1 & 3.5 & 1.4 & 0.2 & setosa\\
    4.9 & 3.0 & 1.4 & 0.2 & setosa\\
    4.7 & 3.2 & 1.3 & 0.2 & setosa\\
    \addlinespace
    4.6 & 3.1 & 1.5 & 0.2 & setosa\\
    5.0 & 3.6 & 1.4 & 0.2 & setosa\\
    5.4 & 3.9 & 1.7 & 0.4 & setosa\\
    \addlinespace
    4.6 & 3.4 & 1.4 & 0.3 & setosa\\
    5.0 & 3.4 & 1.5 & 0.2 & setosa\\
    4.4 & 2.9 & 1.4 & 0.2 & setosa\\
    \addlinespace
    4.9 & 3.1 & 1.5 & 0.1 & setosa\\
    \bottomrule
    \end{tabular}

    行空けを完全に削除したいなら, linesep = '' とこともできます.

表がページよりも長くなってしまうもともあるでしょう. そのような場合は longtable = TRUE を使用できます. このオプションは LaTeX パッケージ longtable を使い表を複数ページに分割します.

table 環境に含まれた場合, つまり表にキャプションを設定した場合は表はデフォルトで中央揃えになります. 表を中央揃えにしたくないなら, centering = FALSE 引数を使用してください.

10.1.11 HTML の表をカスタマイズする (*)

knitr::kable(format = "html") で生成した表をカスタマイズしたいなら, 前節で紹介した共通の引数の他に, 1つだけ table.attr という特別な引数があります. この引数で任意の属性を <table> タグに追加することができます. 例えばこのように.

knitr::kable(mtcars[1:2, 1:2], table.attr = "class=\"striped\"",
  format = "html")
<table class="striped">
 <thead>
  <tr>
   <th style="text-align:left;">   </th>
   <th style="text-align:right;"> mpg </th>
   <th style="text-align:right;"> cyl </th>
  </tr>
 </thead>
<tbody>
  <tr>
   <td style="text-align:left;"> Mazda RX4 </td>
   <td style="text-align:right;"> 21 </td>
   <td style="text-align:right;"> 6 </td>
  </tr>
  <tr>
   <td style="text-align:left;"> Mazda RX4 Wag </td>
   <td style="text-align:right;"> 21 </td>
   <td style="text-align:right;"> 6 </td>
  </tr>
</tbody>
</table>

表に striped クラスを追加しています. しかしクラス名だけでは表の外見を変更するのに不十分です. クラスに対して CSS ルールを定義しなければなりません. 例えば偶数列と奇数列とで色の異なるストライプ背景の表を作るには, 明灰色の背景を偶数または奇数列に追加できます.

.striped tr:nth-child(even) { background: #eee; }

上記の CSS ルールの意味は, striped クラスを持つ要素の子要素ですべての行(つまり <tr> タグ )のうち行番号が偶数属性の (:nth-child(even)) 要素は, 背景色が #eee になるということです.

少しの CSS の記述だけでプレーンの HTML の表の見栄えをよくできます. 図10.1は, 以下の CSS ルールを適用した HTML 表のスクリーンショットです

table {
  margin: auto;
  border-top: 1px solid #666;
  border-bottom: 1px solid #666;
}
table thead th { border-bottom: 1px solid #ddd; }
th, td { padding: 5px; }
thead, tfoot, tr:nth-child(even) { background: #eee; }
HTML と CSS で作成したストライプ背景の表

図 10.1: HTML と CSS で作成したストライプ背景の表