cassionData Analysis

Lesson 1 of 8

Unit · What not to collect

The columns that are not there

This dataset has fourteen columns and no name, no contact detail, no free text, no incident date, no location below district and no perpetrator detail. Every one of those absences is a decision, and every one of them can be justified by what the analysis needs.

PythonR120 minCore Humanitarian Standard (CHS)UNICEF indicator definitions

Start with the schema, not the data

import pandas as pd

referrals = pd.read_csv("protection-referrals-2024.v1.csv")
print(referrals.columns.tolist())
print(f"{len(referrals):,} cases, {len(referrals.columns)} columns")
library(dplyr)

referrals |> glimpse()

Fourteen columns for 1,850 protection cases. Now list what a case management system holds and this extract does not:

Not here Why not
Name, contact details Never leaves the case management system, under any circumstance
Free-text narrative Identifying by construction, and no analysis reads it
Incident date With an area and a category, frequently identifying
Location below admin2 A village plus a category is a person
Exact age Age band answers every disaggregation question age can
Incident type, perpetrator relationship Not shared outside the case management agency at all

Each row of that table is a decision someone made, and each one can be defended by naming the analysis it does not prevent. That is the test: not “is this sensitive” but “what would I be unable to compute without it”.

The principle, stated as a rule about columns

The GBV information management principles are usually taught as ethics. They are also, operationally, instructions about a schema:

  • Collect the minimum necessary. If no indicator needs a field, holding it is pure risk.
  • Share less than you hold. The analysis extract is not the case file. Each step outward drops fields.
  • Never share incident-level data outside the case management agency. Aggregate or do not send.
  • Consent governs use. A person consenting to a referral has not consented to a research dataset.
  • Safety first, always. Where an analysis and a survivor’s safety conflict, the analysis loses.
minimum = {
    "who": ["age_band", "sex", "disability_reported"],
    "where": ["admin1", "admin2"],
    "when": ["referral_month"],
    "what": ["case_category", "service_requested"],
    "pathway": ["consent_to_refer", "referral_made", "referral_accepted",
                "days_to_first_service", "case_status"],
}
print({k: len(v) for k, v in minimum.items()})
# Five groups of fields, each earning its place by an indicator that needs it.

Every field in this extract maps to an indicator. days_to_first_service exists because the 72-hour clinical standard for GBV health care needs it; disability_reported exists because the equity gap is the finding this dataset was built to surface. A field with no indicator behind it should not be in the extract.

What the coarsening costs, measured

The claim that coarsening is cheap should be tested rather than asserted.

print(referrals["age_band"].value_counts(dropna=False))
print(referrals["referral_month"].nunique(), "distinct periods")
print(referrals["admin2"].nunique(), "areas")
referrals |> count(age_band)
referrals |> summarise(periods = n_distinct(referral_month),
                       areas = n_distinct(admin2))

Five age bands, twelve months, six areas. Now ask what an exact age and an exact date would add:

  • Timeliness: measured in days from referral to service, which is already a duration and does not need a calendar date.
  • Seasonality: monthly is the finest resolution any protection trend is read at, and a weekly series on 1,850 cases would be noise.
  • Age disaggregation: the bands are the reporting categories. A child, an adolescent, a working-age adult and an older person are the four distinctions every protection indicator makes.

So the coarsening costs nothing that anyone asked for. That is what makes it defensible, and it is why the argument for it is analytical rather than merely cautious.

The one that is genuinely a loss

Not every omission is free, and pretending otherwise is how a data protection argument loses credibility.

Dropping incident type means you cannot say which forms of violence the referral pathway serves worst. That is a real analytical loss, and the answer is not that it does not matter. The answer is that the analysis belongs inside the case management agency, run by the people who hold the data, and what leaves is the conclusion rather than the file.

print("Question: does the pathway serve some incident types worse than others?")
print("Where it can be answered: inside the case management agency")
print("What leaves the agency: the finding, not the disaggregation")
# The right answer to "we need incident type" is often "we will run it and
# send you the result", not "no".

“You cannot have the file, and here is the answer to your question” is the professional response, and it is available far more often than either a flat refusal or a quiet handover.

The 62 blank age bands

missing = referrals["age_band"].isna()
print(f"{missing.sum()} cases with no age band ({missing.mean():.1%})")
print(referrals.loc[missing, "case_category"].value_counts())
referrals |> filter(is.na(age_band)) |> count(case_category)

Age band is both the most-used disaggregation and the field most often missing in real intake data, because it is asked at a moment when asking is intrusive. Sixty-two cases is 3.4% and it is not distributed evenly, so an age- disaggregated table has a smaller denominator than the pathway table beside it and must say so.

The instinct to impute is wrong here for a reason specific to this sector: an imputed age band attached to a real case in a protection dataset is a fabricated attribute of an identifiable person.

What to write in the methodology note

Data handling

  Analysis extract holds 14 fields for 1,850 cases. It contains no name,
  contact detail, free text, incident date, location below admin2, exact age,
  incident type or perpetrator detail.

  Age is recorded in five bands and time in months. Both are sufficient for
  every indicator reported here, and both reduce the risk that a row can be
  matched to a person.

  Incident-level fields remain in the case management system. Analyses
  requiring them are run by the case management agency and reported as
  findings rather than shared as data.

  62 cases (3.4%) have no age band. Age-disaggregated figures are computed on
  1,788 cases and are labelled accordingly.

Write this before the results, not in an annex. A protection report whose data handling section is at the back has invited every reader to skip the one part that governs what the rest of it is allowed to say.

What comes next

Removing columns reduces risk and does not eliminate it. The next lesson is what happens when you cross-tabulate the columns that are left, and the point at which a perfectly anonymous dataset produces a table that identifies someone.

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.

Start the slideshowRead the slides

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.