4. Tables 4a–4g — relative benchmarks

The question: where does this diet sit in a fixed global reference distribution?

The cut-points are Tables 4a–4g of the methods paper — nine decile boundaries per indicator per demographic group, 560 numbers in all. The result is a band from 1 (lowest impact) to 10.

This article assumes you have run vignette("data-checks") and have a grp vector from vignette("demographic-groups"):

grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex)

Part 1 — Using it

A. One value

# a 40-year-old man, 9e-13 PDF*year of biodiversity loss — group 4
eiod_relative_band(9e-13, group = 4, indicator = "biodiv")
#> [1] 5

The decile boundaries it was placed between:

eiod_relative_band(9e-13, 4, "biodiv", details = TRUE)
#>   value group band    cut_lower    cut_upper
#> 1 9e-13     4    5 8.967337e-13 9.039909e-13

B. One vector (a column from your data)

band <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv")
table(band, useNA = "ifany")
#> band
#>    1    2    3    4    5    6    7    8    9   10 <NA> 
#>  212    3   27   19    1   67   44   99   64   52   12
head(eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv", details = TRUE), 4)
#>          value group band    cut_lower    cut_upper
#> 1 1.670692e-12     4    8 1.537477e-12 2.494486e-12
#> 2 5.547457e-13     4    1           NA 7.041503e-13
#> 3 9.416969e-13     4    6 9.039909e-13 1.224707e-12
#> 4 2.538843e-13     4    1           NA 7.041503e-13

cut_lower is NA in band 1 and cut_upper is NA in band 10 — correctly, because nothing is published below P10 or above P90.

C. Adding the result as a new column

d <- eiod_demo
d$group       <- eiod_age_sex_group(d$age, d$sex)
d$band_biodiv <- eiod_relative_band(d$biodiv_pdf, d$group, "biodiv")
d$band_water  <- eiod_relative_band(d$water_litres, d$group, "water")

head(d[, c("id", "group", "biodiv_pdf", "band_biodiv", "band_water")], 5)
#>   id group   biodiv_pdf band_biodiv band_water
#> 1  1     4 1.670692e-12           8          1
#> 2  2     4 5.547457e-13           1          1
#> 3  3     4 9.416969e-13           6          1
#> 4  4     4 2.538843e-13           1          1
#> 5  5     8 6.225413e-12          10          1

D. Several indicators in one call

bands <- eiod_relative_all(eiod_demo, grp, eiod_demo_map)
round(vapply(bands, mean, numeric(1), na.rm = TRUE), 2)
#>   band_landtotal  band_landarable band_landpasture         band_ghg 
#>             4.68             3.00             5.10             3.81 
#>     band_eutroph       band_water      band_biodiv 
#>             4.85             3.39             5.06

E. Only the indicators you have

Supply any subset — the same named-vector mapping as everywhere else, and nothing is computed for indicators you leave out:

two <- c(landtotal = "land_total_m2yr", water = "water_litres")
res <- eiod_relative_all(eiod_demo, grp, map = two)

str(res)
#> 'data.frame':    600 obs. of  2 variables:
#>  $ band_landtotal: int  4 3 4 4 1 1 5 1 4 1 ...
#>  $ band_water    : int  1 1 1 1 1 3 3 1 4 1 ...
round(vapply(res, mean, numeric(1), na.rm = TRUE), 2)
#> band_landtotal     band_water 
#>           4.68           3.39

Identical to calling the single-indicator form:

identical(res$band_water,
          eiod_relative_band(eiod_demo$water_litres, grp, "water"))
#> [1] TRUE

Binding a subset onto your data frame, and the midpoint form in the same call:

d3 <- cbind(eiod_demo,
            eiod_relative_all(eiod_demo, grp, map = two,
                              midpoint = TRUE, prefix = "mid_"))
head(d3[, c("id", "mid_landtotal", "mid_water")], 4)
#>   id mid_landtotal mid_water
#> 1  1            35         5
#> 2  2            25         5
#> 3  3            35         5
#> 4  4            35         5

Part 2 — What the bands actually mean

The relative benchmark metrics are the part of the Intake framework most often misread, and the misreading changes what the numbers mean. The rest of this article is about that one idea.

The reference distribution is external and fixed

eiod_relative_band() does not rank respondents against each other in your survey. Each one-day diet is compared against a reference distribution that is the same for every analysis anywhere in the world.

Intake built it in three steps:

  1. For each country, average dietary environmental impact per person per day at each decile, from the Planet-Based Diets Database (WWF, 2020).
  2. Those country values population-weighted, using 2010 country populations, to give a global percentile distribution of impacts per person per day.
  3. The resulting all-population decile values scaled to each demographic group by its share of average energy requirements.

The result is a fixed table of numbers, printed in the paper and shipped in this package:

subset(eiod_cutoffs,
       metric == "rel" & indicator == "biodiv" & demogroup == 4 &
         !is.na(percentile),
       select = c(percentile, value))
#>    percentile        value
#> 87         10 7.041503e-13
#> 88         20 7.156354e-13
#> 89         30 8.436509e-13
#> 90         40 8.967337e-13
#> 91         50 9.039909e-13
#> 92         60 1.224707e-12
#> 93         70 1.537477e-12
#> 94         80 2.494486e-12
#> 95         90 3.640560e-12

Your data never changes those numbers.

Why quantile() is the wrong tool

This is the practical consequence. If you compute deciles from your own sample, you get roughly 10% of observations in each band by construction:

# WRONG: ranks the sample against itself
wrong <- cut(eiod_demo$biodiv_pdf,
             breaks = quantile(eiod_demo$biodiv_pdf, seq(0, 1, 0.1),
                               na.rm = TRUE),
             labels = 1:10, include.lowest = TRUE)
table(wrong)
#> wrong
#>  1  2  3  4  5  6  7  8  9 10 
#> 59 59 59 58 59 59 58 59 59 59
# RIGHT: compares against the published global reference
right <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv")
table(right)
#> right
#>   1   2   3   4   5   6   7   8   9  10 
#> 212   3  27  19   1  67  44  99  64  52

The first is flat because it has to be. The second is not, and that shape is the information: it tells you where this population sits relative to global dietary patterns. The first tells you only that some of your respondents have higher impacts than others, which you already knew.

If your whole sample has unusually low impacts, your whole sample lands in low bands. That is the intended behaviour, and is exactly why the metric is comparable across countries and over time.

How the bands are defined

Each table gives ten rows. Rows 1–9 carry the P10…P90 cut-points; row 10 repeats the P90 value as a lower bound.

Band Condition
1 impact < P10
2 P10 ≤ impact < P20
… …
9 P80 ≤ impact < P90
10 impact ≥ P90

The inequality is strict, so a diet sitting exactly on P10 belongs in band 2:

p10 <- unname(eiod_lookup_cutoff("biodiv", 4, "rel", percentile = 10))
eiod_relative_band(c(p10 * (1 - 1e-9), p10), c(4L, 4L), "biodiv")
#> [1] 1 2

Reporting

The paper gives two forms. The first is the distribution:

“x% of one-day diets fall between the 10th and 20th percentile of global per capita dietary total land use.”

round(100 * prop.table(table(eiod_relative_band(eiod_demo$land_total_m2yr, grp,
                                                "landtotal"))), 1)
#> 
#>    1    2    3    4    5    6    7    8    9   10 
#> 14.8  0.3 19.2 23.7 15.8  0.2  2.7 14.5  2.5  6.3

The second is a mean position, for which midpoint = TRUE gives band midpoints (5, 15, … 95):

mid <- eiod_relative_band(eiod_demo$land_total_m2yr, grp, "landtotal",
                          midpoint = TRUE)
mean(mid, na.rm = TRUE)
#> [1] 41.83333

A caution on that mean

Averaging a band index treats the bands as equally spaced on the impact scale. They are emphatically not:

p <- vapply(seq(10, 90, 10),
            function(q) unname(eiod_lookup_cutoff("landpasture", 4, "rel", q)),
            numeric(1))
round(diff(p), 3)
#> [1]  0.006  2.225  2.721  4.845  0.152  0.594 12.788  5.365

In pasture land use the P10–P20 gap is a fraction of a square metre-year while the P80–P90 gap is several. A mean band is a legitimate summary of position in the reference distribution. It is not a summary of impact. If you want a mean impact, average the impact variable itself.

The limitation the paper states about itself

The reference distribution is built from Food Balance Sheet data — national availability averages, not individual consumption. It therefore captures variation between countries, not variation between people or between days.

The authors’ argument is reasonable: for assessment, monitoring and cross-country comparison, this matters less so long as the reference distribution stays fixed, because it still provides a consistent yardstick. But it does mean a band is a statement about where a diet sits relative to national average availability patterns, not relative to other individuals. That distinction belongs in the methods section of anything you publish using these.

And the thing worth repeating

A low band is not evidence of sustainability. The reference distribution describes diets as they are, and most of them exceed the planetary boundaries. For greenhouse gas emissions, every band — including band 1 — lies above the Table 3 absolute threshold:

thr <- unname(eiod_lookup_cutoff("ghg", 4, "abs"))
p10 <- unname(eiod_lookup_cutoff("ghg", 4, "rel", percentile = 10))
c(absolute_threshold = thr, reference_P10 = p10, threshold_is_lower = thr < p10)
#> absolute_threshold      reference_P10 threshold_is_lower 
#>           2.142928           3.105870           1.000000

So “this population is in the lowest decile globally for dietary greenhouse gas emissions” and “this population’s diets are within the planetary boundary” can both be false at the same time, or the first true and the second false. Report the absolute and relative metrics side by side.

Where each absolute threshold falls inside its own reference distribution, for all seven indicators:

for (ind in eiod_indicators$indicator) {
  thr <- unname(eiod_lookup_cutoff(ind, 4, "abs"))
  p   <- vapply(seq(10, 90, 10),
                function(q) unname(eiod_lookup_cutoff(ind, 4, "rel", q)),
                numeric(1))
  k <- sum(thr >= p)
  pos <- if (k == 0) "below P10" else if (k == 9) "at/above P90" else
    sprintf("P%d-P%d", k * 10, (k + 1) * 10)
  cat(sprintf("%-12s %s\n", ind, pos))
}
#> landtotal    P40-P50
#> landarable   P60-P70
#> landpasture  P40-P50
#> ghg          below P10
#> eutroph      P40-P50
#> water        at/above P90
#> biodiv       P60-P70

Greenhouse gases sit below P10 — a diet in the lowest band still exceeds the boundary. Water sits above P90, so essentially every diet passes. The two metric families answer different questions and will not agree.

See also

  • vignette("table3-absolute") — Table 3, the absolute thresholds
  • vignette("table5-composites") — Tables 5a and 5b
  • ?eiod_relative_band, ?eiod_relative_all, ?eiod_lookup_cutoff