Skip to contents

Reads the gdp-population pin and returns population per country and year on the numeric area_code the rest of the package uses. The pin is keyed by ISO3 (in a column confusingly also called area_code) and reports population in thousands; both are converted here, so consumers such as build_food_supply() and build_n_percapita() get the year/area_code/population contract they document.

Regional residual aggregates in the pin (RAFR, RASI, REUR, RLAM, ROCE) are not countries and carry no numeric code. They are dropped and reported rather than silently discarded, since their omission is what makes the result a countries-only total rather than a world total.

area_code here is polity_area_code, which is a bucket, not an identity: several ISO3 codes can share one code, and this function sums them, so some rows are aggregates of more than one territory. WHEP models the reporting members of bucket 999 in their own right, so with the real pin and the default options the only such row is code 206 "Sudan (former)", carrying Sudan plus South Sudan from 2012 on; it resolves to SUD-1956-2011, the pre-secession territory its two members together cover. Under options(whep.unfold_rest_of_world = "none") code 999 folds Syria, North Macedonia, Palestine, Eswatini, Equatorial Guinea and French Guiana as well. Every folded row is named in a message.

The pin does not cover every area WHEP models, and that is a bigger gap than the fold. On the real pin 190 of the 256 area codes the crosswalk resolves get a population row; the 66 that do not include Bhutan, Comoros, New Caledonia and the Faroe Islands, all of which the commodity balances do give food to. build_food_supply() and build_n_percapita() inner-join this table, so those areas are absent from their per-capita output rather than wrong in it. Both warn and name them instead of dropping them silently; options(whep.warn_missing_population = FALSE) silences that warning.

population_source = "pin_wpp_fallback" fills the country-years the pin does not reach from read_wpp_population(), and only those: the pin wins wherever both have a value, so turning it on cannot move a denominator that was already published. On the real inputs it adds 44 areas the pin has no row for at all — Réunion, Bhutan, Comoros, Western Sahara, New Caledonia, the French overseas departments and the small island states — and 4,755 country-years inside the pin's own year span. Filled rows are stamped source_pop = "UN WPP 2024".

It is a gap-filler and not a replacement, because the two sources disagree where they overlap: across 12,309 shared country-years they differ by a median 0.64%, a 95th percentile of 4.4% and a maximum of 81%. That is why the pin keeps every value it has under every composition.

population_source = "pin_wpp_fbs_fallback" then fills what NEITHER of those reaches from read_fbs_population(), the FAOSTAT Food Balance Sheet population. That source is keyed on the FAOSTAT area code rather than a present-day ISO3, so it is the only one that reaches a dissolved reporting area: it closes area 186 Serbia and Montenegro over 1992-2005 (#862) and area 151 Netherlands Antilles over 1961-2010 (#787), the two largest holes in the denominator. It is anti-joined like the WPP fill, so it too cannot move a denominator that was already published.

It is the default (#1133). The sources disagree on the value, not just on the coverage. For area 186 in 2000 FAOSTAT gives 10,801,000; a UN WPP 2024 territorial sum for the same ground (SRB + MNE + XKX) gives 10,104,000, 6.5% lower; and the SRB + MNE sum a successor walk can actually reach today gives 8,311,000, 23% lower, because WPP publishes Kosovo separately and it carries no WHEP area code (#863). Under the default territory_overlap = "federation" area 186 keeps the FAOSTAT figure and the Serbia and Montenegro rows from UN WPP are dropped in those years, so the federation's denominator covers the same territory, from the same publisher, as the FAOSTAT food divided by it. Measured on the real inputs, the default adds 45 areas to the pin's 190 and changes no value the pin already carries; "pin" reproduces the earlier, pin-only table.

population_source = "pin_fbs_fallback" is FAOSTAT's own population behind the pin, skipping UN WPP. It reaches the two dissolved areas but only 12 of the 45 areas the pin lacks (202 areas against 235), because the Food Balance Sheets do not report Bhutan, Comoros, the French overseas departments or most small island states. Where both FBS and WPP would fill the same country-year they agree to a median of about 1e-6.

The UN WPP fill stops at 2023, the last year WPP 2024 publishes as an estimate; 2024-2100 are medium-variant projections and are dropped, with a message, rather than returned as population.

Neither fill can overwrite a key the previous source already has, but an anti-join on (year, area_code) cannot see two different codes naming the same ground in the same year, and all three sources produce such pairs: the pin reports 167 Czechia inside 51 Czechoslovakia for 1850–1992, the UN WPP fill adds the Soviet, Yugoslav and Belgium-Luxembourg successor states in the federation's own years, and the FBS fill adds 186 Serbia and Montenegro beside the 272/273 rows the WPP fill has already added. territory_overlap decides which row survives; the overlaps are found from the polities database's successor relation, so the answer follows upstream rather than a list kept here (#939).

Neither ISO3-keyed source can reach an area whose territory no longer exists, because both are keyed on a present-day ISO3 code. population_source_reach() reports which areas that leaves out and whether the polities database's successor relation could stand in for them. Against UN WPP 2024's vocabulary, exactly one reporting area outside the Rest-of-World bucket is unreachable by either route: area 151 Netherlands Antilles, ANT-1961-2010, which carries commodity-balance food in every year from 1961 to 2010 and for which upstream publishes no successor at all (#787). Reachable is not the same as safe to sum — see that function and the note at the top of R/population_reach.R.

Usage

read_population(
  years = NULL,
  data = list(),
  population_source = c("pin_wpp_fbs_fallback", "pin", "pin_wpp_fallback",
    "pin_fbs_fallback"),
  territory_overlap = c("federation", "successors", "none"),
  example = FALSE
)

Arguments

years

Optional integer vector of calendar years to keep. NULL (default) keeps every year the pin covers.

data

Optional named list of pre-loaded inputs to avoid the pin read: gdp_population (the raw pin, with Year, area_code as ISO3, pop in thousands), wpp_population (a read_wpp_population() output) and fbs_population (a read_fbs_population() output). Falls back to whep_read_file() when absent.

population_source

"pin_wpp_fbs_fallback" (default), which fills the country-years the gdp-population pin does not cover from UN WPP and then what neither reaches from read_fbs_population(); "pin", the pin alone; "pin_wpp_fallback", the pin then UN WPP; or "pin_fbs_fallback", which fills from read_fbs_population() alone and never reads UN WPP.

territory_overlap

Which row survives when two area codes describe overlapping territory in the same year, as a dissolved federation and its successor states do. Overlaps are found from the polities database's successor relation, transitively.

  • "federation" (default) keeps the federation's row and drops the successor states' rows in the years the federation reports. WHEP's own numerator sits on the federation code in exactly those years — the commodity balances carry area 228 for 1961–1991 and Russia only from 1992, area 51 to 1992 and Czechia only from 1993, area 15 to 1999 and Belgium only from 2000 — so this is the row a per-capita divide can actually use, and the sum over areas is the territory counted once.

  • "successors" keeps the successor states and drops the federation. It is the finer grain, and it does not reproduce the federation's own figure: on "pin_wpp_fbs_fallback" the successor sum falls 1.6% short of the USSR at 1961 and runs 2.0% over it at 1991, and falls 14.8% (1961) to 17.5% (1991) short of the Yugoslav SFR — the Kosovo gap of #863. Before 1950 no source has the successors at all, so the federation's population is lost rather than redistributed. It also removes three areas from the table outright, because they exist only in the overlapping years: 51 Czechoslovakia, and — undoing exactly what the FBS fill is for — 186 Serbia and Montenegro (#862) and 151 Netherlands Antilles (#787).

  • "none" composes the sources exactly as before and warns. Any sum over areas then double counts; use it only to reproduce a number published before this argument existed.

The default answers "whose is the USSR's 1961?" the same way resolve_polity_lineage() does on the spatial side (#1004, #1114): its default basis = "historical_polity" places a 1961 row keyed on area 185 Russia onto F228-1945-1991, the polity the grid support carries that year, and its alternative "constant_territory" keeps the modern reporter, exactly as "successors" does here. The two are complementary, not competing: that function says which polity's cells a row belongs on and moves no value, while this one says which of two rows for one territory survives. The package therefore has one convention, stated in both places and selectable in both.

example

If TRUE, return a small fixture instead of reading remote data. Defaults to FALSE.

Value

A tibble with year, area_code, population (persons), source_pop and method_territory_overlap, one row per area code and year, sorted by year then area code, plus the polity columns below. source_pop carries the pin's own vocabulary ("Original", "Linear interpolation", "First value carried backwards"), joined with " + " when a bucket sums ISO3 codes of differing provenance, or "UN WPP 2024" for a fallback-filled row. A row is one country in the common case, but area_code is an aggregation bucket: rows from 2012 on 206 ("Sudan (former)") are sums over several territories rather than a single country, as are rows on 999 ("Rest of World") when the Rest-of-World fold is restored.

Polity columns

Every area-keyed output carries the polity its area_code resolves to in that row's year:

  • polity_area_code: The numeric key rows are AGGREGATED on, for the matrix workflows. It is a bucket, not an identity: use reporting_polity_code to say which territory a row belongs to.

  • reporting_polity_code: The polity itself, e.g. ESP-1846-1914. It is year-aware, so the same area_code resolves to different polities in different years, which is the point of the crosswalk.

  • reporting_polity_name: Its name. It can differ from the area's own name where the area folds into an aggregate.

  • reporting_polity_has_geometry: Whether the polity has a polygon in the WHEP polity database, for callers that need to map or intersect it. FALSE is a documented gap upstream, not an error.

Rows whose area_code resolves to no polity keep the columns with NA rather than being dropped, so a gap is visible instead of silent.

Rows before the back-cast anchor year resolve to the polity live in that anchor year rather than to the polity live in the row's own year, because WHEP's pre-anchor series are back-cast onto the anchor-year territory. See add_polity_code() for the reasoning. Where that polity is not live in the row's own year – 41.5% of the pre-1961 (area, year) cells – add_polity_code() says so as mapping_status == "backcast_anchor", and polity_coverage_gaps() reports it as gap_kind == "backcast_anchor". These columns do not say so either way.

A row whose year no mapped period covers is resolved to the NEAREST period of the same area instead, so reporting_polity_code can name a polity that did not exist in that row's year – FAOSTAT bucket 206 "Sudan (former)" keeps reporting after SUD-1956-2011 ends, and its post-2011 rows carry that code. These columns do not say so: add_polity_code() reports such a row as mapping_status == "out_of_span", and that column is dropped here so that adding it does not change the schema of every area-keyed output at once. polity_coverage_gaps() reports the stand-in rows of a built table, and options(whep.polity_mapping_status = "flag") (or "status") carries the signal on the outputs themselves. Both are opt-in; the default is no extra column.

Examples

read_population(example = TRUE)
#> # A tibble: 5 × 8
#>    year area_code polity_area_code reporting_polity_code reporting_polity_name  
#>   <int>     <int>            <int> <chr>                 <chr>                  
#> 1  2010        41               41 CHN-1950-2025         China (PRC)            
#> 2  2010       100              100 IND-1949-2025         India                  
#> 3  2010       231              231 USA-1959-2025         United States of Ameri…
#> 4  2010       101              101 IDN-2002-2025         Indonesia              
#> 5  2010        21               21 BRA-1909-2025         Brazil                 
#> # ℹ 3 more variables: reporting_polity_has_geometry <lgl>, population <dbl>,
#> #   method_territory_overlap <chr>