Tutorials › Introduction to Statistics › Reproducible Analysis With Scripts

Ethics, Data Quality and Reproducible Work · Tutorial 94 of 996

Reproducible Analysis With Scripts

Build a small, documented script that turns an original data file into a saved, repeatable analysis input.

Beginner 11 min read

What You'll Learn

  • Explain what makes a data-cleaning workflow reproducible.
  • Separate an unchanged source file from a script’s saved output.
  • Translate documented cleaning decisions into explicit script steps.
  • Import, standardize, and save a small dataset with Python and pandas.
  • Describe what must stay consistent for a rerun to reproduce the output.

Why Use a Script?

A cleaning decision is useful only if it can be applied consistently. When steps are carried out by hand, it can be difficult to remember their exact order or repeat them without variation. A script records those steps as instructions: given the same original file and the same computing setup, the script can run them again to produce the same cleaned output.

Here, “reproducible” has a practical meaning. Another run of the recorded workflow should produce the same result from the same source data, rather than depending on a person remembering what to click or change. A script does not decide whether a cleaning choice is appropriate; the choices still need to be documented and justified. Earlier material on documenting data-cleaning decisions provides the foundation for turning those choices into explicit instructions.

Definition: A reproducible scripted analysis is a recorded sequence of computational steps that can be run again on the specified original data to regenerate a specified output. The script makes the transformations explicit; reproducibility also depends on using the intended input file and a sufficiently consistent software environment.

A basic workflow has three parts: an original input file, a script that carries out the documented steps, and a saved output. The original file is read, not silently overwritten. The script creates the cleaned output at a known location, so the workflow can be run again without reconstructing the steps from memory.

A Small Dataset and Its Cleaning Decisions

Suppose a hypothetical clinic dataset contains four records. The original file, named study_raw.csv, has these rows:

record_idgroupweight_kgvisit_date
201 Control 62.02026-04-02
202intervention71.52026-04-03
203CONTROL2026-04-03
204Intervention68.02026-04-04

For this demonstration, the documented decisions are limited and explicit: remove extra spaces from group labels, standardize the two group names to “Control” and “Intervention,” interpret dates in year-month-day format, and retain an empty weight value as missing. These decisions do not imply that missing weights should be filled in or that any record should be removed. They specify how the recorded values are to be represented.

The example uses Python with the pandas library. The script is stored in a project folder alongside a data folder containing the original CSV. It writes the cleaned CSV to an output folder. Keeping the source separate from the output makes it clear which file the script reads and helps prevent accidental changes to the original.

Worked Example: Predicting the Cleaned Rows

Before writing code, apply each stated decision to the four records. The first group label has surrounding spaces, so stripping those spaces gives “Control.” The third label is uppercase, but after case standardization it is also “Control.” The other two labels become “Intervention.”

The date strings already use the stated year-month-day format, so interpreting them as dates does not change their displayed calendar dates. The empty weight for record 203 remains missing. No row is deleted, and the weights for records 201, 202, and 204 remain 62.0 kg, 71.5 kg, and 68.0 kg.

The intended output therefore has four rows, with standardized group labels and the same values for the other fields apart from the date column being interpreted and saved consistently. Writing down this expected result makes the script’s purpose concrete: its output should match these decisions, not introduce extra changes.

Writing a Short Script

The following script uses paths relative to the location of the script file. For this example, save it in the project folder, with the original CSV in data/study_raw.csv. The script creates the output folder if it does not already exist.

Example script:

from pathlib import Path

import pandas as pd

root = Path(__file__).resolve().parent

input_path = root / "data" / "study_raw.csv"

output_path = root / "output" / "study_clean.csv"

df = pd.read_csv(input_path)

df["group"] = df["group"].str.strip().str.lower().map({"control": "Control", "intervention": "Intervention"})

df["visit_date"] = pd.to_datetime(df["visit_date"], format="%Y-%m-%d", errors="raise")

output_path.parent.mkdir(parents=True, exist_ok=True)

df.to_csv(output_path, index=False)

The first lines import the tools the script needs. Path helps specify file locations, while pandas reads and writes tabular data. The root path is set to the folder containing the script. From there, the input and output paths are specified, so the script does not depend on which folder happens to be active when it is run.

The read step loads the CSV into a table called df. The group-label step applies the decisions in order: remove leading and trailing spaces, convert the text to lowercase, then map each recognized label to its standard form. The date step parses each value according to the stated format. Using errors="raise" means an unparseable date causes the script to stop with an error rather than silently being treated as a valid date. The save step creates the output folder if needed and writes the cleaned table as a CSV. The index=False option prevents pandas from adding an extra row-number column.

Worked Example: Following One Record Through the Script

Take record 201, whose original group value is “ Control ”. The stripping operation removes the space before and after the word, producing “Control.” Converting to lowercase gives “control,” and the mapping turns that recognized value back into the standard label “Control.” The steps are explicit even though the final label looks much like the original.

Its date, “2026-04-02,” is parsed using the year-month-day format. Its weight remains 62.0, and its record ID remains 201. The saved row is therefore record 201, group “Control,” weight 62.0, and date 2026-04-02. Applying the same steps to record 203 changes “CONTROL” to “Control” while leaving its missing weight missing.

What the Script Saves

For this example, the resulting CSV has the following content. A blank field in the weight column represents the retained missing value.

record_idgroupweight_kgvisit_date
201Control62.02026-04-02
202Intervention71.52026-04-03
203Control2026-04-03
204Intervention68.02026-04-04

CSV is a plain-text table format, so the saved dates appear as date strings even though the script parsed them as dates before saving. The script leaves the row order unchanged and does not alter the weight values. Saving a transformed file is distinct from changing the original: the source remains available for comparison or for another run.

Worked Example: Regenerating the Output

Suppose the script is run twice, and the original file has not changed between runs. On each run, pandas reads the same four source rows. The group-label transformation is applied to those same values, the same date format is used, and the same output path is written. The second run therefore replaces the previous generated output with the same four cleaned rows.

This repeatability can be described with a transformation \(T\): if the original data are \(D\), the script produces \(T(D)\). With the input \(D\) and the transformation steps held fixed, another run again produces \(T(D)\). This statement depends on the input and computational setup being the same; it is not a claim that every script, or every future version of its software, must always produce identical files.

Why Repeating the Steps Works

For the example’s group labels, the transformation maps each recognized form to a single standard label. Once a label is “Control,” applying the same cleaning again still gives “Control”; the same is true for “Intervention.” This is an idempotent transformation: applying it twice has the same result as applying it once. For a value \(x\) that is one of the recognized labels, write the label transformation as \(g\). Then:

$$ g(g(x)) = g(x) $$

This property is useful because minor differences in capitalization or surrounding spaces do not accumulate across repeated applications. However, idempotence alone does not guarantee a reproducible workflow. If the script reads a different file, uses different mapping rules, or depends on a different software behavior, the output may differ. Reproducibility requires the input and the instructions to be sufficiently well specified as well as the steps to be repeatable.

1
Identify the source.
Read the intended original file and leave it unchanged.
2
Apply documented transformations.
Make each cleaning decision a clear operation in the script rather than an undocumented manual adjustment.
3
Save the generated result.
Write to a specified output location so that the same workflow can create the output again.

What Makes a Workflow Reproducible?

A script is a record of actions, but several practical details determine whether it can be rerun successfully. The input path must point to the intended original file. The script must state the transformations clearly enough that another person can understand what each one does. The output path should be explicit. The required software, such as Python and pandas, must be available, and differences in software versions may matter for some operations.

The scope of this example is deliberately modest. It demonstrates how to record transformations; it does not establish that the chosen cleaning decisions are scientifically appropriate. For instance, standardizing category labels is suitable when the variations are known to represent the same categories. If a label is unfamiliar, a script should not automatically guess its meaning. The decision about what to do with an unexpected value needs to be made and documented rather than hidden inside a transformation.

Key takeaway: A reproducible script reads a specified original file, applies explicit documented steps, and saves an output that can be regenerated. The script records the procedure; it does not replace judgment about whether each procedure is justified.

Common Mistakes

  • Overwriting the source file. The wrong reasoning is that saving the cleaned data over the original is simpler. The correction is to keep the original intact and write the transformed data to a separate output file.
  • Assuming a script automatically makes every decision appropriate. Code can repeat a mistaken transformation consistently. Document and justify the cleaning choices before encoding them.
  • Relying on memory for a step that is absent from the script. If a person edits a value by hand after the script runs, that change will not necessarily appear on the next run. Include intended transformations in the recorded workflow.
  • Ignoring the input and software setup. Running the same instructions on a different source file or under different software conditions may not reproduce the same output. Specify the intended input and relevant computational requirements.

Check Your Understanding

Use the ideas in this tutorial to reason through each question.

  1. In your own words, what does it mean for a script to regenerate an output?
  2. Why does the example write the cleaned table to a separate output file instead of overwriting the original CSV?
  3. For record 203, what happens to the group label and missing weight when the script runs?
  4. What does the date-parsing option that raises an error do if a value does not match the specified date format?
  5. Name two conditions that need to stay consistent for a rerun to produce the same output.