bolster.data_sources.nisra.ashe
NISRA Annual Survey of Hours and Earnings (ASHE) Module.
This module provides access to Northern Ireland’s earnings statistics: - Median weekly, hourly, and annual earnings - Breakdowns by employment type, sector, geography, occupation, industry - Gender pay gap analysis - Historical timeseries from 1997 to present
Data is published annually in October by NISRA’s Economic & Labour Market Statistics Branch.
Data Source: Northern Ireland Statistics and Research Agency provides Annual Survey of Hours and Earnings statistics through their Work, Pay and Benefits section at https://www.nisra.gov.uk/statistics/work-pay-and-benefits/annual-survey-hours-and-earnings. ASHE data covers employee earnings across all sectors based on a sample survey of payroll records from HMRC PAYE data, providing comprehensive earnings statistics for Northern Ireland.
Update Frequency: Annual publications released in October each year, covering earnings data for the reference period of April. The dataset provides the most comprehensive and official source of employee earnings statistics for Northern Ireland, updated once per year with historical revisions as necessary.
- Data Coverage:
Weekly Earnings: 1997 - Present (annual, full-time/part-time/all)
Hourly Earnings: 1997 - Present (annual, excluding overtime)
Annual Earnings: 1999 - Present (annual, full-time/part-time/all)
Geographic: 11 Local Government Districts (workplace vs residence basis)
Sector: Public vs Private sector comparison (2005 - Present)
Examples
>>> from bolster.data_sources.nisra import ashe
>>> # Get latest weekly earnings timeseries
>>> df = ashe.get_latest_ashe_timeseries(metric='weekly')
>>> sorted(df.columns.tolist())
['median_weekly_earnings', 'work_pattern', 'year']
>>> # Get geographic earnings by workplace
>>> df_geo = ashe.get_latest_ashe_geography(basis='workplace')
>>> 'median_weekly_earnings' in df_geo.columns
True
>>> # Get public vs private sector comparison
>>> df_sector = ashe.get_latest_ashe_sector()
>>> 'location' in df_sector.columns
True
- Publication Details:
Frequency: Annual (October publication)
Reference period: April of each year
Published by: NISRA Economic & Labour Market Statistics Branch
Contact: economicstats@nisra.gov.uk
Base: Employee jobs in Northern Ireland (not self-employed)
Attributes
Functions
Get the URL of the latest ASHE publication and its year. |
|
|
Construct URL for ASHE file based on year and file type. |
|
Parse ASHE weekly earnings timeseries. |
|
Parse ASHE hourly earnings timeseries. |
|
Parse ASHE annual earnings timeseries. |
|
Parse ASHE geographic earnings data. |
|
Parse ASHE public vs private sector earnings. |
|
Get the latest ASHE timeseries data. |
|
Get the latest ASHE geographic earnings data. |
|
Get the latest ASHE public vs private sector earnings data. |
|
Filter earnings data for a specific year. |
|
Calculate year-on-year growth rates for earnings. |
|
Parse ASHE gender pay gap timeseries (NI and UK), any publication year. |
|
Parse ASHE hourly earnings by sector and gender timeseries. |
|
Parse ASHE hourly earnings by age group and gender, latest year snapshot. |
Parse ASHE hourly earnings by occupation and gender, latest year snapshot. |
|
|
Parse ASHE hourly earnings by working pattern and gender, latest year snapshot. |
|
Parse ASHE NI vs UK full-time weekly earnings timeseries. |
|
Parse ASHE high-to-low pay ratio by UK region, latest year snapshot. |
|
Parse ASHE distribution of total weekly paid hours, NI, latest year snapshot. |
|
Parse ASHE working pattern pay gap timeseries, NI vs UK. |
|
Parse ASHE mean weekly paid hours by work pattern and gender, NI, latest year. |
|
Get ASHE gender pay gap timeseries for NI and the UK. |
|
Get ASHE hourly earnings by sector and gender timeseries for NI. |
|
Get ASHE hourly earnings by age group and gender for NI, latest year snapshot. |
|
Get ASHE hourly earnings by occupation and gender for NI, latest year snapshot. |
|
Get ASHE hourly earnings by working pattern and gender for NI, latest year snapshot. |
|
Get NI vs UK full-time weekly earnings timeseries. |
|
Get high-to-low pay ratio by UK region, latest year snapshot. |
|
Get distribution of total weekly paid hours for NI employees, latest year snapshot. |
|
Get working pattern pay gap timeseries for NI vs UK. |
|
Get mean weekly paid hours by work pattern and gender for NI, latest year snapshot. |
|
Parse ASHE Figure 2: nominal vs real weekly earnings timeseries for NI. |
|
Parse ASHE Figure 3: annual % change in weekly earnings by work pattern (NI). |
|
Parse ASHE Figure 6: real earnings index (2019=100) by sector for NI. |
|
Parse ASHE Figure 7: annual % change in weekly earnings by occupation (NI, FT). |
|
Parse ASHE Figure 8: annual % change in weekly earnings by industry (NI, FT). |
|
Parse ASHE Figure 11: proportion of low/middle/high-paid employee jobs in NI. |
Parse ASHE Figure 12: pay distribution by working pattern, age, industry, occupation. |
|
|
Get ASHE nominal vs real weekly earnings timeseries for NI (Figure 2). |
|
Get ASHE annual % change in weekly earnings by work pattern (Figure 3). |
|
Get ASHE real earnings index by sector for NI (Figure 6). |
|
Get ASHE annual % change in weekly earnings by occupation, NI (Figure 7). |
|
Get ASHE annual % change in weekly earnings by industry, NI (Figure 8). |
|
Get ASHE low/middle/high-paid proportion timeseries for NI (Figure 11). |
|
Get ASHE pay distribution by classification, NI, latest year (Figure 12). |
Validate ASHE earnings data integrity. |
Module Contents
- bolster.data_sources.nisra.ashe.ASHE_BASE_URL = 'https://www.nisra.gov.uk/statistics/work-pay-and-benefits/annual-survey-hours-and-earnings'[source]
- bolster.data_sources.nisra.ashe.get_latest_ashe_publication_url()[source]
Get the URL of the latest ASHE publication and its year.
Scrapes the NISRA ASHE page to find the most recent publication.
- Returns:
Tuple of (publication_url, year)
- Raises:
NISRADataNotFoundError – If unable to find the latest publication
- Return type:
Example
>>> url, year = get_latest_ashe_publication_url() >>> url.startswith('https://') True
- bolster.data_sources.nisra.ashe.get_ashe_file_url(year, file_type='timeseries')[source]
Construct URL for ASHE file based on year and file type.
- Parameters:
- Returns:
URL to the Excel file
- Return type:
Example
>>> url = get_ashe_file_url(2025, 'timeseries') >>> url.startswith('https://') True
- bolster.data_sources.nisra.ashe.parse_ashe_timeseries_weekly(file_path)[source]
Parse ASHE weekly earnings timeseries.
Extracts the weekly earnings data from the timeseries Excel file.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE timeseries Excel file
- Returns:
year: int
work_pattern: str (‘Full-time’, ‘Part-time’, ‘All’)
median_weekly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'timeseries'), cache_ttl_hours=90*24) >>> df = parse_ashe_timeseries_weekly(path) >>> sorted(df.columns.tolist()) ['median_weekly_earnings', 'work_pattern', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_timeseries_hourly(file_path)[source]
Parse ASHE hourly earnings timeseries.
Extracts the hourly earnings data (excluding overtime) from the timeseries Excel file.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE timeseries Excel file
- Returns:
year: int
work_pattern: str (‘Full-time’, ‘Part-time’, ‘All’)
median_hourly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'timeseries'), cache_ttl_hours=90*24) >>> df = parse_ashe_timeseries_hourly(path) >>> sorted(df.columns.tolist()) ['median_hourly_earnings', 'work_pattern', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_timeseries_annual(file_path)[source]
Parse ASHE annual earnings timeseries.
Extracts the annual earnings data from the timeseries Excel file.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE timeseries Excel file
- Returns:
year: int
work_pattern: str (‘Full-time’, ‘Part-time’, ‘All’)
median_annual_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'timeseries'), cache_ttl_hours=90*24) >>> df = parse_ashe_timeseries_annual(path) >>> sorted(df.columns.tolist()) ['median_annual_earnings', 'work_pattern', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_geography(file_path, basis='workplace', year=None)[source]
Parse ASHE geographic earnings data.
Extracts earnings by Local Government District from the linked tables file.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
basis (str) – ‘workplace’ (MapA) or ‘residence’ (MapB)
year (int) – Year of the data (if not provided, will be extracted from file)
- Returns:
year: int
lgd: str (Local Government District name)
basis: str (‘workplace’ or ‘residence’)
median_weekly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_geography(path, basis='workplace', year=year) >>> 'median_weekly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.parse_ashe_sector(file_path)[source]
Parse ASHE public vs private sector earnings.
Extracts public and private sector earnings timeseries from the linked tables file.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
location: str (‘Northern Ireland’ or ‘United Kingdom’)
sector: str (‘Public’ or ‘Private’)
median_weekly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_sector(path) >>> 'location' in df.columns True
- bolster.data_sources.nisra.ashe.get_latest_ashe_timeseries(metric='weekly', force_refresh=False)[source]
Get the latest ASHE timeseries data.
Downloads and parses the most recent ASHE timeseries publication. Results are cached for 90 days unless force_refresh=True.
- Parameters:
- Returns:
DataFrame with timeseries earnings data (1997-present for weekly/hourly, 1999-present for annual)
- Return type:
Example
>>> df = get_latest_ashe_timeseries(metric='weekly') >>> sorted(df.columns.tolist()) ['median_weekly_earnings', 'work_pattern', 'year']
- bolster.data_sources.nisra.ashe.get_latest_ashe_geography(basis='workplace', force_refresh=False)[source]
Get the latest ASHE geographic earnings data.
Downloads and parses the most recent ASHE linked tables publication. Results are cached for 90 days unless force_refresh=True.
- Parameters:
- Returns:
DataFrame with earnings by Local Government District
- Return type:
Example
>>> df = get_latest_ashe_geography(basis='workplace') >>> 'median_weekly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_latest_ashe_sector(force_refresh=False)[source]
Get the latest ASHE public vs private sector earnings data.
Downloads and parses the most recent ASHE linked tables publication. Results are cached for 90 days unless force_refresh=True.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
DataFrame with public and private sector earnings timeseries (2005-present)
- Return type:
Example
>>> df = get_latest_ashe_sector() >>> 'location' in df.columns True
- bolster.data_sources.nisra.ashe.get_earnings_by_year(df, year)[source]
Filter earnings data for a specific year.
- Parameters:
df (pandas.DataFrame) – ASHE DataFrame
year (int) – Year to filter for
- Returns:
DataFrame with only the specified year’s data
- Return type:
Example
>>> df = get_latest_ashe_timeseries('weekly') >>> df_2025 = get_earnings_by_year(df, 2025) >>> 'median_weekly_earnings' in df_2025.columns True
- bolster.data_sources.nisra.ashe.calculate_growth_rates(df, periods=1)[source]
Calculate year-on-year growth rates for earnings.
- Parameters:
df (pandas.DataFrame) – ASHE DataFrame with ‘year’ and earnings column
periods (int) – Number of years for comparison (default: 1 for YoY)
- Returns:
DataFrame with additional growth rate column
- Return type:
Example
>>> df = get_latest_ashe_timeseries('weekly') >>> df_growth = calculate_growth_rates(df) >>> 'earnings_yoy_growth' in df_growth.columns True
- bolster.data_sources.nisra.ashe.parse_ashe_gender_pay_gap(file_path)[source]
Parse ASHE gender pay gap timeseries (NI and UK), any publication year.
Extracts the NI and UK all-employee gender pay gap from 2005 to present. The gap is defined as the difference between male and female median hourly earnings as a percentage of male median hourly earnings (all employees, excluding overtime).
Note: methodological changes occurred in 2006, 2011 and 2021 — these are annotated in NISRA publications and should be considered when interpreting trend breaks.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
location: str (‘Northern Ireland’ or ‘United Kingdom’)
gender_pay_gap_pct: float — GPG as % of male earnings (positive = men paid more)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_gender_pay_gap(path) >>> sorted(df.columns.tolist()) ['gender_pay_gap_pct', 'location', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_hourly_earnings_by_sector_gender(file_path)[source]
Parse ASHE hourly earnings by sector and gender timeseries.
Identified by column signature [‘Year’, ‘Male public’, ‘Female public’, ‘Male private’, ‘Female private’] — stable across all publication years.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
sector: str (‘Public’ or ‘Private’)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£, excluding overtime)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_hourly_earnings_by_sector_gender(path) >>> sorted(df.columns.tolist()) ['median_hourly_earnings', 'sector', 'sex', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_hourly_earnings_by_age_gender(file_path)[source]
Parse ASHE hourly earnings by age group and gender, latest year snapshot.
Identified by column signature [‘Age group’, ‘Female’, ‘Male’] with subtitle containing ‘age’. Present in all publication years, though in different figure slots.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
age_group: str (e.g. ‘18-21’, ‘22-29’, ‘30-39’, ‘40-49’, ‘50-59’, ‘60+’)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£, excluding overtime)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_hourly_earnings_by_age_gender(path) >>> sorted(df.columns.tolist()) ['age_group', 'median_hourly_earnings', 'sex']
- bolster.data_sources.nisra.ashe.parse_ashe_hourly_earnings_by_occupation_gender(file_path)[source]
Parse ASHE hourly earnings by occupation and gender, latest year snapshot.
Identified by column signature [‘Occupation’, ‘Female’, ‘Male’] with subtitle containing ‘occupation’. Present in all publication years.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
occupation: str (SOC major group label)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£, excluding overtime)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_hourly_earnings_by_occupation_gender(path) >>> sorted(df.columns.tolist()) ['median_hourly_earnings', 'occupation', 'sex']
- bolster.data_sources.nisra.ashe.parse_ashe_hourly_earnings_by_pattern_gender(file_path)[source]
Parse ASHE hourly earnings by working pattern and gender, latest year snapshot.
Identified by column signature [‘Working pattern’, ‘Female’, ‘Male’] with subtitle containing ‘working pattern’ (excluding pay gap / hours tables which share similar columns). Present in all publication years.
Note: part-time females earn more per hour than part-time males in NI — a reversal of the full-time pattern, documented across 2022–2025.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
work_pattern: str (‘Full-time’, ‘Part-time’, ‘All Employees’)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£, excluding overtime)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_hourly_earnings_by_pattern_gender(path) >>> sorted(df.columns.tolist()) ['median_hourly_earnings', 'sex', 'work_pattern']
- bolster.data_sources.nisra.ashe.parse_ashe_ni_uk_earnings_comparison(file_path)[source]
Parse ASHE NI vs UK full-time weekly earnings timeseries.
Identified by column signature [‘Year’, ‘UK’, ‘NI’] with subtitle containing ‘weekly’ and ‘full-time’. Stable across all publication years (always Figure 1).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
location: str (‘NI’ or ‘UK’)
median_weekly_earnings_fulltime: float (£)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_ni_uk_earnings_comparison(path) >>> sorted(df.columns.tolist()) ['location', 'median_weekly_earnings_fulltime', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_uk_regional_pay_ratio(file_path)[source]
Parse ASHE high-to-low pay ratio by UK region, latest year snapshot.
Identified by column signature [‘Region’, ‘Ratio’]. Present in all years but in different figure slots (Figure 14 in 2022, Figure 16 in 2023, Figure 13 in 2024–2025).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
region: str (UK region name)
ratio: float (high-paid / low-paid jobs ratio)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_uk_regional_pay_ratio(path) >>> sorted(df.columns.tolist()) ['ratio', 'region']
- bolster.data_sources.nisra.ashe.parse_ashe_hours_distribution(file_path)[source]
Parse ASHE distribution of total weekly paid hours, NI, latest year snapshot.
Identified by column signature [‘Paid hours worked’, ‘Percentage’]. Present in all years but in different figure slots (Figure 3 in 2022–2023, Figure 9 in 2024–2025).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
paid_hours_worked: int (hours 0–80)
percentage: float (% of employees)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_hours_distribution(path) >>> sorted(df.columns.tolist()) ['paid_hours_worked', 'percentage']
- bolster.data_sources.nisra.ashe.parse_ashe_working_pattern_pay_gap(file_path)[source]
Parse ASHE working pattern pay gap timeseries, NI vs UK.
Identified by column signature [‘Year’, ‘UK’, ‘NI’] with subtitle containing ‘working pattern pay gap’. Present from 2023 onwards (Figure 23 in 2023, Figure 19 in 2024–2025).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
location: str (‘NI’ or ‘UK’)
working_pattern_pay_gap_pct: float (%)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_working_pattern_pay_gap(path) >>> sorted(df.columns.tolist()) ['location', 'working_pattern_pay_gap_pct', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_mean_hours_by_pattern_gender(file_path)[source]
Parse ASHE mean weekly paid hours by work pattern and gender, NI, latest year.
Identified by column signature [‘Working pattern’, ‘Males’, ‘Females’, ‘All’]. Present in all years (Figure 21 in 2022–2023, Figure 20 in 2024–2025).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
work_pattern: str (‘Part-time’, ‘Full-time’, ‘All Employees’)
male_mean_hours: float
female_mean_hours: float
all_mean_hours: float
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_mean_hours_by_pattern_gender(path) >>> 'work_pattern' in df.columns True
- bolster.data_sources.nisra.ashe.get_gender_pay_gap(force_refresh=False)[source]
Get ASHE gender pay gap timeseries for NI and the UK.
Returns the population-level GPG derived from NISRA’s ASHE survey — the difference between male and female median hourly earnings as a percentage of male earnings, for all employees.
This is survey-based (HMRC PAYE sample) and covers the whole NI economy, complementing the mandatory employer-reported GPG data available via
bolster.data_sources.gender_pay_gap(which covers named employers with 250+ staff only).- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year: int (2005–present)
location: str (‘Northern Ireland’ or ‘United Kingdom’)
gender_pay_gap_pct: float
- Return type:
DataFrame with columns
Example
>>> df = get_gender_pay_gap() >>> 'gender_pay_gap_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_hourly_earnings_by_sector_gender(force_refresh=False)[source]
Get ASHE hourly earnings by sector and gender timeseries for NI.
Returns median gross hourly earnings (excl. overtime) for NI employees by public/private sector and sex, from 2005 to present.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year: int (2005–present)
sector: str (‘Public’ or ‘Private’)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> df = get_hourly_earnings_by_sector_gender() >>> 'median_hourly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_hourly_earnings_by_age_gender(force_refresh=False)[source]
Get ASHE hourly earnings by age group and gender for NI, latest year snapshot.
Returns median gross hourly earnings (excl. overtime) for NI employees by age band and sex.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
age_group: str
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> df = get_hourly_earnings_by_age_gender() >>> 'median_hourly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_hourly_earnings_by_occupation_gender(force_refresh=False)[source]
Get ASHE hourly earnings by occupation and gender for NI, latest year snapshot.
Returns median gross hourly earnings (excl. overtime) for NI employees by SOC major occupation group and sex.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
occupation: str
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> df = get_hourly_earnings_by_occupation_gender() >>> 'median_hourly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_hourly_earnings_by_pattern_gender(force_refresh=False)[source]
Get ASHE hourly earnings by working pattern and gender for NI, latest year snapshot.
Returns median gross hourly earnings (excl. overtime) for NI employees by full-time/part-time and sex.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
work_pattern: str (‘Full-time’, ‘Part-time’, ‘All Employees’)
sex: str (‘Male’ or ‘Female’)
median_hourly_earnings: float (£)
- Return type:
DataFrame with columns
Example
>>> df = get_hourly_earnings_by_pattern_gender() >>> 'median_hourly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_ni_uk_earnings_comparison(force_refresh=False)[source]
Get NI vs UK full-time weekly earnings timeseries.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year, location (‘NI’/’UK’), median_weekly_earnings_fulltime
- Return type:
DataFrame with columns
Example
>>> df = get_ni_uk_earnings_comparison() >>> 'median_weekly_earnings_fulltime' in df.columns True
- bolster.data_sources.nisra.ashe.get_uk_regional_pay_ratio(force_refresh=False)[source]
Get high-to-low pay ratio by UK region, latest year snapshot.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
region, ratio
- Return type:
DataFrame with columns
Example
>>> df = get_uk_regional_pay_ratio() >>> 'ratio' in df.columns True
- bolster.data_sources.nisra.ashe.get_hours_distribution(force_refresh=False)[source]
Get distribution of total weekly paid hours for NI employees, latest year snapshot.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
paid_hours_worked, percentage
- Return type:
DataFrame with columns
Example
>>> df = get_hours_distribution() >>> 'percentage' in df.columns True
- bolster.data_sources.nisra.ashe.get_working_pattern_pay_gap(force_refresh=False)[source]
Get working pattern pay gap timeseries for NI vs UK.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year, location (‘NI’/’UK’), working_pattern_pay_gap_pct
- Return type:
DataFrame with columns
Example
>>> df = get_working_pattern_pay_gap() >>> 'working_pattern_pay_gap_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_mean_hours_by_pattern_gender(force_refresh=False)[source]
Get mean weekly paid hours by work pattern and gender for NI, latest year snapshot.
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
work_pattern, male_mean_hours, female_mean_hours, all_mean_hours
- Return type:
DataFrame with columns
Example
>>> df = get_mean_hours_by_pattern_gender() >>> 'male_mean_hours' in df.columns True
- bolster.data_sources.nisra.ashe.parse_ashe_real_earnings(file_path)[source]
Parse ASHE Figure 2: nominal vs real weekly earnings timeseries for NI.
Identified by column signature [‘Year’, ‘Nominal earnings’, ‘Real earnings’]. Real earnings are inflation-adjusted to the latest publication year using NISRA’s deflator. The series covers full-time employees, April 2005 onwards.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
nominal_weekly_earnings: float (£, in nominal terms)
real_weekly_earnings: float (£, inflation-adjusted to latest year)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_real_earnings(path) >>> sorted(df.columns.tolist()) ['nominal_weekly_earnings', 'real_weekly_earnings', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_real_earnings_change_by_pattern(file_path)[source]
Parse ASHE Figure 3: annual % change in weekly earnings by work pattern (NI).
Snapshot for the latest reference year showing nominal vs real (inflation- adjusted) annual change for each working pattern.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
work_pattern: str (‘Part-time’, ‘Full-time’, ‘All employees’)
nominal_change_pct: float (annual % change in nominal terms)
real_change_pct: float (annual % change in real terms)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_real_earnings_change_by_pattern(path) >>> sorted(df.columns.tolist()) ['nominal_change_pct', 'real_change_pct', 'work_pattern']
- bolster.data_sources.nisra.ashe.parse_ashe_real_earnings_index_by_sector(file_path)[source]
Parse ASHE Figure 6: real earnings index (2019=100) by sector for NI.
Identified by column signature [‘Year’, ‘Public’, ‘Private’] with subtitle containing ‘annual index’. Indexed real (inflation-adjusted) median weekly earnings for full-time employees, base year = six years before the latest publication year (typically 2019 in the 2025 publication).
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
sector: str (‘Public’ or ‘Private’)
real_earnings_index: float (index, base year = 100)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_real_earnings_index_by_sector(path) >>> sorted(df.columns.tolist()) ['real_earnings_index', 'sector', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_annual_change_by_occupation(file_path)[source]
Parse ASHE Figure 7: annual % change in weekly earnings by occupation (NI, FT).
Full-time employees only. NISRA labels the column “Industry” in the source spreadsheet, but the rows are SOC major occupation groups — disambiguated from Figure 8 by subtitle keyword ‘by occupation’.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
occupation: str (SOC major group label)
annual_change_pct: float (% change in median gross weekly earnings)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_annual_change_by_occupation(path) >>> sorted(df.columns.tolist()) ['annual_change_pct', 'occupation']
- bolster.data_sources.nisra.ashe.parse_ashe_annual_change_by_industry(file_path)[source]
Parse ASHE Figure 8: annual % change in weekly earnings by industry (NI, FT).
Full-time employees by SIC industry section. Disambiguated from Figure 7 by subtitle keyword ‘by industry’.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
industry: str (SIC section label)
annual_change_pct: float (% change in median gross weekly earnings)
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_annual_change_by_industry(path) >>> sorted(df.columns.tolist()) ['annual_change_pct', 'industry']
- bolster.data_sources.nisra.ashe.parse_ashe_pay_distribution_timeseries(file_path)[source]
Parse ASHE Figure 11: proportion of low/middle/high-paid employee jobs in NI.
Identified by column signature [‘Year’, ‘Low-paid jobs’, ‘Middle-paid jobs’, ‘High-paid jobs’]. Covers April 2005 to the latest publication year.
NISRA defines pay bands relative to UK median hourly earnings: low-paid = below two-thirds, high-paid = above 1.5x, middle-paid = the rest.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
year: int
low_paid_pct: float
middle_paid_pct: float
high_paid_pct: float
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_pay_distribution_timeseries(path) >>> sorted(df.columns.tolist()) ['high_paid_pct', 'low_paid_pct', 'middle_paid_pct', 'year']
- bolster.data_sources.nisra.ashe.parse_ashe_pay_distribution_by_classification(file_path)[source]
Parse ASHE Figure 12: pay distribution by working pattern, age, industry, occupation.
Cross-sectional breakdown of low/middle/high-paid proportions for the latest reference year, with multiple classification axes stacked in a single sheet.
- Parameters:
file_path (str | pathlib.Path) – Path to the ASHE linked tables Excel file
- Returns:
classification: str (e.g. ‘Working pattern’, ‘Age group’, ‘Industry’, ‘Occupation’)
subset: str (the specific category, e.g. ‘Full-time’, ‘22-29’, ‘Education’)
low_paid_pct: float
middle_paid_pct: float
high_paid_pct: float
- Return type:
DataFrame with columns
Example
>>> _, year = get_latest_ashe_publication_url() >>> path = download_file(get_ashe_file_url(year, 'linked'), cache_ttl_hours=90*24) >>> df = parse_ashe_pay_distribution_by_classification(path) >>> sorted(df.columns.tolist()) ['classification', 'high_paid_pct', 'low_paid_pct', 'middle_paid_pct', 'subset']
- bolster.data_sources.nisra.ashe.get_real_earnings(force_refresh=False)[source]
Get ASHE nominal vs real weekly earnings timeseries for NI (Figure 2).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year, nominal_weekly_earnings, real_weekly_earnings
- Return type:
DataFrame with columns
Example
>>> df = get_real_earnings() >>> 'real_weekly_earnings' in df.columns True
- bolster.data_sources.nisra.ashe.get_real_earnings_change_by_pattern(force_refresh=False)[source]
Get ASHE annual % change in weekly earnings by work pattern (Figure 3).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
work_pattern, nominal_change_pct, real_change_pct
- Return type:
DataFrame with columns
Example
>>> df = get_real_earnings_change_by_pattern() >>> 'real_change_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_real_earnings_index_by_sector(force_refresh=False)[source]
Get ASHE real earnings index by sector for NI (Figure 6).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year, sector, real_earnings_index
- Return type:
DataFrame with columns
Example
>>> df = get_real_earnings_index_by_sector() >>> 'real_earnings_index' in df.columns True
- bolster.data_sources.nisra.ashe.get_annual_change_by_occupation(force_refresh=False)[source]
Get ASHE annual % change in weekly earnings by occupation, NI (Figure 7).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
occupation, annual_change_pct
- Return type:
DataFrame with columns
Example
>>> df = get_annual_change_by_occupation() >>> 'annual_change_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_annual_change_by_industry(force_refresh=False)[source]
Get ASHE annual % change in weekly earnings by industry, NI (Figure 8).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
industry, annual_change_pct
- Return type:
DataFrame with columns
Example
>>> df = get_annual_change_by_industry() >>> 'annual_change_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_pay_distribution_timeseries(force_refresh=False)[source]
Get ASHE low/middle/high-paid proportion timeseries for NI (Figure 11).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
year, low_paid_pct, middle_paid_pct, high_paid_pct
- Return type:
DataFrame with columns
Example
>>> df = get_pay_distribution_timeseries() >>> 'low_paid_pct' in df.columns True
- bolster.data_sources.nisra.ashe.get_pay_distribution_by_classification(force_refresh=False)[source]
Get ASHE pay distribution by classification, NI, latest year (Figure 12).
- Parameters:
force_refresh (bool) – If True, bypass cache and download fresh data
- Returns:
classification, subset, low_paid_pct, middle_paid_pct, high_paid_pct
- Return type:
DataFrame with columns
Example
>>> df = get_pay_distribution_by_classification() >>> 'classification' in df.columns True
- bolster.data_sources.nisra.ashe.validate_ashe_data(df)[source]
Validate ASHE earnings data integrity.
- Parameters:
df (pandas.DataFrame) – DataFrame from ASHE functions
- Returns:
True if validation passes, False otherwise
- Return type: