Returns the share of retail-level protein supply that does not become intake,
per country and year, and the divisor 1 / (1 - omega) that turns a
requirement into a supply floor. It replaces part of the unsourced 1.35
multiplier the SJOS-N axis used to carry (whep#753).
Rates come from Gustavsson et al. (2011), Global food losses and food
waste, FAO, Annex 4, which tabulates loss percentages for seven commodity
groups in seven world regions at five food-chain steps. Only the two steps at
or after the retail shelf are composed, because FBS food availability is
already measured there:
omega_group = 1 - (1 - d/2) * (1 - c/2), with d and c the distribution
and consumption rates. Country-year omega is the protein-weighted mean of
the group values over the supplied food basket, so it varies with a
country's diet composition while the underlying rates do not.
method = "gustavsson_half_min" (default) takes the minimum of each rate
across the seven regions and halves it. It is a deliberate lower bound on
unavoidable loss, not an estimate of achievable loss, for two reasons that
the halving does not repair. The consumption-step minimum is sub-Saharan
Africa in every commodity group, and those are scarcity figures rather than
efficiency figures; the distribution-step minima, by contrast, are genuine
best practice. On WHEP's 2010 basket it gives omega near 2.5%, which is
below FAOSTAT's retail-only median of 2.83% on energy (item 21059) even
though this wedge additionally spans the household step – a different basis
and a different statistic, so not a contradiction, but confirmation that the
construction is conservative.
"gustavsson_min" drops the halving and roughly doubles the wedge (near 4.9%
on the same basket); "none" sets it to zero and makes every floor an
explicit lower bound. All are alternatives, never fallbacks: the choice is
stamped in method_loss_wedge.
"gustavsson_regional_actual" is the sensitivity arm, and it is not an
unavoidable-loss estimate at all: it drops both the minimum and the halving
and gives each country its Annex 1 region's own observed rates. On the 2010
world basket that is 14.2% against the default's 2.5%, a floor divisor of
1.166, spanning 4.1% to 21.4% across countries. Its country structure is
contested – Gustavsson's rich-high gradient is the opposite sign to UNEP's
Food Waste Index – so it quantifies that disagreement rather than resolving
it.
Annex 1 lists 152 countries, covering 99.0% of 2010 world food protein.
Areas outside it take the unweighted mean rate across the seven regions by
default (coverage = "global_mean"), stamped as such in method_region;
"annex1_only" returns nothing for them instead. Annex 1's "China" does not
disambiguate, so both WHEP codes take Industrialized Asia: CHN (area 41,
"China, mainland") and the aggregate area 351, which carries no iso3c and
is what the FBS pin reports food on. Keying on iso3c alone would drop area
351 and with it a fifth of world food protein.
Items that Gustavsson's Annex 2 does not place in a commodity group carry no
rate rather than borrowing a neighbour's. They are dropped from the
weighting, which gives them the basket's mean wedge implicitly, and their
share is reported in protein_grouped_share so the choice stays visible. On
the 2010 world basket they are 5.0% of food protein, eggs alone being 3.7%;
assigning eggs to meat or to dairy instead moves omega by less than 0.1
percentage points either way.
Arguments
- data
Named list of injected inputs. Supply the basket either as
protein_supply(year,area_code,item_cbs_code,protein_t) or ascbs_food(year,area_code,item_cbs_code,food_t), which is converted through the same nutrition lookupbuild_food_supply()uses.biomass_coefs,items_full,food_loss_wedge,food_loss_item_groupsandfood_loss_regionsoverride the packaged tables.- method
"gustavsson_half_min"(default),"gustavsson_min","gustavsson_regional_actual"or"none".- coverage
How areas outside Annex 1's 152 countries are handled by
"gustavsson_regional_actual":"global_mean"(default) or"annex1_only". Ignored by the region-invariant methods.- protein_basis
Passed to the nutrition lookup when the basket is given as
cbs_food; seebuild_food_supply(). Defaults to"edible_portion", matching the supply the floor is compared against.
Value
A tibble keyed by year, area_code with omega, floor_divisor,
protein_grouped_share, method_loss_wedge and method_region, plus the
polity columns below.
Polity columns
Every area-keyed output carries the polity its area_code resolves to in
that row's year:
polity_area_code: The numeric key rows are AGGREGATED on, for the matrix workflows. It is a bucket, not an identity: usereporting_polity_codeto say which territory a row belongs to.reporting_polity_code: The polity itself, e.g.ESP-1846-1914. It is year-aware, so the samearea_coderesolves to different polities in different years, which is the point of the crosswalk.reporting_polity_name: Its name. It can differ from the area's own name where the area folds into an aggregate.reporting_polity_has_geometry: Whether the polity has a polygon in the WHEP polity database, for callers that need to map or intersect it.FALSEis a documented gap upstream, not an error.
Rows whose area_code resolves to no polity keep the columns with NA
rather than being dropped, so a gap is visible instead of silent.
Rows before the back-cast anchor year resolve to the polity live in that
anchor year rather than to the polity live in the row's own year, because
WHEP's pre-anchor series are back-cast onto the anchor-year territory. See
add_polity_code() for the reasoning. Where that polity is not live in the
row's own year – 41.5% of the pre-1961 (area, year) cells –
add_polity_code() says so as mapping_status == "backcast_anchor", and
polity_coverage_gaps() reports it as gap_kind == "backcast_anchor".
These columns do not say so either way.
A row whose year no mapped period covers is resolved to the NEAREST period of
the same area instead, so reporting_polity_code can name a polity that did
not exist in that row's year – FAOSTAT bucket 206 "Sudan (former)" keeps
reporting after SUD-1956-2011 ends, and its post-2011 rows carry that code.
These columns do not say so: add_polity_code() reports such a row as
mapping_status == "out_of_span", and that column is dropped here so that
adding it does not change the schema of every area-keyed output at once.
polity_coverage_gaps() reports the stand-in rows of a built table, and
options(whep.polity_mapping_status = "flag") (or "status") carries the
signal on the outputs themselves. Both are opt-in; the default is no extra
column.
Examples
build_loss_wedge(
data = list(
protein_supply = tibble::tribble(
~year, ~area_code, ~item_cbs_code, ~protein_t,
2010L, 10L, 2511L, 100,
2010L, 10L, 2605L, 100
)
)
)
#> # A tibble: 1 × 11
#> year area_code polity_area_code reporting_polity_code reporting_polity_name
#> <int> <int> <int> <chr> <chr>
#> 1 2010 10 10 AUS-1901-2025 Australia
#> # ℹ 6 more variables: reporting_polity_has_geometry <lgl>, omega <dbl>,
#> # protein_grouped_share <dbl>, method_region <chr>, floor_divisor <dbl>,
#> # method_loss_wedge <chr>
