bolster.data_sources.nisra.labour_market ======================================== .. py:module:: bolster.data_sources.nisra.labour_market .. autoapi-nested-parse:: NISRA Labour Market Statistics Module. This module provides access to Northern Ireland Statistics and Research Agency (NISRA) Labour Force Survey (LFS) data. The Labour Force Survey is a sample survey of households providing information on the labour force using internationally agreed concepts and definitions. It provides estimates of employment, unemployment, and economic inactivity. Data is published quarterly covering 3-month rolling periods (e.g., "July to September 2025"). Some tables (particularly Local Government District breakdowns) are published annually only. Data Source: **Mother Page**: https://www.nisra.gov.uk/statistics/labour-market-and-social-welfare This page lists all Labour Market and Social Welfare publications in reverse chronological order (newest first). The module automatically scrapes this page to find the latest "Quarterly Labour Force Survey Tables" publication, then downloads the Excel file from that publication's detail page. This ensures the module always retrieves the most recent data without hardcoding dates or quarters. Update Frequency: Quarterly publications covering 3-month rolling periods are released approximately 6-8 weeks after the reference period ends. Labour Force Survey data is published four times per year with additional annual publications containing Local Government District breakdowns. Data is updated as part of NISRA's regular labour market statistics programme following ONS Labour Force Survey methodology. Architecture: - Automatically discovers latest quarterly LFS publication from NISRA mother page - Downloads quarterly Labour Force Survey Excel files - Parses multiple tables including: * Employment by age band and sex (Table 2.15) * Employment by industry sector (Table 2.17) * Employment by occupation (Table 2.18) * Economic inactivity (Table 2.21) * Unemployment rates (Table 2.22) - Separately handles annual Local Government District data (Table 1.16a) published in dedicated LGD tables file (not in quarterly publications) - Returns long-format DataFrames for flexibility - Uses standardized Excel parsing utilities from _base.py Usage: >>> from bolster.data_sources.nisra import labour_market >>> # Get latest quarterly employment data >>> df = labour_market.get_latest_employment() >>> # Get economic inactivity data >>> df = labour_market.get_latest_economic_inactivity() >>> # Get all tables for a specific quarter >>> data = labour_market.get_quarterly_data(year=2025, quarter="Jul-Sep") >>> # Get annual employment by Local Government District >>> lgd_df = labour_market.get_latest_employment_by_lgd() .. rubric:: Example >>> from bolster.data_sources.nisra import labour_market >>> emp_df = labour_market.get_latest_employment() >>> 'age_group' in emp_df.columns True >>> lgd_df = labour_market.get_latest_employment_by_lgd() >>> 'employment_rate' in lgd_df.columns True Author: Claude Code Date: 2025-12-21 Attributes ---------- .. autoapisummary:: bolster.data_sources.nisra.labour_market.logger bolster.data_sources.nisra.labour_market.LFS_CACHE_DIR bolster.data_sources.nisra.labour_market.LFS_BASE_URL Functions --------- .. autoapisummary:: bolster.data_sources.nisra.labour_market.download_quarterly_lfs bolster.data_sources.nisra.labour_market.parse_employment_by_age_sex bolster.data_sources.nisra.labour_market.parse_economic_inactivity bolster.data_sources.nisra.labour_market.get_latest_lfs_publication_url bolster.data_sources.nisra.labour_market.get_latest_employment bolster.data_sources.nisra.labour_market.get_latest_economic_inactivity bolster.data_sources.nisra.labour_market.get_quarterly_data bolster.data_sources.nisra.labour_market.get_latest_lgd_employment_url bolster.data_sources.nisra.labour_market.parse_employment_by_lgd bolster.data_sources.nisra.labour_market.get_latest_employment_by_lgd bolster.data_sources.nisra.labour_market.validate_labour_market_data Module Contents --------------- .. py:data:: logger .. py:data:: LFS_CACHE_DIR .. py:data:: LFS_BASE_URL :value: 'https://www.nisra.gov.uk' .. py:function:: download_quarterly_lfs(year, quarter, force_refresh = False, cache_ttl_days = 90) Download Labour Force Survey quarterly tables Excel file. :param year: Year (e.g., 2025) :param quarter: Quarter string like "Jul-Sep", "Jan-Mar", etc. :param force_refresh: If True, always download fresh data ignoring cache :param cache_ttl_days: Cache time-to-live in days (default: 90 days) :returns: Path to the downloaded Excel file :raises NISRADataNotFoundError: If the file cannot be downloaded .. rubric:: Example >>> file_path = download_quarterly_lfs(2025, "Jul-Sep") >>> file_path.exists() True .. py:function:: parse_employment_by_age_sex(file_path) Parse Table 2.15: Employment by Age Band and Sex. Extracts employment numbers and percentages broken down by age group and sex. :param file_path: Path to the quarterly LFS Excel file :returns: - quarter_period: Quarter label (e.g., "July to September 2025") - age_group: Age band (e.g., "16 to 19", "20 to 24", "25 to 29", etc.) - sex: "Male", "Female", or "All Persons" - percentage: Percentage of employment in this age group (for this sex) - number: Absolute number employed (from Table 2.15b) :rtype: DataFrame with columns .. rubric:: Example >>> url, year, quarter = get_latest_lfs_publication_url() >>> path = download_file(url, cache_ttl_hours=90*24) >>> df = parse_employment_by_age_sex(path) >>> 'age_group' in df.columns True .. py:function:: parse_economic_inactivity(file_path) Parse Table 2.21: Economically Inactive by Sex (Time Series). Extracts economic inactivity data broken down by sex with historical time series. Table 2.21 shows data for the same quarter (e.g., Jul-Sep) across multiple years (2012-2025). :param file_path: Path to the quarterly LFS Excel file :returns: - time_period: Quarter label (e.g., "Jul to Sep 2025") - sex: "Male", "Female", or "All Persons" - economically_inactive_number: Number of people economically inactive - economic_inactivity_rate: Percentage economically inactive :rtype: DataFrame with columns .. note:: Economically inactive persons are those not in employment and not seeking work (students, retired, caring for family, long-term sick/disabled, etc.). This table provides **historical time series** for the same quarter across years, allowing year-over-year comparisons for the same seasonal period. .. rubric:: Example >>> url, year, quarter = get_latest_lfs_publication_url() >>> path = download_file(url, cache_ttl_hours=90*24) >>> df = parse_economic_inactivity(path) >>> 'economic_inactivity_rate' in df.columns True .. py:function:: get_latest_lfs_publication_url() Find the latest Labour Force Survey quarterly tables publication. Scrapes the NISRA Labour Market statistics mother page to find the most recent "Quarterly Labour Force Survey Tables" publication. :returns: Tuple of (excel_file_url, year, quarter) - excel_file_url: Full URL to the Excel file - year: Data year as string (e.g., "2025") - quarter: Quarter string (e.g., "Jul-Sep") :raises NISRADataNotFoundError: If no publication found .. rubric:: Example >>> url, year, quarter = get_latest_lfs_publication_url() >>> url.startswith('https://') True .. py:function:: get_latest_employment(force_refresh = False) Get the latest quarterly employment data by age and sex. Automatically discovers and downloads the most recent quarterly LFS publication from the NISRA website. :param force_refresh: If True, always download fresh data ignoring cache :returns: DataFrame with employment statistics by age group and sex .. rubric:: Example >>> df = get_latest_employment() >>> 'age_group' in df.columns True >>> young_adults = df[df['age_group'] == '25 to 29'] >>> len(young_adults) >= 0 True .. py:function:: get_latest_economic_inactivity(force_refresh = False) Get the latest quarterly economic inactivity data. Automatically discovers and downloads the most recent quarterly LFS publication from the NISRA website. :param force_refresh: If True, always download fresh data ignoring cache :returns: DataFrame with economic inactivity statistics by sex .. rubric:: Example >>> df = get_latest_economic_inactivity() >>> 'sex' in df.columns True .. py:function:: get_quarterly_data(year, quarter, tables = None, force_refresh = False) Get Labour Force Survey data for a specific quarter. :param year: Year (e.g., 2025) :param quarter: Quarter string like "Jul-Sep", "Jan-Mar", etc. :param tables: List of table names to parse (default: ['employment', 'economic_inactivity']) Options: 'employment', 'economic_inactivity' :param force_refresh: If True, always download fresh data ignoring cache :returns: - 'employment': Employment by age and sex (Table 2.15) - 'economic_inactivity': Economic inactivity data (Table 2.21) :rtype: Dictionary mapping table names to DataFrames .. rubric:: Example >>> data = get_quarterly_data(2025, "Jul-Sep") >>> sorted(data.keys()) ['economic_inactivity', 'employment'] >>> emp_df = data['employment'] >>> 'age_group' in emp_df.columns True .. py:function:: get_latest_lgd_employment_url() Get the URL of the latest LFS Local Government District tables publication. Uses known URL pattern to construct the file URL. The LGD tables are published annually but not listed on the main Labour Market page. :returns: Tuple of (excel_url, year) :raises NISRADataNotFoundError: If unable to find the latest publication .. rubric:: Example >>> url, year = get_latest_lgd_employment_url() >>> url.startswith('https://') True .. py:function:: parse_employment_by_lgd(file_path, year = None) Parse Table 1.16a: Employment by Local Government District (ages 16+). Extracts employment statistics for all 11 Northern Ireland LGDs from the annual LFS LGD tables file. :param file_path: Path to the LFS LGD tables Excel file :param year: Year of the data (if not provided, will be extracted from sheet name) :returns: - year: int - lgd: str (Local Government District name) - population_16plus: int (thousands) - economically_active: int (thousands) - in_employment: int (thousands) - full_time_employment: int (thousands) - part_time_employment: int (thousands) - economically_inactive: int (thousands) - economic_activity_rate: float (%) - employment_rate: float (%) :rtype: DataFrame with columns .. rubric:: Example >>> url, year = get_latest_lgd_employment_url() >>> path = download_file(url, cache_ttl_hours=180*24) >>> df = parse_employment_by_lgd(path, year=year) >>> 'employment_rate' in df.columns True .. py:function:: get_latest_employment_by_lgd(force_refresh = False) Get the latest Employment by Local Government District data. Downloads and parses the most recent annual LFS LGD tables publication. Results are cached for 180 days unless force_refresh=True. :param force_refresh: If True, bypass cache and download fresh data :returns: DataFrame with employment statistics for all 11 NI Local Government Districts .. rubric:: Example >>> df = get_latest_employment_by_lgd() >>> 'employment_rate' in df.columns True >>> len(df) > 0 True .. py:function:: validate_labour_market_data(df) Validate labour market data integrity. :param df: DataFrame from labour market functions :returns: True if validation passes, False otherwise