The Scientific Python Lectures are a community-based effort and require constant maintenance and improvements. New contributions such as wording improvements or inclusion of new topics are welcome.
To propose bugfixes or straightforward improvements to the lectures, see the contribution guide below.
For new topics, read the objectives first and open an issue on the GitHub project to discuss it with the editors.
Contributors should keep the following objectives and design choices of the Scientific Python Lectures in mind.
Objectives:
- Provide a self-contained introduction to Python and its primary computational packages, the ”Scientific Python stack“.
- Provide tutorials for a selection of widely-used and stable computational libraries. Currently, we cover Pandas, Statmodels, some of Seaborn, Scikit-image, Scikit-learn, and Sympy.
- We would like to apply automated testing to the code examples as much as possible.
Design choices:
- Each chapter should provide a useful basis for a 1‒2 h tutorial.
- The code should be readable.
- An idiomatic style should be followed, e.g.
import numpy as np, preference for array operations, PEP8 coding conventions.
The directory guide contains instructions on how to contribute:
To generate the html output for on-screen display, Type:
make html
the generated html files can be found in build/html
The first build takes a long time, but information is cached and subsequent builds will be faster.
Build requirements are listed in the
{download}requirements file <requirements.txt>:
Ensure that you have a virtual environment or conda environment set up, then install requirements with:
pip install -r requirements.txt
Note that you will also need the following system packages:
- Python C development headers (the
python3-devpackage on Debian, e.g.), - a C compiler like gcc,
- GNU Make,
- a full LaTeX distribution such as TeX Live (
texlive-latex-base,texlive-latex-extra,texlive-fonts-extra, andlatexmkon Debian/Ubuntu), - dvipng,
- latexmk,
- git.
Use Inkscape to modify the cover in images/, then export to PDF:
inkscape --export-filename=cover-2025.pdf cover-2025.svg
Ensure that the images/cover.pdf symlink points to the correct
file.
Use ::: for
<div> blocks (JupyterBook allows
this):
So, for example, prefer:
::: {note}
My note
:::
to the more standard Myst markup of:
<!-- #region -->
``` {note}
My note
```
<!-- #endregion -->
Note the region and endregion markup in the second form; this makes more
sure that Jupytext does not confuse the {note} with a code block. One of the
advantages of the ::: markup is that you don't need these #region
demarcations.
For the same reason, prefer the ::: form for other content blocks, such as
warnings and admonitions. For example, prefer:
::: {admonition} A custom title
My admonition
:::
We use sphinx-exercise for the exercises and solutions.
Mark all exercises and solutions with gated markers, like this:
::: {exercise-start}
:label: my-exercise-label
:class: dropdown
:::
My exercise.
::: {exercise-end}
:::
::: {solution-start} my-exercise-label
:class: dropdown
:::
My solution.
::: {solution-end}
:::
The gated markers (of form solution-start and solution-end etc) allow you
to embed code cells in the exercise or solution, because this allows code cells
to be at the top level of the notebook, where Jupyter needs them to be.
The gated markers also make it possible to for the process_notebooks.py
script to recognize exercise and solutions blocks, to parse them correctly.
(notebook-processing)=
The pages are designed both as pages for pretty HTML output, and to be used as interactive notebooks in e.g. JupyterLite.
There is some markup that we need for the pretty HTML output that looks ugly in
a Jupyter interface such as JupyterLite.
To deal with this in part, we install the
jupyterlab_myst extension by
default, so that Myst markup (mostly) appears as it should inside JupyterLab
when opened as a notebook. Another difference we want to see between the HTML
and the notebook version is that we want to avoid putting the solutions in the
notebook version, to allow more space for thought about the exercise. Both to
modify any ugly formatting, and to remove the exercise solutions, we
post-process the pages with a script _scripts/process_notebooks.py to load
the pages as text notebooks, and write out .ipynb files with modified markup
that looks better in a Jupyter interface. Some of the authoring advice here is
to allow that process to work smoothly, because the process_notebooks.py file
reads the input Myst-MD format notebooks using
Jupytext before converting to Jupyter
.ipynb files.
There may well be cases where you will want to put cells in the rendered
notebook that test values, as part of the exposition. For example, from the
intro/scipy/index.md notebook / page:
```{code-cell}
A_upper = np.triu(A)
A_upper
```
```{code-cell}
np.allclose(sp.linalg.solve_triangular(A_upper, b, lower=False),
sp.linalg.solve(A_upper, b))
```
Notice that, in this case, we do want the reader to see that test, as part of the exposition.
However, there are cases where the test would be useful, to, for example,
detect changes in the output over versions of the packages being used. We want
to avoid the situation where the text says one thing, but the values contradict
it. But we may not want the reader to have to read such tests as part of the
exposition. Here, for example, is a test from the intro/scipy/index.md
notebook:
```{code-cell}
log_a = sp.special.gammaln(500)
log_b = sp.special.gammaln(499)
log_res = log_a - log_b
res = np.exp(log_res)
res
```
```{code-cell}
:tags: [remove-cell, test]
assert np.allclose(res, 499)
```
Note that the test confirms that Scipy is still giving the output implied in
the text. Note too that we have given the testing code cell the tag
remove-cell. This drops the cell from the HTML output, and our
post-processing of the notebooks also drops these cells,
so someone opening the notebook in e.g. JupyterLite will not see them.
Accordingly, please make sure you are not defining anything in these test cells
that the notebook will need in later cells.
Be judicious — testing the output of np.ones(3) is probably not useful
— Numpy would have to break in order for that test to fail.
Run this once, in the repository directory:
pip install pre_commit
pre-commit install
Before each commit that you will push:
pre-commit run --all
Among other things, this runs the codespell check, also run by CI.