| Title: | Dietary Environmental Impact Metrics from the Intake EIOD Framework |
|---|---|
| Description: | Calculates the dietary environmental impact metrics defined in Intake's Environmental Impacts of Foods methods paper: absolute threshold metrics against per capita planetary boundary shares, relative benchmark metrics against a fixed global reference distribution, and the two composite metrics that combine diet quality with environmental impact. Cut-offs are age- and sex-specific. The package does not contain the Environmental Impacts of Foods database itself; it applies the published metric cut-offs to impact values the user has already derived. Includes a data quality check suite covering variable type, plausibility, numerical precision and unit magnitude. |
| Authors: | Nicholus Tint Zaw [aut, cre] (ORCID: <https://orcid.org/0009-0002-8082-401X>) |
| Maintainer: | Nicholus Tint Zaw <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-27 08:07:16 UTC |
| Source: | https://github.com/nicholustintzaw/reiod |
Applies [eiod_absolute_threshold()] to several indicators in one call and returns a data frame of results, one column per indicator.
eiod_absolute_all(data, group, map, prefix = "abs_")eiod_absolute_all(data, group, map, prefix = "abs_")
data |
A data frame with one row per respondent-day. |
group |
Integer vector of demographic groups 1-8, from [eiod_age_sex_group()], of length 'nrow(data)'. |
map |
Named character vector: names are indicator keys, values are column names in 'data'. See [eiod_demo_map] for the shape. |
prefix |
Prefix for the returned column names. Default '"abs_"'. |
A data frame with 'nrow(data)' rows and one integer column per indicator in 'map', named 'paste0(prefix, indicator)'.
'map' states, explicitly, which column of your data holds which indicator. The **name** is the indicator key reiod knows ('"ghg"'); the **value** is whatever your column is actually called ('"ghg_kgco2e"'). They are different things, and nothing in reiod has ever required them to match - the vector API means 'eiod_absolute_threshold(d$anything, grp, "ghg")' works.
Supply any subset. Indicators you do not name are simply absent from the result; there is no need to have all seven.
[eiod_absolute_threshold()] for the single-indicator form and the full discussion of the metric; [eiod_relative_all()].
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # your own column names, stated once res <- eiod_absolute_all(eiod_demo, grp, map = c(ghg = "ghg_kgco2e", biodiv = "biodiv_pdf")) colMeans(res, na.rm = TRUE) # all seven, using the mapping shipped with the demo data all7 <- eiod_absolute_all(eiod_demo, grp, eiod_demo_map) round(100 * colMeans(all7, na.rm = TRUE), 1)data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # your own column names, stated once res <- eiod_absolute_all(eiod_demo, grp, map = c(ghg = "ghg_kgco2e", biodiv = "biodiv_pdf")) colMeans(res, na.rm = TRUE) # all seven, using the mapping shipped with the demo data all7 <- eiod_absolute_all(eiod_demo, grp, eiod_demo_map) round(100 * colMeans(all7, na.rm = TRUE), 1)
Codes a one-day diet '1' if its per capita environmental impact falls below the demographic group's share of the per capita daily planetary boundary, and '0' if it does not.
eiod_absolute_threshold(value, group, indicator, details = FALSE)eiod_absolute_threshold(value, group, indicator, details = FALSE)
value |
Numeric vector of per capita daily impact, already summed across everything the respondent consumed that day, in the unit shown by [eiod_list_indicators()]. |
group |
Integer vector of demographic groups 1-8, from [eiod_age_sex_group()]. |
indicator |
One of '"landtotal"', '"landarable"', '"landpasture"', '"ghg"', '"eutroph"', '"water"', '"biodiv"'. |
details |
If 'TRUE', return a data frame that also reports the threshold each observation was actually compared against, rather than just the result. Default 'FALSE'. |
An integer vector: '1' below the threshold, '0' at or above it, 'NA' where the impact or the group is missing.
If 'details = TRUE', a data frame with one row per observation and the columns 'value', 'group', 'cutoff' and 'below'. 'cutoff' is the Table 3 threshold applied to that row, so a reader can audit the comparison without opening the reference table. (Equivalent to 'threshvar()' in the companion Stata command.) The columns are the same for every indicator; an extra 'value_kg' column existed only while eutrophication needed converting, and is gone.
"x% of one-day diets fall below the absolute per capita daily threshold established for \[indicator\]."
This is a pass/fail against a planetary boundary share. It says nothing about how the diet compares with other diets; for that, see [eiod_relative_band()]. The two metric families answer different questions and will not agree.
The published cut-offs carry a '<' sign, so a diet sitting exactly on the threshold is *not* below it and is coded '0'.
A missing impact returns 'NA', not '0'. This matters more than it looks: in R, 'NA < threshold' is 'NA', but a careless implementation using 'ifelse(value < threshold, 1, 0)' on a comparison that has already been coerced can turn missing data into an apparent threshold breach. Here the guard is explicit.
Between July and October 2026 this function divided eutrophication values by 1000, because the methods paper printed the Table 3 eutrophication row in kilograms while Table 4f and the EIOF database used grams.
**Intake confirmed in October 2026 that both tables are in grams** and supplied corrected Table 3 thresholds. They are in [eiod_cutoffs], flagged by its 'value_source' column, and every indicator now compares directly.
**Supply eutrophication in grams and convert nothing.** If you are revisiting a script that divided by 1000 for this function, remove the division. See 'vignette("eutrophication")'.
An integer 'value' is refused with an error. 'is.numeric(1L)' is 'TRUE', so an integer column would otherwise pass every guard and be compared against the threshold with its fractional part already truncated away. Measured on 200,000 simulated respondent-days in the companion Stata implementation, that biased mean greenhouse gas emissions down by 13%. Convert with 'as.double()'.
This is the only data-quality test on the default path. The fuller suite - sign, zeros, unit magnitude, retained significant digits, cut-off proximity - is in [eiod_check_data()] and [eiod_check_precision()], and is **not** run for you. Run it first; see 'vignette("precision")'.
[eiod_relative_band()], [eiod_absolute_all()], [eiod_lookup_cutoff()], [eiod_check_data()]
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # the column is called ghg_kgco2e; the indicator key is "ghg". # They are different things, and your data will not use the keys. ghg_ok <- eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg") table(ghg_ok, useNA = "ifany") # which number each respondent was compared against head(eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg", details = TRUE)) # all seven indicators: state the mapping from indicator key to column eiod_absolute_all(eiod_demo, grp, eiod_demo_map)[1:3, ]data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # the column is called ghg_kgco2e; the indicator key is "ghg". # They are different things, and your data will not use the keys. ghg_ok <- eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg") table(ghg_ok, useNA = "ifany") # which number each respondent was compared against head(eiod_absolute_threshold(eiod_demo$ghg_kgco2e, grp, "ghg", details = TRUE)) # all seven indicators: state the mapping from indicator key to column eiod_absolute_all(eiod_demo, grp, eiod_demo_map)[1:3, ]
Every cut-off in the Intake framework is scaled to one of eight age/sex groups, because average dietary energy requirement differs across them. This function builds that grouping variable, which all other 'eiod_*' metric functions require.
eiod_age_sex_group( age, sex, age_units = c("years", "months"), male = NULL, female = NULL )eiod_age_sex_group( age, sex, age_units = c("years", "months"), male = NULL, female = NULL )
age |
Numeric vector of respondent ages. |
sex |
Factor, character or numeric vector of respondent sex. See "Which type should 'sex' be?" above. |
age_units |
Either '"years"' (the default) or '"months"'. |
male, female
|
Optional. The value of 'sex' that means male and female respectively. Required when 'sex' is numeric; optional for factor and character input, where they override the automatic label matching. Supply both or neither. |
An integer vector of group codes 1 to 8, with 'NA' where age or sex is missing. The result carries a '"labels"' attribute giving the group names, and can be turned into a labelled factor with 'factor(x, 1:8, attr(x, "labels"))'.
| 1 | Male 0-1.9 yr |
| 2 | Male 2-4.9 yr |
| 3 | Male 5-9.9 yr |
| 4 | Male 10+ yr |
| 5 | Female 0-1.9 yr |
| 6 | Female 2-4.9 yr |
| 7 | Female 5-9.9 yr |
| 8 | Female 10+ yr |
The paper prints the groups as "0-1.9", "2-4.9", "5-9.9" and "10+". These are read as half-open intervals: 'age < 2', '2 <= age < 5', '5 <= age < 10', 'age >= 10'. A child aged 1.95 years therefore belongs in the first group. Testing 'age <= 1.9' instead would leave everyone between 1.9 and 2.0 unassigned, and they would then drop out of every downstream metric without any error being raised.
All three common types are accepted, but they are not equally safe.
A factor is the recommended input. Its levels carry the meaning, so the data is self-documenting and no separate codebook is needed; 'table()' and model output print "male" and "female" rather than 1 and 2; and the function can resolve it without being told anything further.
Character works identically and is what 'read.csv()' produces by default in R 4.0 and later. Converting it to a factor costs nothing and is worth doing.
Numeric requires 'male' and 'female' to be given explicitly. This is a deliberate refusal rather than a missing feature. Survey data uses 1/2 and 0/1 about equally often, and there is no way to tell them apart from the values alone: both are just two distinct numbers. If the function guessed and guessed wrong, every result would be silently inverted, with no error and nothing visibly odd in the output. Rather than carry that risk, numeric input errors until the codes are stated.
The recommended route for numeric data is to convert once, near the top of the analysis, with [eiod_as_sex_factor()]. After that every downstream call is unambiguous.
For factor and character input, these labels are recognised automatically, case-insensitively and ignoring surrounding spaces: 'm', 'male', 'males', 'man', 'men', 'boy', 'boys', and 'f', 'female', 'females', 'woman', 'women', 'girl', 'girls'. Anything else raises an error listing the unrecognised values; they are never dropped silently, because that would quietly shrink the analysis sample.
[eiod_as_sex_factor()] to convert a numeric sex code once.
# the recommended input: sex as a factor eiod_age_sex_group(age = c(1.5, 3, 7, 40), sex = factor(c("male", "male", "female", "female"))) # character works the same way eiod_age_sex_group(age = c(1.5, 40), sex = c("M", "F")) # numeric requires the codes to be stated eiod_age_sex_group(age = c(1.5, 40), sex = c(1, 2), male = 1, female = 2) # age in months eiod_age_sex_group(age = c(23, 24), sex = c("male", "male"), age_units = "months")# the recommended input: sex as a factor eiod_age_sex_group(age = c(1.5, 3, 7, 40), sex = factor(c("male", "male", "female", "female"))) # character works the same way eiod_age_sex_group(age = c(1.5, 40), sex = c("M", "F")) # numeric requires the codes to be stated eiod_age_sex_group(age = c(1.5, 40), sex = c(1, 2), male = 1, female = 2) # age in months eiod_age_sex_group(age = c(23, 24), sex = c("male", "male"), age_units = "months")
Turns a numeric or character sex variable into a two-level factor with the levels '"male"' and '"female"'. Doing this once near the top of an analysis removes the 0/1 versus 1/2 ambiguity from every later call.
eiod_as_sex_factor(x, male, female, labels = c("male", "female"))eiod_as_sex_factor(x, male, female, labels = c("male", "female"))
x |
A numeric or character vector of sex codes. |
male, female
|
The value of 'x' that means male and female respectively. Both are required: see the discussion in [eiod_age_sex_group()] for why this is not inferred. |
labels |
The two level labels to use, male first. |
A factor with two levels, 'NA' preserved.
eiod_as_sex_factor(c(1, 2, 2, 1), male = 1, female = 2) eiod_as_sex_factor(c(0, 1, 1), male = 1, female = 0) eiod_as_sex_factor(c("M", "F"), male = "M", female = "F")eiod_as_sex_factor(c(1, 2, 2, 1), male = 1, female = 2) eiod_as_sex_factor(c(0, 1, 1), male = 1, female = 0) eiod_as_sex_factor(c("M", "F"), male = "M", female = "F")
Runs every 'eiod_check_*' function over a data frame and returns a single report. This is the function to call first, before [eiod_absolute_threshold()] or anything else.
eiod_check_data( data, impacts = NULL, age = NULL, sex = NULL, gdqs = NULL, age_units = c("years", "months"), male = NULL, female = NULL, tolerance = 1e-06 ) ## S3 method for class 'eiod_check' print(x, ...) ## S3 method for class 'eiod_check' summary(object, ...)eiod_check_data( data, impacts = NULL, age = NULL, sex = NULL, gdqs = NULL, age_units = c("years", "months"), male = NULL, female = NULL, tolerance = 1e-06 ) ## S3 method for class 'eiod_check' print(x, ...) ## S3 method for class 'eiod_check' summary(object, ...)
data |
A data frame. |
impacts |
A named character vector mapping indicator names to column names, for example 'c(biodiv = "biodiv_pdf", ghg = "ghg_kg")'. Names must be among the seven in [eiod_list_indicators()]. |
age, sex, gdqs
|
Column names, or 'NULL' to skip that check. |
age_units |
'"years"' (default) or '"months"'. |
male, female
|
Optional sex codes; see [eiod_age_sex_group()]. |
tolerance |
Relative distance from a cut-off below which a classification is treated as not robust. Default '1e-6'. |
x |
An 'eiod_check' object. |
... |
Ignored. |
object |
An 'eiod_check' object. |
An object of class 'eiod_check'. Use 'summary()' for a single pass/fail, or treat it as a data frame to filter the findings.
**Structure** - that each named column exists, and that impact columns are doubles rather than factors, characters or integers.
**Validity** - no negative or non-finite impacts, exact zeros flagged, missingness reported.
**Precision** - significant digits actually retained, and how many observations sit close enough to a cut-off that rounding could change their classification. See [eiod_check_precision()].
**Unit magnitude** - each impact compared against the published global reference, with likely unit errors named. See [eiod_check_magnitude()].
**Demographics** - age range and units, sex coding, coverage of the eight groups, and the count of under-twos.
**GDQS** - type and range.
'error' means a metric computed on this data would be wrong, not merely uncertain; fix it first. 'warning' means proceed, but you need to have looked. 'note' is information worth having in the record.
[eiod_check_impact()], [eiod_check_precision()], [eiod_check_magnitude()], [eiod_check_demographics()], [eiod_check_gdqs()]
data(eiod_demo) report <- eiod_check_data( eiod_demo, impacts = c(biodiv = "biodiv_pdf", ghg = "ghg_kgco2e"), age = "age", sex = "sex", gdqs = "gdqs" ) report summary(report)data(eiod_demo) report <- eiod_check_data( eiod_demo, impacts = c(biodiv = "biodiv_pdf", ghg = "ghg_kgco2e"), age = "age", sex = "sex", gdqs = "gdqs" ) report summary(report)
Verifies that age and sex can be turned into the eight demographic groups, and reports group coverage, including how many respondents fall below the age-2 floor that the composite metrics apply.
eiod_check_demographics( age, sex, age_units = c("years", "months"), male = NULL, female = NULL )eiod_check_demographics( age, sex, age_units = c("years", "months"), male = NULL, female = NULL )
age |
Numeric vector of ages. |
sex |
Factor, character or numeric vector of sex. |
age_units |
'"years"' (default) or '"months"'. |
male, female
|
Optional codes; see [eiod_age_sex_group()]. |
Like the other check functions, this reports rather than stops. Where [eiod_age_sex_group()] would raise an error, this records it as a finding so you can see every problem in one pass instead of one per run.
An object of class 'eiod_check'.
data(eiod_demo) eiod_check_demographics(eiod_demo$age, eiod_demo$sex) # numeric sex with no codes given: reported, not guessed eiod_check_demographics(eiod_demo$age, as.integer(eiod_demo$sex == "male"))data(eiod_demo) eiod_check_demographics(eiod_demo$age, eiod_demo$sex) # numeric sex with no codes given: reported, not guessed eiod_check_demographics(eiod_demo$age, as.integer(eiod_demo$sex == "male"))
Check the Global Diet Quality Score variable
eiod_check_gdqs(gdqs, cutoff = 23, max_score = 49)eiod_check_gdqs(gdqs, cutoff = 23, max_score = 49)
gdqs |
Numeric vector of GDQS values. |
cutoff |
The healthy-diet threshold, default 23. |
max_score |
The maximum attainable score, used for the range check. Default 49. |
An object of class 'eiod_check'.
data(eiod_demo) eiod_check_gdqs(eiod_demo$gdqs)data(eiod_demo) eiod_check_gdqs(eiod_demo$gdqs)
Structural and plausibility checks on one impact vector: storage type, sign, zeros, missingness and non-finite values.
eiod_check_impact(value, name = "impact", indicator = NULL)eiod_check_impact(value, name = "impact", indicator = NULL)
value |
The impact vector to check. May be any type; that is the point. |
name |
A label for the variable, used in the report. |
indicator |
Optionally the indicator name, used to report the expected unit. |
An object of class 'eiod_check'.
Three import failures destroy impact data silently, and all three are common with the very small values biodiversity loss takes:
A **factor** column. 'as.numeric()' on a factor returns the level codes, so '3.7e-13' becomes '1'. A plausible-looking number with no warning is far worse than 'NA', because nothing downstream looks wrong.
A **character** column, usually caused by one stray cell such as '"<0.001"' or a decimal comma. Coercion then yields 'NA' with a warning that is easy to miss.
An **integer** column. It cannot represent '1e-13' at all; the value is already '0' by the time you see it.
data(eiod_demo) eiod_check_impact(eiod_demo$biodiv_pdf, name = "biodiv_pdf") # a factor column, the worst of the three import failures eiod_check_impact(factor(c("3.7e-13", "5.2e-13")), name = "biodiv")data(eiod_demo) eiod_check_impact(eiod_demo$biodiv_pdf, name = "biodiv_pdf") # a factor column, the worst of the three import failures eiod_check_impact(factor(c("3.7e-13", "5.2e-13")), name = "biodiv")
Compares the median of your data against the published global 50th percentile for that indicator, weighted to your sample's demographic mix, and names the likely conversion when the ratio lands near a common unit factor.
eiod_check_magnitude(value, indicator, group = NULL, name = "impact")eiod_check_magnitude(value, indicator, group = NULL, name = "impact")
value |
Numeric vector of impact values. |
indicator |
The indicator name. |
group |
Demographic groups 1-8, used to weight the expected median. |
name |
A label for the variable, used in the report. |
This is the check that catches a unit error. A greenhouse gas column supplied in grams rather than kilograms will sit about 1000 times above the reference and be reported as such, rather than quietly producing a result in which every diet exceeds every threshold.
An object of class 'eiod_check'.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) eiod_check_magnitude(eiod_demo$ghg_kgco2e, "ghg", grp, name = "ghg_kgco2e") # the same data in grams eiod_check_magnitude(eiod_demo$ghg_kgco2e * 1000, "ghg", grp, name = "ghg_grams")data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) eiod_check_magnitude(eiod_demo$ghg_kgco2e, "ghg", grp, name = "ghg_kgco2e") # the same data in grams eiod_check_magnitude(eiod_demo$ghg_kgco2e * 1000, "ghg", grp, name = "ghg_grams")
Two related checks: how many significant digits the stored values actually retain, and how many observations sit close enough to their own cut-off that the classification could flip under rounding.
eiod_check_precision( value, group = NULL, indicator = NULL, name = "impact", tolerance = 1e-06, metric = c("abs", "rel"), percentile = NULL )eiod_check_precision( value, group = NULL, indicator = NULL, name = "impact", tolerance = 1e-06, metric = c("abs", "rel"), percentile = NULL )
value |
Numeric vector of impact values. |
group |
Optional demographic groups 1-8. Required for the cut-off proximity check. |
indicator |
Optional indicator name. Required for the proximity check. |
name |
A label for the variable, used in the report. |
tolerance |
Relative distance from a cut-off below which a classification is treated as not robust. Default '1e-6'. |
metric |
Which cut-off to measure distance from: '"abs"' (default) or '"rel"'. |
percentile |
Required when 'metric = "rel"'. |
An object of class 'eiod_check'.
R's 'numeric' is an IEEE 754 double: around 15 to 17 significant decimal digits, with an exponent range to about 1e+/-308. A biodiversity value of '3.6896e-13' is comfortably representable, and comparison with '<' is exact. No epsilon is needed, provided both sides carry full precision.
The real risk is **ingestion fidelity** - digits lost before R ever sees the number. Excel displays '3.69E-13' and can write back only what it displays; CSV exports written with a ' 'float' storage holds about 7 significant digits; database 'REAL' columns are single precision for the same reason.
The first finds, for each distinct value, the smallest number of significant digits that reproduces it exactly. Across many distinct values, a maximum of 7 or fewer is consistent with single-precision storage or a truncated export. Measured on simulated data, full-precision values return 17, a single-precision round trip returns exactly 7, and an Excel three-digit re-save returns 3, so the cases separate cleanly.
The second is the one that answers whether precision matters *here*. It computes, for each observation, the relative distance to the cut-off that applies to it, and counts those within 'tolerance'. Usually the answer is zero and the worry can be set aside; when it is not, you get the row numbers.
Nothing is repaired. Digits that were never read cannot be recovered.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) eiod_check_precision(eiod_demo$biodiv_pdf, grp, "biodiv", name = "biodiv_pdf") # the same values after a single-precision round trip eiod_check_precision(signif(eiod_demo$biodiv_pdf, 7), grp, "biodiv", name = "biodiv_float")data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) eiod_check_precision(eiod_demo$biodiv_pdf, grp, "biodiv", name = "biodiv_pdf") # the same values after a single-precision round trip eiod_check_precision(signif(eiod_demo$biodiv_pdf, 7), grp, "biodiv", name = "biodiv_float")
Every cut-off in Tables 3 and 4a-4g of the Intake methods paper, in long format: one row per metric, indicator, demographic group and table row.
eiod_cutoffseiod_cutoffs
A data frame with 616 rows and 15 columns:
'"abs"' for Table 3, '"rel"' for Tables 4a-4g
Source table: '"3"', '"4a"' ... '"4g"'
Short indicator name used by the 'eiod_*' functions
Human-readable indicator name
Unit **this table is printed in**, per capita per day. The same for both tables for six indicators; for eutrophication, kg for Table 3 and g for Table 4f
Demographic group, 1-8
Group name, e.g. "Male 10+ yr"
Factor, '"male"' or '"female"'
Age bounds in years; 999 means no upper bound
Position in the source table
10-90 for relative rows 1-9; 'NA' for absolute rows and for row 10, which repeats P90 as a lower bound
**The cut-off the metric functions compare against.** Nothing is rescaled: for 608 rows this is exactly what the paper prints, and for the other eight it is exactly what Intake supplied
The same value as a character string, exactly as received. Shipped so the figure can be compared character for character, not as a rendering of a double
'"published"' for the 608 rows transcribed from the paper; '"intake correction 2026-10"' for the eight Table 3 eutrophication thresholds Intake corrected. **Those eight do not match the currently published PDF** - see the section above
Tables 5a and 5b are deliberately **not** stored here. They are composites that reuse values already present - the HESD criterion is 'metric == "abs" & indicator == "biodiv"', and the HDLEI criterion is 'metric == "rel" & indicator == "biodiv" & percentile == 30'. A second copy could drift from the first.
**'value' is the cut-off exactly as this package received it, for all 616 rows**, and 'value_published_str' is the same number as literal text. The package applies no conversion to a cut-off and none to your data.
For 608 rows "as received" means "as printed in the methods paper", and you can check them against the PDF character for character. The remaining eight are described in the next section.
The **Table 3 eutrophication thresholds** in 'eiod_cutoffs' are **not** the numbers the currently published paper prints. They are corrections supplied by Intake in October 2026, and the 'value_source' column marks them:
subset(eiod_cutoffs, value_source != "published",
c(demogroup_label, unit, value, value_source))
**What was wrong.** The paper prints those eight cells in **kilograms** PO4(3-)eq while Table 4f, and the EIOF food databases, use **grams**. Intake confirmed by email that both tables are intended to be in grams, and supplied the corrected Table 3 values.
**What changed, and what did not.** The corrected values are the printed ones multiplied by 1000, carried to full precision - the quantity is identical, only the unit it is expressed in has changed. Dividing any corrected value by 1000 reproduces the printed figure to the ten decimals the PDF gives, and 'data-raw/build_data.R' asserts exactly that. **No classification moves**: tested on 400,000 simulated gram-scale values, old and new behaviour agreed everywhere except within about one unit in the last place of a cut-off, where the new path is the more exact of the two because it no longer divides before comparing.
**Consequence for you: supply eutrophication in grams**, which is what the EIOF databases give you ('Eutr. (g PO43-e/kg)' x kg food = g). Every indicator now uses one unit across both tables, and nothing is converted anywhere. Between July and October 2026 this package divided eutrophication values by 1000 on your behalf; that step is gone.
See 'vignette("eutrophication")' for the full history, including the evidence that led to the question being raised.
Two columns carry the same number in two forms, so that a reader can check the package without having to trust any numeric rendering:
| `value_published_str` | the literal characters from the paper, never through a numeric type |
| `value` | the nearest IEEE 754 double to that text - and what the metric functions compare against |
'data-raw/build_data.R' asserts that 'as.numeric(value_published_str) == value' for all 616 rows, and that each parsed value still prints back to exactly its published digits.
A 'double' cannot hold most decimal fractions exactly - that is true of every statistical package, not of this one. What is guaranteed here is that the stored double is the nearest double to the published decimal, that it round-trips back to that decimal, and that the decimal itself is in the file as text. **If you need an exact decimal comparison, compare against 'value_published_str'.**
Note that R's default 'options(digits = 7)' will not show you a published value in full: 15 of the 616 need 14 significant digits. [eiod_lookup_cutoff()] prints at full precision for this reason.
Intake-EIOD-Methods-Paper-Jan2026-Final.pdf, Tables 3 and 4a-4g (pages 16-19), available at <https://www.intake.org/sites/default/files/2026-01/Intake-EIOD-Methods-Paper-Jan2026-Final.pdf>. Transcribed programmatically; see 'data-raw/build_data.R'. The same source file drives a companion Stata implementation.
data(eiod_cutoffs) str(eiod_cutoffs) # the HESD biodiversity criterion subset(eiod_cutoffs, metric == "abs" & indicator == "biodiv", c(demogroup_label, value))data(eiod_cutoffs) str(eiod_cutoffs) # the HESD biodiversity criterion subset(eiod_cutoffs, metric == "abs" & indicator == "biodiv", c(demogroup_label, value))
Six hundred simulated respondent-days with age, sex, the seven impact totals and a GDQS score, for use in examples, vignettes and tests.
eiod_demoeiod_demo
A data frame with 600 rows and 11 columns:
Row identifier
Age in years, 0.5 to 65
Factor with levels '"male"' and '"female"'
Land use, m2*year
Greenhouse gas emissions, kg CO2eq
Eutrophication potential, g PO4(3-)eq. Grams,
like the EIOF databases and both cut-off tables. Renamed from
eutroph_kgpo4e and rescaled in 0.1.0; see
vignette("eutrophication")
Water use, litres
Biodiversity loss, PDF*year
Global Diet Quality Score
**These are not real survey data.** The values were drawn from lognormal distributions chosen to be roughly plausible. No prevalence computed from this dataset carries any substantive meaning.
A small amount of missingness is included in 'biodiv_pdf' and 'gdqs' so examples show how it propagates.
Until October 2026 the impact columns were called 'landtotal', 'ghg', 'biodiv' and so on - the same strings the 'indicator' argument takes. That made the examples tidy and taught something false: a loop over 'eiod_demo[[ind]]' works only because the columns happen to be named after the keys, and readers concluded that reiod required those names.
It never did. reiod takes vectors, so any column name has always worked. The columns now carry survey-realistic names with their units, which forces every example to state the indicator-to-column mapping explicitly. See [eiod_demo_map] and [eiod_absolute_all()].
[eiod_demo_map] for the indicator-to-column mapping. A deliberately broken copy ships as 'system.file("extdata", "eiod_broken.csv", package = "reiod")' and is used in 'vignette("troubleshooting")'.
data(eiod_demo) head(eiod_demo)data(eiod_demo) head(eiod_demo)
A named character vector in the shape every '*_all()' function expects: the **name** is the indicator key reiod knows, the **value** is the column in [eiod_demo] that holds it.
eiod_demo_mapeiod_demo_map
A named character vector of length 7.
It is shipped rather than retyped in each example, and it exists at all because [eiod_demo]'s columns are deliberately *not* named after the indicator keys. Write the equivalent line for your own data once, near the top of your script, and every later call reads from it.
[eiod_absolute_all()], [eiod_relative_all()], [eiod_demo]
data(eiod_demo_map) eiod_demo_map # the same shape, for your own data: # my_map <- c(ghg = "ghg_total_kg", biodiv = "pdf_year", water = "water_l")data(eiod_demo_map) eiod_demo_map # the same shape, for your own data: # my_map <- c(ghg = "ghg_total_kg", biodiv = "pdf_year", water = "water_l")
Combines the same diet quality criterion as [eiod_hesd()] with a **relative** low-impact criterion.
eiod_hdlei( biodiversity, gdqs, group, form = c("category", "binary", "components"), gdqs_cutoff = 23, percentile = 30 )eiod_hdlei( biodiversity, gdqs, group, form = c("category", "binary", "components"), gdqs_cutoff = 23, percentile = 30 )
biodiversity |
Numeric vector of per capita daily biodiversity loss. |
gdqs |
Numeric vector of Global Diet Quality Score values. |
group |
Integer vector of demographic groups 1-8. |
form |
'"category"', '"binary"' or '"components"'; see [eiod_hesd()]. |
gdqs_cutoff |
GDQS threshold, default 23. |
percentile |
Relative cut-off, default 30 as published. |
healthy: GDQS >= 23
low environmental impacts: biodiversity loss below the 30th percentile of the Table 4g relative benchmark distribution
The diet quality criterion is identical to HESD's. The only difference is the environmental one: HESD tests biodiversity against the absolute planetary boundary share, HDLEI against a position in the global reference distribution.
A factor, integer vector, or data frame, depending on 'form'.
The HDLEI environmental cut-off is uniformly about 1.52 times stricter than the HESD one, in every demographic group, because both are the same all-population figure scaled by the same weight. So 'eiod_hdlei(...) == 1' implies 'eiod_hesd(...) == 1', and HESD prevalence can never fall below HDLEI prevalence. Reporting both is still informative: the gap between them is the share of diets sitting between the two cut-offs. But they are not two independent dimensions.
As for HESD, results are 'NA' for respondents under 2 years.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_hdlei(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp)) # the nesting: every HDLEI diet is also a HESD diet a <- eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "binary") b <- eiod_hdlei(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "binary") table(HESD = a, HDLEI = b, useNA = "ifany")data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_hdlei(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp)) # the nesting: every HDLEI diet is also a HESD diet a <- eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "binary") b <- eiod_hdlei(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "binary") table(HESD = a, HDLEI = b, useNA = "ifany")
The diet quality half of both composite metrics: a Global Diet Quality Score of 23 or more. Exported on its own so it can be reported separately from the composites.
eiod_healthy_diet(gdqs, cutoff = 23)eiod_healthy_diet(gdqs, cutoff = 23)
gdqs |
Numeric vector of Global Diet Quality Score values. |
cutoff |
The score at or above which a diet counts as healthy. Default 23, as published. Change only with a documented reason. |
An integer vector: '1' healthy, '0' not, 'NA' where 'gdqs' is missing.
eiod_healthy_diet(c(10, 22.9, 23, 30, NA))eiod_healthy_diet(c(10, 22.9, 23, 30, NA))
Combines a diet quality criterion with an **absolute** environmental sustainability criterion.
eiod_hesd( biodiversity, gdqs, group, form = c("category", "binary", "components"), gdqs_cutoff = 23 )eiod_hesd( biodiversity, gdqs, group, form = c("category", "binary", "components"), gdqs_cutoff = 23 )
biodiversity |
Numeric vector of per capita daily biodiversity loss. |
gdqs |
Numeric vector of Global Diet Quality Score values. |
group |
Integer vector of demographic groups 1-8. |
form |
What to return. '"category"' (default) gives the four categories of Table 5a as a factor; '"binary"' gives 1 for category 1 and 0 otherwise; '"components"' gives a data frame with both sub-indicators and both combined forms. |
gdqs_cutoff |
GDQS threshold, default 23. |
healthy: GDQS >= 23
environmentally sustainable: biodiversity loss below the Table 3 absolute threshold
Both criteria are also available on their own, as [eiod_healthy_diet()] and [eiod_sustainable_diet()]. This function calls those two rather than re-implementing them, so the sub-indicators and the composite cannot drift apart.
A factor, integer vector, or data frame, depending on 'form'.
The methods paper restricts the composite metrics to respondents aged 2 years and older, because the GDQS has not been validated below that age. Table 5a prints six demographic columns, not eight. Results are therefore 'NA' for groups 1 and 5, and a message reports how many observations that affects. If your sample includes under-twos, decide explicitly whether they belong in the analysis denominator at all.
HESD and [eiod_hdlei()] are nested, not independent: every HDLEI diet is also a HESD diet. See [eiod_low_impact()] for why. Do not present them as two dimensions of a 2x2.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp)) # everything at once parts <- eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "components") head(parts)data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp)) # everything at once parts <- eiod_hesd(eiod_demo$biodiv_pdf, eiod_demo$gdqs, grp, form = "components") head(parts)
Indicator names accepted by the 'eiod_*' functions, with their labels and the units your impact values must be in.
eiod_indicatorseiod_indicators
A data frame with 7 rows and 3 columns:
Short name, as passed to the 'eiod_*' functions
Human-readable name
Required unit, per capita per day
data(eiod_indicators) eiod_indicatorsdata(eiod_indicators) eiod_indicators
Prints the indicator names the 'eiod_*' functions accept, with their labels and the units your impact values must be in.
eiod_list_indicators(quiet = FALSE)eiod_list_indicators(quiet = FALSE)
quiet |
If 'TRUE', return the table without printing it. |
A data frame of indicator, label and unit, invisibly when printing.
**Supply it as it comes - nothing needs converting.** The units in this table are the EIOF database's own: 'Land (m2/kg)', 'GHG (kg CO2e/kg)', 'Eutr. (g PO43-e/kg)', 'Water Use (L/kg)'. Multiply by kilograms of food consumed, sum to the respondent-day, and pass the result straight in.
kg food consumed x EIOF intensity per kg = the value this package wants
**This changed in October 2026.** Eutrophication used to be the exception: the methods paper printed its Table 3 threshold in kilograms while the database reported grams, so a conversion was needed and this package applied one. Intake has since confirmed that both cut-off tables are in grams and supplied corrected Table 3 values. Every indicator is now on a single unit and no conversion happens anywhere.
If you are revisiting a script written before that change and it divides eutrophication by 1000, **remove the division** - the package no longer expects it. '?eiod_cutoffs' has the detail.
Not every food has an impact factor. The EIOF country databases mark foods with no available estimate, and such a food contributes **nothing** to the day's total - so a respondent who ate one has an impact that is understated by an unknown amount.
**This package cannot detect that.** It receives one number per respondent-day; whether that number was summed over complete or incomplete coverage is not recoverable from the number itself. The only exception is the extreme case: if every food lacked a factor and the sum was coded '0' rather than 'NA', [eiod_check_impact()] flags the zero.
It matters more here than in most analyses because these metrics compare each respondent against a **fixed external cut-off** rather than against your sample, so uneven coverage shifts results with nothing in the output looking wrong.
Count it where it is still visible - when you aggregate to the respondent-day - preferably as the **share of consumed weight** carrying a factor rather than a count of items, and report it with any prevalence. See 'vignette("troubleshooting")'.
eiod_list_indicators()eiod_list_indicators()
Returns the cut-off itself, rather than applying it. Useful for reporting which number a respondent was compared against, and for checking the package against the printed tables.
eiod_lookup_cutoff( indicator, group = NULL, metric = c("abs", "rel"), percentile = NULL, published = FALSE ) ## S3 method for class 'eiod_cutoff' print(x, digits = 15, ...) ## S3 method for class 'eiod_cutoff' Ops(e1, e2)eiod_lookup_cutoff( indicator, group = NULL, metric = c("abs", "rel"), percentile = NULL, published = FALSE ) ## S3 method for class 'eiod_cutoff' print(x, digits = 15, ...) ## S3 method for class 'eiod_cutoff' Ops(e1, e2)
indicator |
One of the seven indicator names. |
group |
Demographic group 1-8, or 'NULL' for all eight. |
metric |
'"abs"' for the Table 3 absolute threshold, or '"rel"' for a Table 4 relative benchmark cut-point. |
percentile |
Required when 'metric = "rel"': a multiple of 10 from 10 to 90. |
published |
**No longer has any effect**, and retained only so existing scripts keep working. Until October 2026 the package stored 80 relative eutrophication rows pre-rescaled, and this argument switched between the stored and the published figure. Nothing is rescaled any more - 'value' is the published figure for all 616 rows - so both settings return the same thing. See 'vignette("eutrophication")'. |
x |
An 'eiod_cutoff' object. |
digits |
Significant digits to display. The default of 15 shows every published digit; the widest cut-off in the tables needs 14. |
... |
Ignored. |
e1, e2
|
Operands. |
A named numeric vector of class 'eiod_cutoff', named by demographic group label. Use 'print(x, digits = )' to change the displayed precision, or 'as.numeric(x)' to drop the class.
The published cut-offs need up to **14 significant digits** - 'water' for Female 10+ is ‘1726.3821057877', and 15 of the 616 values need all 14. R’s default 'options(digits = 7)' would print that as '1726.382', which looks as though the package had rounded the guideline.
Storage was never the problem; the default *display* was. The returned object therefore carries a class and a print method that uses 15 significant digits and shows, alongside each value, the literal text transcribed from the paper:
“' Male 10+ yr 14.2377570161 as printed 14.2377570161 “'
The 'as printed' column is 'value_published_str' from [eiod_cutoffs] - a character string that has never been through a numeric type. If you need an exact decimal comparison against the PDF, compare against that, not against a rendering of a double. (The identical complaint, and the identical fix, arose in the companion Stata package in October 2026.)
The object is an ordinary named numeric underneath: '[', 'c()' and arithmetic all return plain numbers, so nothing downstream needs to know about the class.
**Every value this function returns is exactly what the methods paper prints**, for all 616 cut-offs. The package does not adjust a published number anywhere, so you can check any row of [eiod_cutoffs] against the PDF and expect a character-for-character match.
Where the paper's own units need reconciling - which happens for eutrophication, and only for eutrophication - the adjustment is applied to **your data** at the moment of comparison, is announced in a message, and is described in 'vignette("eutrophication")'.
# the HESD biodiversity criterion, all groups eiod_lookup_cutoff("biodiv", metric = "abs") # the HDLEI biodiversity criterion eiod_lookup_cutoff("biodiv", metric = "rel", percentile = 30) # one group, printed to every published digit eiod_lookup_cutoff("water", group = 8, metric = "abs") # what R's default options(digits = 7) would have shown instead print(c(eiod_lookup_cutoff("water", group = 8, metric = "abs")))# the HESD biodiversity criterion, all groups eiod_lookup_cutoff("biodiv", metric = "abs") # the HDLEI biodiversity criterion eiod_lookup_cutoff("biodiv", metric = "rel", percentile = 30) # one group, printed to every published digit eiod_lookup_cutoff("water", group = 8, metric = "abs") # what R's default options(digits = 7) would have shown instead print(c(eiod_lookup_cutoff("water", group = 8, metric = "abs")))
The environmental half of the HDLEI metric: biodiversity loss below the 30th percentile of the Table 4g **relative** benchmark distribution. Exported on its own so it can be reported separately from the composite.
eiod_low_impact(biodiversity, group, percentile = 30)eiod_low_impact(biodiversity, group, percentile = 30)
biodiversity |
Numeric vector of per capita daily biodiversity loss. |
group |
Integer vector of demographic groups 1-8. |
percentile |
The relative cut-off. Default 30, as published. Any other value is a sensitivity analysis, not the HDLEI metric, and is labelled as such by a message. |
An integer vector: '1' low impact, '0' not, 'NA' where missing.
This criterion is uniformly stricter than [eiod_sustainable_diet()]. For biodiversity the relative P30 cut-off sits below the absolute threshold in every one of the eight demographic groups, by the same constant factor of about 1.52, because both are the same all-population figure scaled by the same energy-requirement weight. So a diet meeting this criterion necessarily meets the HESD one, and HESD prevalence can never fall below HDLEI prevalence. The two are nested, not independent.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_low_impact(eiod_demo$biodiv_pdf, grp), useNA = "ifany")data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_low_impact(eiod_demo$biodiv_pdf, grp), useNA = "ifany")
Applies [eiod_relative_band()] to several indicators in one call and returns a data frame of results, one column per indicator.
eiod_relative_all(data, group, map, midpoint = FALSE, prefix = "band_")eiod_relative_all(data, group, map, midpoint = FALSE, prefix = "band_")
data |
A data frame with one row per respondent-day. |
group |
Integer vector of demographic groups 1-8, of length 'nrow(data)'. |
map |
Named character vector: names are indicator keys, values are column names in 'data'. |
midpoint |
If 'TRUE', return band midpoint percentiles rather than band numbers. See [eiod_relative_band()] for why a mean band is not a mean impact. |
prefix |
Prefix for the returned column names. Default '"band_"'. |
A data frame with 'nrow(data)' rows and one integer column per indicator in 'map'.
'map' states, explicitly, which column of your data holds which indicator. The **name** is the indicator key reiod knows ('"ghg"'); the **value** is whatever your column is actually called ('"ghg_kgco2e"'). They are different things, and nothing in reiod has ever required them to match - the vector API means 'eiod_absolute_threshold(d$anything, grp, "ghg")' works.
Supply any subset. Indicators you do not name are simply absent from the result; there is no need to have all seven.
[eiod_relative_band()], [eiod_absolute_all()]
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) bands <- eiod_relative_all(eiod_demo, grp, eiod_demo_map) sapply(bands, function(b) round(mean(b, na.rm = TRUE), 2))data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) bands <- eiod_relative_all(eiod_demo, grp, eiod_demo_map) sapply(bands, function(b) round(mean(b, na.rm = TRUE), 2))
Places a one-day diet in one of ten decile bands of a global reference distribution of per capita daily dietary environmental impacts.
eiod_relative_band(value, group, indicator, midpoint = FALSE, details = FALSE)eiod_relative_band(value, group, indicator, midpoint = FALSE, details = FALSE)
value |
Numeric vector of per capita daily impact. |
group |
Integer vector of demographic groups 1-8, from [eiod_age_sex_group()]. |
indicator |
One of the seven indicator names; see [eiod_list_indicators()]. |
midpoint |
If 'TRUE', return the band midpoint percentile (5, 15, ... 95) instead of the band number. Default 'FALSE'. |
details |
If 'TRUE', return a data frame that also reports the band boundaries each observation was placed between. Default 'FALSE'. |
An integer vector of bands 1-10, or of midpoints if 'midpoint = TRUE'. 'NA' where the impact or the group is missing.
If 'details = TRUE', a data frame with one row per observation and the columns 'value', 'group', 'band', 'cut_lower' and 'cut_upper'. 'cut_lower' is the highest published percentile the value cleared, and is 'NA' in band 1 - correctly, because nothing is published below P10. 'cut_upper' is 'NA' in band 10 for the same reason. (Equivalent to 'cutvar()' in the companion Stata command.)
This does **not** rank respondents against each other in your survey. Each diet is compared against a fixed external reference distribution that is the same for every analysis anywhere in the world. Intake built it by taking per-country average dietary impact per person per day at each decile from the Planet-Based Diets Database, population-weighting to a global percentile distribution using 2010 country populations, and scaling the result to each demographic group by its share of average energy requirements.
The practical consequence: **do not compute this with 'quantile()', 'cut()' or 'ntile()'**. Those rank your sample against itself and will place roughly 10 different statistic with no cross-country meaning. The cut-points come from the published tables, not from your data.
If your whole sample has low impacts, your whole sample lands in low bands. That is intended, and is the reason the metric is comparable across countries and over time.
The reference distribution describes diets as they are, not as they would need to be. For greenhouse gas emissions the Table 3 absolute planetary-boundary threshold lies *below* the 10th percentile of the reference distribution, so a diet in band 1 still exceeds the planetary boundary. For water use the threshold sits *above* the 90th percentile, so essentially every diet passes. Report the two metric families together and never one as a proxy for the other.
| 1 | impact < P10 |
| 2 | P10 <= impact < P20 |
| ... | ... |
| 9 | P80 <= impact < P90 |
| 10 | impact >= P90 |
The inequality is strict, so a diet exactly on P10 belongs in band 2.
The paper also reports a mean position. Use 'midpoint = TRUE' to get band midpoints (5, 15, ... 95) for that purpose, but note the limitation: averaging an ordinal band index treats the bands as equally spaced on the impact scale, and they are not. In Table 4c the P10-P20 gap is 0.002 m2*year while the P80-P90 gap is 1.5 m2*year. A mean band is a summary of *position in the reference distribution*; it is not a summary of impact. For a mean impact, average the impact variable itself.
As for [eiod_absolute_threshold()], an integer 'value' is refused. See that function for why 'is.numeric()' alone is not enough. The rest of the data quality suite remains opt-in; run 'vignette("precision")' first.
[eiod_absolute_threshold()], [eiod_relative_all()], [eiod_low_impact()]
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # the column is biodiv_pdf; the indicator key is "biodiv" band <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv") table(band, useNA = "ifany") # mean position in the global reference distribution mid <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv", midpoint = TRUE) mean(mid, na.rm = TRUE) # the decile boundaries each diet was judged against head(eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv", details = TRUE))data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) # the column is biodiv_pdf; the indicator key is "biodiv" band <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv") table(band, useNA = "ifany") # mean position in the global reference distribution mid <- eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv", midpoint = TRUE) mean(mid, na.rm = TRUE) # the decile boundaries each diet was judged against head(eiod_relative_band(eiod_demo$biodiv_pdf, grp, "biodiv", details = TRUE))
The environmental half of the HESD metric: biodiversity loss below the Table 3 **absolute** threshold. Exported on its own so it can be reported separately from the composite.
eiod_sustainable_diet(biodiversity, group)eiod_sustainable_diet(biodiversity, group)
biodiversity |
Numeric vector of per capita daily biodiversity loss (PDF*year). |
group |
Integer vector of demographic groups 1-8. |
Biodiversity loss is used as the proxy for total environmental impact, following the methods paper.
An integer vector: '1' sustainable, '0' not, 'NA' where missing.
[eiod_low_impact()], the stricter relative criterion used by HDLEI.
data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_sustainable_diet(eiod_demo$biodiv_pdf, grp), useNA = "ifany")data(eiod_demo) grp <- eiod_age_sex_group(eiod_demo$age, eiod_demo$sex) table(eiod_sustainable_diet(eiod_demo$biodiv_pdf, grp), useNA = "ifany")