Chapter 10 — Communicating your pipeline

Reproducible reports, dashboards, and uncertainty views

Build a report, dashboard, or annotated notebook for Chapter 10, with linked views, documented exclusions, and a view of uncertainty.

Chapter 10 turns the analysis into something another person can inspect, run, and read. This page carries starter scaffolding for the integrative artifact (§10.3.4) and pointers for the environment-freezing exercise (§10.1.2).

What you will produce. One artifact for an audience outside your field, carrying at least three coordinated views and an explicit uncertainty layer, built as a reproducible report, an interactive dashboard, or an annotated notebook.

Materials for this chapter

Starter scaffolding

Quarto report and Streamlit dashboard skeletons, below on this page.

Communication tools

Reporting, dashboard, and visualization tools, maintained as software changes.

Worked notebooks

Read code alongside saved outputs, then assess which parts you can inspect and what you would need to rerun the analysis.

Studies on communication

Forty-four published studies annotated for how inspectable and reproducible their pipelines are. Useful models before you build your own.

The standards this artifact is judged against (inspectability, reproducibility, interpretability) are developed in Chapter 9.

Freezing the environment (§10.1.2)

In your Python environment, record the installed packages:

python -m pip freeze > requirements.txt

For R, run these commands in an R session:

renv::init()
renv::snapshot()                       # writes renv.lock

A Python requirements file records installed packages; it is not a complete environment lock. Keep the language version, operating system, and other dependencies with it. Then create a clean environment, install from your records, and rerun the analysis. Record random seeds and any differences in output. See the pip freeze documentation for its scope.

Choosing the form of the artifact (§10.3.4)

Choose a form based on how your audience will use the result:

Form Best when Tools
Reproducible report The audience reads linearly and you want code and narrative in one artifact Quarto, R Markdown, Jupyter Book
Interactive dashboard The audience wants to filter and drill into the data themselves Streamlit, Shiny, Dash, Observable
Annotated notebook The audience is technical and wants to see the pipeline Jupyter, Quarto notebook

Reproducible report skeleton (Quarto)

---
title: "Collaboration and topical framing in <your field>"
author: "<you>"
date: today
format:
  html:
    toc: true
    code-fold: true        # code available, but not in the reader's way
execute:
  warning: false
---

code-fold: true lets readers expand the code when they want to inspect it.

Dashboard skeleton (Streamlit)

import streamlit as st, pandas as pd

st.title("Research collaboration in <field>")
df = pd.read_parquet("data/processed/analysis_table.parquet")

year_min, year_max = int(df.year.min()), int(df.year.max())
year = (year_min, year_max)
if year_min < year_max:
    year = st.slider("Publication year", year_min, year_max, year)
sub = df[df.year.between(*year)]

st.caption(f"{len(sub):,} works, {year[0]}–{year[1]}. "
           "Excludes records without abstracts (n = ...).")   # document the filtering

col1, col2 = st.columns(2)
col1.subheader("Publications by year"); col1.line_chart(sub.groupby("year").size())
col2.subheader("Topic prevalence");     col2.bar_chart(sub.groupby("topic").size())

Replace the caption’s placeholder with your exclusion count so readers can see what was left out. Add a third view that answers another question about the same selected records.

The uncertainty layer (§10.3.5)

Community detection can assign different numeric labels to the same groups on different runs. Compare the groups’ membership, rather than the label numbers. This example measures how consistently each node shares a community with the same peers across pairs of runs:

from itertools import combinations
import community as community_louvain          # pip install python-louvain

def membership_stability(runs):
    """Mean Jaccard overlap of each node's co-members across pairs of runs."""
    if len(runs) < 2:
        raise ValueError("Compare at least two runs.")
    nodes = set(runs[0])
    if any(set(run) != nodes for run in runs):
        raise ValueError("All runs must contain the same nodes.")
    groups = []
    for run in runs:
        members = {}
        for node, label in run.items():
            members.setdefault(label, set()).add(node)
        groups.append({node: members[label] for node, label in run.items()})
    pairs = list(combinations(groups, 2))
    stability = {}
    for node in nodes:
        overlaps = []
        for left, right in pairs:
            a, b = left[node] - {node}, right[node] - {node}
            union = a | b
            overlaps.append(len(a & b) / len(union) if union else 1.0)
        stability[node] = sum(overlaps) / len(overlaps)
    return stability

runs = [community_louvain.best_partition(G, random_state=s) for s in range(20)]
stability = membership_stability(runs)
# Darker nodes: more consistent co-membership; lighter nodes: less consistent.

A score of 1 means the node has the same community peers in every run; lower scores mean membership varies. Two singleton assignments count as agreement. Use color and a clear legend to show this variation, and inspect the less stable nodes before interpreting their position. This is a measure of sensitivity to the algorithm’s seed, not a probability that a community is correct. The pairwise calculation is intended for small teaching graphs.

Graceful degradation and maintenance

Provide a static fallback for readers without JavaScript, with a text summary or accessible table that explains the views. A PNG alone does not make a chart accessible to a screen reader. Check keyboard navigation as well. Then write the maintenance memo the exercise asks for: what breaks this artifact in five years, and which parts of the analysis are portable across whatever replaces today’s tools.

Where to go next