cassionData Analysis

Lesson 2 of 8

A working environment in Python and R

Install Python and R so they still work on a bad connection, lay out a project directory that survives a handover, and run the same first script in both languages.

PythonR60 min

Why this lesson is not optional

The most common reason an M&E analysis stops being reproducible is not a statistical error. It is that the person who wrote it installed a package one afternoon, never recorded which one, and left. Six months later the script raises an import error on a laptop in a field office with a satellite link, and the quarterly report goes back to being made by hand in a spreadsheet.

Ten minutes of setup now is what prevents that.

Python: use uv, not the system interpreter

Your operating system ships a Python. Do not analyse data with it — it is there to run the operating system, and changing it can break things you did not know depended on it.

uv installs an isolated Python and resolves packages fast enough to be usable on a slow connection. It also writes a lock file, which is what makes the environment reproducible on someone else’s machine.

# Install uv (macOS and Linux)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create a project and pin an interpreter
uv init muac-analysis
cd muac-analysis
uv python install 3.13

# Add what this course uses
uv add pandas pyarrow matplotlib

uv add writes both pyproject.toml (what you asked for) and uv.lock (exactly what was installed, down to the hash). Commit both. A colleague then reproduces your environment with one command:

uv sync

If you are behind a proxy or working offline, uv can install from a local wheel directory. Downloading the wheels once, onto a USB stick, and installing from it is a normal workflow in this sector and is worth setting up before you need it.

R: use Projects and renv

R’s equivalent problem is the global library — packages installed into one shared location, invisibly shared across every analysis, and silently upgraded under you.

# Once per machine
install.packages("renv")

# Once per analysis, from inside an RStudio Project
renv::init()

# Add what this course uses
install.packages(c("readr", "dplyr", "tidyr", "ggplot2", "janitor"))

# Record exactly what is installed
renv::snapshot()

renv.lock is the equivalent of uv.lock. Commit it. A colleague runs renv::restore() and gets your library, not theirs.

Two habits matter as much as the tooling:

  • Work inside a Project. The working directory is then the project root, for everyone, always. Paths like data/raw/muac.csv work on any machine.
  • Never write setwd(). It encodes your username into the analysis and guarantees it fails for the next person.

A project layout that survives a handover

The same structure works in both languages. It is worth adopting exactly, because the value is in it being predictable rather than in it being clever.

muac-analysis/
  data/
    raw/          # exactly as it arrived. Read-only. Never edited.
    interim/      # intermediate output. Disposable.
    processed/    # analysis-ready. Regenerable from raw + code.
  scripts/
    01-read.py
    02-clean.py
    03-indicators.py
  output/
    figures/
    tables/
  README.md

One rule carries most of the weight: data/raw/ is read-only. If you open the export in Excel, fix three cells and save it, you have destroyed the only record of what actually arrived, and nobody — including you — can ever again tell what was original and what was a correction.

Everything downstream of data/raw/ should be reproducible by deleting it and rerunning the scripts. If it is not, something is being done by hand, and that something is the part that will break.

A useful test: delete data/processed/ and output/ entirely, rerun your scripts, and see whether you get the same results. If you cannot bring yourself to try it, you already know the answer.

The same first script, in both languages

Read the register, look at its shape, and print the first few rows. Nothing more — the point is to confirm the environment works before you depend on it.

import pandas as pd

muac = pd.read_csv("data/raw/muac-screening-artibonite-2024.v1.csv")

print(muac.shape)
print(muac.head())
print(muac.dtypes)
library(readr)
library(dplyr)

muac <- read_csv("data/raw/muac-screening-artibonite-2024.v1.csv")

dim(muac)
head(muac)
glimpse(muac)

Both should report 4,218 rows and 8 columns. If they do not, stop here — a row count that disagrees with the source is the cheapest error you will ever catch, and it gets much more expensive three scripts later.

What the two languages disagree about

You will use both, so it helps to know where they differ in ways that matter.

Concern pandas dplyr / readr
Missing value NaN, and None for objects NA, typed per column
Reading a CSV pd.read_csv guesses types read_csv guesses and reports
Chaining steps Method chaining or reassignment The pipe, |>
Grouped summary groupby().agg() group_by() |> summarise()
Categories category dtype, opt-in Factors, and they bite

Neither is better. The one that matters is the one your team already runs, and the reason this course teaches both is that you will change teams.

What comes next

The next lesson reads the export properly — which is harder than read_csv(path), because the defaults will quietly damage identifiers, dates and the missing-value code before you have looked at anything.

Teach this lesson

The lesson as a slide deck, with the prose kept in the speaker notes rather than on the slide. Generated from this page, so it cannot fall out of step with it.

The PDF needs no software and projects from any machine. The PowerPoint file is there to be edited — add your organisation's branding, cut a section for a shorter session, or merge two lessons into a workshop.