{ "cells": [ { "cell_type": "markdown", "id": "c2830bc1", "metadata": {}, "source": [ "# Health Professions Associate's Geography Flows - PSEO Analysis\n", "\n", "This notebook downloads, loads, filters, and analyzes data from the Census\n", "Bureau's **Post-Secondary Employment Outcomes (PSEO) Employment Flows**\n", "dataset to examine **where** Associate's-level graduates in **Health\n", "Professions and Related Programs** work after graduation, relative\n", "to a pooled **\"Other Associate's Programs\"** baseline and a small set of\n", "comparison programs chosen to contrast licensure/reciprocity regimes.\n", "\n", "**Research questions:**\n", "\n", "- **RQ1:** Nationally, does the in-state retention rate for Health Professions grads\n", " decline, hold steady, or something else across Year 1 → Year 5 → Year 10?\n", " How does that trajectory compare to Other Associate's Programs?\n", "- **RQ2:** Same trajectory question, but Health Professions vs. three comparison programs\n", " chosen to vary by licensure status and interstate portability:\n", " - Education (CIP 13) — licensed, low reciprocity\n", " - Business, Mgmt & Marketing (CIP 52) — unlicensed, fully portable\n", " - Computer & Info Sciences (CIP 11) — unlicensed, portable/remote-friendly\n", "- **RQ3:** At Year 1, what's the full 3-way geographic split (in-state /\n", " same division-different state / different division) for Health Professions vs. the\n", " other groups above?\n", "- **RQ4:** Across all states, what's each state's own in-state retention\n", " rate for Health Professions grads? Where does the anchor state (default: Colorado)\n", " rank, and is there a state worth naming as a benchmark/competitor?\n", "- **RQ5:** Zooming into the anchor state's own graduates — what does the\n", " 3-way geographic split look like rendered on real state/division geography?\n", "\n", "**Degree scope:** Associate's degrees only (`degree_level == '03'`) by default.\n", "\n", "**Data source:** U.S. Census Bureau, PSEO Employment Flows (comprehensive,\n", "all-institutions release).\n", "Documentation: https://lehd.ces.census.gov/data/pseo_experimental.html\n", "Code samples reference: https://lehd.ces.census.gov/data/lehd-code-samples/sections/pseo.html\n", "\n", "**Reusability note:** every function below takes its target CIP code /\n", "degree level / anchor state as an argument rather than hard-coding Health Professions /\n", "Colorado, so this notebook can be pointed at a different program or state by\n", "changing the Configuration cell only.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "873d15df", "metadata": {}, "outputs": [], "source": [ "import os\n", "import gzip\n", "import shutil\n", "import urllib.request\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import matplotlib.ticker as mticker\n" ] }, { "cell_type": "markdown", "id": "414ea471", "metadata": {}, "source": [ "## Configuration\n", "\n", "Defined here at the top so every downstream cell can reference these values\n", "without running into undefined-variable errors. Change these to repoint the\n", "whole notebook at a different program, degree level, or anchor state.\n", "\n", "- `TARGET_CIP` / `TARGET_LABEL` — the 2-digit CIP code being analyzed and its display name\n", "- `OTHER_LABEL` — display name for the pooled \"Other Associate's Programs\" baseline\n", "- `DEGREE_LEVEL` — degree level filter (`'03'` = Associate's)\n", "- `ANCHOR_STATE` / `ANCHOR_STATE_INSTITUTION_FIPS` — the case-study state (postal abbreviation and its FIPS code)\n", "- `COMPARISON_CIPS` — the small set of contrast programs for RQ2/RQ3, chosen to vary by licensure status and interstate portability\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "66fd4463", "metadata": {}, "outputs": [], "source": [ "TARGET_CIP = \"51\"\n", "TARGET_LABEL = \"Health Professions\"\n", "OTHER_LABEL = \"Other Associate's Programs\"\n", "\n", "DEGREE_LEVEL = \"03\" # Associate's degrees\n", "DEGREE_LABEL = \"Associate's\"\n", "\n", "ANCHOR_STATE = \"CO\" # 2-letter postal abbreviation\n", "ANCHOR_STATE_INSTITUTION_FIPS = \"08\" # Census/FIPS-style state code used in\n", " # the PSEO \"all institutions in state\"\n", " # rollup rows (inst_level == 'S')\n", "\n", "# Comparison programs, chosen to vary along two axes: whether the field is\n", "# licensed, and whether that license/credential travels easily across state\n", "# lines. This is meant to be a small, readable set for a live webinar, not a\n", "# comprehensive survey of all CIP codes.\n", "COMPARISON_CIPS = {\n", " \"13\": \"Education\",\n", " \"52\": \"Business, Mgmt & Marketing\",\n", " \"11\": \"Computer & Info Sciences\",\n", "}\n", "\n", "YEARS = (\"y1\", \"y5\", \"y10\")\n", "YEAR_LABELS = {\"y1\": \"Year 1\", \"y5\": \"Year 5\", \"y10\": \"Year 10\"}\n", "\n", "DATA_DIR = \"data\"\n", "FIGURES_DIR = \"figures\"\n", "os.makedirs(DATA_DIR, exist_ok=True)\n", "os.makedirs(FIGURES_DIR, exist_ok=True)\n" ] }, { "cell_type": "markdown", "id": "456177b8", "metadata": {}, "source": [ "## Geographic reference data\n", "\n", "Standard Census Bureau region/division definitions. These are stable,\n", "published groupings (not something that comes bundled inside the PSEO flows\n", "file itself), so they're hard-coded here as reference data. See:\n", "https://www2.census.gov/geo/pdfs/maps-data/maps/reference/us_regdiv.pdf\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "248f7f0f", "metadata": {}, "outputs": [], "source": [ "DIVISION_NAMES = {\n", " 1: \"New England\",\n", " 2: \"Middle Atlantic\",\n", " 3: \"East North Central\",\n", " 4: \"West North Central\",\n", " 5: \"South Atlantic\",\n", " 6: \"East South Central\",\n", " 7: \"West South Central\",\n", " 8: \"Mountain\",\n", " 9: \"Pacific\",\n", "}\n", "\n", "STATE_TO_DIVISION = {\n", " # New England\n", " \"CT\": 1, \"ME\": 1, \"MA\": 1, \"NH\": 1, \"RI\": 1, \"VT\": 1,\n", " # Middle Atlantic\n", " \"NJ\": 2, \"NY\": 2, \"PA\": 2,\n", " # East North Central\n", " \"IL\": 3, \"IN\": 3, \"MI\": 3, \"OH\": 3, \"WI\": 3,\n", " # West North Central\n", " \"IA\": 4, \"KS\": 4, \"MN\": 4, \"MO\": 4, \"NE\": 4, \"ND\": 4, \"SD\": 4,\n", " # South Atlantic\n", " \"DE\": 5, \"FL\": 5, \"GA\": 5, \"MD\": 5, \"NC\": 5, \"SC\": 5, \"VA\": 5,\n", " \"WV\": 5, \"DC\": 5,\n", " # East South Central\n", " \"AL\": 6, \"KY\": 6, \"MS\": 6, \"TN\": 6,\n", " # West South Central\n", " \"AR\": 7, \"LA\": 7, \"OK\": 7, \"TX\": 7,\n", " # Mountain\n", " \"AZ\": 8, \"CO\": 8, \"ID\": 8, \"MT\": 8, \"NV\": 8, \"NM\": 8, \"UT\": 8, \"WY\": 8,\n", " # Pacific\n", " \"AK\": 9, \"CA\": 9, \"HI\": 9, \"OR\": 9, \"WA\": 9,\n", "}\n", "\n", "STATE_NAMES = {\n", " 'AL': 'Alabama', 'AK': 'Alaska', 'AZ': 'Arizona', 'AR': 'Arkansas',\n", " 'CA': 'California', 'CO': 'Colorado', 'CT': 'Connecticut', 'DE': 'Delaware',\n", " 'FL': 'Florida', 'GA': 'Georgia', 'HI': 'Hawaii', 'ID': 'Idaho',\n", " 'IL': 'Illinois', 'IN': 'Indiana', 'IA': 'Iowa', 'KS': 'Kansas',\n", " 'KY': 'Kentucky', 'LA': 'Louisiana', 'ME': 'Maine', 'MD': 'Maryland',\n", " 'MA': 'Massachusetts', 'MI': 'Michigan', 'MN': 'Minnesota', 'MS': 'Mississippi',\n", " 'MO': 'Missouri', 'MT': 'Montana', 'NE': 'Nebraska', 'NV': 'Nevada',\n", " 'NH': 'New Hampshire', 'NJ': 'New Jersey', 'NM': 'New Mexico', 'NY': 'New York',\n", " 'NC': 'North Carolina', 'ND': 'North Dakota', 'OH': 'Ohio', 'OK': 'Oklahoma',\n", " 'OR': 'Oregon', 'PA': 'Pennsylvania', 'RI': 'Rhode Island', 'SC': 'South Carolina',\n", " 'SD': 'South Dakota', 'TN': 'Tennessee', 'TX': 'Texas', 'UT': 'Utah',\n", " 'VT': 'Vermont', 'VA': 'Virginia', 'WA': 'Washington', 'WV': 'West Virginia',\n", " 'WI': 'Wisconsin', 'WY': 'Wyoming', 'DC': 'District of Columbia',\n", "}\n", "\n", "ANCHOR_DIVISION = STATE_TO_DIVISION[ANCHOR_STATE]\n", "ANCHOR_DIVISION_LABEL = DIVISION_NAMES[ANCHOR_DIVISION]\n", "\n", "# All CIP codes we need labels/colors for, in one place, so plotting\n", "# functions can share a consistent palette across every chart.\n", "ALL_GROUPS = {TARGET_CIP: TARGET_LABEL, **COMPARISON_CIPS}\n", "GROUP_COLORS = {\n", " TARGET_CIP: \"#C44E52\", # red -- Health Professions, always the throughline\n", " \"OTHER\": \"#4C72B0\", # blue -- pooled Other Programs baseline\n", " \"13\": \"#DD8452\", # orange -- Education\n", " \"52\": \"#55A868\", # green -- Business\n", " \"11\": \"#8172B2\", # purple -- CIS\n", "}\n" ] }, { "cell_type": "markdown", "id": "d4c62631", "metadata": {}, "source": [ "## 1. Download the PSEO data files\n", "\n", "Three files are needed:\n", "\n", "1. **`pseof_all.csv.gz`** -- the comprehensive PSEO Employment Flows file,\n", " covering every participating institution, degree level, CIP code, and\n", " graduation cohort, crossed with industry and geography of employment.\n", " This is downloaded and unzipped.\n", "2. **`pseo_all_institutions.csv`** -- a small lookup file mapping each\n", " institution code to its name and state, used later to merge in\n", " institution labels.\n", "3. **`label_cipcode.csv`** -- CIP code labels (2-digit level only -- the\n", " flows file, unlike the earnings file, does not include 4-digit CIP\n", " detail).\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "fe84604b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found existing data\\pseof_all.csv, skipping download.\n", "Found existing data\\pseo_all_institutions.csv, skipping download.\n", "Found existing data\\label_cipcode.csv, skipping download.\n" ] }, { "data": { "text/plain": [ "'data\\\\label_cipcode.csv'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def download_and_cache(url, dest_path, description):\n", " \"\"\"Download a file to dest_path if it doesn't already exist locally.\"\"\"\n", " if os.path.exists(dest_path):\n", " print(f\"Found existing {dest_path}, skipping download.\")\n", " return dest_path\n", " print(f\"Downloading {description}...\")\n", " urllib.request.urlretrieve(url, dest_path)\n", " print(f\"Downloaded to {dest_path}\")\n", " return dest_path\n", "\n", "\n", "def download_and_unzip_gz(url, gz_path, csv_path, description):\n", " if os.path.exists(csv_path):\n", " print(f\"Found existing {csv_path}, skipping download.\")\n", " return csv_path\n", " print(f\"Downloading {description} (gzipped)...\")\n", " urllib.request.urlretrieve(url, gz_path)\n", " print(\"Download complete. Unzipping...\")\n", " with gzip.open(gz_path, \"rb\") as f_in, open(csv_path, \"wb\") as f_out:\n", " shutil.copyfileobj(f_in, f_out)\n", " print(f\"Unzipped to {csv_path}\")\n", " return csv_path\n", "\n", "\n", "FLOWS_GZ_URL = \"https://lehd.ces.census.gov/data/pseo/latest_release/all/pseof_all.csv.gz\"\n", "INSTITUTIONS_URL = \"https://lehd.ces.census.gov/data/pseo/latest_release/all/pseo_all_institutions.csv\"\n", "CIP_LABELS_URL = \"https://lehd.ces.census.gov/data/schema/latest/label_cipcode.csv\"\n", "\n", "FLOWS_GZ_PATH = os.path.join(DATA_DIR, \"pseof_all.csv.gz\")\n", "FLOWS_CSV_PATH = os.path.join(DATA_DIR, \"pseof_all.csv\")\n", "INSTITUTIONS_PATH = os.path.join(DATA_DIR, \"pseo_all_institutions.csv\")\n", "CIP_LABELS_PATH = os.path.join(DATA_DIR, \"label_cipcode.csv\")\n", "\n", "download_and_unzip_gz(\n", " FLOWS_GZ_URL, FLOWS_GZ_PATH, FLOWS_CSV_PATH,\n", " \"PSEO employment flows file (~several MB compressed)\"\n", ")\n", "download_and_cache(INSTITUTIONS_URL, INSTITUTIONS_PATH, \"PSEO institutions lookup file\")\n", "download_and_cache(CIP_LABELS_URL, CIP_LABELS_PATH, \"CIP code label file\")\n" ] }, { "cell_type": "markdown", "id": "9f6fea8a", "metadata": {}, "source": [ "## 2. Load the data" ] }, { "cell_type": "code", "execution_count": 10, "id": "9b8f5796", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loaded flows file: 42,162,750 rows, 30 columns\n", "Loaded institutions file: 952 rows\n", "Loaded CIP label file: 2,737 rows\n" ] }, { "data": { "text/html": [ "
| \n", " | institution | \n", "label | \n", "institution_state | \n", "statefips | \n", "
|---|---|---|---|---|
| 0 | \n", "00105100 | \n", "University of Alabama | \n", "AL | \n", "01 | \n", "
| 1 | \n", "00105200 | \n", "University of Alabama at Birmingham | \n", "AL | \n", "01 | \n", "
| 2 | \n", "00105500 | \n", "University of Alabama in Huntsville | \n", "AL | \n", "01 | \n", "
| 3 | \n", "00108100 | \n", "Arizona State University | \n", "AZ | \n", "04 | \n", "
| 4 | \n", "00108200 | \n", "Northern Arizona University | \n", "AZ | \n", "04 | \n", "
| \n", " | agg_level_pseo | \n", "inst_level | \n", "institution | \n", "degree_level | \n", "cip_level | \n", "cipcode | \n", "grad_cohort | \n", "grad_cohort_years | \n", "geo_level | \n", "geography | \n", "... | \n", "status_y5_grads_emp | \n", "status_y5_grads_emp_instate | \n", "status_y10_grads_emp | \n", "status_y10_grads_emp_instate | \n", "status_y1_grads_nme | \n", "status_y5_grads_nme | \n", "status_y10_grads_nme | \n", "label | \n", "institution_state | \n", "statefips | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "40 | \n", "I | \n", "00134500 | \n", "03 | \n", "2 | \n", "24 | \n", "0000 | \n", "5 | \n", "N | \n", "00 | \n", "... | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "Adams State University | \n", "CO | \n", "08 | \n", "
| 1 | \n", "40 | \n", "I | \n", "00134600 | \n", "03 | \n", "2 | \n", "01 | \n", "0000 | \n", "5 | \n", "N | \n", "00 | \n", "... | \n", "5 | \n", "5 | \n", "5 | \n", "5 | \n", "5 | \n", "5 | \n", "5 | \n", "Arapahoe Community College | \n", "CO | \n", "08 | \n", "
| 2 | \n", "40 | \n", "I | \n", "00134600 | \n", "03 | \n", "2 | \n", "09 | \n", "0000 | \n", "5 | \n", "N | \n", "00 | \n", "... | \n", "5 | \n", "5 | \n", "-1 | \n", "-1 | \n", "5 | \n", "5 | \n", "-1 | \n", "Arapahoe Community College | \n", "CO | \n", "08 | \n", "
| 3 | \n", "40 | \n", "I | \n", "00134600 | \n", "03 | \n", "2 | \n", "11 | \n", "0000 | \n", "5 | \n", "N | \n", "00 | \n", "... | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "Arapahoe Community College | \n", "CO | \n", "08 | \n", "
| 4 | \n", "40 | \n", "I | \n", "00134600 | \n", "03 | \n", "2 | \n", "12 | \n", "0000 | \n", "5 | \n", "N | \n", "00 | \n", "... | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "1 | \n", "Arapahoe Community College | \n", "CO | \n", "08 | \n", "
5 rows × 33 columns
\n", "