Skip to contents

The question: is this diet below the per capita daily share of a planetary boundary?

The cut-offs are Table 3 of the methods paper — one absolute threshold per indicator per demographic group, 56 numbers in all. The result is 1 if the diet is below its threshold, 0 if at or above it.

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)

A. One value

Useful for checking the package against the printed table by hand. Pass length-one vectors:

# a 40-year-old man, 2.1 kg CO2eq for the day — group 4
eiod_absolute_threshold(2.1, group = 4, indicator = "ghg")
#> [1] 1
# the number it was compared against
eiod_lookup_cutoff("ghg", group = 4, metric = "abs")
#> Table 3 absolute threshold - Greenhouse gas emissions
#>   unit: kg CO2eq per capita per day
#> 
#>   group        value         as printed
#>   Male 10+ yr  2.1429277142  2.1429277142
#> 
#>   printed at 15 significant digits.
#>   `as printed` is the literal text this package ships, which has
#>   never been through a numeric type. Nothing is rescaled here.

So 2.1 < 1.0836... is false, and the result is 0. The comparison is strict — the published cut-offs carry a < sign, so a diet sitting exactly on the threshold is coded 0, not 1.

B. One vector (a column from your data)

The ordinary case. reiod has no dependencies beyond base R, so the API is plain vectors: you pass df$column, not column.

ghg_ok <- eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg")
table(ghg_ok, useNA = "ifany")
#> ghg_ok
#>   0   1 
#> 467 133
round(100 * mean(ghg_ok, na.rm = TRUE), 1)
#> [1] 22.2

Read as: “22.2% of one-day diets fall below the absolute per capita daily threshold established for greenhouse gas emissions.”

The indicator key and your column name are different things

Note what that call does not say. "ghg" is the indicator key reiod knows; ghg_kgco2e is what the column happens to be called in this dataset. They are separate things and have never had to match.

This is worth stating because it is easy to conclude otherwise. The demo data used to have columns named ghg, biodiv, landtotal — the keys themselves — which made a tidy-looking loop possible and taught people that a rename was required. It never was. The columns were renamed in October 2026 for exactly that reason.

C. Adding the result as a new column

d <- eiod_demo
d$group   <- eiod_age_sex_group(d$age, d$sex)
d$abs_ghg <- eiod_absolute_threshold(d$ghg_kgco2e, d$group, "ghg")

head(d[, c("id", "age", "sex", "group", "ghg_kgco2e", "abs_ghg")], 5)
#>   id  age    sex group ghg_kgco2e abs_ghg
#> 1  1 16.4   male     4   7.709933       0
#> 2  2 33.0   male     4   7.400251       0
#> 3  3 45.5   male     4   1.127271       1
#> 4  4 16.9   male     4   1.493590       1
#> 5  5 51.2 female     8   2.790514       0

Two or three columns at a time, the same way:

d$abs_water  <- eiod_absolute_threshold(d$water_litres, d$group, "water")
d$abs_biodiv <- eiod_absolute_threshold(d$biodiv_pdf,   d$group, "biodiv")

round(100 * colMeans(d[, c("abs_ghg", "abs_water", "abs_biodiv")],
                     na.rm = TRUE), 1)
#>    abs_ghg  abs_water abs_biodiv 
#>       22.2       95.2       57.1

D. Several indicators in one call

State the mapping once — the name is the indicator key, the value is your column — and pass it to eiod_absolute_all():

eiod_demo_map
#>           landtotal          landarable         landpasture                 ghg 
#>   "land_total_m2yr"  "land_arable_m2yr" "land_pasture_m2yr"        "ghg_kgco2e" 
#>             eutroph               water              biodiv 
#>     "eutroph_gpo4e"      "water_litres"        "biodiv_pdf"
abs_all <- eiod_absolute_all(eiod_demo, grp, eiod_demo_map)
round(100 * colMeans(abs_all, na.rm = TRUE), 1)
#>   abs_landtotal  abs_landarable abs_landpasture         abs_ghg     abs_eutroph 
#>            70.2            80.8            68.7            22.2            53.5 
#>       abs_water      abs_biodiv 
#>            95.2            57.1

For your own data the line is the same shape:

my_map <- c(ghg = "ghg_total_kg", biodiv = "pdf_year", water = "water_l")

E. Only the indicators you have

Supply any subset. Indicators you do not name are simply absent from the result; there is no need to have all seven, and nothing is computed for the ones you leave out.

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

str(res)
#> 'data.frame':    600 obs. of  2 variables:
#>  $ abs_landtotal: int  1 1 1 1 1 1 1 1 1 1 ...
#>  $ abs_water    : int  1 1 1 1 1 1 1 1 1 1 ...
round(100 * colMeans(res, na.rm = TRUE), 1)
#> abs_landtotal     abs_water 
#>          70.2          95.2

One indicator alone works too:

head(eiod_absolute_all(eiod_demo, grp, map = c(water = "water_litres")), 3)
#>   abs_water
#> 1         1
#> 2         1
#> 3         1

The batch form is a loop and nothing more — it gives exactly what the single-indicator calls give:

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

Binding a subset straight onto your data frame:

d2 <- cbind(eiod_demo, eiod_absolute_all(eiod_demo, grp, map = two))
names(d2)
#>  [1] "id"                "age"               "sex"              
#>  [4] "land_total_m2yr"   "land_arable_m2yr"  "land_pasture_m2yr"
#>  [7] "ghg_kgco2e"        "eutroph_gpo4e"     "water_litres"     
#> [10] "biodiv_pdf"        "gdqs"              "abs_landtotal"    
#> [13] "abs_water"

Rename the output prefix with prefix = if abs_ collides with something you already have.

Which number was each respondent compared against?

Per group:

eiod_lookup_cutoff("ghg", metric = "abs")
#> Table 3 absolute threshold - Greenhouse gas emissions
#>   unit: kg CO2eq per capita per day
#> 
#>   group            value         as printed
#>   Male 0-1.9 yr    0.6155661134  0.6155661134
#>   Male 2-4.9 yr    0.9550617056  0.9550617056
#>   Male 5-9.9 yr    1.3152930335  1.3152930335
#>   Male 10+ yr      2.1429277142  2.1429277142
#>   Female 0-1.9 yr  0.5964328302  0.5964328302
#>   Female 2-4.9 yr  0.8845537944  0.8845537944
#>   Female 5-9.9 yr  1.2076082237  1.2076082237
#>   Female 10+ yr    1.7237261333  1.7237261333
#> 
#>   printed at 15 significant digits.
#>   `as printed` is the literal text this package ships, which has
#>   never been through a numeric type. Nothing is rescaled here.

Those are printed to 15 significant digits on purpose. The published cut-offs need up to 14, and R’s default options(digits = 7) would show 1726.3821057877 as 1726.382, which looks as though the package had rounded the guideline. It had not — only the display was short. The as printed column is the literal text transcribed from the paper, so you can check it character for character rather than trusting a rendering of a double.

Per observation:

head(eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg",
                             details = TRUE), 4)
#>      value group   cutoff below
#> 1 7.709933     4 2.142928     0
#> 2 7.400251     4 2.142928     0
#> 3 1.127271     4 2.142928     1
#> 4 1.493590     4 2.142928     1

Two cautions specific to this table

Eutrophication must be supplied in grams. Eight of the Table 3 cut-offs were corrected by Intake in October 2026 and differ from the published PDF, which prints that row in kilograms. If you have a script that divided eutrophication by 1000 before calling this function, remove the division. See vignette("eutrophication").

This metric and the relative one answer different questions and will not agree. For greenhouse gases the planetary-boundary threshold lies below the 10th percentile of the reference distribution; for water it lies above the 90th. Never use a relative band as a proxy for this result, or the reverse — vignette("table4-relative") shows where each threshold falls.