| Title: | Tidy Access to Spanish INE Demographic Data |
|---|---|
| Description: | Provides tidy, harmonized access to demographic data from the Spanish National Statistics Institute (INE) fenomenos demograficos domain, including population, births, and deaths, retrieved live via the official 'ineapir' API wrapper, with optional spatial integration at municipality and province level via 'mapSpain'. Mortality indicators follow the Human Mortality Database Methods Protocol, including the average age at death in infancy method of Andreev and Kingkade (2015) <doi:10.4054/DemRes.2015.33.13>. |
| Authors: | J. R. Caro-Barrera [aut, cre, cph] |
| Maintainer: | J. R. Caro-Barrera <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0 |
| Built: | 2026-10-05 17:06:22 UTC |
| Source: | https://github.com/jrcarob/inedemogr |
Computes youth, old-age, and total age dependency ratios per province and
year from age-disaggregated population data. Requires an age breakdown,
so it operates on get_ine_population()'s output, not
get_ine_demog()'s population_total indicator (which has no age
column and cannot be used here).
age_dependency_ratio(pop_df, young_max = 14L, old_min = 65L)age_dependency_ratio(pop_df, young_max = 14L, old_min = 65L)
pop_df |
Population tibble as returned by |
young_max |
Integer, the maximum age counted as "young" (default
|
old_min |
Integer, the minimum age counted as "old" (default |
A tibble with nuts3_code, province_name, year,
youth_dependency_ratio, old_age_dependency_ratio,
total_dependency_ratio (all per 100 working-age population).
pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 20, 70), total = c(100, 300, 150) ) age_dependency_ratio(pop)pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 20, 70), total = c(100, 300, 150) ) age_dependency_ratio(pop)
Computes age-specific fertility rates per province, year, and single year of mother's age, following INE's methodology for its Indicadores Demograficos Basicos (INE, Indicadores Demograficos Basicos: Metodologia, pp. 4 and 9-10):
age_specific_fertility_rate( births_age_df, pop_df, age_min = 15L, age_max = 49L )age_specific_fertility_rate( births_age_df, pop_df, age_min = 15L, age_max = 49L )
births_age_df |
Tibble as returned by |
pop_df |
Population tibble as returned by |
age_min, age_max
|
Integer bounds of the reproductive age span (default 15-49). |
Exposure is the mean annual female population, the average of the
January-1 stocks of years t and t + 1. If the t + 1 stock is not in
pop_df, the t stock is used and the row is flagged with
is_boundary_year = TRUE (with a warning).
Out-of-range births are folded into the boundary ages: births to
mothers younger than age_min (including INE's "under 15" group, coded
age = NA, age_group = "under15") are added to age age_min, and births
to mothers older than age_max (INE's "50 and over") to age age_max.
No births are dropped.
A tibble with nuts3_code, province_name, year, age,
births, women (mean annual female population), asfr (births per
woman), asfr_female (female births per woman, for
gross_reproduction_rate() and net_reproduction_rate()),
asfr_per_1000 (1000 * asfr, for display), and is_boundary_year.
births_age <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), female = c(10, 25), total = c(20, 50) ) pop <- tibble::tibble( nuts3_code = "ES111", year = rep(2023:2024, each = 2), age = c(20, 30, 20, 30), female = c(2000, 2500, 2100, 2400) ) age_specific_fertility_rate(births_age, pop)births_age <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), female = c(10, 25), total = c(20, 50) ) pop <- tibble::tibble( nuts3_code = "ES111", year = rep(2023:2024, each = 2), age = c(20, 30, 20, 30), female = c(2000, 2500, 2100, 2400) ) age_specific_fertility_rate(births_age, pop)
Computes the aging index (elderly per 100 children) per province and
year from age-disaggregated population data. Requires an age breakdown,
so it operates on get_ine_population()'s output, not
get_ine_demog()'s population_total indicator.
aging_index(pop_df, young_max = 14L, old_min = 65L)aging_index(pop_df, young_max = 14L, old_min = 65L)
pop_df |
Population tibble as returned by |
young_max |
Integer, the maximum age counted as "young" (default
|
old_min |
Integer, the minimum age counted as "old" (default |
A tibble with nuts3_code, province_name, year,
aging_index.
pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 20, 70), total = c(100, 300, 150) ) aging_index(pop)pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 20, 70), total = c(100, 300, 150) ) aging_index(pop)
Period (m0-based) formulas of Andreev and Kingkade (2015) as tabulated in HMD Methods Protocol V6, Table 3. Each interval is closed at its lower bound.
andreev_kingkade_a0(m0, sex = c("female", "male"))andreev_kingkade_a0(m0, sex = c("female", "male"))
m0 |
Central death rate at age 0 (a single number). |
sex |
|
| Sex | m0 range | a0 |
| Male | [0, 0.02300) | 0.14929 - 1.99545 m0 |
| Male | [0.02300, 0.08307) | 0.02832 + 3.26021 m0 |
| Male | [0.08307, Inf) | 0.29915 |
| Female | [0, 0.01724) | 0.14903 - 2.05527 m0 |
| Female | [0.01724, 0.06891) | 0.04667 + 3.88089 m0 |
| Female | [0.06891, Inf) | 0.31411 |
Numeric a0 value, NA if m0 is NA.
Andreev, E. M. and Kingkade, W. W. (2015). Average age at death in infancy and infant mortality level: Reconsidering the Coale-Demeny formulas at current levels of low mortality. Demographic Research 33(13): 363-390. doi:10.4054/DemRes.2015.33.13
andreev_kingkade_a0(0.004, "female") andreev_kingkade_a0(0.15, "male")andreev_kingkade_a0(0.004, "female") andreev_kingkade_a0(0.15, "male")
Computes the birth-to-death ratio (live births per death) per
geography and year. Unlike age_dependency_ratio()/aging_index()/
sex_ratio(), this needs no age breakdown, so it operates directly on
get_ine_demog()'s totals (System A) rather than get_ine_population()
(System B) — fetch both births_total and deaths_total in one call
and pass the result straight through.
birth_death_ratio(df)birth_death_ratio(df)
df |
Output of |
A tibble with GEOID, NAME, year, birth_death_ratio. A
value of 2 means 2 live births per death; 0.5 means 1 birth per
2 deaths.
df <- tibble::tibble( GEOID = c("15", "28"), NAME = c("A Coruna", "Madrid"), year = 2023, births_total = c(3000, 30000), deaths_total = c(6000, 25000) ) birth_death_ratio(df)df <- tibble::tibble( GEOID = c("15", "28"), NAME = c("A Coruna", "Madrid"), year = 2023, births_total = c(3000, 30000), deaths_total = c(6000, 25000) ) birth_death_ratio(df)
Constructs an abridged period life table (age groups "00-04",
"05-09", ..., "95-99", "100+") independently from observed grouped
central death rates. It is a separate estimate, not an abridgement of the
single-year table from build_life_tables(): it uses no Kannisto
smoothing, and its open interval is 100+ rather than 110+, so the two
tables' life expectancies differ slightly by construction.
build_abridged_life_table(mx_1x1, mx_5x1, sex = c("female", "male"))build_abridged_life_table(mx_1x1, mx_5x1, sex = c("female", "male"))
mx_1x1 |
tibble with columns |
mx_5x1 |
tibble with columns |
sex |
|
Closed groups from age 5 use Greville's nax with the Chiang
conversion nqx = n nmx / (1 + (n - nax) nmx) and
nLx = n l(x+n) + nax ndx (Preston, Heuveline & Guillot 2001, Box 3.1),
falling back to a constant hazard (nqx = 1 - exp(-n nmx),
nLx = ndx / nmx) where Greville's slope term is undefined. Both keep
nmx = ndx / nLx exactly. The open interval has qx = 1,
Lx = lx / mx.
Complements build_life_table()'s single-year (1x1) tables - use
this when comparing against other agencies' published abridged
tables, or when only grouped-age data is available. The youngest
group ("00-04") is the one exception to using mx_5x1 alone: it is
built from an exact single-year sub-table using mx_1x1's
Andreev-Kingkade a0 (see andreev_kingkade_a0()), avoiding the
external nax(0-4) regression a from-mx_5x1-alone method would
otherwise need for that interval's sharp infant-mortality gradient.
tibble: age_group, age_start, n (interval width, NA
for the open interval), mx, ax, qx, lx, dx, Lx, Tx, ex.
mx1 <- data.frame(age = 0:4, mx = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002)) mx5 <- data.frame( age_group = c("00-04", "05-09", "100+"), mx = c(0.0006, 0.0001, 0.35) ) build_abridged_life_table(mx1, mx5, "female")mx1 <- data.frame(age = 0:4, mx = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002)) mx5 <- data.frame( age_group = c("00-04", "05-09", "100+"), mx = c(0.0006, 0.0001, 0.35) ) build_abridged_life_table(mx1, mx5, "female")
Constructs female, male, and both-sex abridged (5-year age group)
period life tables for every province and year present in mx_5x1,
from compute_death_rates()'s mx_1x1/mx_5x1 output. See
build_abridged_life_table() for the method; build_life_tables()
for the single-year (1x1) equivalent this complements.
build_abridged_life_tables(mx_1x1, mx_5x1)build_abridged_life_tables(mx_1x1, mx_5x1)
mx_1x1 |
Output of the |
mx_5x1 |
Output of the |
list(fltper, mltper, bltper, qc): each a tibble with
nuts3_code, province_name, year, age_group, age_start,
n, mx, ax, qx, lx, dx, Lx, Tx, ex; qc is a named
list of validate_abridged_life_table() results.
mx_1x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = 0:4, mx_female = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002), mx_male = c(0.005, 0.0004, 0.0003, 0.0003, 0.0003), mx_total = c(0.0045, 0.00035, 0.00025, 0.00025, 0.00025) ) mx_5x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age_group = c("00-04", "100+"), mx_female = c(0.0006, 0.35), mx_male = c(0.0008, 0.4), mx_total = c(0.0007, 0.38) ) alt <- build_abridged_life_tables(mx_1x1, mx_5x1) alt$fltpermx_1x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = 0:4, mx_female = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002), mx_male = c(0.005, 0.0004, 0.0003, 0.0003, 0.0003), mx_total = c(0.0045, 0.00035, 0.00025, 0.00025, 0.00025) ) mx_5x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age_group = c("00-04", "100+"), mx_female = c(0.0006, 0.35), mx_male = c(0.0008, 0.4), mx_total = c(0.0007, 0.38) ) alt <- build_abridged_life_tables(mx_1x1, mx_5x1) alt$fltper
Standard life-table recursion from a complete schedule of central death
rates, per HMD Methods Protocol V6: Andreev-Kingkade a0 at age 0,
ax = 0.5 at every other closed age, and an open final interval with
qx = 1, ax = 1/mx, Lx = lx/mx. No smoothing is applied; use
build_life_tables() for Kannisto old-age smoothing of observed data.
build_life_table(mx_df, sex = c("female", "male"), a0 = NULL)build_life_table(mx_df, sex = c("female", "male"), a0 = NULL)
mx_df |
Data frame with columns |
sex |
|
a0 |
Optional override for a0 (used for combined-sex tables). |
tibble: age, mx, qx, ax, lx, dx, Lx, Tx, ex
(radix l0 = 100000).
mx <- data.frame(age = 0:100, mx = 0.0003 * exp(0.07 * (0:100))) build_life_table(mx, "female")mx <- data.frame(age = 0:100, mx = 0.0003 * exp(0.07 * (0:100))) build_life_table(mx, "female")
Constructs female, male, and combined-sex single-year period life tables
(ages 0 to 110+) for every province and year in mx_1x1.
build_life_tables(mx_1x1, kannisto_age = NULL, fit_min = KANNISTO_FIT_MIN)build_life_tables(mx_1x1, kannisto_age = NULL, fit_min = KANNISTO_FIT_MIN)
mx_1x1 |
The |
kannisto_age |
Optional fixed replacement age |
fit_min |
First age of the Kannisto fitting window (default 80, as in HMD; the window ends at 99). For sensitivity analysis. |
A list:
fltper, mltper, bltper: tibbles with nuts3_code,
province_name, year, age, mx, qx, ax, lx, dx, Lx,
Tx, ex.
qc: tibble with one row per table built (nuts3_code, year,
sex, kannisto_age = Y, passed, issues).
failed: tibble of withheld tables (nuts3_code, province_name,
year, sex, reason).
Follows HMD Methods Protocol V6, Section 7.1:
Observed rates are used below the replacement age Y, the lowest age
from 80 with at most 100 female or 100 male deaths, capped at 95 (the
same Y for both sexes).
From Y upward, rates come from a Kannisto model fitted to deaths and
exposures at ages 80-99 by Poisson maximum likelihood, with both
parameters constrained to be non-negative so smoothed rates cannot fall
with age.
Combined-sex rates at ages Y+ weight the smoothed sex-specific rates by
a smoothed female share of exposure; combined-sex a0 is the
death-weighted average of the sex-specific Andreev-Kingkade values.
Deviations from HMD V6, all forced by the INE inputs: (i) exposure uses an
even Lexis-triangle split because INE deaths are not classified by cohort
(see compute_exposure()); (ii) the Kannisto fit uses ages 80-99 because
INE's single-year detail ends at an open interval 100+, whereas HMD fits to
80-110+; (iii) no birth-by-month exposure correction.
A province-year whose input lacks finite rates and exposures at every
single age 0-100 is not tabulated: its three tables are withheld and the
reason is reported in failed. This covers INE releases whose population
or deaths are top-coded below age 100 (for example, some historical years
for Ceuta and Melilla).
age <- 0:100 e <- 1e5 mx <- 0.0003 * exp(0.07 * age) mx_1x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = age, mx_female = mx, mx_male = 1.2 * mx, mx_total = 1.1 * mx, d_female = mx * e, d_male = 1.2 * mx * e, d_total = 2.2 * mx * e, e_female = e, e_male = e, e_total = 2 * e ) lt <- build_life_tables(mx_1x1) lt$fltperage <- 0:100 e <- 1e5 mx <- 0.0003 * exp(0.07 * age) mx_1x1 <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = age, mx_female = mx, mx_male = 1.2 * mx, mx_total = 1.1 * mx, d_female = mx * e, d_male = 1.2 * mx * e, d_total = 2.2 * mx * e, e_female = e, e_male = e, e_total = 2 * e ) lt <- build_life_tables(mx_1x1) lt$fltper
Computes 1x1 (single-year age/period), 5x1 (5-year age group), and
age-standardised (ESP 2013) death rates for every province present in
both deaths and exposure, per SHMD Protocol Section 12, Step 5.
compute_death_rates(deaths, exposure)compute_death_rates(deaths, exposure)
deaths |
Output of the |
exposure |
Output of the |
list(mx_1x1, mx_5x1, asdr, qc):
mx_1x1: nuts3_code, province_name, year, age, central death
rates mx_female, mx_male, mx_total, and the deaths (d_*) and
exposures (e_*) they come from (needed by build_life_tables()).
mx_5x1: the same by five-year group (age_group), pooled as
sum(deaths) / sum(exposure).
asdr: age-standardised death rates per 100,000 (European Standard
Population 2013; the 95+ weight of 200 is spread evenly over ages
95-99 and 100+). NA unless all ages 0-100 have finite rates.
qc: output of validate_death_rates() on mx_1x1.
INE's age-specific deaths table omits cells with zero deaths, so an age
missing from a province-year that is otherwise present is treated as a
structural zero. An explicit NA count (unavailable or suppressed and not
recoverable) is kept as NA and propagates to the rate, the grouped rate,
and the ASDR, and makes validate_death_rates() fail. A province-year with
exposure but no death records at all is unavailable, not zero: it is
excluded with a warning.
deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(1, 0), male = c(1, 1), total = c(2, 1) ) exposure <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(500, 490), male = c(510, 505), total = c(1010, 995) ) rates <- compute_death_rates(deaths, exposure) rates$mx_1x1 rates$asdrdeaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(1, 0), male = c(1, 1), total = c(2, 1) ) exposure <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(500, 490), male = c(510, 505), total = c(1010, 995) ) rates <- compute_death_rates(deaths, exposure) rates$mx_1x1 rates$asdr
Computes E(x,t) = 1/2[P(x,t) + P(x,t+1)] + 1/6[D_L(x,t) - D_U(x,t)]
(HMD Methods Protocol V6, Eq. 57) for every province present in both
population and deaths, where D_L and D_U are deaths in the lower
(cohort born in t - x) and upper (cohort born in t - x - 1) Lexis
triangles.
compute_exposure(population, deaths)compute_exposure(population, deaths)
population |
Output of the |
deaths |
Output of the |
INE's death tables are not classified by birth cohort. Unless deaths
carries a cohort column, deaths are split evenly between the two
triangles, the correction term is zero, and exposure is the mean of the
two January-1 populations. This is an adaptation of the HMD protocol,
which splits 1x1 deaths by regression (Appendix A) and corrects for the
monthly distribution of births (Appendix E); its effect is quantified in
the package's validation study (see reproducibility/).
If P(x,t+1) is unavailable for a year that is kept, exposure falls back
to P(x,t) and the row is flagged with is_boundary_year = TRUE.
Death registration lags population estimates by about a year, so
population commonly includes a most-recent year (e.g. a Jan-1 stock
snapshot) with no corresponding rows anywhere in deaths yet. That
year is still used as the P(x,t+1) boundary for computing the
previous year's exposure, but is not included in the returned
exposure rows itself — treating an entirely-absent year as zero deaths
(rather than missing data) would mechanically produce zero mortality
for that year, and downstream a nonsensical inflated life expectancy.
list(data, qc): data is a tibble with columns nuts3_code,
province_name, year, age, female, male, total (exposure),
is_boundary_year, is_missing_source_pop; qc is the output of
validate_exposure().
population <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), age = 0, female = c(100, 105), male = c(105, 110), total = c(205, 215) ) deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), age = 0, female = c(1, 1), male = c(1, 2), total = c(2, 3) ) compute_exposure(population, deaths)population <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), age = 0, female = c(100, 105), male = c(105, 110), total = c(205, 215) ) deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), age = 0, female = c(1, 1), male = c(1, 2), total = c(2, 3) ) compute_exposure(population, deaths)
Computes the crude birth rate (births per 1,000 population) per province
and year, joining get_ine_births() and get_ine_population() output.
crude_birth_rate(births_df, pop_df)crude_birth_rate(births_df, pop_df)
births_df |
Births tibble as returned by |
pop_df |
Population tibble as returned by |
This is a crude birth rate (total births / total population), not
a total fertility rate (TFR). get_ine_births() itself has no
age-of-mother breakdown, so crude_birth_rate() cannot compute a TFR
on its own — for a true TFR, use get_ine_births_by_age() with
age_specific_fertility_rate()/total_fertility_rate() instead. Do not
substitute crude_birth_rate() for TFR in fertility analysis.
A tibble with nuts3_code, province_name, year, cbr.
births <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, total = 100 ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(0, 1), total = c(5000, 5000) ) crude_birth_rate(births, pop)births <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, total = 100 ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(0, 1), total = c(5000, 5000) ) crude_birth_rate(births, pop)
Computes the crude death rate (deaths per 1,000 population) per
province and year, joining get_ine_deaths() and get_ine_population()
output. The symmetric counterpart to crude_birth_rate() — see
rate_of_natural_increase() to combine the two.
crude_death_rate(deaths_df, pop_df)crude_death_rate(deaths_df, pop_df)
deaths_df |
Deaths tibble as returned by |
pop_df |
Population tibble as returned by |
A tibble with nuts3_code, province_name, year, cdr.
deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), total = c(1, 49) ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(0, 1), total = c(5000, 5000) ) crude_death_rate(deaths, pop)deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), total = c(1, 49) ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(0, 1), total = c(5000, 5000) ) crude_death_rate(deaths, pop)
Attributes the difference in life expectancy at birth (e0) between
two life tables to age-specific contributions, using either Arriaga's
(1984) or Pollard's (1988) method. Arriaga's is an exact discrete
decomposition: sum(contribution) recovers e2(0) - e1(0) exactly.
Pollard's is exact in continuous time; here each closed single-year
interval is integrated with the midpoint rule and the open interval
exactly under its constant hazard, so sum(contribution) differs from
e2(0) - e1(0) by a small discretisation residual. The residual is
returned as an attribute so it can be reported and checked against a
tolerance; treat Arriaga's method (the default) as the primary result
and Pollard's as a cross-check. Use this to answer "how much
of the life-expectancy gap
between province A and B (or between year Y1 and Y2) comes from
mortality at each age?" - complementing life_expectancy_summary()/
life_expectancy(), which report e0/e65 but don't explain why
two values differ.
decompose_life_expectancy(lt1, lt2, method = c("arriaga", "pollard"))decompose_life_expectancy(lt1, lt2, method = c("arriaga", "pollard"))
lt1 |
A single life table (the "before"/reference table) - one
province-year-sex slice of |
lt2 |
The "after"/comparison life table, same shape as |
method |
|
A tibble with age, contribution (years of the e2(0) - e1(0) gap attributable to mortality differences at that age;
positive means that age group contributed to a gain from lt1 to
lt2). Attributes e0_difference (the actual e2(0) - e1(0)) and
residual (e0_difference - sum(contribution), zero up to rounding
for Arriaga).
age <- 0:100 lt1 <- build_life_table( data.frame(age = age, mx = 0.0004 * exp(0.075 * age)), "female" ) lt2 <- build_life_table( data.frame(age = age, mx = 0.0003 * exp(0.070 * age)), "female" ) decomp <- decompose_life_expectancy(lt1, lt2) sum(decomp$contribution) lt2$ex[1] - lt1$ex[1]age <- 0:100 lt1 <- build_life_table( data.frame(age = age, mx = 0.0004 * exp(0.075 * age)), "female" ) lt2 <- build_life_table( data.frame(age = age, mx = 0.0003 * exp(0.070 * age)), "female" ) decomp <- decompose_life_expectancy(lt1, lt2) sum(decomp$contribution) lt2$ex[1] - lt1$ex[1]
Runs the requested stage(s) of the SHMD (Spanish subnational Human
Mortality Database) protocol pipeline — retrieval of province-level
births/deaths/population from INE, exposure-to-risk, central death
rates, and period life tables — and writes the results into out_dir
as .csv and/or HMD-format .txt files, so they can be inspected,
shared, or fed into other HMD-compatible tooling without staying in the
R session.
download_ine_data( out_dir, stages = c("births", "deaths", "population", "exposure", "mx", "life_tables"), format = c("csv", "txt"), n_periods = 30 )download_ine_data( out_dir, stages = c("births", "deaths", "population", "exposure", "mx", "life_tables"), format = c("csv", "txt"), n_periods = 30 )
out_dir |
Directory to write into (created if it doesn't exist). |
stages |
Character vector of stages to include. One or more of
|
format |
Character vector, one or both of |
n_periods |
Number of most recent years to fetch for the births/deaths/population retrieval stages. |
Later stages depend on earlier ones (exposure needs population and
deaths; mx needs deaths and exposure; life_tables needs mx).
Requesting a later stage automatically fetches/computes and writes its
dependencies too, since they were already retrieved along the way.
Invisibly, a list with the in-memory results of every stage that
was run (births, deaths, population, exposure, death_rates,
life_tables, each NULL if not run) and files, a character vector
of every path written.
## Not run: # Not run: requires live network access to the INE API, and writes files # to disk. # Everything, both formats, into ./ine_data download_ine_data("ine_data") # Just births and deaths, CSV only download_ine_data("ine_data", stages = c("births", "deaths"), format = "csv") # Full pipeline through life tables download_ine_data("ine_data", stages = "life_tables", n_periods = 40) ## End(Not run)## Not run: # Not run: requires live network access to the INE API, and writes files # to disk. # Everything, both formats, into ./ine_data download_ine_data("ine_data") # Just births and deaths, CSV only download_ine_data("ine_data", stages = c("births", "deaths"), format = "csv") # Full pipeline through life tables download_ine_data("ine_data", stages = "life_tables", n_periods = 40) ## End(Not run)
Computes the general fertility rate (births per 1,000 women of
reproductive age) per province and year, joining get_ine_births()
and get_ine_population() output.
general_fertility_rate(births_df, pop_df, age_min = 15L, age_max = 49L)general_fertility_rate(births_df, pop_df, age_min = 15L, age_max = 49L)
births_df |
Births tibble as returned by |
pop_df |
Population tibble as returned by |
age_min |
Integer, the youngest age counted as reproductive-age
(default |
age_max |
Integer, the oldest age counted as reproductive-age
(default |
GFR improves on crude_birth_rate() by dividing births by the female
population actually at risk of childbearing (age age_min-age_max,
default 15-49) instead of the total population — this removes the
distortion crude_birth_rate() suffers when two provinces have
different age/sex structures (e.g. one with proportionally more
elderly residents) despite similar underlying fertility behavior.
GFR is still not a total fertility rate (TFR): it is a single
aggregate rate over all reproductive-age women, not an age-specific
schedule. For a true TFR, use get_ine_births_by_age() with
age_specific_fertility_rate()/total_fertility_rate() instead.
A tibble with nuts3_code, province_name, year, gfr.
births <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, total = 100 ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(10, 25, 40, 60), female = c(2000, 2500, 2500, 3000) ) general_fertility_rate(births, pop)births <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, total = 100 ) pop <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(10, 25, 40, 60), female = c(2000, 2500, 2500, 3000) ) general_fertility_rate(births, pop)
Retrieves, cleans, and validates Spanish annual live-birth counts by
province and sex from INE's Tempus3 JSON API, per the SHMD Protocol
(Section 12, Step 1: "Birth Count Assembly"). Unlike get_ine_demog()
(which returns a Total-sex-only province count), this returns
sex-disaggregated counts joined to NUTS-3 codes, suitable as an
HMD-format Births input file - see download_ine_data() to write it to disk.
get_ine_births( table_id = 6506, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )get_ine_births( table_id = 6506, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )
table_id |
INE Tempus3 table ID. Default |
n_periods |
Number of most recent years to fetch. |
use_cache |
Logical (default |
force |
Logical (default |
cache_dir |
Directory for cached data. The default |
list(data, qc): data is a tibble with columns ine_code,
nuts3_code, nuts2_code, province_name, year, female, male,
total; qc is the output of validate_births().
## Not run: # Not run: requires live network access to the INE API. births <- get_ine_births(n_periods = 30) births$data births$qc$issues # Re-fetch even if a cached copy exists: births <- get_ine_births(n_periods = 30, force = TRUE) ## End(Not run)## Not run: # Not run: requires live network access to the INE API. births <- get_ine_births(n_periods = 30) births$data births$qc$issues # Re-fetch even if a cached copy exists: births <- get_ine_births(n_periods = 30, force = TRUE) ## End(Not run)
Retrieves, cleans, and validates Spanish annual live-birth counts by
province, single year of age of the mother (15-49, plus the two open
intervals under15/50plus), and sex of the newborn, from INE's raw
Tempus3 JSON API. Unlike get_ine_births() (which has no age-of-mother
breakdown), this is the data source needed for age-specific fertility
rates - see age_specific_fertility_rate(), total_fertility_rate(),
mean_age_at_childbearing(), gross_reproduction_rate(), and
net_reproduction_rate().
get_ine_births_by_age( table_id = 6508, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )get_ine_births_by_age( table_id = 6508, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )
table_id |
INE Tempus3 table ID. Default |
n_periods |
Number of most recent years to fetch. This table has
~6,000 series (province x age x newborn-sex), so fetches are slower
than |
use_cache |
Logical (default |
force |
Logical (default |
cache_dir |
Directory for cached data. The default |
list(data, qc): data is a tibble with columns ine_code,
nuts3_code, nuts2_code, province_name, year, age (integer,
NA for the under15 group), age_group (character: "under15",
"15".."49", "50plus"), female, male, total (births to
mothers of that age, by sex of the newborn); qc is the output of
validate_births_by_age().
## Not run: # Not run: requires live network access to the INE API. births_age <- get_ine_births_by_age(n_periods = 10) births_age$data ## End(Not run)## Not run: # Not run: requires live network access to the INE API. births_age <- get_ine_births_by_age(n_periods = 10) births_age$data ## End(Not run)
Retrieves, cleans, and validates Spanish annual death counts by province
and sex from INE's Tempus3 JSON API, per the SHMD Protocol (Section 12,
Step 1: "Death Count Assembly"). Fetches both an age-less table (for the
national total and a national-vs-provincial reconciliation check) and an
age-specific table (needed by compute_exposure() /
compute_death_rates(), which require D(x,t), not just D(t)).
get_ine_deaths( table_id = 6545, age_table_id = 6547, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )get_ine_deaths( table_id = 6545, age_table_id = 6547, n_periods = 100, use_cache = TRUE, force = FALSE, cache_dir = NULL )
table_id |
INE table ID for the age-less (province + sex) series.
Default |
age_table_id |
INE table ID for the age-specific series. Default
|
n_periods |
Number of most recent years to fetch. |
use_cache |
Logical (default |
force |
Logical (default |
cache_dir |
Directory for cached data. The default |
list(data_provinces, data_national, qc, qc_age):
data_provinces is age-specific (ine_code, nuts3_code,
nuts2_code, province_name, year, age, female, male,
total); data_national is Spain's age-less total.
## Not run: # Not run: requires live network access to the INE API. deaths <- get_ine_deaths(n_periods = 30) deaths$data_provinces # Re-fetch even if a cached copy exists: deaths <- get_ine_deaths(n_periods = 30, force = TRUE) ## End(Not run)## Not run: # Not run: requires live network access to the INE API. deaths <- get_ine_deaths(n_periods = 30) deaths$data_provinces # Re-fetch even if a cached copy exists: deaths <- get_ine_deaths(n_periods = 30, force = TRUE) ## End(Not run)
Retrieves demographic data from INE via the official
ineapir::get_data_table() API wrapper, and tidies it into a data frame
with one row per geography x year and one column per indicator.
get_ine_demog( indicator, geo_level = NULL, year = NULL, region = NULL, sex = "Total", geometry = FALSE )get_ine_demog( indicator, geo_level = NULL, year = NULL, region = NULL, sex = "Total", geometry = FALSE )
indicator |
Character vector of indicator codes (see
|
geo_level |
Character, the geographic level to fetch. Must match the
level at which every requested indicator is actually published (see
|
year |
Numeric or character, the year of data. Default ( |
region |
Optional character vector to subset by region name (regular expressions allowed, matched against the geography name). |
sex |
Character, one of |
geometry |
Logical, join geometries from |
Each indicator is mapped to a real INE table id via the ine_variables
registry (see list_ine_indicators()). Different indicators are only
available at different geographic granularities in INE's source tables
(e.g. population is published by municipality, but births/deaths only
down to province) — all requested indicators must share the same
geo_level, or request them separately.
A tidy tibble with columns GEOID, NAME, year, and one column
per requested indicator; an sf object if geometry = TRUE.
## Not run: # Not run: requires live network access to the INE API. pop_data <- get_ine_demog(indicator = "population_total", year = 2023) births_deaths <- get_ine_demog( indicator = c("births_total", "deaths_total"), year = 2023 ) ## End(Not run)## Not run: # Not run: requires live network access to the INE API. pop_data <- get_ine_demog(indicator = "population_total", year = 2023) births_deaths <- get_ine_demog( indicator = c("births_total", "deaths_total"), year = 2023 ) ## End(Not run)
Retrieves spatial geometries for INE geographic levels via the
mapSpain package, which sources
boundaries from Instituto Geografico Nacional / GISCO. Geometries are
returned with a GEOID column that matches the geographic codes returned
by get_ine_demog(), so the two can be joined directly.
get_ine_geo( geo_level = c("municipality", "province"), region = NULL, moveCAN = TRUE, can_gap_km = 60 )get_ine_geo( geo_level = c("municipality", "province"), region = NULL, moveCAN = TRUE, can_gap_km = 60 )
geo_level |
Character, one of |
region |
Optional character vector to subset by region name (regular expressions allowed). |
moveCAN |
Logical, shift the Canary Islands next to the mainland for
compact national maps (default |
can_gap_km |
Numeric, the gap in kilometers left between the shifted
Canary Islands and the closest point on the mainland coast (default
|
An sf object with columns GEOID, NAME, and geometry,
projected to ETRS89 / UTM zone 30N (EPSG:25830). When moveCAN = TRUE
and the shift was applied, an sfc rectangle marking the shifted
islands' extent is attached as the "can_box" attribute (used by
plot_ine_map() to draw an inset frame).
## Not run: # Not run: requires live network access to fetch boundaries via mapSpain. prov_geo <- get_ine_geo(geo_level = "province") mun_geo <- get_ine_geo(geo_level = "municipality", region = "Sevilla") ## End(Not run)## Not run: # Not run: requires live network access to fetch boundaries via mapSpain. prov_geo <- get_ine_geo(geo_level = "province") mun_geo <- get_ine_geo(geo_level = "municipality", region = "Sevilla") ## End(Not run)
Retrieves, cleans, and validates Spanish January-1st provincial population estimates by single-year age and sex from INE's Padron Municipal Continuo, per the SHMD Protocol (Section 12, Step 3: "Population Estimates"). Covers 1996-present; pre-1996 years require the intercensal survival method applied to the 1981/1991 censuses, which is out of scope here.
get_ine_population( table_id = 56945, n_periods = 30, use_cache = TRUE, force = FALSE, cache_dir = NULL )get_ine_population( table_id = 56945, n_periods = 30, use_cache = TRUE, force = FALSE, cache_dir = NULL )
table_id |
INE table ID. Default |
n_periods |
Number of most recent years to fetch. |
use_cache |
Logical (default |
force |
Logical (default |
cache_dir |
Directory for cached data. The default |
Some province x age x sex population tables are too large for INE's JSON API and are rejected outright; this function automatically falls back to INE's full CSV bulk export in that case (a larger download, but with no series-count limit).
list(data, qc): data is a tibble with columns ine_code,
nuts3_code, nuts2_code, province_name, year, age, female,
male, total; qc is the output of validate_population().
## Not run: # Not run: requires live network access to the INE API. pop <- get_ine_population(n_periods = 10) pop$data # Re-fetch even if a cached copy exists: pop <- get_ine_population(n_periods = 10, force = TRUE) ## End(Not run)## Not run: # Not run: requires live network access to the INE API. pop <- get_ine_population(n_periods = 10) pop$data # Re-fetch even if a cached copy exists: pop <- get_ine_population(n_periods = 10, force = TRUE) ## End(Not run)
Sum of age_specific_fertility_rate()'s asfr_female (female births per
woman): expected daughters per woman, ignoring mortality. Uses INE's
births by sex of the newborn directly rather than scaling the TFR by an
assumed sex ratio at birth.
gross_reproduction_rate(asfr_df)gross_reproduction_rate(asfr_df)
asfr_df |
Output of |
A tibble with nuts3_code, province_name, year, grr
(daughters per woman).
asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) gross_reproduction_rate(asfr)asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) gross_reproduction_rate(asfr)
A registry mapping indicator codes used throughout inedemogR to the real
INE table each is retrieved from via ineapir::get_data_table().
ine_variablesine_variables
A tibble with 3 rows and 6 variables:
Indicator code, e.g. "population_total".
Short human-readable name.
Longer description of what the indicator measures.
INE idTable identifier, or NA if not yet wired to a
live table.
Name of the geographic dimension column returned by
ineapir::get_data_table(..., metanames = TRUE) for this table.
Geographic granularity the table is published at
("municipality" or "province").
Instituto Nacional de Estadistica (INE), "fenomenos demograficos".
Computes the infant mortality rate (deaths under age 1 per 1,000 live
births) per province and year, joining get_ine_deaths() and
get_ine_births() output.
infant_mortality_rate(deaths_df, births_df)infant_mortality_rate(deaths_df, births_df)
deaths_df |
Deaths tibble as returned by |
births_df |
Births tibble as returned by |
This is the standard demographic IMR: infant deaths in a year divided
by live births in that same year (the birth cohort those deaths are
drawn from), not by population — a different denominator convention
from crude_death_rate(). It is closely related to, but not
identical to, q0 (probability of dying before age 1) in
build_life_table()'s output: q0 is derived from the exposure-based
central death rate m0 via the Andreev-Kingkade a0 (per HMD Methods
Protocol V6), while IMR here uses the simpler, more standard
deaths/births ratio directly. The two will usually be close but need
not match exactly.
A tibble with nuts3_code, province_name, year, imr.
deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1, 65), total = c(3, 1, 40) ) births <- tibble::tibble(nuts3_code = "ES111", year = 2023, total = 1000) infant_mortality_rate(deaths, births)deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1, 65), total = c(3, 1, 40) ) births <- tibble::tibble(nuts3_code = "ES111", year = 2023, total = 1000) infant_mortality_rate(deaths, births)
Convenience wrapper for a single province: builds a period life table
(Andreev-Kingkade a0, Kannisto old-age smoothing, per HMD Methods
Protocol V6) from already-computed central death rates and extracts
life expectancy at birth (e0) and at age 65 (e65) for every year
present. Operates on compute_death_rates()'s mx_1x1 output
directly — data already retrieved/computed via the mortality
pipeline — the same way plot_lexis_diagram() does, rather than
requiring build_life_tables() to be run for every province first
and life_expectancy_summary() applied afterwards.
life_expectancy(mx_df, province, sex = c("total", "female", "male"))life_expectancy(mx_df, province, sex = c("total", "female", "male"))
mx_df |
Output of the |
province |
Character, a province name (regular expressions
allowed, matched against |
sex |
Character, one of |
A tibble with nuts3_code, province_name, year, e0,
e65, sex.
age <- 0:100 e <- 1e5 m <- 0.0003 * exp(0.07 * age) mx <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = age, mx_female = m, mx_male = 1.2 * m, mx_total = 1.1 * m, d_female = m * e, d_male = 1.2 * m * e, d_total = 2.2 * m * e, e_female = e, e_male = e, e_total = 2 * e ) life_expectancy(mx, province = "A Coruna", sex = "female")age <- 0:100 e <- 1e5 m <- 0.0003 * exp(0.07 * age) mx <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = age, mx_female = m, mx_male = 1.2 * m, mx_total = 1.1 * m, d_female = m * e, d_male = 1.2 * m * e, d_total = 2.2 * m * e, e_female = e, e_male = e, e_total = 2 * e ) life_expectancy(mx, province = "A Coruna", sex = "female")
Extracts life expectancy at birth (e0) and at age 65 (e65) per
province and year from a build_life_tables() period life table.
Feeds map_life_expectancy(), which bridges this summary with
get_ine_geo()'s province geometries.
life_expectancy_summary(lt_df, sex = c("female", "male", "both"))life_expectancy_summary(lt_df, sex = c("female", "male", "both"))
lt_df |
One of |
sex |
Character label to attach to the output ( |
A tibble with nuts3_code, province_name, year, e0,
e65, sex.
lt <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 65), ex = c(86.97, 20.5) ) life_expectancy_summary(lt, sex = "female")lt <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 65), ex = c(86.97, 20.5) ) life_expectancy_summary(lt, sex = "female")
Returns a data frame of available demographic indicators.
list_ine_indicators()list_ine_indicators()
A data frame with indicator codes, names, and descriptions.
list_ine_indicators()list_ine_indicators()
General-purpose choropleth: joins a tidy indicator tibble (keyed by
GEOID, matching get_ine_geo()'s output — e.g. get_ine_demog()'s
output already has it) onto province or municipality geometry and
renders it, either as a binned/discrete-legend map (the default,
matching the classic "N provinces above/below a threshold" style of
choropleth) or a continuous gradient. map_life_expectancy() is a
thin wrapper around this function for System B's life-table output.
map_indicator( df, value_col, geo_level = c("province", "municipality"), region = NULL, binned = TRUE, breaks = NULL, palette = if (binned) "PiYG" else "D", moveCAN = TRUE, can_gap_km = 60, legend_title = NULL, title = NULL )map_indicator( df, value_col, geo_level = c("province", "municipality"), region = NULL, binned = TRUE, breaks = NULL, palette = if (binned) "PiYG" else "D", moveCAN = TRUE, can_gap_km = 60, legend_title = NULL, title = NULL )
df |
A tibble with a |
value_col |
Character, the name of the numeric column to map. |
geo_level |
Character, one of |
region |
Optional character vector to subset the geometry by
region name (regular expressions allowed). See |
binned |
Logical (default |
breaks |
Numeric vector of bin edges when |
palette |
Character, a |
moveCAN |
Logical, shift the Canary Islands next to the mainland
(default |
can_gap_km |
Numeric, gap in km for the shifted Canary Islands
(default |
legend_title |
Optional legend title (defaults to |
title |
Optional plot title. |
A ggplot object.
## Not run: # Not run: get_ine_geo() requires live network access to fetch boundaries. pop_data <- get_ine_demog(indicator = "population_total", year = 2023) map_indicator(pop_data, "population_total", geo_level = "province") ## End(Not run)## Not run: # Not run: get_ine_geo() requires live network access to fetch boundaries. pop_data <- get_ine_demog(indicator = "population_total", year = 2023) map_indicator(pop_data, "population_total", geo_level = "province") ## End(Not run)
Bridges System B's mortality pipeline (build_life_tables() via
life_expectancy_summary()) with System A's spatial layer
(get_ine_geo()) to render a province-level choropleth of life
expectancy at birth. A thin wrapper around map_indicator() (a
continuous gradient, since life expectancy has no natural discrete
bins) that handles the nuts3_code -> GEOID bridge via
province_lookup. See plot_ine_map() for the analogous
region-highlighting map built purely on get_ine_geo().
map_life_expectancy( le_df, year, sex = "both", moveCAN = TRUE, can_gap_km = 60, title = NULL )map_life_expectancy( le_df, year, sex = "both", moveCAN = TRUE, can_gap_km = 60, title = NULL )
le_df |
Output of |
year |
Numeric, the year to plot. |
sex |
Character, one of |
moveCAN |
Logical, shift the Canary Islands next to the mainland
(default |
can_gap_km |
Numeric, gap in km for the shifted Canary Islands
(default |
title |
Optional plot title. |
A ggplot object.
## Not run: # Not run: get_ine_geo() requires live network access to fetch province # boundaries. lt <- build_life_tables(rates$mx_1x1) le <- life_expectancy_summary(lt$fltper, sex = "female") map_life_expectancy(le, year = max(le$year), sex = "female") ## End(Not run)## Not run: # Not run: get_ine_geo() requires live network access to fetch province # boundaries. lt <- build_life_tables(rates$mx_1x1) le <- life_expectancy_summary(lt$fltper, sex = "female") map_life_expectancy(le, year = max(le$year), sex = "female") ## End(Not run)
ASFR-weighted mean of the mid-points x + 0.5 of the completed-age
intervals, as in INE's methodology.
mean_age_at_childbearing(asfr_df)mean_age_at_childbearing(asfr_df)
asfr_df |
Output of |
A tibble with nuts3_code, province_name, year, mac (years).
asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) mean_age_at_childbearing(asfr)asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) mean_age_at_childbearing(asfr)
sum(asfr_female * Lx / l0): expected daughters per woman allowing for
the mother's survival, with Lx from the female period life table of the
same province and year (build_life_tables()'s fltper, radix
l0 = 100000).
net_reproduction_rate(asfr_df, lt_df)net_reproduction_rate(asfr_df, lt_df)
asfr_df |
Output of |
lt_df |
|
A tibble with nuts3_code, province_name, year, nrr.
asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) fltper <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(20, 30), Lx = c(99000, 98500) ) net_reproduction_rate(asfr, fltper)asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) fltper <- tibble::tibble( nuts3_code = "ES111", year = 2023, age = c(20, 30), Lx = c(99000, 98500) ) net_reproduction_rate(asfr, fltper)
Generic time-series line chart for any indicator tibble keyed by
nuts3_code/province_name/year, such as the output of
age_dependency_ratio(), aging_index(), sex_ratio(),
crude_birth_rate(), or life_expectancy_summary().
plot_demog_trend(df, value_col, region = NULL, title = NULL, ylab = NULL)plot_demog_trend(df, value_col, region = NULL, title = NULL, ylab = NULL)
df |
A tibble with |
value_col |
Character, the name of the column to plot. |
region |
Optional character vector to subset by province name
(regular expressions allowed, matched against |
title |
Optional plot title. |
ylab |
Optional y-axis label (defaults to |
A ggplot object.
trend <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), aging_index = c(150, 160) ) plot_demog_trend(trend, "aging_index")trend <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = c(2022, 2023), aging_index = c(150, 160) ) plot_demog_trend(trend, "aging_index")
Plots the full map of Spain at the requested geographic level (provinces
or municipalities), with one or more regions highlighted in a different
color. Useful for confirming which regions a region filter passed to
get_ine_demog() or get_ine_geo() actually matches, since
get_ine_geo() uses regex substring matching by default (see
vignette examples in test_cordoba.R).
plot_ine_map( geo_level = c("province", "municipality"), highlight = NULL, base_color = "grey90", highlight_color = "firebrick", title = NULL, can_box = TRUE, can_gap_km = 60 )plot_ine_map( geo_level = c("province", "municipality"), highlight = NULL, base_color = "grey90", highlight_color = "firebrick", title = NULL, can_box = TRUE, can_gap_km = 60 )
geo_level |
Character, one of |
highlight |
Character vector of region names to highlight (regular
expressions allowed, matched against the geography name — same
matching rules as |
base_color |
Fill color for non-highlighted regions. Default
|
highlight_color |
Fill color for highlighted regions. Default
|
title |
Optional plot title. |
can_box |
Logical, draw an inset separator box around the Canary
Islands, which are shifted next to the mainland for a compact map
(default |
can_gap_km |
Numeric, how close (in km) the shifted Canary Islands
sit next to the mainland (default |
A ggplot object.
## Not run: # Not run: get_ine_geo() requires live network access to fetch boundaries. plot_ine_map(geo_level = "province", highlight = "^Córdoba$") plot_ine_map(geo_level = "municipality", highlight = "Córdoba") ## End(Not run)## Not run: # Not run: get_ine_geo() requires live network access to fetch boundaries. plot_ine_map(geo_level = "province", highlight = "^Córdoba$") plot_ine_map(geo_level = "municipality", highlight = "Córdoba") ## End(Not run)
Builds the classic demographic Lexis surface — age (y-axis) by
calendar year (x-axis), shaded by mortality rate, with birth-cohort
diagonals overlaid — for one province, from the age x year central
death rates already computed by compute_death_rates(). Operates on
data already retrieved/computed via the mortality pipeline
(get_ine_deaths() -> compute_exposure() -> compute_death_rates());
it does not fetch anything itself.
plot_lexis_diagram( mx_df, province, sex = c("total", "female", "male"), log_scale = TRUE, rate_per = 1000, cohort_lines = TRUE, cohort_step = 10, title = NULL )plot_lexis_diagram( mx_df, province, sex = c("total", "female", "male"), log_scale = TRUE, rate_per = 1000, cohort_lines = TRUE, cohort_step = 10, title = NULL )
mx_df |
Output of the |
province |
Character, a province name (regular expressions
allowed, matched against |
sex |
Character, one of |
log_scale |
Logical (default |
rate_per |
Numeric, express the mortality rate as deaths per this
many people (default |
cohort_lines |
Logical (default |
cohort_step |
Numeric, spacing in years between overlaid cohort
diagonals (default |
title |
Optional plot title (defaults to the matched province name). |
"Calculate" here means deriving the tidy age x year surface for the
requested province/sex and the cohort-diagonal positions to overlay
(age = year - cohort, for cohorts every cohort_step years across the
data's range) — not fitting a new mortality model; the rates
themselves come straight from compute_death_rates().
The white diagonal lines are intentional, not an artifact. They
are birth-cohort lines (age = year - cohort): every point along one
line represents the same group of people born in the same year, aging
one year for every calendar year that passes — the defining feature of
a Lexis diagram, letting you trace one cohort's mortality experience
across its lifetime instead of only reading across a fixed year or up
a fixed age. Set cohort_lines = FALSE to turn them off.
Grey cells mean no rate could be computed for that age x year cell, almost always because the source population data has no single-year age breakdown for the oldest ages in the earliest years of the series (so exposure, the rate's denominator, is missing there) — not a bug in the calculation. This typically shows up as a rectangular block in the upper-left (oldest ages, earliest years), clearing up once the source data's age detail becomes complete. When present, grey gets its own "Missing data" legend key (a continuous color scale can't show this on its own, so it's added via a small dummy layer).
A ggplot object.
mx <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = rep(2020:2023, each = 3), age = rep(c(0, 50, 90), 4), mx_total = c( 0.004, 0.003, 0.15, 0.0038, 0.0031, 0.14, 0.0035, 0.0029, 0.16, 0.0033, 0.0028, 0.15 ) ) plot_lexis_diagram(mx, province = "A Coruna")mx <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = rep(2020:2023, each = 3), age = rep(c(0, 50, 90), 4), mx_total = c( 0.004, 0.003, 0.15, 0.0038, 0.0031, 0.14, 0.0035, 0.0029, 0.16, 0.0033, 0.0028, 0.15 ) ) plot_lexis_diagram(mx, province = "A Coruna")
Draws a population pyramid for one province and year from
age-disaggregated population data. Requires an age/sex breakdown, so it
operates on get_ine_population()'s output, not get_ine_demog()'s
population_total indicator.
plot_population_pyramid(pop_df, year, region = NULL, title = NULL)plot_population_pyramid(pop_df, year, region = NULL, title = NULL)
pop_df |
Population tibble as returned by |
year |
Numeric, the year to plot. |
region |
Optional character vector to subset by province name
(regular expressions allowed, matched against |
title |
Optional plot title. |
A ggplot object.
pop <- tibble::tibble( province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(100, 90), male = c(105, 95) ) plot_population_pyramid(pop, year = 2023)pop <- tibble::tibble( province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(100, 90), male = c(105, 95) ) plot_population_pyramid(pop, year = 2023)
Computes the rate of natural increase (RNI = CBR - CDR, per 1,000
population) per province and year, combining already-computed
crude_birth_rate() and crude_death_rate() output rather than
re-deriving from raw births/deaths/population — consistent with how
life_expectancy_summary() consumes build_life_tables()'s output
rather than recomputing it.
rate_of_natural_increase(cbr_df, cdr_df)rate_of_natural_increase(cbr_df, cdr_df)
cbr_df |
Output of |
cdr_df |
Output of |
A tibble with nuts3_code, province_name, year, rni. A
positive value means births outnumber deaths (population growing
from natural change alone, ignoring migration); negative means the
reverse.
cbr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, cbr = 6.5 ) cdr <- tibble::tibble(nuts3_code = "ES111", year = 2023, cdr = 12.3) rate_of_natural_increase(cbr, cdr)cbr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, cbr = 6.5 ) cdr <- tibble::tibble(nuts3_code = "ES111", year = 2023, cdr = 12.3) rate_of_natural_increase(cbr, cdr)
Computes the sex ratio (males per 100 females) per province and year
from age-disaggregated population data. Requires the female/male
columns get_ine_population() provides; get_ine_demog()'s
population_total indicator has no sex breakdown and cannot be used
here.
sex_ratio(pop_df, by_age = FALSE)sex_ratio(pop_df, by_age = FALSE)
pop_df |
Population tibble as returned by |
by_age |
Logical, keep the |
A tibble with nuts3_code, province_name, year (and age if
by_age = TRUE), sex_ratio.
pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(100, 95), male = c(105, 100) ) sex_ratio(pop) sex_ratio(pop, by_age = TRUE)pop <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(100, 95), male = c(105, 100) ) sex_ratio(pop) sex_ratio(pop, by_age = TRUE)
Sum of age_specific_fertility_rate()'s asfr (births per woman) over
single years of age: the expected number of children per woman under the
year's age schedule.
total_fertility_rate(asfr_df)total_fertility_rate(asfr_df)
asfr_df |
Output of |
A tibble with nuts3_code, province_name, year, tfr
(children per woman).
asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) total_fertility_rate(asfr)asfr <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(20, 30), asfr = c(0.03, 0.08), asfr_female = c(0.015, 0.04) ) total_fertility_rate(asfr)
Checks each indicator wired in ine_variables for its latest published
year (via get_ine_demog()) and, if newer than the local cache, downloads
and caches it for offline use.
update_ine_data(force = FALSE, data_dir)update_ine_data(force = FALSE, data_dir)
force |
Logical, force update even if data is current. |
data_dir |
Character, directory for cached data. No default: this
function only writes when a directory is explicitly supplied, per CRAN
policy against writing to the user's home filespace by default. Pass
e.g. |
Invisibly, a named list of the latest cached year per indicator.
## Not run: # Not run: requires live network access to the INE API, and writes to # the supplied cache directory. update_ine_data(data_dir = tools::R_user_dir("inedemogR", "cache")) ## End(Not run)## Not run: # Not run: requires live network access to the INE API, and writes to # the supplied cache directory. update_ine_data(data_dir = tools::R_user_dir("inedemogR", "cache")) ## End(Not run)
Fails when any column is missing or non-finite, lx increases, Lx
or ex is not positive, qx lies outside [0, 1], or the identity
mx = dx / Lx fails in a closed interval from age 5 upward (the 0-4
group is built from single-year rates, so its grouped mx need not
satisfy it exactly).
validate_abridged_life_table(alt)validate_abridged_life_table(alt)
alt |
Output of |
list(passed = logical, issues = named list of flagged tibbles).
mx1 <- data.frame(age = 0:4, mx = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002)) mx5 <- data.frame( age_group = c("00-04", "05-09", "100+"), mx = c(0.0006, 0.0001, 0.35) ) alt <- build_abridged_life_table(mx1, mx5, "female") validate_abridged_life_table(alt)mx1 <- data.frame(age = 0:4, mx = c(0.004, 0.0003, 0.0002, 0.0002, 0.0002)) mx5 <- data.frame( age_group = c("00-04", "05-09", "100+"), mx = c(0.0006, 0.0001, 0.35) ) alt <- build_abridged_life_table(mx1, mx5, "female") validate_abridged_life_table(alt)
Checks: sex ratio at birth (SRB) implausibly far from the expected value, accounting for sample size; no negative/missing counts; no duplicate province-year rows; continuous year coverage.
validate_births(df)validate_births(df)
df |
Output of the |
The SRB check does not use a single fixed band (an earlier version used [1.030, 1.070], which flagged ~60% of all province-years, including 38% of Madrid/Barcelona's — clearly miscalibrated: annual province-level SRB is naturally noisy for small birth counts, and this data's own pooled SRB runs about 1.06, already above that band's upper bound even before any noise). Instead it flags a province-year only if both (a) the observed SRB is statistically implausible given that year's birth count, assuming a true underlying SRB of 1.06 (a two-tailed z-test on the male-birth proportion at z > 4, i.e. roughly a 1-in-16,000 event under the null - deliberately strict, since thousands of province-years are tested at once), and (b) the deviation is also practically meaningful (SRB more than 0.03 from 1.06), so statistically-significant-but-tiny deviations in large provinces aren't flagged just because their sample size makes the test hyper-sensitive. An SRB outside [0.90, 1.25] is additionally always flagged as a sanity floor for gross data errors, but only once total births reach 200: below that, even a large swing (e.g. 23 vs. 17 births) is unremarkable binomial noise, not a data problem, and the z-test above already accounts for the small sample correctly.
list(passed = logical, issues = named list of flagged tibbles).
births_df <- tibble::tibble( nuts3_code = c("ES611", "ES611", "ES612", "ES612"), province_name = c("Almeria", "Almeria", "Cadiz", "Cadiz"), year = c(2020, 2021, 2020, 2021), female = c(950, 970, 1200, 1210), male = c(1000, 1020, 1260, 1270), total = female + male ) validate_births(births_df)births_df <- tibble::tibble( nuts3_code = c("ES611", "ES611", "ES612", "ES612"), province_name = c("Almeria", "Almeria", "Cadiz", "Cadiz"), year = c(2020, 2021, 2020, 2021), female = c(950, 970, 1200, 1210), male = c(1000, 1020, 1260, 1270), total = female + male ) validate_births(births_df)
Checks: no negative/missing counts; no duplicate province-year-age
rows; single-year ages (15-49) present for every province-year found
in the data (the two open intervals, under15/50plus, are not
required to be present since they are typically very small or zero).
validate_births_by_age(df)validate_births_by_age(df)
df |
Output of the |
list(passed = logical, issues = named list of flagged tibbles).
# Only 3 of the 35 required single-year ages (15-49) are present here, # so this deliberately triggers the incomplete age coverage check. births_age_df <- tibble::tibble( nuts3_code = "ES611", province_name = "Almeria", year = 2020, age = c(20, 21, 22), age_group = c("20", "21", "22"), female = c(30, 28, 25), male = c(32, 29, 27), total = female + male ) validate_births_by_age(births_age_df)# Only 3 of the 35 required single-year ages (15-49) are present here, # so this deliberately triggers the incomplete age coverage check. births_age_df <- tibble::tibble( nuts3_code = "ES611", province_name = "Almeria", year = 2020, age = c(20, 21, 22), age_group = c("20", "21", "22"), female = c(30, 28, 25), male = c(32, 29, 27), total = female + male ) validate_births_by_age(births_age_df)
Fails on negative, missing, or infinite rates; on rates >= 1 below age 90; and on any province-year without unique, complete single ages 0-100.
validate_death_rates(mx_df)validate_death_rates(mx_df)
mx_df |
Output of the |
list(passed = logical, issues = named list of flagged tibbles).
deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(1, 0), male = c(1, 1), total = c(2, 1) ) exposure <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(500, 490), male = c(510, 505), total = c(1010, 995) ) rates <- compute_death_rates(deaths, exposure) validate_death_rates(rates$mx_1x1)deaths <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(1, 0), male = c(1, 1), total = c(2, 1) ) exposure <- tibble::tibble( nuts3_code = "ES111", province_name = "A Coruna", year = 2023, age = c(0, 1), female = c(500, 490), male = c(510, 505), total = c(1010, 995) ) rates <- compute_death_rates(deaths, exposure) validate_death_rates(rates$mx_1x1)
Checks: sex ratio at death in [0.90, 1.50]; no negative/missing
counts; no duplicate province-year rows; continuous year coverage;
provincial sum vs. national total (if nat_df supplied).
validate_deaths(prov_df, nat_df = tibble::tibble())validate_deaths(prov_df, nat_df = tibble::tibble())
prov_df |
Province-level cleaned deaths tibble. |
nat_df |
National-level cleaned deaths tibble (optional). |
list(passed = logical, issues = named list of flagged tibbles).
deaths_df <- tibble::tibble( nuts3_code = c("ES611", "ES611", "ES612", "ES612"), province_name = c("Almeria", "Almeria", "Cadiz", "Cadiz"), year = c(2020, 2021, 2020, 2021), female = c(480, 500, 610, 630), male = c(510, 520, 640, 655), total = female + male ) validate_deaths(deaths_df)deaths_df <- tibble::tibble( nuts3_code = c("ES611", "ES611", "ES612", "ES612"), province_name = c("Almeria", "Almeria", "Cadiz", "Cadiz"), year = c(2020, 2021, 2020, 2021), female = c(480, 500, 610, 630), male = c(510, 520, 640, 655), total = female + male ) validate_deaths(deaths_df)
Checks: no negative/missing counts; no duplicate province-age-year rows; continuous year coverage per province.
validate_deaths_age(df)validate_deaths_age(df)
df |
Age-specific province deaths tibble. |
list(passed = logical, issues = named list of flagged tibbles).
deaths_age_df <- tibble::tibble( nuts3_code = c("ES611", "ES611"), province_name = c("Almeria", "Almeria"), year = c(2020, 2020), age = c(0, 1), female = c(2, 0), male = c(3, 1), total = female + male ) validate_deaths_age(deaths_age_df)deaths_age_df <- tibble::tibble( nuts3_code = c("ES611", "ES611"), province_name = c("Almeria", "Almeria"), year = c(2020, 2020), age = c(0, 1), female = c(2, 0), male = c(3, 1), total = female + male ) validate_deaths_age(deaths_age_df)
Checks: cells with no usable source population (flagged for audit, not treated as an error); no other negative/NA exposures; exposure should not exceed 2x population (a generous plausibility bound).
validate_exposure(exposure_df, population_df)validate_exposure(exposure_df, population_df)
exposure_df |
Output of the |
population_df |
The population tibble originally passed in. |
list(passed = logical, issues = named list of flagged tibbles).
exposure_df <- tibble::tibble( nuts3_code = "ES611", year = 2020, age = c(0, 1), female = c(495, 480), male = c(520, 500), total = c(1015, 980), is_boundary_year = FALSE, is_missing_source_pop = FALSE ) population_df <- tibble::tibble( nuts3_code = "ES611", year = 2020, age = c(0, 1), total = c(1000, 970) ) validate_exposure(exposure_df, population_df)exposure_df <- tibble::tibble( nuts3_code = "ES611", year = 2020, age = c(0, 1), female = c(495, 480), male = c(520, 500), total = c(1015, 980), is_boundary_year = FALSE, is_missing_source_pop = FALSE ) population_df <- tibble::tibble( nuts3_code = "ES611", year = 2020, age = c(0, 1), total = c(1000, 970) ) validate_exposure(exposure_df, population_df)
Fails when any of the following holds: ages are not unique and contiguous
from 0; any of mx, qx, ax, lx, dx, Lx, Tx, ex is missing or
non-finite; lx increases with age; Lx or ex is not positive; qx lies
outside [0, 1]; mx = dx/Lx does not hold; or life expectancy at birth is
outside the plausibility band (15, 100] years.
validate_life_table(lt)validate_life_table(lt)
lt |
Output of |
list(passed = logical, issues = named list).
mx <- data.frame(age = 0:100, mx = 0.0003 * exp(0.07 * (0:100))) validate_life_table(build_life_table(mx, "female"))mx <- data.frame(age = 0:100, mx = 0.0003 * exp(0.07 * (0:100))) validate_life_table(build_life_table(mx, "female"))
Checks: no negative/missing counts; full age range (0..100) present per province-year; no duplicate province-age-year rows; continuous year coverage; no implausible age-to-age jumps (>50%, informational only).
validate_population(df)validate_population(df)
df |
Output of the |
list(passed = logical, issues = named list of flagged tibbles).
population_df <- tibble::tibble( nuts3_code = c("ES611", "ES611"), province_name = c("Almeria", "Almeria"), year = c(2020, 2020), age = c(0, 1), female = c(480, 490), male = c(500, 505), total = female + male ) validate_population(population_df)population_df <- tibble::tibble( nuts3_code = c("ES611", "ES611"), province_name = c("Almeria", "Almeria"), year = c(2020, 2020), age = c(0, 1), female = c(480, 490), male = c(500, 505), total = female + male ) validate_population(population_df)