Intake corrected the Table 3 eutrophication thresholds. 2026-10-22.
Intake confirmed by email that both Table 3 and Table 4f are expressed in grams PO4(3-)eq, and supplied corrected Table 3 thresholds. The published PDF prints those eight cells in kilograms.
Between July and October 2026 eiod_absolute_threshold() divided your
eutrophication values by 1000 to match the printed kilograms. It no longer
does. Supply grams — which is what the EIOF databases give you — and convert
nothing. Dividing now would make results wrong by a factor of a thousand in
the other direction.
Every other indicator is unaffected; there never was a conversion for any of them.
The corrected values are the printed ones × 1000, carried to full precision:
divide any of them by 1000 and you recover the figure in the PDF to its ten
decimals. data-raw/build_data.R and tests/testthat/test-reference_values.R
both assert that, so the two can never silently part company.
Tested on 400,000 simulated gram-scale values, the old behaviour (divide the value, compare against the PDF's kilograms) and the new one (compare grams directly) agreed everywhere. They can differ only within about one ULP of a cut-off, and there the new path is the more exact, because it no longer divides before comparing.
They are the only eight of the 616 that do not, and a new value_source
column marks them — "published" for 608, "intake correction 2026-10" for
these. eiod_cutoff() reports it, and ?eiod_cutoffs documents it.
subset(eiod_cutoffs, value_source != "published",
select = c(demogroup_label, value, value_source))
These values may differ from the Intake guideline PDF as published today.
That is deliberate and on Intake's instruction, not an error in reiod.
eiod_demo$eutroph_kgpo4e is renamed eutroph_gpo4e and resimulated in
grams, and eiod_demo_map follows it.
This had to change. With the conversion gone, the shipped map pointed
eutroph at a kilogram column, so
eiod_absolute_all(eiod_demo, grp, eiod_demo_map) returned 100% below
threshold and the relative metric put every respondent in band 1 — the
package's own demo demonstrating the 1000× mistake this release warns about.
Nothing errored; it just quietly classified everything the same way.
Two guards now exist so it cannot recur: data-raw/build_data.R refuses to
save demo data whose median is more than 20× away from its indicator's median
cut-off, and tests/testthat/test-reference_values.R asserts the same thing
plus that no indicator comes out degenerate under the shipped map.
The home page and Get started page had both become catch-alls. They are now scoped, and the detail moved into articles rather than being dropped.
data-checks, demographic-groups), The metrics,
table by table (table3-absolute, table4-relative,
table5-composites), and Reference notes (precision,
troubleshooting, eutrophication).Each metric article now works the same five ways through its table: a single
value, a column from a dataset, adding results to a data frame, all seven
indicators at once, and only the indicators you have. That last one was
already supported and barely documented — eiod_absolute_all() and
eiod_relative_all() have always accepted any subset of the seven.
vignette("relative-benchmarks") is renamed vignette("table4-relative")
for consistency with the other table articles. Four articles are new:
data-checks, demographic-groups, table3-absolute, table5-composites.
.eiod_to_cutoff_unit() is removed. Nothing in the package rescales
anything now, for any indicator.details = TRUE on eiod_absolute_threshold() returns four columns.
The value_kg column is gone; there is no converted value to show.vignette("eutrophication") rewritten around the correction.Documentation only. No computed result changes. 2026-10-15.
Incomplete EIOF coverage is now documented as an upstream responsibility. Not every
food has an impact factor; a food without one contributes nothing to the day total, so the
total is understated - and reiod cannot detect it, because it receives one number per
respondent-day and the aggregation has already discarded the evidence. The one detectable
case, every food uncosted and summed to 0 rather than NA, was already caught by the zero
check in eiod_check_impact().
New section in vignette("troubleshooting"), "The one you have to handle yourself", with
what to compute upstream: the share of consumed weight carrying a factor, reported alongside
any prevalence. Cross-referenced from vignette("precision") and ?eiod_list_indicators.
The methods paper is now hyperlinked wherever it is cited.
The eutrophication conversion is now visible. 2026-10-14.
details = TRUE on eiod_absolute_threshold() returns the converted value as
a value_kg column (renamed from value_compared), so the whole chain -
value in grams, value in kilograms, published cut-off, result - reads off one
table. The companion Stata command does the same thing in the shape Stata
users expect: it leaves <yourvar>_kg in the dataset.vignette("eutrophication") gains a worked check of the arithmetic.No computed result changes.
The reference data now matches the methods paper on every row. 2026-10-14.
The input unit for eutroph changes from kg PO4(3-)eq to g PO4(3-)eq —
which is what the EIOF database gives you, so for most users this means
removing a conversion rather than adding one.
Every other indicator is unchanged.
value_published and scale_to_db_unit are gone. value is now the cut-off
exactly as printed in the paper, for all 616 rows, and
value_published_str is the same number as literal text.
Until now, the 80 relative eutrophication rows were stored pre-divided by 1000
so every cut-off sat on one internal scale. It worked, but it meant 80 of the
shipped numbers could not be found anywhere in the source document — which
defeats the purpose of shipping the reference data at all. You can now check
any row of eiod_cutoffs against the PDF and expect a character-for-character
match.
Note that d$value_published will now partial-match value_published_str
and silently return a character vector. If you referenced that column, change
it to d$value.
eiod_absolute_threshold() divides eutrophication values by 1000 before
comparing against Table 3, which the paper prints in kilograms. It emits a
message when it does. eiod_relative_band() does nothing, because Table 4f is
printed in grams.
.eiod_to_cutoff_unit(), used by
both metric functions and by eiod_check_precision();details = TRUE gains a value_compared column for eutroph, so the table
cannot be misread as comparing grams with kilograms;eiod_cutoffs$unit now records what each table is printed in — kg for
Table 3 eutrophication, g for Table 4f. Every other indicator has one unit
across both;eiod_indicators$unit reports the input unit, now g PO4(3-)eq.vignette("eutrophication") — the whole issue in one place: what to
supply, what the package does, the evidence, and what changes if Intake's
answer differs.eiod_lookup_cutoff()'s published argument no longer has any effect and is
retained only so existing scripts keep working. Both settings return the
published value, because that is now the only value there is.Documentation only. No computed result changes. 2026-10-14.
The EIOF country databases store eutrophication as Eutr. (g PO43-e/kg) — in
grams — while every other indicator matches the methods paper's unit
(Land (m2/kg), GHG (kg CO2e/kg), Water Use (L/kg)). Checked across the
Burkina Faso, Ethiopia, Ghana and Viet Nam files; it is the EIOF standard.
So the obvious pipeline produces the wrong unit for exactly one indicator:
kg food consumed x g PO4e per kg food = GRAMS PO4e per person per day
Divide that total by 1000 before passing it to reiod, or every
eutrophication result is wrong by a factor of a thousand. No other indicator
needs converting. eiod_check_magnitude() detects it, but it is opt-in — so
the warning is now stated wherever the expected unit is, including in the
printed output of eiod_list_indicators().
Still an inference, still unconfirmed by Intake — but the case is now four independent lines rather than two, and one of them is new and decisive about Intake's operational unit:
See ?eiod_cutoffs and vignette("reiod"). A full write-up, including a
pending decision about which unit the package should work in, is in
outputs/r-package-development/EUTROPHICATION_UNITS.md.
Renamed, and a cross-platform CI failure fixed. 2026-10-14.
Every exported function, dataset, S3 class and file has been renamed
eiof_* → eiod_*; the package itself is reiod.
EIOF is Intake's Environmental Impacts of Foods database. EIOD is the Environmental Impacts of Diets metrics — Tables 3, 4a–4g, 5a and 5b, which is what this package implements and all it implements. The old name advertised a database the package explicitly does not contain, which is why the README had to spend a paragraph disowning it.
The rename also brings the package into line with the companion Stata
implementation, whose commands have always been eiod_*. One vocabulary across
both now.
Update any script by substituting eiof → eiod throughout; nothing else
changed. No function signature, default or result is different.
References to the EIOF database in the prose are unchanged and still say EIOF, because that is still its name.
R CMD check failed on macOS (arm64) and passed everywhere else. A test
added in 0.0.0.9001 asserted
identical(as.numeric(value_published_str), value_published). That demanded
bit-identical decimal-to-binary conversion on two different platforms, which
is not guaranteed for values needing a large decimal exponent.
16 of 616 cut-offs disagreed, every one of them biodiversity — the only indicator whose values all sit near 1e-13 — and every disagreement was one unit in the last place, about 2.2e-16 relative.
No result was affected, and the shipped data was never wrong. Nudging every demonstration value by ±2 ULP across all seven indicators changes 0 of 8,400 classifications. The assertion was simply stricter than the quantity it was testing.
It is replaced by the two invariants that actually matter and that hold on
every platform: every shipped double reproduces its published digits, and a
re-parse agrees to within a few ULP. The reasoning is recorded in
tests/testthat/test-reference_values.R so the assertion is not tightened
back.
Changes from the Stata cross-check, 2026-10-14.
The companion Stata implementation went through a usability and precision rebuild in early October 2026, driven by complaints from real use. Seven of its sixteen findings also applied here. All seven are now addressed.
eiod_demo's impact columns have been renamed away from the indicator
keys: landtotal is now land_total_m2yr, ghg is ghg_kgco2e, biodiv is
biodiv_pdf, and so on. The values are unchanged, bit for bit.
The old names made for (ind in eiod_indicators$indicator) eiod_demo[[ind]]
possible, which implied the package required your columns to be named after
the keys. It never did — reiod takes vectors. The rename forces every
example to state the indicator-to-column mapping explicitly. eiod_demo_map
ships that mapping.
Code using eiod_demo$ghg and friends must be updated; code using your own
data is unaffected.
is.numeric(1L) is TRUE, so an integer column previously passed every guard
on the default path and was compared against the cut-off with its fractional
part already truncated away. Measured on 200,000 simulated respondent-days in
the companion Stata implementation, the same fault biased mean greenhouse gas
emissions down from 3.836 to 3.336 — 13%, silently. This is the only data
quality test on the default path; everything else remains opt-in through
eiod_check_data().eiod_absolute_all() and eiod_relative_all() apply a metric to several
indicators in one call, taking a named map from indicator key to column name.details = TRUE on eiod_absolute_threshold() and eiod_relative_band()
returns the cut-off each observation was actually compared against —
cutoff for the absolute metric, cut_lower/cut_upper for the relative one.
Equivalent to threshvar() and cutvar() in the Stata commands.eiod_lookup_cutoff() now returns a classed object with a print method at 15
significant digits, shown beside the literal text from the paper. R's default
options(digits = 7) would have displayed the published 1726.3821057877 as
1726.382, which looked as though the package had rounded the guideline. It
had not; only the display was short. The object behaves as a plain named
numeric under [, c() and arithmetic.eiod_cutoffs gains value_published_str: the cut-off as the literal
characters transcribed from the paper, never through a numeric type. For an
exact decimal check, compare against this rather than a rendering of a double.vignette("precision") — the precision check as a pre-flight step, with
the measured evidence for which precision worries are real. The troubleshooting
vignette now carries a fifth planted fault, an integer column.First working version. Implements Tables 3, 4a-4g, 5a and 5b of the Intake Environmental Impacts of Foods methods paper (January 2026).
eiod_age_sex_group() builds the eight-category demographic group that every
cut-off is scaled by, and eiod_as_sex_factor() converts a numeric sex code
into the recommended factor input.eiod_absolute_threshold() (Table 3) and eiod_relative_band()
(Tables 4a-4g) for all seven environmental impact indicators.eiod_hesd() (Table 5a) and eiod_hdlei() (Table 5b), each returning the two
criteria as separate sub-indicators as well as the combined measure.eiod_check_data() and five component checks covering variable type,
plausibility, numerical precision and unit magnitude.eiod_cutoffs, a 616-row long-format dataset built from the
same source file as the companion Stata implementation.vignette("reiod"), section "A caution on
eutrophication". Confirm with Intake before publishing eutrophication results.