This page is an ipython notebook, meaning you can download it and run the executable cells. It directly follows Activity 4.2. Reference answers appear after the questions, challenges, predictions, and reflections. They show one reasonable solution or interpretation, but other correct approaches may exist.
Reproducibility & Reporting with Jupyter and Quarto¶
Tools: Python, Jupyter, Quarto, pandas, matplotlib, Git
Learning objectives¶
By the end of this tutorial, you should be able to:
evaluate whether a notebook is reproducible;
eliminate hidden execution-state dependencies;
create a computational report;
combine Markdown, Python, tables, and figures;
render a Quarto document;
generate HTML and other outputs from one source;
dynamically compute reported quantities;
import reusable functions from
src/;record Python dependencies;
control randomness;
separate raw, processed, and report outputs;
perform a final reproducibility test.
project/
├── README.md
├── data/
│ ├── raw/
│ └── processed/
├── notebooks/
├── src/
├── figures/
├── reports/
└── tests/Create:
reports/week08_report.qmdAlso create:
notebooks/week08_reproducibility.ipynbCreate a small Quarto project configuration at the repository root:
_quarto.ymlwith:
project:
type: default
execute-dir: projectexecute-dir: project tells Quarto to execute report code from the project root. This keeps paths such as data/processed/week08_data.csv and imports such as from src... consistent with the rest of the course.
python --versionCheck:
quarto --versionIf quarto --version fails in the official course Codespace, treat that as a course-environment issue rather than installing Quarto manually.
Then:
python -m pip show pandasand:
python -m pip show matplotlibReflection¶
Why could package versions matter if the same source code is run several years later?
notebooks/week08_reproducibility.ipynbcreate cells in this order.
Cell 1¶
import pandas as pd
import numpy as npCell 2¶
mean_scoreCell 3¶
rng = np.random.default_rng(556)
df = pd.DataFrame({
"group": rng.choice(
["A", "B"],
size=50
),
"score": rng.normal(
80,
10,
size=50
)
})Cell 4¶
mean_score = df["score"].mean()Now execute:
Cell 1
Cell 3
Cell 4
Cell 2The notebook appears to work.
Reference answer¶
Package versions matter because APIs, defaults, numerical behavior, and rendering can change over time. Recording dependencies helps recreate the original environment.
Part 3 — Restart and run all¶
Restart the kernel.
Then run all cells from top to bottom.
Question¶
What fails?
Why?
Fix the notebook by moving computations before anything that depends on them.
Repeat:
Restart kernel
Run alluntil the notebook succeeds.
Reference answer¶
Restart-and-run-all exposes hidden notebook state. If a variable only exists because cells were executed out of order, a clean top-to-bottom run will fail and reveal the dependency.
Part 4 — Add a reproducible random generator¶
Compare:
x = np.random.normal(
size=5
)with:
rng = np.random.default_rng(556)
x = rng.normal(
size=5
)Restart the kernel and rerun.
Question¶
Which one reproduces the same values?
Why will this matter during future simulation modules?
Reference answer¶
Using an explicit random generator and seed makes stochastic output reproducible while keeping randomness visible as part of the analysis.
rng = np.random.default_rng(556)
n = 100
df = pd.DataFrame({
"id": range(1, n + 1),
"group": rng.choice(
["A", "B"],
size=n
),
"age": rng.normal(
35,
9,
size=n
),
"score": rng.normal(
80,
10,
size=n
)
})Then:
df["age"] = df["age"].round(1)Save:
df.to_csv(
"data/processed/week08_data.csv",
index=False
)Reference answer¶
Saving a processed dataset creates a reproducible boundary between preparation and reporting; the report can consume the same well-defined input each time.
Part 6 — Create a reusable summary function¶
In:
src/week08_analysis.pywrite:
import pandas as pdThen:
def summarize_groups(
data: pd.DataFrame
) -> pd.DataFrame:
return (
data.groupby(
"group"
)["score"]
.agg(
n="count",
mean="mean",
sd="std"
)
.reset_index()
)Save.
Reference answer¶
A reusable summary function prevents copied logic from drifting across reports. Fixes and changes can be made once.
from src.week08_analysis import (
summarize_groups
)Then:
summary = summarize_groups(df)
summaryReflection¶
Why is this preferable to copying the summary logic into every report?
reports/week08_report.qmdAdd:
---
title: "STA 556 Week 8 Reproducible Report"
author: "Your Name"
format: html
jupyter: python3
---Then:
# Introduction
This report demonstrates a reproducible statistical
workflow using Python and Quarto.Reference answer¶
A document that combines narrative with executable code keeps analysis and interpretation synchronized and easier to regenerate.
{python} import pandas as pd import matplotlib.pyplot as plt
from src.week08_analysis import summarize_groups
`
Depending on where your report is located, you may need to ensure the project root is the working directory.
Question¶
Why should path assumptions be explicit?
{python} df = pd.read_csv( “data/processed/week08_data.csv” )
df.head()
`
Important¶
Do not manually recreate the DataFrame inside the report.
The report should consume the processed analytical data.
Reference answer¶
Project-relative paths are preferable to machine-specific absolute paths because the project can be cloned or moved without editing file locations.
{python} assert len(df) == 100 assert df[“id”].is_unique assert df[“score”].notna().all()
`
Question¶
Why include checks in a report-generation workflow?
What happens if the upstream data unexpectedly change?
{python} summary = summarize_groups(df) summary
`
Add narrative:
## Descriptive statistics
The table below summarizes assessment scores by group.Reference answer¶
Generating summary tables from code ensures displayed values correspond to the current data rather than manually copied numbers.
overall_mean = df["score"].mean()Use the result in your narrative.
If your chosen Quarto/Jupyter workflow supports inline execution, insert it dynamically.
If not, generate a compact results object in a code block and reference the reported value immediately below it.
Principle¶
Do not manually copy a statistic if the document can generate it.
Reference answer¶
If the data change, ideally nothing is manually updated; rerendering should recompute dynamic values automatically.
{python} fig, ax = plt.subplots( figsize=(7, 5) )
ax.hist( df[“score”], bins=15 )
ax.set( title=“Distribution of assessment scores”, xlabel=“Assessment score”, ylabel=“Frequency” )
plt.show()
`
Render later and verify the figure appears.
{python} #| label: fig-score-distribution #| fig-cap: “Distribution of assessment scores.”
fig, ax = plt.subplots()
ax.hist( df[“score”], bins=15 )
ax.set( xlabel=“Assessment score”, ylabel=“Frequency” )
plt.show()
`
Now reference the figure in your prose according to Quarto’s cross-reference syntax.
quarto render reports/week08_report.qmdLocate the resulting HTML file.
Open it.
Questions¶
Did the Python code execute?
Did the table appear?
Did the figure appear?
Did the narrative render correctly?
quarto preview reports/week08_report.qmdIn GitHub Codespaces, Quarto runs the preview server inside the Codespace, not on your laptop. Codespaces detects the preview server and exposes it through a forwarded port. Use the Open in Browser notification or the VS Code Ports panel rather than manually typing a localhost address.
Edit the report and observe the preview update.
Reflection¶
When is preview useful?
When would you use render instead?
Reference answer¶
Previewing is useful during development, but the final artifact should still be reproducible from a clean execution.
execute:
echo: falseRender.
What changed?
Now remove it and instead hide only one code block:
`
{python} #| echo: false
summary
Reference answer¶
Hiding code may improve reader-facing presentation, but the source should preserve the executable analysis needed for reproducibility.
format:
html:
code-fold: trueReference answer¶
Code folding lets readers focus on interpretation while still allowing inspection of implementation details.
format:
html: default
docx: defaultThen:
quarto render reports/week08_report.qmdReference answer¶
Rendering multiple formats from one source reduces copy/paste divergence because the analysis logic remains the same.
pdf: defaultand render.
If PDF rendering fails because a TeX dependency is missing, document the dependency rather than treating it as an analysis error.
Reference answer¶
PDF is useful when fixed layout matters, though it may require extra rendering dependencies; the underlying analysis should remain unchanged.
score
age
groupwith reader-friendly labels where appropriate:
Assessment score
Age (years)
Study groupPrinciple¶
The report is for a reader, not for the DataFrame.
# Introduction
# Data
# Methods
# Results
## Descriptive statistics
## Visualization
# Discussion## Results
[computed table]
Group A had a higher mean score than Group B.The table is computational output.
The sentence is statistical interpretation.
Question¶
Why should these remain conceptually distinct?
Reference answer¶
Keep computation in executable code and interpretation in prose. Numerical results should be generated, not manually typed, whenever possible.
data/processed/week08_data.csvtemporarily.
Render the report.
Question¶
What happens?
Why is an obvious failure preferable to silently using old manually copied results?
Restore the file afterward.
Reference answer¶
A deliberate failure checks that the report truly depends on documented inputs/environment rather than stale state.
python -m pip freeze > requirements.txtOpen:
requirements.txtReference answer¶
Record package/runtime versions so another analyst can reconstruct the software context.
Part 27 — Reproducibility test in a fresh environment¶
Conceptually, another user should be able to run:
python -m pip install -r requirements.txtThis command documents how an environment can be recreated, but students normally do not need to run it in STA 556 Codespaces because the course dependencies are installed automatically when the Codespace is created. Treat requirements.txt as part of the reproducibility record rather than as a weekly installation step.
and then:
quarto render reports/week08_report.qmdYou do not need to destroy your current environment.
Instead, explain the steps another researcher would follow.
Reference answer¶
A fresh-environment test removes hidden local assumptions and is strong evidence that the workflow is reproducible.
Part 28 — Make a clean execution test¶
Before the final render:
restart the Jupyter kernel;
run the notebook from beginning to end;
save it;
render the Quarto report;
inspect warnings/errors;
verify the reported numbers.
Question¶
Why is this stronger than simply checking whether the final HTML file exists?
Reference answer¶
A clean execution test asks whether the artifact can be rebuilt from documented inputs, in order, without prior interactive state.
Part 29 — Remove manual values¶
Search your report for manually typed statistical quantities.
For example:
The mean score was 81.37.Ask:
Could this value be generated computationally?
If yes, replace the manual workflow.
Part 30 — One-source-many-outputs challenge¶
Configure the report to produce at least:
HTML
DOCXfrom the same .qmd.
Render both.
Compare:
tables;
figures;
headings;
code;
overall readability.
Reflection¶
What formatting differences exist despite sharing one source?
Reference answer¶
One-source-many-outputs prevents copy/edit drift: the same source can generate multiple formats while keeping calculations synchronized.
report_A.qmd
report_B.qmdthink:
report.qmd
+
group parameterSketch how your functions would need to work:
def prepare_group_report(
data,
group
):
...Question¶
What advantages does this have over copying and editing reports?
Reference answer¶
Parameterized reports reuse one template while varying an explicit parameter, reducing duplication and making repeated reporting systematic.
python src/prepare_data.py
quarto render reports/week08_report.qmdYou do not need sophisticated automation yet.
The point is to see the report as the final step of a computational pipeline.
Reference answer¶
Automation reduces repetitive manual steps and the risk of forgetting to regenerate part of a workflow.
Part 33 — Cache discussion¶
Suppose your analysis takes two hours.
Would you want Quarto to recompute it every time you change one sentence?
Discuss:
benefits of caching;
risk of stale computation;
when recomputation is preferable.
Reference answer¶
Caching can save time for expensive computations but can also preserve stale results if dependencies are not tracked correctly.
Part 34 — Audit the report¶
Use this checklist.
Data¶
Is the data source clear?
Is the processed file generated reproducibly?
Are raw data preserved?
Code¶
Are transformations in code?
Are reusable functions in
src/?Are assumptions checked?
Environment¶
Is
requirements.txtpresent?
Randomness¶
Are seeds controlled?
Report¶
Are statistics generated dynamically?
Are figures generated from code?
Are tables generated from code?
Can the report render from beginning to end?
Part 35 — Peer reproducibility test¶
If possible, exchange repositories with a classmate.
Without verbal instructions, attempt to determine:
Which file is the report source?
Which data file does it use?
Which Python module does it import?
How should the report be rendered?
What dependencies are required?
Important¶
If your partner must ask you where everything is, improve the project documentation.
## Week 8 report
To render the report:bash quarto render reports/week08_report.qmd
Required Python dependencies are listed in:text requirements.txt
Also document where input data are located.
git statusReview carefully.
Do you want to commit:
.qmd source?
requirements.txt?
src module?
processed data?
rendered HTML?
rendered Word document?Your choice should be deliberate.
Then:
git add .
git commit -m "Complete Week 8 reproducible reporting workflow"
git pushPart 38 — Final reflection¶
Answer in Markdown.
1. Reproducibility¶
What information is required to reproduce a computational analysis?
2. Jupyter¶
What is hidden state, and why can it be dangerous?
3. Literate programming¶
What does it mean to combine narrative and computation?
4. Quarto¶
What role does Quarto play in the workflow?
5. Dynamic reporting¶
Why are dynamically generated statistics preferable to manually copied values?
6. Environments¶
Why should package dependencies be recorded?
7. Randomness¶
Why should random seeds be explicit?
8. Rendered outputs¶
Why should rendered Word/HTML/PDF files usually not become the primary source document?
9. Modularity¶
How did the Week 7 function/module work improve this week’s report?
10. Scientific validity¶
Why can a workflow be reproducible but still statistically wrong?
Completion checklist¶
Created Week 8 notebook
Created a Quarto
.qmdreportDemonstrated hidden notebook state
Restarted and ran a notebook from top to bottom
Controlled randomness with a generator/seed
Created processed data
Created a reusable analysis module
Imported module functions into the workflow
Added Quarto YAML metadata
Loaded data from the report
Added validation checks
Generated a dynamic summary table
Generated a figure from code
Added a figure caption
Rendered an HTML report
Used
quarto previewControlled code visibility
Experimented with code folding
Generated a Word report
Explored PDF rendering if available
Added reader-friendly labels
Structured the report into narrative sections
Deliberately triggered a reproducibility failure
Created
requirements.txtDocumented how to restore dependencies
Performed a clean execution test
Removed manually copied statistical values
Produced multiple output formats from one source
Explored parameterized-report design
Explored automated rendering
Audited the project for reproducibility
Attempted a peer reproducibility test
Updated the README
Committed Week 8 work to Git
Pushed work to GitHub
What you should now understand¶
Raw data
↓
Documented environment
↓
Modular code
↓
Computational document
↓
Execute from beginning to end
↓
Generate tables + figures + values
↓
Render
↓
HTML / Word / PDF
↓
Another person can reproduce itNext week we move into Matrix Computation & Linear Algebra, including broadcasting, vectorized operations, SVD, and QR decomposition for statistical computing.
Reference answer¶
A reproducible workflow records dependencies, uses project-relative paths, controls randomness, separates reusable code from reporting, avoids manually copied results, and rebuilds cleanly from source.