cassionData Analysis

Lesson 3 of 8

Unit · Coping is a sequence, not a score

A household is classified by its worst strategy

Ten strategies, three severity phases, and the rule is the maximum rather than the sum. 18.2% of these households used an emergency strategy, and one district's answer to "not applicable" makes its prevalence figures wrong while leaving its classification intact.

PythonR135 minIntegrated Food Security Phase Classification (IPC)Sphere StandardsCore Humanitarian Standard (CHS)

The module, and why it is not another index

The Livelihood Coping Strategies module asks whether a household used any of ten strategies in the last thirty days. Each strategy is pre-classified into a severity phase before any data is collected, and the household is then classified by the most severe strategy it used.

Phase Strategies What they have in common
Stress Sold household assets, spent savings, borrowed money, sold more animals than usual Reduce the ability to deal with future shocks; reversible
Crisis Sold productive assets, withdrew children from school, cut health spending Reduce future productivity; hard to reverse
Emergency Sold house or land, begged, sold last female breeding animals Reduce future productivity and are close to irreversible

The classification is a maximum, not a sum, and not a count. A household that sold its last breeding animals once is worse off than one that borrowed money four times, and any arithmetic adding the two together says the opposite.

import pandas as pd

coping = pd.read_csv("livelihood-coping-2024.v1.csv")

PHASE = {
    "sold_household_assets": "stress",
    "spent_savings": "stress",
    "borrowed_money": "stress",
    "sold_more_animals_than_usual": "stress",
    "sold_productive_assets": "crisis",
    "withdrew_children_from_school": "crisis",
    "reduced_health_expenditure": "crisis",
    "sold_house_or_land": "emergency",
    "begged": "emergency",
    "sold_last_female_animals": "emergency",
}
RANK = {"none": 0, "stress": 1, "crisis": 2, "emergency": 3}

used = coping[list(PHASE)].eq("yes")
severity = pd.Series("none", index=coping.index)
for strategy, phase in PHASE.items():
    higher = used[strategy] & (severity.map(RANK) < RANK[phase])
    severity[higher] = phase

print((severity.value_counts(normalize=True) * 100).round(1))
library(dplyr)

phase <- c(sold_household_assets = "stress", spent_savings = "stress",
           borrowed_money = "stress", sold_more_animals_than_usual = "stress",
           sold_productive_assets = "crisis",
           withdrew_children_from_school = "crisis",
           reduced_health_expenditure = "crisis",
           sold_house_or_land = "emergency", begged = "emergency",
           sold_last_female_animals = "emergency")
rank <- c(none = 0, stress = 1, crisis = 2, emergency = 3)
Classification Households Share
None 339 16.0%
Stress 754 35.5%
Crisis 642 30.3%
Emergency 386 18.2%

48.5% used a crisis or emergency strategy. That single figure is what the IPC evidence table takes from this module, and it is a very different picture from the 7.3% with poor food consumption.

Not applicable is not no

A household with no animals cannot sell its last breeding female. The module records that as not-applicable, and it is a third answer rather than a flavour of no.

answers = coping["sold_last_female_animals"].value_counts()
print(answers)

applicable = coping["sold_last_female_animals"].isin(["yes", "no"])
print(f"prevalence among applicable: "
      f"{coping.loc[applicable, 'sold_last_female_animals'].eq('yes').mean():.1%}")
print(f"prevalence if n/a counted as no: "
      f"{coping['sold_last_female_animals'].eq('yes').mean():.1%}")
coping |> count(sold_last_female_animals)

coping |>
  filter(sold_last_female_animals %in% c("yes", "no")) |>
  summarise(prevalence = mean(sold_last_female_animals == "yes"), n = n())
Strategy Among households it applies to If n/a counted as no
Sold more animals than usual 37.4% 27.5%
Sold productive assets 19.8% 17.3%
Withdrew children from school 19.7% 16.5%
Sold last female animals 8.6% 5.8%

Ten points of difference on the first row. The denominator for a per-strategy prevalence is the households the strategy is available to, and counting the rest as non-users understates every one of them.

The household-level classification survives this and the per-strategy prevalence does not, because a maximum over yes values does not care whether the non-yes values are no or not-applicable. That asymmetry is worth knowing: it is why the same file can support a correct classification and a wrong prevalence table on the same page.

The district where the distinction was lost

by_district = coping.groupby("district")["sold_last_female_animals"].agg(
    yes=lambda s: (s == "yes").sum(),
    applicable=lambda s: s.isin(["yes", "no"]).sum(),
    not_applicable=lambda s: (s == "not-applicable").sum(),
)
by_district["correct"] = by_district["yes"] / by_district["applicable"]
by_district["naive"] = by_district["yes"] / coping.groupby("district").size()
print((by_district[["correct", "naive"]] * 100).round(1))
coping |>
  summarise(yes = sum(sold_last_female_animals == "yes"),
            applicable = sum(sold_last_female_animals %in% c("yes", "no")),
            n = n(), .by = district) |>
  mutate(correct = yes / applicable, naive = yes / n)
District Correct denominator Every household
Nord-Ouest 12.3% 6.8%
Artibonite 9.7% 5.3%
Sud 7.5% 4.5%
Centre 6.5% 6.4%

Centre’s two columns are almost identical, and every other district’s are not. The reason is in the raw data: Centre has no not-applicable cells at all, because its enumerators recorded them as no throughout.

print(coping.groupby("district")[list(PHASE)]
      .apply(lambda block: (block == "not-applicable").sum().sum()))
coping |> summarise(across(all_of(names(phase)),
                    ~ sum(.x == "not-applicable")), .by = district)

On the correct denominator Nord-Ouest is nearly twice Centre. On the wrong one they are the same. A district comparison built from the naive column would conclude that livestock distress selling is uniform across the response area, and would be reading one team’s data entry habit.

The partial module

Fifty-eight households have the three emergency questions blank.

emergency = [s for s, phase in PHASE.items() if phase == "emergency"]
partial = coping[emergency].eq("").all(axis=1) | coping[emergency].isna().all(axis=1)
print(f"{partial.sum()} households cannot be classified above crisis")
coping |> filter(if_all(c(sold_house_or_land, begged, sold_last_female_animals),
                        ~ is.na(.x))) |> nrow()

A maximum computed over what is present classifies them as crisis at worst. So 18.2% in emergency is a lower bound, and the honest report says so rather than presenting it as the estimate.

The alternative — dropping them — makes the emergency share an unbiased estimate of a slightly different population. Both are defensible; picking silently is not.

Why coping is measured separately from consumption

The instruments do not overlap the way people expect.

survey = pd.read_csv("food-security-survey-2024.v1.csv")
merged = survey.merge(coping, on="household_id", how="left", indicator=True)
print(merged["_merge"].value_counts())
survey |> left_join(coping, by = "household_id") |>
  summarise(matched = sum(!is.na(district.y)), n = n())

Households resort to coping strategies before their food consumption falls — that is the point of coping. A household selling assets to keep eating has intact consumption and a collapsing asset base, and an analysis reading only the FCS records it as food secure right up until the assets run out.

That is the argument for measuring both, and the next lesson is what happens when you do.

Two things to check on the join. Nine households appear in the module and not in the survey, because the module was administered from a separate listing — an inner join drops them without a word. And the survey’s twelve duplicate households appear once in the module, so the join is not one-to-one.

What comes next

Four instruments now exist on these households: consumption, hunger, coping behaviour and livelihood strategies. The next lesson asks which of them agree about who is food insecure, and the answer is most of the reason the IPC exists.

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.