情報系ならレポート執筆もアジャイルにしたい
この記事では,レポートの執筆環境として Jupyter Notebook を紹介します.Jupyter Notebook を利用するメリットとして,ソースコードの実行結果や図表を,コピペすることなくレポートに埋め込めることがあります.このことにより,アジャイル的なアプローチでレポートを執筆することができます.一方で,Jupyter Notebook には PDF へのエクスポート機能が貧弱という問題があります.この問題への対処法として Quarto を紹介します.
1. はじめに
1.1. レポートの執筆工程はウォーターフォールモデル
さて,早速ありきたりな質問で恐縮ですが,みなさんはどんなレポート執筆環境を利用していますか?おおむね回答は次の通りでしょう:
- Microsoft Word
- LaTeX
- Markdown
しかし,この記事ではこれらの優劣について特に語るつもりはありません.なぜなら,情報工学科の学生にとってこれらには,共通する致命的な問題があるからです.それは,レポートの執筆工程がウォーターフォールモデルである ということです.
レポートの執筆は,しばしばウォーターフォールモデルになりがちです.レポートを書くタイミングというのは,たとえば自然科学系なら実験がすべて終わったタイミング,情報系ならコーディングがすべて終わったタイミングということになるでしょう.
ウォーターフォールモデルになりがちな理由として,手戻りが発生した際のコストが大きいことが挙げられるでしょう.たとえば,ソースコードに修正箇所が見つかれば,実行結果を取りなおしたり,図表を作りなおしたり,多くの作業をやりなおす必要があります.このような操作を何度も繰り返すことは手間です.そのため,自然とウォーターフォールモデルになってしまうのです.
1.2. Prolog,あるいはプロローグ
ある男の話をさせてください.どこにでもいる,ごく普通の情報工学科の学生です.
彼は,あるプログラミングの講義を履修していました.プログラミング言語は Prolog でした.多くの講義がそうであるように,この講義でも毎週レポート課題がありました.教科書の演習問題を解いて考察を示すだけのありふれた課題です.ただし,ほかの講義とは少しだけ異なる点がありました.それは,翌週の講義の冒頭で,課題の解説を兼ねて優秀な学生のレポートを紹介していたことでした.これは,学生の学習意欲(というか自己顕示欲)を刺激するよい仕掛けになっていました.
実際のところ,彼はまんまとその術中にはまっていました.彼は,講義の冒頭で紹介されるべく,評価されるレポートを書くための努力をしました.プログラムの改善点を考察し,それから機能を改善したソースコードとその実行結果を示しました.授業では紹介されなかった言語の機能を利用してみたこともありました.
ある週のことでした.彼のレポートが講義の冒頭で紹介されました.彼にとってそれははじめてのことでした.彼は満ち足りるのを感じていました.
異変が起きたのは,考察で示したソースコードを先生が実際に実行したときでした.なにやら様子がおかしいです.プロジェクターのスクリーンには エラーが表示 されていたのでした.彼は状況がつかめませんでした.そして,先生は次のような趣旨のことを口にしました:
出力結果を想像で書いたのでしょうね
彼の頭は真っ白になりました.そんなはずはありませんでした.レポートを書いたとき,それは確かに動いていました.それなのに,それなのにこれでは,まるで彼はウソつきです.それだけではありません.彼はたった今,学問に対して捏造という極めて不誠実な態度を取ったと見なされたのでした.それは彼が最も軽蔑していることのひとつでした.
錯綜する思考の中で,彼はあるひとつの答えにたどり着きました.それは次のようなものでした.Prolog
の実行環境はインタラクティブであり,ファイルを毎回読み込みなおす必要がありました.しかし,ソースコードを修正した際にファイルを読み込みなおすことを忘れて,修正前のソースコードの実行結果をそのまま示してしまったのです.わかってしまえばまったく単純なことでした.
彼は友人に弁明しました.それに対して,友人は次のように答えました:
結果のみが真実
これは 当時流行していたアニメーション作品 内のセリフでした.きっと深い意図はありませんでした.しかし,ことこの状況においては的確な指摘であったと言えるでしょう.結果として誤った実行結果を示してしまえば,それは 捏造に違いない のです.
そして,彼の脳内でイマジナリー 貝木泥舟 が次のように語りかけるのです:
今回の件からお前が得るべき教訓は,実行結果などコピペするなということだ
1.3. 本記事の目指すところ
ここまでの話を読んで,なんとなくやりたいことがわかってきたのではないでしょうか(この記事のタイトルにすでに書いてあるようなものですが).つまり,話の趣旨は次の通りです:
レポート執筆にもアジャイルを導入したい.そして,レポート執筆がウォーターフォールモデルになっている要因である実行結果や図表の更新を自動化したい.
2. Jupyter Notebook
2.1. Jupyter Notebook の特徴
アジャイルなレポート執筆を実現する方法として,Jupyter Notebook を紹介します.Jupyter Notebook ファイルの主な特徴は次のとおりです:
- ファイル形式
- 拡張子は
.ipynb
- 中身は JSON 形式
- 拡張子は
- 構成
- Markdown セル
- MyST Markdown 形式でドキュメントを記述
- LaTeX 形式の数式が記述可能
- Code セル
- Python などのソースコードが実行可能
- シェルコマンドも実行可能
- 標準出力や図表などの実行結果も表示
- Markdown セル
エディタはソースコードの編集をシームレスに行うことができる Visual Studio Code がおすすめです.Jupyter 用の拡張機能を追加し,拡張子 .ipynb
のファイルを作ればよいです.Code セルの実行には Python の実行環境と ipykernel
パッケージが必要です.あとで紹介しますが,Dev Container で仮想環境を構築してしまうのが便利だと思います.
2.2. Jupyter Notebook の魅力
そんな Jupyter Notebook ですが,ほかのレポート執筆環境と比較するとおおむね次のような魅力があると言えます:
- 保存形式が JSON 形式であるためバージョン管理可能
- 本文は Markdown 形式であるため記述が容易
- 数式は LaTeX 形式の美しい数式が記述可能
- Code セルは実行結果がそのまま表示されるため実行結果のコピペが不要
特に 4 つ目の実行結果のコピペが不要という点が重要で,これによりソースコードの変更に対して容易に対応できます.このことにより,レポート執筆がウォーターフォールモデルになりがちであった要因を取り除くことができるのです.
2.3. Jupyter Notebook のユースケース
まだまだ Jupyter Notebook の威力が伝わっていないと思うので,ユースケースを紹介します.紹介するユースケースは次のとおりです.
- ディレクトリ構成の表示
- ソースコードの表示
- ソースコードの実行例の表示
- プログラムの実行時間の表示
- Python で作成した図表の挿入
こうしてみると,情報系のレポートの至るところで利用できることがわかります.
2.3.1. ディレクトリ構成の表示
レポートでは,ソースコードが入った ZIP ファイルのディレクトリ構成について説明が求められることがあります.tree
コマンドを使うと簡単にディレクトリ構成の情報を取得することができます.しかし,このままではディレクトリ構成を変更するたびにレポートを書きなおす必要がありまだ面倒です.こんなときは Code セルで次のように記述して実行します:
!tree
こうすることで,ディレクトリ構成に変更があった場合にも,セルを実行しなおすだだけでレポートを適切に更新することができます.
ディレクトリ構成を表示する Code セルの例
2.3.2. ソースコードの表示
レポートでは,ソースコードを表示したいことがあるでしょう.そんなときは cat
コマンドを使います.たとえば次のとおりです:
!cat example.c
こうすることで,ソースコードに変更があった場合にも,セルを実行しなおすだけでレポートを適切に更新することができます.
ソースコードを表示する Code セルの例
2.3.3. ソースコードの実行例の表示
C や Java などの Python 以外の言語によるソースコードの実行結果を示すには,Code セルに !
を先頭に追加したうえで実行するためのシェルコマンドを記述します.たとえば C の場合は次のとおりです:
!gcc example.c -o example && ./example
あるいは,Java の場合は次のとおりです:
!java Example.java
実行結果は Code セルの直下に表示されます.このようにすることで,ソースコードを修正した際に,シームレスに実行結果を更新することができます.
ソースコードの実行例を表示する Code セルの例
ほかの言語についても同じ要領で実行結果を示すことができます.
工夫が必要な例として,プログラムの中で標準入力が与えられる場合があります.そんなときは echo
コマンドと |
によるパイプ機能を使うとよいでしょう.たとえば次のように Code セルに記述します:
!echo "Hello, World!" | java Example.java
2.3.4. プログラムの実行時間の表示
レポートでは,しばしば実行時間を示すことがあります.そんなときは time
コマンドを使います.たとえば次のとおりです:
!time ./example
こうすることで,ソースコードに変更があった場合にも,セルを実行しなおすだけでレポートを適切に更新することができます.
プログラムの実行時間を表示する Code セルの例
2.3.5. Python で作成した図表の挿入
グラフ描画ライブラリ matplotlib
を用いて作成された美しい図表を挿入することもできます.たとえば次のように Code セルに記述します:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
## データを生成
x = np.linspace(0, 10, 100)
y = np.sin(x)
## グラフのプロット
plt.plot(x, y)
plt.show()
このようにすることで,プログラムの実行結果からグラフを作るような状況において,ソースコードを編集した際に,セルを実行するだけで図表を更新することができます.
Python で作成した図表を挿入する例
3. Quarto
3.1. Jupyter Notebook の問題点
ここまで Jupyter Notebook の魅力について見てきましたが,そうはいっても Jupyter Notebook も完ぺきというわけにはいきません.ちょっとした問題点がいくつかあります:
- Jupyter Notebook から PDF へのエクスポート機能が貧弱
- 図表の表示のためのソースコードなどの不要なソースコードも表示される
特に,PDF へのエクスポート機能が貧弱という問題点が致命的です.Jupyter Notebook から PDF へエクスポートするための主要な手法は以下のとおりです.
- nbconverter
- html + ブラウザの印刷機能
- webpdf
- pdf(LaTeX 経由)
- Pandoc
- Google Colaboratory の印刷機能
このうち,nbconverter, Pandoc, Google Colabratory あたりは出力が不安定です.LaTeX ベースの方法だと日本語のフォント周りの設定が上手くいかないことが多いです.また,HTML 経由の方法は,ソースコードや実行結果が見切れたり,Web ベースなので日本語組版が残念な感じになったりしてしまいます.
そういうわけでなにかいい方法はないかと調べて見つけたのが次のふたつです:
- Jupyter Book
- pdfhtml
- pdflatex
- Quarto
- html + ブラウザの印刷機能
このうち Jupyter Book は不要なソースコードを非表示にする機能もあって理想的ではあったのですが,デフォルトだと見開きデザインになっているので色々と調整が必要でした.また,Sphinx ベースということもあって,少し癖のある見た目になるほか,内部的に XeLaTex を使っているので日本語組版がやや見劣りします.
次に見つけた Quarto が個人的にはかなりよさげに見えました.というわけで,ここでは Quarto を紹介したいと思います.
3.2. Quarto の概要
とはいったもののちょっと疲れてきたので,参考にしたほかの人の記事を貼ってさぼろうと思います:
ええ,実はこれ自体も Jupyter Notebook みたいに運用できたりはするのですが.なんなら Mermaid 記法とかにも対応していたりして,Quarto をそのまま使えばいいじゃん疑惑はあったりします(あとで検証するつもりです).ここではその点には目をつぶって Quarto のエクスポート機能だけを利用することにします.
Jupyter Notebook の冒頭に Markdown セルでたとえば次のように記述します:
---
title: "タイトル"
subtitle: "サブタイトル"
author: 著者
abstract: "アブストラクト"
date: "2023 年 12 月 29 日"
format: pdf
classoption: [twocolumn]
pdf-engine: lalatex
documentclass: jlreq
include-in-header:
file: ./luatex_header
papersize: a4
jupyter: python3
lang: ja
highlight-style: github
number-sections: true
code-block-bg: "#F6F6F6"
---
include-in-header
には以下のように記述します:
\usepackage{luatexja}
\usepackage{luatexja-fontspec}
\setmainjfont{IPAexMincho}
\setsansjfont{IPAexGothic}
エクスポートするときは次のようにコマンドを実行します:
quarto render example.ipynb
これでいい感じの PDF が出力されます.
ここで強調したいのは,LuaLaTeX が使えるということです.日本語 LaTeX の最新のトレンドと言えば,LuaLaTeX + jlreq ですよねー!これがやりたかった.
3.3. 環境構築
自分は Dev Container + Dockerfile で構築しました.少し沼った要素があったので備忘録的に書いておこうと思います.まず devcontainer.json
は次のとおりです:
{
"name": "Jupyter Notebook + Quarto",
"build": {
// Dockerfile のパスを指定
"dockerfile": "Dockerfile"
},
"remoteUser": "root",
"customizations": {
"vscode": {
"extensions": [
"ms-toolsai.jupyter",
"ms-python.python",
"mhutchie.git-graph",
"ms-azuretools.vscode-docker",
"ms-python.black-formatter"
]
}
},
"postCreateCommand": ". /opt/venv/bin/activate && pip install jupyter black"
}
こちらは特に何の変哲もないと思います.
問題は Dockerfile です.Dockerfile
は次のとおりです:
FROM texlive/texlive:latest
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
locales \
python3 \
python3-pip \
python3.11-venv \
fonts-ipaexfont \
wget && \
localedef -i ja_JP -c -f UTF-8 ja_JP.UTF-8 && \
wget https://github.com/quarto-dev/quarto-cli/releases/download/v1.3.450/quarto-1.3.450-linux-amd64.deb -P /tmp && \
apt-get install -y /tmp/quarto-1.3.450-linux-amd64.deb && \
python3 -m venv /opt/venv && \
rm -rf /var/lib/apt/lists/* /tmp/*.deb
ENV LANG ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
texlive/texlive:latest
イメージを使っていますが,LuaLaTeX を使うのにあたってロケールの設定が必要になります.それも,texlive/texlive:latest
イメージには ja_JP.UTF-8
のロケールの設定がないみたいで,定義してあげる必要があるみたいです.もしかしたらもっとスマートな方法があるかもしれません.
Quarto のインストールはとりあえずバージョンを固定して wget
でダウンロードして直接インストールしました.以下のサイトを参考にすればもう少しすっきり記述できると思います.
3.4. 動作デモ
実際に動かしてみた結果をお見せします:
音声情報処理のレポート p.1
音声情報処理のレポート p.2
良さげな感じじゃないですかね?
3.5. Quarto の問題点
Quarto をさっと使ってみて感じた問題点を書いておこうと思います:
- ソースコードや実行結果が自動で改行されない
- 2 カラムレイアウトにするとアブストラクトにまで適用されてしまう
これらはそこまで致命的ではありません.ソースコードについては,リンター / フォーマッタあたりで設定することで何とかなるでしょう.あるいはカスタムテンプレートを用意すればなんとかなるでしょう(たぶん).実行結果は,う~ん,どうしたらいいんでしょうね.アブストラクトのレイアウトに関しては,include-in-header
に直接アブストラクトを記述するようにすれば一応解決できます(まぁスマートではありませんね).
4. おわりに
いかがでしたか?
Jupyter Notebook でレポートを記述することにより,実行結果や図表をセルを実行しなおすだけで更新することができます.これにより,従来ウォーターフォールモデルになりがちだったレポートの執筆作業をアジャイルっぽいアプローチで実行することができます.
また,Jupyter Notebook の問題点である PDF へのエクスポート機能が貧弱である点については,Quarto を用いることで解決できます.ネイティブに記述された LaTeX と同等程度の見栄えの PDF を生成することができ,申し分のない結果と言えるでしょう.
さて,ここで改めて当初の目標を確認しましょう:
レポート執筆にもアジャイルを導入したい.そして,レポート執筆がウォーターフォールモデルになっている要因である実行結果や図表の更新を自動化したい.
まぁ少し問題点はありますが(もし解決方法がわかる方がいれば,ぜひコメントで教えてください),これでおおむね所望の環境を手に入れられたと言えるのではないでしょうか.みなさんのレポート執筆の参考になれば幸いです.
Discussion