diff --git a/input/ES/align_popProjections.xlsx b/input/ES/align_popProjections.xlsx new file mode 100644 index 0000000..30fa84b Binary files /dev/null and b/input/ES/align_popProjections.xlsx differ diff --git a/input/ES/projections_fertility.xlsx b/input/ES/projections_fertility.xlsx new file mode 100644 index 0000000..ca5abbc Binary files /dev/null and b/input/ES/projections_fertility.xlsx differ diff --git a/input/ES/projections_mortality.xlsx b/input/ES/projections_mortality.xlsx new file mode 100644 index 0000000..6d6efde Binary files /dev/null and b/input/ES/projections_mortality.xlsx differ diff --git a/input/ES/time_series_factor.xlsx b/input/ES/time_series_factor.xlsx new file mode 100644 index 0000000..b00d1b9 Binary files /dev/null and b/input/ES/time_series_factor.xlsx differ diff --git a/input_processing/time_series/ES/do_files/00_master.do b/input_processing/time_series/ES/do_files/00_master.do new file mode 100644 index 0000000..48d50cd --- /dev/null +++ b/input_processing/time_series/ES/do_files/00_master.do @@ -0,0 +1,90 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Master file for constructing Spain time-series inputs +* AUTHORS: Ashley Burdett +* LAST UPDATE: 10/08/2026 +* COUNTRY: Spain +******************************************************************************** +* NOTES: +* +* This master do-file constructs the additional time-series inputs required +* by SimPaths for Spain. It defines the project directories and then calls +* each series-specific do-file in the required order. +* +* Directory structure: +* +* time_series/ +* input_data/ Shared raw source data (Eurostat, etc.) +* ES/ Spain-specific files and outputs +* do_files/ +* data/ +* +* Before running: +* 1. Update the directory globals below if required. +* 2. Ensure the shared input_data folder contains all required raw data +* (see individual do-files for details). +* 3. Run this file from start to finish. +* +* The following series are constructed: +* - Real GDP index +* - Inflation (HICP) index +* - Real wage index and annual real wage growth +* - Population series and projections (by region, age and year) +* - Fertility rate +* - Mortality rates (by age and year) +* +* Note: +* inflation.do must be run before real_wage_growth.do because the latter +* uses the saved inflation dataset created by the former. +*******************************************************************************/ + +clear all +set more off +macro drop _all +set type double +set maxvar 30000 +set matsize 1000 + + +***************************** SET MACROS *************************************** + +global country "ES" + + +************************* SET DIRECTORIES ************************************** + +* Working directory which contains sub-folders and will contain formatted output +global dir_work "/Users/ashleyburdett/Library/CloudStorage/Box-Box/CeMPA shared area/_SimPaths/_SimPathsEU/input_processing/time_series/${country}" + +* Directory which contains raw data +global dir_input_data "/Users/ashleyburdett/Library/CloudStorage/Box-Box/CeMPA shared area/_SimPaths/_SimPathsEU/input_processing/time_series/input_data" + +* Directory which contains do files +global dir_do "$dir_work/do_files" + +* Directory which contains intermediate data +global dir_data "$dir_work/data" + + +***************************** CALL FILES *************************************** + +do "$dir_do/RGDP.do" + +do "$dir_do/inflation.do" + +do "$dir_do/real_wage_growth.do" + +do "$dir_do/population_projections.do" + +do "$dir_do/fertility_rate.do" + +do "$dir_do/mortality_rate.do" + + +******************************* TIDY UP **************************************** + +erase "$dir_do/inflation.dta" +erase "$dir_do/mortality_rate.dta" +erase "$dir_do/population_projections.dta" +erase "$dir_do/total_fertility_rate.dta" diff --git a/input_processing/time_series/ES/do_files/RGDP.do b/input_processing/time_series/ES/do_files/RGDP.do new file mode 100644 index 0000000..78cc71e --- /dev/null +++ b/input_processing/time_series/ES/do_files/RGDP.do @@ -0,0 +1,144 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Real GDP (RGDP) +* AUTHORS: Ashley Burdett +* LAST UPDATE: 10/08/2026 +* COUNTRY: Spain +******************************************************************************** +* NOTES: Constructs the real GDP alignment series for Spain using +* Eurostat national accounts data. +* +* Uses GDP at market prices (B1GQ) measured in chain-linked +* volumes (CLV15_MEUR), converted to an index with 2015 = 100. +* +* Eurostat observation flags (e.g. "p") are removed prior to +* converting the series to numeric values. +* +* https://ec.europa.eu/eurostat/cache/metadata/en/nama10_esms.htm +* https://ec.europa.eu/eurostat/databrowser/explore/all/economy?sort=category&lang=en&subtheme=na10.nama10.nama_10_ma&display=list +*******************************************************************************/ +clear all +set more off +capture log close + + +* Import data +import delimited using "$dir_input_data/estat_nama_10_gdp.tsv", /// + delimiters(tab) clear + +* Organize dataset +forvalues i = 1/83 { + + local y = 1973 + `i' + capture rename v`i' y`y' + +} + +rename y1974 freq_unit_na_item_geo + +drop if y1975 == "1975 " + +* Select country of interest +keep if substr(freq, -2, 2) == "${country}" + +* Select total GDP +keep if substr(freq, -7, 4) == "B1GQ" + +* Select chain-linked volumnes series for RGDP +keep if substr(freq, 3, 10) == "CLV15_MEUR" + +* Prep formatting +drop freq + + +* Remove Eurostat flags from year values +foreach var of varlist y* { + replace `var' = word(`var', 1) + replace `var' = "" if `var' == ":" +} + +* Convert year values to numeric +destring y*, replace + +* Only keep relevant years +drop y1975-y1994 + +* Transpose +xpose, clear + +gen Year = 1995 +replace Year = Year[_n-1] + 1 if Year[_n-1] != . + +rename v1 RGDP + +order Year RGDP + +* Create RGDP index with base in 2015 +summ RGDP if Year == 2015, meanonly +local base = r(mean) + +gen Value = RGDP / `base' * 100 + + +******************************* EXPORT TO EXCEL ******************************** + +* Info sheet + +putexcel set "$dir_work/time_series_factor${country}.xlsx", /// + sheet("Info") replace + +putexcel A1 = "Purpose:" /// + B1 = "This file stores the macroeconomic uprating index series used by SimPaths to revalue monetary amounts and wage-related inputs across years." + +putexcel A2 = "Developers:" /// + B2 = "Spain series prepared by Ashley Burdett (AB)." + +putexcel A3 = "First version:" /// + B3 = "10/08/2026 (AB)" + +putexcel A4 = "Last version:" /// + B4 = "10/08/2026 (AB)" + +putexcel A5 = "Created using:" /// + B5 = "Do-files RGDP.do, inflation.do, wages.do contained in time_series/do_files." + +putexcel A6 = "Sheets used by model:" /// + B6 = "gdp, inflation, wage_growth." + +putexcel A7 = "Source - gdp:" /// + B7 = "Eurostat National Accounts (nama_10_gdp). GDP at market prices (B1GQ), measured in chain-linked volumes with reference year 2015 (CLV15_MEUR), is used to construct the real GDP series for Spain. The series is converted to an index with 2015 = 100." + +putexcel A8 = "Source - inflation:" /// + B8 = "Eurostat Harmonised Index of Consumer Prices (HICP), annual data (prc_hicp_aind). The annual average all-items HICP index (INX_A_AVG, CP00) is used for Spain." + +putexcel A9 = "Source - wage_growth:" /// + B9 = "Eurostat annual Labour Cost Index (lc_lci_r2_a). The wages and salaries component (D11) for industry, construction and services (B-S) is used. The nominal LCI is rebased to 2015 = 100 and deflated using the all-items HICP index to construct a real wage index. The annual percentage growth rate of this real wage index is also provided." + +putexcel A10 = "Units / interpretation:" /// + B10 = "The gdp and inflation sheets store index values. In the wage_growth sheet, Value is the real wage index (2015 = 100) constructed from the deflated Labour Cost Index, while Growth is its year-on-year percentage change. The model treats the index series as relative series and rebases them to BASE_PRICE_YEAR (currently 2015) after loading." + +putexcel A11 = "Coverage in current workbook:" /// + B11 = "gdp: 1995-2025; inflation: 1996-2025; wage_growth: 2000-2025." + +putexcel A12 = "How gdp is used:" /// + B12 = "Mapped to UpratingCase.Capital, ModelInitialise, and Pension. After rebasing, it is used where the model needs a GDP-based uprating factor for capital-like or model-initialisation terms." + +putexcel A13 = "How inflation is used:" /// + B13 = "Mapped to UpratingCase.TaxDonor. After rebasing, it is used to normalise donor-tax-system monetary values between policy-system years and the model base-price year." + +putexcel A14 = "How wage_growth is used:" /// + B14 = "Mapped to UpratingCase.Earnings and passed into the person-level wage regressions through the RealWageGrowth regressor. The wage_growth sheet contains both the real wage index (Value) and its annual percentage growth rate (Growth) so that alternative specifications can be explored." + +putexcel A15 = "Date produced / maintenance note:" /// + B15 = "Workbook first prepared for Spain on 10/08/2026. The series should be refreshed only with documented source notes and date stamps; if new years are appended, record both the source and the method used for any forecast or extrapolated values." + +putexcel A16 = "NOTE:" /// + B16 = "All cells read by the model should contain plain numeric data only. The wage_growth sheet contains Year, Value (real wage index), and Growth (annual percentage change in the real wage index). Remove formulas, merged headers, and non-numeric values from active sheets." + +putexcel A1:A16, bold + + +* Add data +export excel Year Value using "$dir_work/time_series_factor${country}.xlsx", /// + sheet("gdp") sheetmodify firstrow(variables) diff --git a/input_processing/time_series/ES/do_files/fertility_rate.do b/input_processing/time_series/ES/do_files/fertility_rate.do new file mode 100644 index 0000000..bc69540 --- /dev/null +++ b/input_processing/time_series/ES/do_files/fertility_rate.do @@ -0,0 +1,255 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Fertility rate +* AUTHORS: Ashley Burdett +* LAST UPDATE: 6/9/26 +* COUNTRY: Spain +******************************************************************************** +* NOTES: This file imports and cleans observed and projected +* fertility data downloaded from EUROSTAT. +* +* Data accessed: 6/9/26 +* +* Observed total fertility rates (TFR) are taken from the +* EUROSTAT series demo_find (2011-2024): +* https://ec.europa.eu/eurostat/databrowser/explore/all/popul?sort=category&lang=en&subtheme=demo.demo_fer&display=list +* +* Projected total fertility rates are taken from the +* EUROSTAT series proj_23naasfr (2022-2100): +* https://ec.europa.eu/eurostat/databrowser/view/proj_23naasfr/default/table?lang=en +* +* Inspection of the overlap (2022-2024) indicated that the +* projected series lay consistently above the observed +* series. To ensure continuity, the projected TFR is rebased +* to the final observed year (2024) while preserving the +* projected year-to-year trajectory. +* +* The rebased TFR is converted into an approximate General +* Fertility Rate (GFR), expressed as births per 1,000 women +* aged 18-49, for use in SimPaths. +*******************************************************************************/ +clear all +set more off +capture log close + + +****************************** TFR Historic Data ******************************* + +* Import data +import delimited using "$dir_input_data/estat_demo_find.tsv", /// + delimiters(tab) clear + +* Organize data +forvalues i = 1/66 { + + local y = 1958 + `i' + capture rename v`i' y`y' + +} + +rename y1959 freq_indic_de_geo + +drop if freq == "freq,indic_de,geo\TIME_PERIOD" + + +* Keep only Spanish observations +keep if substr(freq, -2, 2) == "${country}" + +* Keep only total fertility rate +keep if substr(freq, 3, 8) == "TOTFERRT" + +* Only keep relevant years +keep y2011-y2024 + +* Reshape from one observation with years in columns to 1 observation per year +gen id = 1 +reshape long y, i(id) j(year) +drop id + +rename y tfr + +destring tfr, replace + + +save "$dir_data/total_fertility_rate", replace + + +****************************** TFR Projections ********************************* + +* Code of country of interest +local country "ES" + + +* Import data +import delimited using "$dir_input_data/estat_proj_23naasfr.tsv", /// + delimiters(tab) clear + +* Organize data +forvalues i = 1/80 { + + local y = 2020 + `i' + capture rename v`i' y`y' + +} + +rename y2021 freq_projection_age_unit_geo + +drop if freq == "freq,projection,age,unit,geo\TIME_PERIOD" + + +* Keep only Spanish observations +keep if substr(freq, -2, 2) == "${country}" + +* Keep total fertility rate +* This uniquely identifies the baseline projection series in the dataset +keep if substr(freq, 7, 5) == "TOTAL" + +* Tidy up +drop freq + +* Reshape from years in columns to one observation per year +gen id = 1 +reshape long y, i(id) j(year) +drop id + +* Flag projection observations for subsequent merge with the observed series +gen x = 1 + + +******************************* Combine Series ********************************* + +* Add historic series +append using "$dir_data/total_fertility_rate" + +sort year + +* Inspect +summ tfr if year == 2024 & missing(x), meanonly +local tfr_obs = r(mean) + +summ y if year == 2024 & x == 1, meanonly +local tfr_proj = r(mean) + +local scale = `tfr_obs' / `tfr_proj' + +display "Observed TFR 2024: `tfr_obs'" +display "Projected TFR 2024: `tfr_proj'" +display "Scaling factor: `scale'" + +list year tfr y if inrange(year,2022,2024) + +/* +Inspection of the 2022-2024 overlap shows that projected TFR is above the +observed series in each year, with the gap widening over time as observed +fertility declines more rapidly than assumed in the projection. . +*/ + + +********************************* Rebase *************************************** + +/* +To ensure continuity between the historical and projected series, the +projection is rebased to the final observed year (2024). The projected +year-to-year trajectory is retained, while the level is adjusted to match the +last observed value. +*/ +summ tfr if year == 2024 & missing(x), meanonly +local tfr_obs = r(mean) + +summ y if year == 2024 & x == 1, meanonly +local tfr_proj = r(mean) + +local scale = `tfr_obs' / `tfr_proj' + +gen tfr_final = tfr +replace tfr_final = y * `scale' if x == 1 & year >= 2025 + + +* Tidy up +drop if inrange(year,2022,2023) & x == 1 +drop y x tfr + +rename tfr_final tfr + +format tfr %9.2f + + +************************************ GFR *************************************** + +/* +SimPaths aligns fertility using the General Fertility Rate (GFR). The GFR is +approximated from the Total Fertility Rate (TFR) by assuming births are +distributed uniformly across the modelled fertility ages (18-49 years). +Expressed as births per 1,000 women aged 18-49. +*/ +gen gfr = tfr/(49-18+1)*1000 + + +******************************* Export to Excel ******************************** + +putexcel set "$dir_work/projections_fertility${country}.xlsx", /// + sheet("Info") replace + +putexcel A1 = "Purpose:" /// + B1 = "This file stores the observed and projected fertility series used by SimPaths." + +putexcel A2 = "Developers:" /// + B2 = "Spain series prepared by Ashley Burdett (AB)." + +putexcel A3 = "First version:" /// + B3 = "10/08/2026 (AB)" + +putexcel A4 = "Last version:" /// + B4 = "10/08/2026 (AB)" + +putexcel A5 = "Created using:" /// + B5 = "Do-file fertility_rate.do contained in time_series/ES/do_files." + +putexcel A6 = "Source - observed fertility:" /// + B6 = "Eurostat fertility indicators (demo_find). The observed Total Fertility Rate (TFR) series (totferrt) is used for 2011-2024." + +putexcel A7 = "Source - projected fertility:" /// + B7 = "Eurostat population projections (proj_25naasfr). The baseline Total Fertility Rate (A, BSL, TOTAL, NR) series is used." + +putexcel A8 = "Coverage in current workbook:" /// + B8 = "Observed TFR: 2011-2024; projected TFR: 2025-2100 after rebasing." + +putexcel A9 = "Rebasing method:" /// + B9 = "The projected TFR is rebased to the final observed year (2024). A single adjustment factor is calculated from the ratio of the observed and projected TFR in 2024 and applied to all projected years, removing the discontinuity at the join while preserving the projected trajectory." + +putexcel A10 = "Transformation:" /// + B10 = "The rebased Total Fertility Rate (TFR) is converted into an approximate General Fertility Rate (GFR), expressed as births per 1,000 women aged 18-49, for use in SimPaths." + +putexcel A11 = "NOTE:" /// + B11 = "All cells read by the model should contain plain numeric year/value data only. Remove formulas, merged headers and non-numeric values from active sheets." + + +putexcel set "$dir_work/projections_fertility${country}.xlsx", /// + sheet("${country}") modify + +putexcel A1 = "Fertility" +putexcel A2 = "Value" + +local col = 2 + +forvalues y = 2011/2045 { + + quietly summarize gfr if year == `y', meanonly + local mean = r(mean) + + // Convert column number to Excel letters + local c = `col' + local letter "" + + while (`c' > 0) { + local rem = mod(`c' - 1, 26) + local letter = char(`rem' + 65) + "`letter'" + local c = floor((`c' - 1) / 26) + } + + putexcel `letter'1 = `y' + putexcel `letter'2 = `mean' + + local ++col +} diff --git a/input_processing/time_series/ES/do_files/inflation.do b/input_processing/time_series/ES/do_files/inflation.do new file mode 100644 index 0000000..4a3df7e --- /dev/null +++ b/input_processing/time_series/ES/do_files/inflation.do @@ -0,0 +1,74 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Inflation +* AUTHORS: Ashley Burdett +* LAST UPDATE: 10/08/2026 +* COUNTRY: Spain +******************************************************************************** +* NOTES: Constructs the inflation alignment series for Spain using +* Eurostat Harmonised Index of Consumer Prices (HICP). +* +* Observed data are taken from Eurostat dataset +* prc_hicp_aind using the annual average all-items HICP +* index (INX_A_AVG, CP00). +* +* +* Eurostat metadata: +* https://ec.europa.eu/eurostat/cache/metadata/en/prc_hicp_esms.htm +* https://ec.europa.eu/eurostat/data/database?node_code=prc_hicp_aind +*******************************************************************************/ +clear all +set more off +capture log close + + +* Load data +import delimited using "$dir_input_data/estat_prc_hicp_aind.tsv", /// + delimiters(tab) clear + +* Organize dataset +forvalues i = 1/35 { + + local y = 1994 + `i' + capture rename v`i' y`y' + +} + + +rename y1995 freq_unit_coicop_geo + +* Select country of interest +keep if substr(freq, -2, 2) == "${country}" + +* Select all items +keep if substr(freq, -7, 4) == "CP00" + +* Select chain-linked volumnes series for RGDP +keep if substr(freq, 3, 9) == "INX_A_AVG" + +* Prep formatting +drop freq + +* Convert year values to numeric +destring y*, replace + +* Transpose +xpose, clear + +gen Year = 1996 +replace Year = Year[_n-1] + 1 if Year[_n-1] != . + +rename v1 CPI + +save "$dir_work/data/inflation", replace + +rename CPI Value + +order Year Value + + +******************************* EXPORT TO EXCEL ******************************** + +export excel Year Value using "$dir_work/time_series_factor${country}.xlsx", /// + sheet("inflation") sheetmodify firstrow(variables) diff --git a/input_processing/time_series/ES/do_files/mortality_rate.do b/input_processing/time_series/ES/do_files/mortality_rate.do new file mode 100644 index 0000000..f95929f --- /dev/null +++ b/input_processing/time_series/ES/do_files/mortality_rate.do @@ -0,0 +1,459 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Mortality rate +* AUTHORS: Ashley Burdett +* LAST UPDATE: 6/9/26 +* COUNTRY: Spain +******************************************************************************** +* NOTES: Imports historical and projected mortality rates from +* Eurostat and constructs the SimPaths mortality input. +* +* The final output contains the one-year probability of dying +* between exact ages x and x+1 (qx), expressed per 100,000. +* +* Data accessed: 6/9/26 +* +* Projections series: estat_proj_23naasmr +* Years: 2022 - 2100 +* Metadata for projections: https://ec.europa.eu/eurostat/cache/metadata/en/proj_23n_esms.htm +* The EUROPOP2023 projection data provide age-specific central +* mortality rates (Mx). These are converted to one-year +* probabilities of dying (qx) using the standard life-table +* formula: +* +* qx = 2Mx / (2 + Mx) +* +* for ages 1+, and +* +* q0 = M0 / (1 + 0.8M0) +* +* for age 0. +* +* The resulting probabilities are multiplied by 100,000 to +* match the SimPaths input format. +* +* Historic series: estat_demo_mlifetable +* Years: 1960 - 2024 +* Metadata: https://ec.europa.eu/eurostat/cache/metadata/en/demo_mor_esms.htm +* Historical central mortality rates are converted to qx in +* the same way. Observed data are top-coded at age 95+. +* Exact ages 95-100 are extrapolated using a three-age moving +* average of age-specific percentage changes, anchored to the +* projected mortality profile in 2025. +*******************************************************************************/ +clear all +set more off +capture log close + + +************************** Mortality Projections ******************************* + + +* Country of interest +local country "ES" + +* Load data +import delimited using "$dir_input_data/estat_proj_23naasmr.tsv", /// + delimiters(tab) clear + + +* Organize dataset +forvalues i = 1/83 { + + local y = 2020 + `i' + capture rename v`i' y`y' + +} + +rename y2021 freq_projection_sex_age_unit_geo + +* Keep basleine projection +keep if substr(freq,3,3) == "BSL" + +* Select country +keep if substr(freq,-2,2) == "${country}" + +* Gender +gen Gender = 0 if substr(freq, 7, 1) == "F" +replace Gender = 1 if substr(freq, 7, 1) == "M" +drop if Gender == . + +label def Gender 0 "Female" 1 "Male" +label values Gender Gender + +order Gender + +* Age +gen Age = substr(freq,10,2) if substr(freq,12,1) == "," +replace Age = substr(freq,10,1) if Age == "" +replace Age = "0" if substr(freq,11,3) == "LT1" +replace Age = "100" if substr(freq,11,5) == "GE100" +destring Age, replace + +order Gender Age + +drop freq + +destring _all, replace + +sort Gender Age + +* Convert into probabiility of dying +forvalues year = 2022/2100 { + + replace y`year' = (2*y`year')/(2+y`year') if Age != 0 + + replace y`year' = (y`year')/(1+ (0.8 * y`year')) if Age == 0 + +} + +* Convert into rate per 100,000 people +forvalues year = 2022/2100 { + + replace y`year' = y`year' * 100000 + +} + + +rename y2022 y2022_proj +rename y2023 y2023_proj +rename y2024 y2024_proj + +save "$dir_data/mortality_rate", replace + + +****************************** Mortality Data ********************************** + +/* This appears to be the central death rate because I get the same probability +of death as in the data. Use death rate because get decimal places. Also +indicated in thee 2015 documentation. +*/ + +* Country of interest +local country "ES" + +* Load data +import delimited using "$dir_input_data/estat_demo_mlifetable.tsv", /// + delimiters(tab) clear + +* Organize dataset +forvalues i = 1/85 { + + local y = 1958 + `i' + capture rename v`i' y`y' + +} + +rename y1959 freq_indic_de_sex_age_geo + +* Selec relevant series +keep if substr(freq,3,9) == "DEATHRATE" +keep if substr(freq,-2,2) == "${country}" + +* Gender +gen Gender = 0 if substr(freq, 13, 1) == "F" +replace Gender = 1 if substr(freq, 13, 1) == "M" +drop if Gender == . + +label def Gender 0 "Female" 1 "Male" +label values Gender Gender + +order Gender + +* Age +gen Age = substr(freq,16,2) if substr(freq,18,1) == "," +replace Age = substr(freq,16,1) if Age == "" +replace Age = "0" if substr(freq,17,3) == "LT1" +replace Age = "95" if substr(freq,17,4) == "GE95" + +drop if substr(freq,17,4) == "GE85" + + +destring Age, replace + +order Gender Age + +drop freq + +* Select years +drop y1960-y1989 + +* Turn observations into doubles + +* Remove letters +replace y2014 = substr(y2020,1,7) +* Remove collons +forvalues y = 1990/2013 { + + replace y`y' = "." if y`y' == ": " + +} + +destring _all, replace + + +sort Gender Age + +* Convert into probability of dying +forvalues year = 1990/2024 { + + cap replace y`year' = (2*y`year')/(2+y`year') if Age != 0 + + cap replace y`year' = (y`year')/(1+(0.8*y`year')) if Age == 0 +} + +* Convert into rate per 100,000 people +forvalues year = 1990/2024 { + + replace y`year' = y`year' * 100000 +} + +* Merge in projections +merge 1:1 Gender Age using "$dir_data/mortality_rate" + +drop _m + +* Check overlap +gen diff2022 = y2022 - y2022_proj +gen diff2023 = y2023 - y2023_proj +gen diff2024 = y2024 - y2024_proj + +list Age y2022 y2022_proj diff2022 if Gender == 0 +list Age y2023 y2023_proj diff2023 if Gender == 0 +list Age y2024 y2024_proj diff2024 if Gender == 0 + +gen pctdiff2022 = 100 * (y2022 - y2022_proj) / y2022_proj +gen pctdiff2023 = 100 * (y2023 - y2023_proj) / y2023_proj +gen pctdiff2024 = 100 * (y2024 - y2024_proj) / y2024_proj + +* Comparison plots +* Absolute difference +twoway /// + (line diff2024 Age if Gender == 0, sort), /// + yline(0, lpattern(dash)) /// + xtitle("Age") /// + ytitle("Absolute difference") /// + title("Absolute difference between obs and proj mortality") /// + subtitle("Females, 2024") + +* Percentage difference +twoway /// + (line pctdiff2024 Age if Gender == 0, sort), /// + yline(0, lpattern(dash)) /// + xtitle("Age") /// + ytitle("% difference") /// + title("Percentage difference between obs and proj mortality") /// + subtitle("Females, 2024") + + graph drop _all + +/* +Inspection of the 2022-2024 overlap shows no systematic discontinuity between +observed and projected mortality. Differences are age-specific rather than a +common level shift, so observed values are retained through 2024 and the +projected series is used from 2025 onward without rebasing. +*/ + +drop y2022_p y2023_p y2024_p diff202* pctdiff202* + +sort Gender Age + + +******************************* Extrapolation ********************************** +/* +The observed life-table data are top-coded at age 85 through 2013 and age 95 +from 2014 onwards. Exact-age mortality rates above these thresholds are +constructed by extrapolating the observed series. The extrapolation uses a +three-age moving average of the age-specific percentage change, with the +projected mortality schedule providing the age profile beyond the observed +data. +*/ + + +* 1990-2013: ages 85+ + + +* Calculate age-to-age percentage change from 2025 projection for ages 85+ +bysort Gender (Age): gen perc_change = /// + (y2025 - y2025[_n-1]) / y2025[_n-1] if Age > 84 + +forvalues year = 1990/2013 { + + * Start with projected age-to-age changes at ages 85+ + gen perc_change_`year' = perc_change + + * Use observed age-to-age changes where observed data are available + bysort Gender (Age): replace perc_change_`year' = /// + (y`year' - y`year'[_n-1]) / y`year'[_n-1] /// + if Age < 85 + + * Three-age moving average around transition and through extrapolated ages + bysort Gender (Age): gen ma_`year' = /// + (perc_change_`year' + /// + perc_change_`year'[_n-1] + /// + perc_change_`year'[_n-2]) / 3 /// + if Age > 84 + + * Extrapolate mortality recursively from age 84 onwards + bysort Gender (Age): replace y`year' = /// + y`year'[_n-1] * (1 + ma_`year') /// + if Age > 84 +} + +drop perc_change perc_change_1990-perc_change_2013 /// + ma_1990-ma_2013 + + + +* 2014-2024: ages 95+ + + +* Remove the observed 95+ aggregate: this is not mortality at exact age 95 +forvalues year = 2014/2024 { + replace y`year' = . if Age == 95 +} + +* Calculate age-to-age percentage change from 2025 projection for ages 95+ +bysort Gender (Age): gen perc_change = /// + (y2025 - y2025[_n-1]) / y2025[_n-1] if Age > 94 + +forvalues year = 2014/2024 { + + * Start with projected age-to-age changes at ages 95+ + gen perc_change_`year' = perc_change + + * Use observed age-to-age changes where observed data are available + bysort Gender (Age): replace perc_change_`year' = /// + (y`year' - y`year'[_n-1]) / y`year'[_n-1] /// + if Age < 95 + + * Three-age moving average around transition and through extrapolated ages + bysort Gender (Age): gen ma_`year' = /// + (perc_change_`year' + /// + perc_change_`year'[_n-1] + /// + perc_change_`year'[_n-2]) / 3 /// + if Age > 94 + + * Extrapolate mortality recursively from age 94 onwards + bysort Gender (Age): replace y`year' = /// + y`year'[_n-1] * (1 + ma_`year') /// + if Age > 94 +} + +drop perc_change perc_change_2014-perc_change_2024 /// + ma_2014-ma_2024 + +* Checks of the extrapolation points +twoway /// + (line y2012 Age if Gender == 0 & Age >= 80, sort) /// + (line y2013 Age if Gender == 0 & Age >= 80, sort) /// + (line y2014 Age if Gender == 0 & Age >= 80, sort), /// + xtitle("Age") /// + ytitle("Probability of death per 100,000") /// + legend(order(1 "2012" 2 "2013" 3 "2014")) /// + title("Mortality around change in historical age coverage, females") + +twoway /// + (line y2012 Age if Gender == 1 & Age >= 80, sort) /// + (line y2013 Age if Gender == 1 & Age >= 80, sort) /// + (line y2014 Age if Gender == 1 & Age >= 80, sort), /// + xtitle("Age") /// + ytitle("Probability of death per 100,000") /// + legend(order(1 "2012" 2 "2013" 3 "2014")) /// + title("Mortality around change in historical age coverage, males") + +gen d2013 = y2013 - y2013[_n-1] +list Age d2013 if Gender == 0 & inrange(Age,82,88) +list Age d2013 if Gender == 1 & inrange(Age,82,88) + + +twoway /// + (line y2024 Age if Gender == 0 & Age >= 90, sort), /// + xtitle("Age") /// + ytitle("Probability of death per 100,000") /// + title("Observed and extrapolated mortality") /// + subtitle("Females, 2024") + +twoway /// + (line y2024 Age if Gender == 1 & Age >= 90, sort), /// + xtitle("Age") /// + ytitle("Probability of death per 100,000") /// + title("Observed and extrapolated mortality") /// + subtitle("Males, 2024") + +gen d2024 = y2024 - y2024[_n-1] +list Age d2024 if Gender == 0 & inrange(Age,92,98) +list Age d2024 if Gender == 1 & inrange(Age,92,98) + +drop d2024 d2013 + +graph drop _all + + +****************************** Export to Excel ********************************* + +export excel using "$dir_work/projections_mortality${country}.xlsx", /// + firstrow(var) sheet("${country}") replace + + +* Replace Excel column headings with calendar years +putexcel set "$dir_work/projections_mortality${country}.xlsx", /// + sheet("${country}") modify + +local col = 3 // Column C: Gender=A, Age=B + +forvalues year = 1990/2100 { + + local c = `col' + local letter "" + + while (`c' > 0) { + local rem = mod(`c' - 1, 26) + local letter = char(`rem' + 65) + "`letter'" + local c = floor((`c' - 1) / 26) + } + + putexcel `letter'1 = `year' + + local ++col +} + +putexcel set "$dir_work/projections_mortality${country}", /// + sheet("Info") modify + +putexcel A1 = "Purpose:" /// + B1 = "This file stores the observed and projected mortality series used by SimPaths." + +putexcel A2 = "Developers:" /// + B2 = "Spain series prepared by Ashley Burdett (AB)." + +putexcel A3 = "First version:" /// + B3 = "10/08/2026 (AB)" + +putexcel A4 = "Last version:" /// + B4 = "10/08/2026 (AB)" + +putexcel A5 = "Created using:" /// + B5 = "Do-file mortality_rate.do contained in time_series/ES/do_files." + +putexcel A6 = "Source - observed mortality:" /// + B6 = "Eurostat life tables (demo_mlifetable). The observed central death rate (DEATHRATE) is used for 1960-2024." + +putexcel A7 = "Source - projected mortality:" /// + B7 = "Eurostat mortality assumptions underlying the EUROPOP2023 population projections (proj_23naasmr). The baseline age-specific mortality rate (ASMR) series is used for 2022-2100." + +putexcel A8 = "Coverage in current workbook:" /// + B8 = "Observed mortality: 1960-2024; projected mortality: 2025-2100." + +putexcel A9 = "Transformation:" /// + B9 = "Observed and projected central death rates are converted into the probability of death between exact ages x and x+1 using the Eurostat life-table formula. The resulting probabilities are expressed as deaths per 100,000 population." + +putexcel A10 = "Joining method:" /// + B10 = "Comparison of the observed and projected series over the overlapping period (2022-2024) indicated no systematic discontinuity. Observed data are therefore retained through 2024 and the projected series is used from 2025 onwards without rebasing." + +putexcel A11 = "Age extrapolation:" /// + B11 = "Observed mortality is top-coded at age 85 through 2013 and age 95 from 2014 onwards. Exact-age mortality rates above these thresholds are extrapolated using a three-age moving average of the age-specific percentage change, with the projected mortality schedule providing the age profile beyond the observed data." + +putexcel A12 = "NOTE:" /// + B12 = "All cells read by the model should contain plain numeric year/value data only. Remove formulas, merged headers and non-numeric values from active sheets." diff --git a/input_processing/time_series/ES/do_files/population_projections.do b/input_processing/time_series/ES/do_files/population_projections.do new file mode 100644 index 0000000..eff139b --- /dev/null +++ b/input_processing/time_series/ES/do_files/population_projections.do @@ -0,0 +1,328 @@ +/******************************************************************************* +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Population projections +* AUTHORS: Ashley Burdett +* LAST UPDATE: 5/9/26 +* COUNTRY: Spain +******************************************************************************** +* NOTES: This file imports and cleans observed and projected +* population data downloaded from EUROSTAT by age, sex and +* NUTS1 region for Spain. +* +* Data accressed: 6/9/26 +* +* Observed population figures are taken from the EUROSTAT +* series demo_r_d2jan (2011-2025): +* https://ec.europa.eu/eurostat/databrowser/explore/all/popul?lang=en&subtheme=demo.demopreg&display=list&sort=category&extractionId=demo_r_d2jan +* +* Population projections are taken from the EUROSTAT series +* proj_19rp3 (2019-2060, baseline projection): +* https://ec.europa.eu/eurostat/web/population-demography/population-projections/database +* +* Population projections are provided at the NUTS3 level and +* aggregated to NUTS1. To ensure continuity between the +* observed and projected series, the projected population is +* rebased to the final observed year (2025) using a +* Gender-Age-Region specific scaling factor. This preserves +* the projected growth path while removing the discontinuity +* at the join. +*******************************************************************************/ +clear all +set more off +capture log close + + +*************************** Population Projections ***************************** + +* Import data +import delimited using "$dir_input_data/estat_proj_19rp3.tsv", /// + delimiters(tab) clear +// Data at NUTS3 level + +* Rename variables +forvalues i = 1/83 { + + local y = 2017 + `i' + rename v`i' y`y' + +} + +drop y2061-y2100 +rename y2018 freq_proj_age_sex_unit_geo + +drop if freq == "freq,projection,age,sex,unit,geo\TIME_PERIOD" +drop if strpos(freq, "TOTAL") > 0 + +* Select country of interest +keep if substr(freq, -5, 2) == "${country}" + +* Keep baseline projections +keep if substr(freq, 3, 3) == "BSL" + +* Gender +gen Gender = 0 if substr(freq, -11, 1) == "F" +replace Gender = 1 if substr(freq, -11, 1) == "M" +drop if Gender == . + +label def Gender 0 "Female" 1 "Male" +label values Gender Gender + +order Gender + +* Age +gen Age = substr(freq,8,2) if substr(freq,9,1) != "," +replace Age = substr(freq,8,1) if Age == "" +replace Age = "0" if substr(freq,9,3) == "LT1" +replace Age = "100" if substr(freq,9,5) == "GE100" +destring Age, replace + +order Gender Age + +* NUTS 3 region +gen region_NUTS3 = substr(freq,-5,5) + +order Gender Age region + +* NUTS 1 +gen Region = 1 if substr(region_NUTS3,3,1) == "1" +replace Region = 2 if substr(region_NUTS3,3,1) == "2" +replace Region = 3 if substr(region_NUTS3,3,1) == "3" +replace Region = 4 if substr(region_NUTS3,3,1) == "4" +replace Region = 5 if substr(region_NUTS3,3,1) == "5" +replace Region = 6 if substr(region_NUTS3,3,1) == "6" +replace Region = 7 if substr(region_NUTS3,3,1) == "7" + +label def Region 1 "ES1" 2 "ES2" 3 "ES3" 4 "ES4" 5 "ES5" 6 "ES6" 7 "ES7" +label values Region Region + +order Gender Age region Region + +drop freq region_NUTS3 + +* Aggregate NUTS3 projections to NUTS1 +collapse (sum) y2019-y2060, by(Gender Age Region) + +* Check that Gender-Age-Region uniquely identifies observations +isid Gender Age Region +assert _N == 2 * 101 * 7 + +* Preserve projected 2025 separately for subsequent rebasing +rename y2025 y2025_proj + +save "$dir_data/population_projections", replace + + +************************* Historic Population Data ***************************** + +* Import data +import delimited using "$dir_input_data/estat_demo_r_d2jan.tsv", /// + delimiters(tab) clear +// Data at NUTS1 and NUTS2 level + +* Rename variables +forvalues i = 1/40 { + + local y = 1988 + `i' + capture rename v`i' y`y' + +} + +rename y1989 freq_unit_age_geo + +drop if freq == "freq,unit,sex,age,geo\TIME_PERIOD" + +* Keep Spain national total and NUTS1 regions +keep if substr(freq, -3, 3) == "${country}1" | /// + substr(freq, -3, 3) == "${country}2" | /// + substr(freq, -3, 3) == "${country}3" | /// + substr(freq, -3, 3) == "${country}4" | /// + substr(freq, -3, 3) == "${country}5" | /// + substr(freq, -3, 3) == "${country}6" | /// + substr(freq, -3, 3) == "${country}7" | /// + substr(freq, -2, 2) == "${country}" + +* Gender +gen Gender = 0 if substr(freq, 6, 1) == "F" +replace Gender = 1 if substr(freq, 6, 1) == "M" +drop if missing(Gender) + +label def Gender 0 "Female" 1 "Male" +label val Gender Gender +order Gender + +* Age +gen Age = substr(freq,9,2) if substr(freq,11,1) == "," +replace Age = substr(freq,9,1) if Age == "" +replace Age = "0" if substr(freq,10,3) == "LT1" +replace Age = "100" if substr(freq,10,4) == "OPEN" + +drop if inlist(Age, "O", "_", "NK") + +destring Age, replace + +order Gender Age + +* Construct NUTS1 region; national Spain observations remain missing +gen Region = 1 if substr(freq,-1,1) == "1" +replace Region = 2 if substr(freq,-1,1) == "2" +replace Region = 3 if substr(freq,-1,1) == "3" +replace Region = 4 if substr(freq,-1,1) == "4" +replace Region = 5 if substr(freq,-1,1) == "5" +replace Region = 6 if substr(freq,-1,1) == "6" +replace Region = 7 if substr(freq,-1,1) == "7" + +label def Region 1 "ES1" 2 "ES2" 3 "ES3" 4 "ES4" 5 "ES5" 6 "ES6" 7 "ES7" +label values Region Region + +order Gender Age Region + +* Convert population figures to numeric +sort Region Gender Age + +forvalues i = 1990/2025 { + + replace y`i' = "" if y`i' == ": " + destring y`i', replace + +} + +* Drop national Spain observations; retain NUTS1 regions only +drop if missing(Region) + +* Check structure of historical population data +assert inlist(Gender, 0, 1) +assert inrange(Age, 0, 100) +assert inrange(Region, 1, 7) + +isid Gender Age Region +assert _N == 2 * 101 * 7 + +sort Gender Age Region + + +******************************* Combine Series ********************************* + +* Drop national Spain observations; retain NUTS1 regions only +drop if missing(Region) + +* Confirm one observation per Gender-Age-Region cell +isid Gender Age Region + +* Merge in population projections +merge 1:1 Gender Age Region using "$dir_data/population_projections" + +* Check merge +tab _merge +assert _merge == 3 +drop _merge + + +* Keep relevant historical period +drop y1990-y2010 + +* Organize dataset +order Gender Region Age +sort Gender Region Age + + +********************************* Rebase *************************************** + +/* +Rebase projected population to the final observed year (2025). A separate +scaling factor is calculated for each Gender-Age-Region cell and applied to +all projected years from 2026 onwards. This preserves the projected growth +path within each cell while removing the discontinuity at the join. +*/ + +assert y2025_proj > 0 +gen factor = y2025 / y2025_proj + +* Check scaling factors +summ factor, detail +count if missing(factor) + +assert r(N) == 0 + +* Inspect unusually large adjustments +list Gender Region Age y2025 y2025_proj factor /// + if factor < 0.8 | factor > 1.2 + +table Region, statistic(mean factor) + +* Apply cell-specific scaling factor to projections +forvalues year = 2026/2060 { + + replace y`year' = round(y`year' * factor) + +} + +drop factor y2025_proj freq_unit_age_geo + + +******************************* Export to Excel ******************************** + +export excel using "$dir_work/align_popProjections${country}.xlsx", /// + firstrow(var) sheet("${country}") replace + +* Replace Excel column headings with calendar years +putexcel set "$dir_work/align_popProjections${country}.xlsx", /// + sheet("${country}") modify + +local col = 4 // A=Gender, B=Region, C=Age, D=2011 + +forvalues year = 2011/2060 { + + local c = `col' + local letter "" + + while (`c' > 0) { + local rem = mod(`c'-1,26) + local letter = char(`rem'+65) + "`letter'" + local c = floor((`c'-1)/26) + } + + putexcel `letter'1 = (`year') + + local ++col +} + + +putexcel set "$dir_work/align_popProjections${country}.xlsx", /// + sheet("info") modify + +putexcel A1 = "Purpose:" /// + B1 = "This file stores the observed and projected population series used by SimPaths for population alignment." + +putexcel A2 = "Developers:" /// + B2 = "Spain series prepared by Ashley Burdett (AB)." + +putexcel A3 = "First version:" /// + B3 = "10/08/2026 (AB)" + +putexcel A4 = "Last version:" /// + B4 = "10/08/2026 (AB)" + +putexcel A5 = "Created using:" /// + B5 = "Do-file population_projections.do contained in time_series/ES/do_files." + +putexcel A6 = "Source - observed population:" /// + B6 = "Eurostat population on 1 January by age, sex and NUTS 2 region (demo_r_d2jan). Observed values for 2011-2025 are used." + +putexcel A7 = "Source - projected population:" /// + B7 = "Eurostat regional population projections (proj_19rp3). Baseline population projections are used." + +putexcel A8 = "Coverage in current workbook:" /// + B8 = "Observed population: 2011-2025; projected population: 2026-2060 after rebasing." + +putexcel A9 = "Dimensions:" /// + B9 = "Population values are provided by gender, age, region and year." + +putexcel A10 = "Projection scenario:" /// + B10 = "The Eurostat baseline population projection scenario is used." + +putexcel A11 = "Rebasing method:" /// + B11 = "Projected population is rebased to the final observed year (2025). A separate scaling factor is calculated for each gender-age-region cell as the ratio of observed to projected population in 2025. This factor is applied to projected values from 2026 onwards, removing the discontinuity at the join while preserving the projected growth path within each cell." + +putexcel A12 = "NOTE:" /// + B12 = "All cells read by the model should contain plain numeric year/value data only. Remove formulas, merged headers and non-numeric values from active sheets." diff --git a/input_processing/time_series/ES/do_files/real_wage_growth.do b/input_processing/time_series/ES/do_files/real_wage_growth.do new file mode 100644 index 0000000..2541cb6 --- /dev/null +++ b/input_processing/time_series/ES/do_files/real_wage_growth.do @@ -0,0 +1,110 @@ +******************************************************************************** +* PROJECT: SimPaths EU +* SECTION: Additional series +* OBJECT: Wage growth +* AUTHORS: Ashley Burdett +* LAST UPDATE: 17/02/25 +* COUNTRY: Poland + +* NOTES: Use the wage information from the EU-SILC panel data set. +* Some calculations aplied to the Excel files after populated +* +* Useful break down of the codes in the dataset: +* https://ec.europa.eu/eurostat/databrowser/explore/all/popul?sort=category&lang=en&subtheme=labour.lc.lci&display=list + +******************************************************************************** +clear all +set more off +capture log close + + +* Import data +import delimited using "$dir_input_data/estat_lc_lci_r2_a.tsv", /// + delimiters(tab) clear + +* Organize data +forvalues i = 1/31 { + + local y = 1994 + `i' + capture rename v`i' y`y' + +} + +rename y1995 freq_unit_nace_r2_lcstruct_geo + +drop if freq == "freq,unit,nace_r2,lcstruct,geo\TIME_PERIOD" + + +* Select series + +keep if substr(freq, -2, 2) == "${country}" + +* 2020 index +keep if substr(freq, 3, 3) == "I20" + +* Industry, construction and services +* (except activities of households as employers and extra-territorial +* organisations and bodies) +keep if substr(freq, 7, 3) == "B-S" + +* Wages and salaries (total) +keep if substr(freq, 11, 3) == "D11" + +drop freq + +* Convert values to numeric +forvalues i = 1996/2025 { + + replace y`i' = word(y`i', 1) + replace y`i' = "" if y`i' == ":" + destring y`i', replace +} + +drop y1996-y1999 + +* Transpose +xpose, clear + +gen Year = 1999 +replace Year = Year[_n-1] + 1 if Year[_n-1] != . + +drop if Year == 1999 + +rename v1 LCI +order Year LCI + +* Rebase the index to 2015 +summ LCI if Year == 2015, meanonly +local base = r(mean) + +replace LCI = (LCI / `base') * 100 + +* Deflate +merge 1:1 Year using "$dir_work/data/inflation" +drop if _m != 3 +drop _m + +gen r_LCI = . +replace r_LCI = (LCI / CPI) * 100 + +* Create growth rate +* Construct annual growth in real LCI (%) +sort Year + +gen r_LCI_growth = 100 * (r_LCI / r_LCI[_n-1] - 1) /// + if Year == Year[_n-1] + 1 + + +rename r_LCI Value +rename r_LCI_growth Growth +drop LCI CPI + +order Year Value Growth + +******************************* Export to Excel ******************************** + +export excel Year Value Growth using /// + "$dir_work/time_series_factor${country}.xlsx", sheet("wage_growth") /// + sheetmodify firstrow(variables) + +