Course 0W · Module 6 Markdown, pandoc & Jupyter
Python for Actuarial Students · Course 0W
Module 6

Markdown, pandoc & Jupyter Notebooks

Write in plain text, publish to HTML, PDF, or Word — and mix live code with prose in notebooks.

Module 6 · OverviewBy the end of this module you can…

  • Explain why programmers prefer text-based document formats
  • Write Markdown: headings, lists, emphasis, links, code, tables, and math
  • Install pandoc and render Markdown to HTML, PDF, and Word
  • Explain “literate programming” and what a Jupyter notebook is
  • Launch Jupyter Lab inside a uv-managed environment
  • Create, run, and tidy a notebook — and know its pitfalls
50 min
Concepts & demos
40 min
README + first notebook
Part 1

Text Is King

Why programmers write documents in plain text — and how Markdown makes it pretty.

Why text?What’s inside a Word document?

Rename report.docx to report.zip and open it:

report.zip\
├── [Content_Types].xml
├── _rels\
├── docProps\
└── word\
    ├── document.xml   your words, buried in XML
    ├── styles.xml
    ├── settings.xml
    └── theme\

A .docx is a compressed bundle of machine-oriented files.

Plain-text documents are…

  • Diffable — Git shows exactly which sentence changed
  • Searchable — Select-String works on them
  • Future-proof — readable in any editor in 30 years
  • Tool-neutral — no license required
  • Automatable — code can generate and convert them

MarkdownThe bridge between plain text and rich documents

Plain text.txt
Portable, but no headings, links, or tables
←
Markdown.md
Plain text with light conventions that render as rich documents
→
Rich documents.docx / .pages
Beautiful, but opaque to Git and scripts

Markdown is designed to be readable as-is — the source looks like a neatly typed email — and to render beautifully when processed.

You’ll meet it everywhere: GitHub READMEs, Jupyter, VS Code, Teams & Slack messages, Obsidian notes, Quarto reports… and these slides.

MarkdownSyntax in five minutes

# Lapse Study 2026

## Summary

Lapses were **higher** than expected in
policy years 2–3, *especially* for
single-premium business.

- Data: `claims_2025.csv`
- Method: actual vs. expected
- See the [SOA table](https://mort.soa.org)

1. Load data
2. Clean data
3. Compare to basis

> Draft — not for distribution.

Lapse Study 2026

Summary

Lapses were higher than expected in policy years 2–3, especially for single-premium business.

  • Data: claims_2025.csv
  • Method: actual vs. expected
  • See the SOA table
  1. Load data
  2. Clean data
  3. Compare to basis
Draft — not for distribution.

MarkdownCode, tables, and math

Run the model with:

```powershell
uv run python src/model.py
```

| Policy year | A/E  |
|------------:|-----:|
| 1           | 0.94 |
| 2           | 1.12 |
| 3           | 1.07 |

Annuity-due: $\ddot{a}_x = \sum_{k\ge0} v^k\,{}_kp_x$

Run the model with:

uv run python src/model.py
Policy yearA/E
10.94
21.12
31.07

Annuity-due: $\ddot{a}_x = \sum_{k\ge0} v^k\,{}_kp_x$

Math uses LaTeX notation between $…$ (inline) or $$…$$ (display). Supported by pandoc, Jupyter, and GitHub.

Part 2

pandoc: The Universal Converter

One Markdown source → HTML, PDF, Word, slides, and more.

pandocOne source, many outputs

report.mdwritten once, versioned in Git
→pandoc
.htmlweb page
.pdfprint-ready
.docxfor reviewers
.pptxquick slides

Install

winget install --id JohnMacFarlane.Pandoc -e
# new terminal:
pandoc --version

Write the valuation memo in Markdown next to the code. Produce a Word copy for the reviewer — with the company template — in one command.

pandocPDFs need a “PDF engine”

pandoc converts to PDF by handing a typesetting program the job. Pick one:

Typst — recommended for this course

Small, fast, modern, single .exe.

winget install --id Typst.Typst -e
pandoc README.md -o README.pdf --pdf-engine=typst

LaTeX (MiKTeX) — the classic

Gorgeous math typesetting; a large install. Also needed later by nbconvert --to pdf.

winget install --id MiKTeX.MiKTeX -e
pandoc README.md -o README.pdf

Zero-install fallback: render to HTML, open it in a browser, and Print → Save as PDF.

pandocCommands and options you’ll use

pandoc README.md -o README.html                                  # HTML fragment
pandoc README.md -s -o README.html                               # complete, standalone page
pandoc README.md -s --toc --mathml -o README.html                # + table of contents + math
pandoc README.md -s -c style.css --embed-resources -o README.html   # styled, single file
pandoc README.md -o README.pdf --pdf-engine=typst                # PDF
pandoc memo.md -o memo.docx --reference-doc=company_template.docx   # Word, house style
-o fileOutput file; the extension picks the format-sStandalone: full document with header
--tocAdd a table of contents--mathmlRender math in HTML (no internet needed)
-c style.cssApply a stylesheet--embed-resourcesInline CSS & images into one file

pandocDocument metadata: YAML front matter

---
title: "Lapse Experience Study"
author: "Jane Doe"
date: 2026-09-29
---

## Summary

Lapses were higher than expected…
  • A block between --- lines at the very top of the file
  • pandoc uses it for the title block, PDF metadata, and the HTML <title>
  • Start your sections at ## — the title is already the top heading

Without a title, pandoc -s warns you. Either add front matter or pass --metadata title="…".

Part 3

Jupyter Notebooks

Literate programming: code, results, and explanation in one document.

ConceptLiterate programming

Write programs for people first, computers second.

— the idea behind Donald Knuth’s “literate programming” (1984)

A Jupyter notebook (.ipynb) interleaves:

  • Markdown cells — explanation, assumptions, math
  • Code cells — Python you can run
  • Outputs — tables and charts, saved with the file

Lapse rates by policy year

Exposure is measured in policy-years; rates are crude.

[1]:
import pandas as pd df = pd.DataFrame({ "year": [1, 2, 3], "exposure": [1000, 880, 790], "lapses": [120, 90, 55], }) df["rate"] = df["lapses"] / df["exposure"] df
[1]:
yearexposurelapsesrate
0110001200.120000
12880900.102273
23790550.069620

ConceptWhy notebooks are great for analysis

Interactive

Run one step, look at the result, adjust, run again. Ideal for exploring a new dataset.

Shows its working

The reasoning, the code, and the numbers sit together — a reviewer can follow every step.

Shareable

GitHub renders .ipynb files; export to HTML or PDF for non-programmers (Module 8).

Think of a notebook as an Excel workbook where every formula is visible, documented, and re-runnable from top to bottom.

SetupJupyter Lab inside your managed environment

cd C:\actuarial_work\lapse_analytics
uv add --dev jupyterlab      # once per project (done in Module 4)
uv run jupyter lab           # every session
  • Jupyter Lab opens in your browser
  • Left: file browser, rooted at your project
  • The kernel — the Python that runs your cells — is the project’s .venv Python

Because Jupyter runs via uv run, every notebook sees exactly the packages in uv.lock. No “but it worked in my notebook” mysteries.

Finished? Save, close the browser tab, then Ctrl+C in the terminal to stop the server.

Using notebooksThe keys that make you fast

Two modes: Edit (typing in a cell, Enter) and Command (acting on cells, Esc).

Shift+EnterRun cell, move to next
Ctrl+EnterRun cell, stay put
Esc / EnterCommand / edit mode

In command mode:

A / BInsert cell above / below
M / YMake cell Markdown / code
D DDelete cell
ZUndo cell deletion
0 0Restart the kernel

Using notebooksPitfalls — and the habits that avoid them

PitfallWhy it bitesHabit
Hidden stateCells run out of order; a deleted cell’s variable still exists in memoryKernel → Restart Kernel and Run All Cells before trusting or sharing results
Noisy diffs.ipynb is JSON with outputs embedded; Git diffs are hard to readClear outputs before committing when outputs don’t matter
Everything in one notebookReusable logic gets copy-pasted between notebooksMove stable functions into src\*.py and import them (Module 8)
Absolute pathsC:\Users\Jane\… breaks for everyone elseRelative paths from the project root (Module 1!)

Hands-on Lab · 40 minDocument and explore your project

Part A — README

  1. Install pandoc and Typst with winget
  2. Write README.md for lapse_analytics (Module 5 repo)
  3. Render it to HTML and PDF in output\
  4. Commit the README (not the renders)

Part B — Notebook

  1. Launch Jupyter Lab with uv run
  2. Create notebooks\01_first_look.ipynb
  3. Mix Markdown and code cells; restart & run all
  4. Commit the notebook

Lab · Part AWrite the README

cd C:\actuarial_work\lapse_analytics then notepad README.md — replace uv’s stub with something like:

---
title: "Lapse Analytics"
author: "Your Name"
date: 2026-09-29
---

## Purpose

Explore policy **lapse experience** by policy year.

## Setup

```powershell
uv sync
uv run check_env.py
```
## Layout

| Folder       | Contents              |
|--------------|-----------------------|
| `data/`      | inputs (not in Git)   |
| `notebooks/` | exploratory notebooks |
| `output/`    | rendered reports      |

## Method

Crude lapse rate in policy year $t$:

$$ w_t = \frac{L_t}{E_t} $$

Lab · Part ARender and commit

mkdir output -Force
pandoc README.md -s --mathml -o output\README.html
pandoc README.md -o output\README.pdf --pdf-engine=typst
start output\README.html
start output\README.pdf

git status          # output\ is ignored — only README.md shows
git add README.md
git commit -m "Write project README"

Edit, re-render, refresh the browser. Tweak until you’re happy — then commit.

Push to GitHub (if you set up a remote) and see GitHub render the same README on your repo’s front page.

Lab · Part BYour first notebook

mkdir notebooks -Force
uv run jupyter lab
  1. In the file browser, open notebooks, then File → New → Notebook (kernel: Python 3)
  2. Rename it 01_first_look.ipynb
  3. Cell 1 (Markdown): a title and one sentence of purpose
  4. Cell 2 (code): the lapse table from the earlier slide
  5. Cell 3 (Markdown): what you notice about the rates
  6. Kernel → Restart Kernel and Run All Cells

Then, in a second terminal tab:

git add notebooks\01_first_look.ipynb
git commit -m "Add first-look lapse notebook"
git log --oneline

Stretch: add a cell computing the average rate with df["rate"].mean(), and a Markdown cell with the formula in $$…$$.

RecapModule 6 checklist

  • I can explain why text formats suit version control
  • I can write Markdown headings, lists, emphasis, links, code blocks, tables, and math
  • pandoc and a PDF engine are installed; I can render HTML and PDF
  • I know -s, -o, --toc, and YAML front matter
  • I can launch Jupyter Lab with uv run jupyter lab and stop it cleanly
  • I “Restart & Run All” before trusting a notebook
  • My repo now has a README and a notebook, both committed
Up next
Module 7

Introduction to Visual Studio Code

Terminal, Git, notebooks, and Python — all in one window.