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.
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.