feat(stage1): audit GSHS outcome components

This commit is contained in:
Jinotech
2026-09-20 08:35:13 +12:00
parent b86ab7eacf
commit f0a4538242
8 changed files with 9856 additions and 149 deletions
@@ -0,0 +1,399 @@
"""Audit GSHS primary-outcome coverage across all selected components.
The audit is deliberately conservative. It verifies observed column relations and
records local questionnaire/codebook provenance, but it does not approve item
equivalence, missing-value recodes, population scope, or human review.
"""
from __future__ import annotations
import csv
import hashlib
import json
from collections import Counter, defaultdict
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
STAGE_DIR = ROOT / "research" / "stage1"
GSHS_ROOT = ROOT / "Dataset" / "GSHS-全球学生健康调查数据" / "GSHS"
DATA = GSHS_ROOT / "01_data" / "GSHS.csv"
COMPONENTS = GSHS_ROOT / "02_documentation" / "调查组件筛选明细.csv"
QUESTIONNAIRES = GSHS_ROOT / "Questionnaire" / "GSHS Per Country"
OUT_JSON = STAGE_DIR / "gshs_primary_outcome_component_audit.json"
OUT_MD = STAGE_DIR / "gshs_primary_outcome_component_audit.md"
RAW_VARIABLES = {
"raw_mh_considersui": {
"construct": "suicide_ideation",
"question_text": "During the past 12 months, did you seriously consider attempting suicide?",
"response_type": "binary",
"codes": {"1": "Yes", "2": "No"},
},
"raw_mh_plansui": {
"construct": "suicide_plan",
"question_text": "During the past 12 months, did you make a plan about how you would attempt suicide?",
"response_type": "binary",
"codes": {"1": "Yes", "2": "No"},
},
"raw_mh_attemptsui": {
"construct": "suicide_attempt",
"question_text": "During the past 12 months, how many times did you attempt suicide?",
"response_type": "ordinal_frequency",
"codes": {
"1": "0 times",
"2": "1 time",
"3": "2 or 3 times",
"4": "4 or 5 times",
"5": "6 or more times",
},
},
}
DERIVED_VARIABLES = {
"raw_mh_b_considersui": {
"construct": "suicide_ideation",
"source_variable": "raw_mh_considersui",
"response_type": "binary_derived_or_duplicate",
"codes": {"1": "Yes", "2": "No"},
"expected_rule": {"1": "1", "2": "2"},
},
"raw_mh_b_plansui": {
"construct": "suicide_plan",
"source_variable": "raw_mh_plansui",
"response_type": "binary_derived_or_duplicate",
"codes": {"1": "Yes", "2": "No"},
"expected_rule": {"1": "1", "2": "2"},
},
"raw_mh_b_attemptsui": {
"construct": "suicide_attempt",
"source_variable": "raw_mh_attemptsui",
"response_type": "binary_derived",
"codes": {"1": "Yes", "2": "No"},
"expected_rule": {"1": "2", "2": "1", "3": "1", "4": "1", "5": "1"},
},
}
ALL_VARIABLES = list(RAW_VARIABLES) + list(DERIVED_VARIABLES)
SOURCE_PACKAGES = {
"GHA_2022_GSHS_v01::gha_sek_tak2022": {
"question_numbers": {"ideation": "Q33", "plan": "Q34", "attempt": "Q35"},
"documents": [
("Ghana 2022/Sekondi-Takoradi-2022_GSHS_Questionnaire-Final.pdf", "questionnaire", "page 6"),
("Ghana 2022/2022_Ghana_(Sekondi-Takoradi)_Codebook.pdf", "raw codebook", "pages 7-8"),
("Ghana 2022/2022_Ghana_(Sekondi-Takoradi)_Binary_Codebook.pdf", "binary codebook", "pages 7-8"),
],
"source_language_evidence": ["English"],
},
"IND_2022_GSHS_v01::ind_jaipur_2022": {
"question_numbers": {"ideation": "Q34", "plan": "Q35", "attempt": "Q36"},
"documents": [
("India 2022/Jaipur-2022_GSHS_Questionnaire-Final.docx", "questionnaire", "Q34-Q36"),
("India 2022/2022_India_(Jaipur)_Codebook.docx", "raw codebook", "MH_CONSIDERSUI through MH_ATTEMPTSUI"),
("India 2022/2022_India_(Jaipur)_Binary_Codebook.docx", "binary codebook", "MH_B_CONSIDERSUI through MH_B_ATTEMPTSUI"),
],
"source_language_evidence": ["English"],
},
"JAM_2023_GSHS_v01::jam_st_cath_2023": {
"question_numbers": {"ideation": "Q28", "plan": "Q29", "attempt": "Q30"},
"documents": [
("Jamaica 2023/FINAL_St. Catherine-2023-GSHS-Questionnaire.docx", "questionnaire", "Q28-Q30"),
("Jamaica 2023/2023_Jamaica_(St_Catherine)_Codebook.docx", "raw codebook", "MH_CONSIDERSUI through MH_ATTEMPTSUI"),
("Jamaica 2023/2023_Jamaica_(St_Catherine)_Binary_Codebook.docx", "binary codebook", "MH_B_CONSIDERSUI through MH_B_ATTEMPTSUI"),
],
"source_language_evidence": ["English"],
},
"MNG_2023_GSHS_v01::mng2023": {
"question_numbers": {"ideation": "Q36", "plan": "Q37", "attempt": "Q38"},
"documents": [
("Mongolia 2023/Mongolia-GSHS-Questionnaire-ENGLISH.docx", "English questionnaire", "Q36-Q38"),
("Mongolia 2023/Mongolia-GSHS-Questionnaire-MONGOLIAN.docx", "Mongolian questionnaire", "Q36-Q38"),
("Mongolia 2023/2023 Mongolia Codebook.docx", "raw codebook", "MH_CONSIDERSUI through MH_ATTEMPTSUI"),
("Mongolia 2023/2023 Mongolia Binary_Codebook.docx", "binary codebook", "MH_B_CONSIDERSUI through MH_B_ATTEMPTSUI"),
],
"source_language_evidence": ["English", "Mongolian"],
},
"2023_MAR_GSHS_v01::mar_fez2023": {
"question_numbers": {"ideation": "Q32", "plan": "Q33", "attempt": "Q34"},
"documents": [
("Morocco 2023/Fez_2022_GSHS_Questionnaire_Final_FRENCH.pdf", "French questionnaire", "page 6"),
("Morocco 2023/Fez_2022_GSHS_Questionnaire_Final_ENGLISH.pdf", "English questionnaire", "page 6"),
("Morocco 2023/2023_Maroc_(Fes)_Codebook.pdf", "raw codebook", "pages 7-8"),
("Morocco 2023/2023_Maroc_(Fes)_Binary_Codebook.pdf", "binary codebook", "pages 7-8"),
],
"source_language_evidence": ["French", "English reference"],
},
"WLF_2023_GSHS_v01::wlf_2023": {
"question_numbers": {"ideation": "Q29", "plan": "Q30", "attempt": "Q31"},
"documents": [
("Wallis and Futuna 2023/Wallis and Futuna - GSHS Questionnaire - Final - French.docx", "French questionnaire", "Q29-Q31"),
("Wallis and Futuna 2023/Wallis and Futuna - GSHS Questionnaire - Final - English.docx", "English questionnaire", "Q29-Q31"),
("Wallis and Futuna 2023/2023 Wallis and Futuna Codebook.docx", "raw codebook", "MH_CONSIDERSUI through MH_ATTEMPTSUI"),
("Wallis and Futuna 2023/2023 Wallis and Futuna Binary_Codebook.docx", "binary codebook", "MH_B_CONSIDERSUI through MH_B_ATTEMPTSUI"),
],
"source_language_evidence": ["French", "English reference"],
},
}
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def read_component_metadata() -> dict[str, dict]:
with COMPONENTS.open(encoding="utf-8-sig", newline="") as handle:
rows = list(csv.DictReader(handle))
selected = {row["dataset_id"]: row for row in rows if row["include_in_master"] == "1"}
if len(selected) != 191:
raise AssertionError(f"Expected 191 selected components, found {len(selected)}")
return selected
def scan_data() -> dict:
row_counts: Counter[str] = Counter()
nonmissing: dict[str, Counter[str]] = defaultdict(Counter)
values: dict[str, dict[str, Counter[str]]] = defaultdict(lambda: defaultdict(Counter))
pairs: dict[str, Counter[tuple[str, str]]] = defaultdict(Counter)
with DATA.open(encoding="utf-8-sig", newline="") as handle:
reader = csv.reader(handle)
header = next(reader)
indices = {name: header.index(name) for name in ["dataset_id", *ALL_VARIABLES]}
for row in reader:
component = row[indices["dataset_id"]]
row_counts[component] += 1
observed = {name: row[indices[name]].strip() for name in ALL_VARIABLES}
for name, value in observed.items():
if value:
nonmissing[component][name] += 1
values[component][name][value] += 1
for derived, spec in DERIVED_VARIABLES.items():
source = spec["source_variable"]
pairs[derived][(observed[source], observed[derived])] += 1
return {
"row_counts": row_counts,
"nonmissing": nonmissing,
"values": values,
"pairs": pairs,
}
def source_package(component_id: str) -> dict:
package = SOURCE_PACKAGES.get(component_id)
if package is None:
return {
"status": "pending_local_direct_source",
"documents": [],
"question_numbers": None,
"source_language_evidence": [],
"note": "No country questionnaire or codebook for this component was found in the local source package.",
}
documents = []
for relative, role, section in package["documents"]:
path = QUESTIONNAIRES / relative
if not path.exists():
raise FileNotFoundError(path)
documents.append(
{
"source_file": str(path.relative_to(ROOT)).replace("\\", "/"),
"role": role,
"page_or_section": section,
"sha256": sha256(path),
}
)
return {
"status": "local_questionnaire_and_codebooks_located",
"documents": documents,
"question_numbers": package["question_numbers"],
"source_language_evidence": package["source_language_evidence"],
"note": "Available-language documents do not by themselves prove the language actually administered to every respondent.",
}
def serial_counter(counter: Counter) -> dict:
return {str(key): value for key, value in sorted(counter.items(), key=lambda item: str(item[0]))}
def build_audit() -> dict:
metadata = read_component_metadata()
scan = scan_data()
data_components = set(scan["row_counts"])
if data_components != set(metadata):
raise AssertionError("GSHS.csv component IDs do not match the 191 selected metadata rows")
components = []
row_count_mismatches = []
for component_id, meta in sorted(metadata.items()):
expected_rows = int(meta["row_count"])
observed_rows = scan["row_counts"][component_id]
if expected_rows != observed_rows:
row_count_mismatches.append(
{"component_id": component_id, "metadata_rows": expected_rows, "observed_rows": observed_rows}
)
variable_counts = {
name: {
"nonmissing": scan["nonmissing"][component_id][name],
"value_counts": serial_counter(scan["values"][component_id][name]),
}
for name in ALL_VARIABLES
}
has_primary = any(item["nonmissing"] for item in variable_counts.values())
components.append(
{
"component_id": component_id,
"reference_id": meta["reference_id"],
"catalog_id": meta["catalog_id"],
"country": meta["country"],
"iso3": meta["iso3"] or None,
"collection_start": meta["collection_start"],
"survey_component": meta["survey_component"],
"survey_scope": meta["survey_scope"],
"population": "school-attending students; exact sampled ages/grades remain pending component source review",
"observed_rows": observed_rows,
"canonical_relative_path": meta["canonical_relative_path"],
"catalog_url": meta["catalog_url"],
"primary_outcomes_observed": has_primary,
"variables": variable_counts,
"source_package": source_package(component_id) if has_primary else None,
"review_status": "provisional",
"reviewer_1": None,
"reviewer_2": None,
}
)
relation_checks = []
for derived, spec in DERIVED_VARIABLES.items():
observed_pairs = scan["pairs"][derived]
unexpected = []
for (raw, binary), count in observed_pairs.items():
expected = "" if raw == "" else spec["expected_rule"].get(raw)
if binary != expected:
unexpected.append({"raw": raw or None, "derived": binary or None, "count": count})
relation_checks.append(
{
"source_variable": spec["source_variable"],
"derived_variable": derived,
"expected_rule": spec["expected_rule"],
"observed_pair_counts": {
f"{raw or '<missing>'}->{binary or '<missing>'}": count
for (raw, binary), count in sorted(observed_pairs.items())
},
"unexpected_pairs": unexpected,
"check_status": "passed" if not unexpected else "failed",
}
)
available = [component for component in components if component["primary_outcomes_observed"]]
direct_sources = [
component for component in available if component["source_package"]["status"] == "local_questionnaire_and_codebooks_located"
]
return {
"schema_version": "1.0",
"generated_on": "2026-09-20",
"status": "provisional",
"purpose": "Gate 1 component-level coverage and provenance audit for GSHS suicide ideation, plan, and attempt.",
"inputs": {
"data_file": str(DATA.relative_to(ROOT)).replace("\\", "/"),
"data_sha256": sha256(DATA),
"component_registry": str(COMPONENTS.relative_to(ROOT)).replace("\\", "/"),
"component_registry_sha256": sha256(COMPONENTS),
},
"summary": {
"selected_components": len(components),
"components_with_primary_outcomes": len(available),
"components_without_primary_outcomes": len(components) - len(available),
"components_with_local_direct_sources": len(direct_sources),
"components_pending_local_direct_source": len(available) - len(direct_sources),
"observed_rows": sum(scan["row_counts"].values()),
"row_count_mismatches": len(row_count_mismatches),
},
"item_definitions": {
"administered_raw_items": RAW_VARIABLES,
"derived_or_duplicate_representations": DERIVED_VARIABLES,
"missing_rule_status": "unresolved; blank values are retained as missing and are not yet assigned a canonical missing type",
},
"relation_checks": relation_checks,
"row_count_mismatches": row_count_mismatches,
"components": components,
"review_boundary": {
"human_reviewers_required": 2,
"ai_review_is_human_review": False,
"population_scope_approved": False,
"missing_rules_approved": False,
"cross_component_equivalence_approved": False,
"confirmatory_analysis_allowed": False,
},
}
def render_markdown(audit: dict) -> str:
available = [c for c in audit["components"] if c["primary_outcomes_observed"]]
lines = [
"# GSHS primary-outcome component audit",
"",
"Version: 1.0 | Status: provisional | Generated: 2026-09-20",
"",
"## Result",
"",
f"The selected GSHS master table contains {audit['summary']['selected_components']} components and {audit['summary']['observed_rows']:,} rows. Exactly {audit['summary']['components_with_primary_outcomes']} components contain the three primary suicide outcomes; the remaining {audit['summary']['components_without_primary_outcomes']} contain no observed values in those six columns. Metadata row counts match the consolidated table for all components.",
"",
"The country questionnaires and ordinary codebooks identify three administered items: past-12-month suicide ideation, plan, and attempt frequency. The Binary Codebooks identify the `_b_` columns as binary indicators derived from those items. Row-level checks are exact: ideation and plan binary columns duplicate the raw yes/no coding, while attempt maps 0 times to No and one or more times to Yes. Therefore the `_b_` columns must not be counted as independent administered items.",
"",
"## Components with observed primary outcomes",
"",
"| Component | Country | Rows | Ideation nonmissing | Plan nonmissing | Attempt nonmissing | Direct local sources |",
"|---|---|---:|---:|---:|---:|---|",
]
for component in available:
variables = component["variables"]
source = component["source_package"]["status"]
lines.append(
f"| `{component['component_id']}` | {component['country']} | {component['observed_rows']:,} | {variables['raw_mh_considersui']['nonmissing']:,} | {variables['raw_mh_plansui']['nonmissing']:,} | {variables['raw_mh_attemptsui']['nonmissing']:,} | {source} |"
)
lines += [
"",
"## Representation checks",
"",
"| Raw item | Binary representation | Result | Observed mapping |",
"|---|---|---|---|",
]
for check in audit["relation_checks"]:
mapping = "; ".join(f"{key}: {value:,}" for key, value in check["observed_pair_counts"].items())
lines.append(
f"| `{check['source_variable']}` | `{check['derived_variable']}` | {check['check_status']} | {mapping} |"
)
lines += [
"",
"## Source boundary",
"",
f"Local questionnaires plus raw and binary codebooks were located for {audit['summary']['components_with_local_direct_sources']} of the 7 components: Ghana, India, Jamaica, Mongolia, Morocco, and Wallis and Futuna. No country-specific Solomon Islands 2023 questionnaire/codebook was found locally, so that component remains pending direct-source acquisition.",
"",
"Available English/French/Mongolian files establish wording evidence but do not by themselves prove the language administered to every respondent. Exact sampled ages/grades, component-specific eligibility, and canonical missing reasons also remain unresolved.",
"",
"## Gate 1 decision",
"",
"Gate 1 remains **in progress**. This audit resolves the 191-component coverage question and the raw-versus-`_b_` representation issue, but it does not satisfy the population/missing-rule or two-human-reviewer requirements. All records remain provisional and are not approved for confirmatory analysis or anchor construction.",
"",
"## Reproduction",
"",
"Run `research/stage1/audit_gshs_primary_outcome_components.py` in the project Python environment. The implementation streams the consolidated CSV and does not load it into pandas memory.",
"",
]
return "\n".join(lines)
def main() -> None:
audit = build_audit()
OUT_JSON.write_text(json.dumps(audit, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
OUT_MD.write_text(render_markdown(audit), encoding="utf-8")
print(json.dumps(audit["summary"], ensure_ascii=False))
if __name__ == "__main__":
main()
@@ -26,11 +26,22 @@ OUT_MD = STAGE_DIR / "primary_outcome_registry.md"
PRIMARY_CONSTRUCTS = {"suicide_ideation", "suicide_plan", "suicide_attempt"}
GSHS_CONSTRUCT = {
"raw_mh_considersui": "suicide_ideation",
"raw_mh_b_considersui": "suicide_ideation",
"raw_mh_plansui": "suicide_plan",
"raw_mh_b_plansui": "suicide_plan",
"raw_mh_attemptsui": "suicide_attempt",
"raw_mh_b_attemptsui": "suicide_attempt",
}
GSHS_DERIVED = {
"raw_mh_b_considersui": ("suicide_ideation", "raw_mh_considersui", "identity binary representation"),
"raw_mh_b_plansui": ("suicide_plan", "raw_mh_plansui", "identity binary representation"),
"raw_mh_b_attemptsui": (
"suicide_attempt",
"raw_mh_attemptsui",
"1=Yes when attempt frequency is one or more; 2=No when frequency is zero",
),
}
GSHS_QUESTION = {
"raw_mh_considersui": "During the past 12 months, did you seriously consider attempting suicide?",
"raw_mh_plansui": "During the past 12 months, did you make a plan about how you would attempt suicide?",
"raw_mh_attemptsui": "During the past 12 months, how many times did you attempt suicide?",
}
NSDUH_YOUTH_RAW = {"YUSUITHK": "suicide_ideation", "YUSUIPLN": "suicide_plan"}
NSDUH_YOUTH_DERIVED = {"YUSUITHKYR": "suicide_ideation", "YUSUIPLNYR": "suicide_plan"}
@@ -60,36 +71,56 @@ def parquet_columns(path: Path) -> set[str]:
return set(pq.ParquetFile(path).schema_arrow.names)
def build_gshs() -> list[dict]:
def build_gshs() -> tuple[list[dict], list[dict]]:
frame = pd.read_excel(GSHS_BOOK, sheet_name="Master_Columns", dtype=str, keep_default_na=False)
frame = frame[frame["column_name"].isin(GSHS_CONSTRUCT)].copy()
records = []
frame = frame[frame["column_name"].isin(set(GSHS_CONSTRUCT) | set(GSHS_DERIVED))].copy()
records: list[dict] = []
derived_records: list[dict] = []
for row in frame.sort_values("ordinal").to_dict("records"):
records.append(
{
"survey": "GSHS",
"year": None,
"source_variable": row["column_name"],
"construct": GSHS_CONSTRUCT[row["column_name"]],
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": None,
"source_label": clean_text(row["label_variants"]),
"answer_options": parse_json_object(row["value_labels_example"]),
"source_file": str(GSHS_BOOK.relative_to(ROOT)).replace("\\", "/"),
"source_url": None,
"page_or_section": "Master_Columns",
"present_in_local_data": True,
"source_verification_status": "pending_questionnaire_review",
"review_status": "provisional",
"reviewer_1": None,
"reviewer_2": None,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence.",
}
)
return records
variable = row["column_name"]
common = {
"survey": "GSHS",
"year": None,
"source_variable": variable,
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": clean_text(row["label_variants"]),
"answer_options": parse_json_object(row["value_labels_example"]),
"source_file": str(GSHS_BOOK.relative_to(ROOT)).replace("\\", "/"),
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": None,
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": True,
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": None,
"reviewer_2": None,
"link_status": "not_evaluated",
}
if variable in GSHS_CONSTRUCT:
common.update(
{
"construct": GSHS_CONSTRUCT[variable],
"record_type": "administered_raw_candidate",
"question_text": GSHS_QUESTION[variable],
"notes": "Country questionnaires and ordinary codebooks identify this as an administered item. Solomon Islands 2023 direct local source remains pending; population and missing rules are not approved.",
}
)
records.append(common)
else:
construct, source_variable, derivation_rule = GSHS_DERIVED[variable]
common.update(
{
"construct": construct,
"record_type": "derived_or_duplicate_binary_indicator",
"question_text": None,
"derived_from": source_variable,
"derivation_rule": derivation_rule,
"notes": "Binary Codebooks and exact row-level mapping show this is not an independent administered item.",
}
)
derived_records.append(common)
return records, derived_records
def build_yrbs() -> list[dict]:
@@ -235,7 +266,7 @@ def render_markdown(registry: dict) -> str:
lines = [
"# Primary outcome source registry",
"",
"Version: 1.1 | Status: provisional | Generated: 2026-09-20",
"Version: 1.2 | Status: provisional | Generated: 2026-09-20",
"",
"## Scope and decision boundary",
"",
@@ -248,7 +279,7 @@ def render_markdown(registry: dict) -> str:
]
for survey in ["GSHS", "YRBS", "NSDUH"]:
boundary = {
"GSHS": "Workbook labels/options only; component questionnaires still require direct review.",
"GSHS": "Three administered items occur in 7/191 components; local direct sources cover 6/7, with Solomon Islands pending.",
"YRBS": "51 annual candidates; 2021/2023 data remain quarantined; annual PDFs require direct review.",
"NSDUH": "Attempt is administered as YSUI03/YUSUICTRY, but the raw and recoded attempt fields are absent from the local public-use package.",
}[survey]
@@ -261,7 +292,7 @@ def render_markdown(registry: dict) -> str:
"",
"## Material conflicts and exclusions",
"",
"- GSHS exposes both `_b_` and unprefixed variants. They differ in naming and, for attempt, response form (binary versus frequency). They remain separate until questionnaire/component provenance is verified.",
"- GSHS ordinary codebooks identify the three unprefixed variables as administered items. Binary Codebooks plus exact row-level relations identify the three `_b_` columns as derived or duplicate representations, not independent items.",
"- GSHS self-harm variables are not included as suicide-attempt items.",
"- YRBS metadata contains stale absolute paths from another machine and some automatically extracted neighboring-text contamination. Only exact primary-construct rows were retained, and their direct PDF review is still pending.",
"- YRBS 2021 and 2023 item metadata are retained for future migration checks, but their respondent data are quarantined pending layout/import validation.",
@@ -271,7 +302,7 @@ def render_markdown(registry: dict) -> str:
"",
"## Gate 1 status",
"",
"Gate 1 remains **in progress**. Before any candidate can enter confirmatory analysis, the project still needs direct official questionnaire/codebook review, exact population and skip-logic verification, canonical response/missing rules, component-specific GSHS provenance, and two actual human reviewers for primary outcomes.",
"Gate 1 remains **in progress**. Before any candidate can enter confirmatory analysis, the project still needs direct YRBS and Solomon Islands GSHS source review, exact population and skip-logic verification, canonical response/missing rules, and two actual human reviewers for primary outcomes.",
"",
"## Reproduction",
"",
@@ -282,17 +313,17 @@ def render_markdown(registry: dict) -> str:
def main() -> None:
gshs = build_gshs()
gshs, gshs_derived = build_gshs()
yrbs = build_yrbs()
nsduh_raw, nsduh_derived, nsduh_excluded = build_nsduh()
registry = {
"schema_version": "1.1",
"schema_version": "1.2",
"generated_on": "2026-09-20",
"status": "provisional",
"allowed_uses": ["pipeline_development", "source_review_queue"],
"prohibited_uses": ["confirmatory_analysis", "approved_anchor_links", "claim_of_human_double_review"],
"candidate_records": gshs + yrbs + nsduh_raw,
"derived_records_not_independent_items": nsduh_derived,
"derived_records_not_independent_items": gshs_derived + nsduh_derived,
"excluded_nsduh_records": nsduh_excluded,
"availability_constraints": [
{
@@ -314,7 +345,7 @@ def main() -> None:
OUT_MD.write_text(render_markdown(registry), encoding="utf-8")
print(json.dumps({
"candidate_records": len(registry["candidate_records"]),
"derived_records": len(nsduh_derived),
"derived_records": len(registry["derived_records_not_independent_items"]),
"excluded_nsduh_records": len(nsduh_excluded),
"json": str(OUT_JSON.relative_to(ROOT)),
"report": str(OUT_MD.relative_to(ROOT)),
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,43 @@
# GSHS primary-outcome component audit
Version: 1.0 | Status: provisional | Generated: 2026-09-20
## Result
The selected GSHS master table contains 191 components and 673,499 rows. Exactly 7 components contain the three primary suicide outcomes; the remaining 184 contain no observed values in those six columns. Metadata row counts match the consolidated table for all components.
The country questionnaires and ordinary codebooks identify three administered items: past-12-month suicide ideation, plan, and attempt frequency. The Binary Codebooks identify the `_b_` columns as binary indicators derived from those items. Row-level checks are exact: ideation and plan binary columns duplicate the raw yes/no coding, while attempt maps 0 times to No and one or more times to Yes. Therefore the `_b_` columns must not be counted as independent administered items.
## Components with observed primary outcomes
| Component | Country | Rows | Ideation nonmissing | Plan nonmissing | Attempt nonmissing | Direct local sources |
|---|---|---:|---:|---:|---:|---|
| `2023_MAR_GSHS_v01::mar_fez2023` | Morocco | 4,172 | 3,994 | 4,019 | 4,086 | local_questionnaire_and_codebooks_located |
| `GHA_2022_GSHS_v01::gha_sek_tak2022` | Ghana | 3,796 | 3,645 | 3,645 | 3,682 | local_questionnaire_and_codebooks_located |
| `IND_2022_GSHS_v01::ind_jaipur_2022` | India | 4,299 | 4,174 | 4,106 | 4,228 | local_questionnaire_and_codebooks_located |
| `JAM_2023_GSHS_v01::jam_st_cath_2023` | Jamaica | 3,041 | 2,879 | 2,850 | 2,936 | local_questionnaire_and_codebooks_located |
| `MNG_2023_GSHS_v01::mng2023` | Mongolia | 3,311 | 3,274 | 3,258 | 3,238 | local_questionnaire_and_codebooks_located |
| `SLB_2023_GSHS_v01::slb_2023` | Solomon Islands | 1,995 | 1,890 | 1,953 | 1,963 | pending_local_direct_source |
| `WLF_2023_GSHS_v01::wlf_2023` | Wallis and Futuna | 919 | 894 | 907 | 905 | local_questionnaire_and_codebooks_located |
## Representation checks
| Raw item | Binary representation | Result | Observed mapping |
|---|---|---|---|
| `raw_mh_considersui` | `raw_mh_b_considersui` | passed | <missing>-><missing>: 652,749; 1->1: 4,854; 2->2: 15,896 |
| `raw_mh_plansui` | `raw_mh_b_plansui` | passed | <missing>-><missing>: 652,761; 1->1: 4,363; 2->2: 16,375 |
| `raw_mh_attemptsui` | `raw_mh_b_attemptsui` | passed | <missing>-><missing>: 652,461; 1->2: 16,732; 2->1: 2,457; 3->1: 1,139; 4->1: 325; 5->1: 385 |
## Source boundary
Local questionnaires plus raw and binary codebooks were located for 6 of the 7 components: Ghana, India, Jamaica, Mongolia, Morocco, and Wallis and Futuna. No country-specific Solomon Islands 2023 questionnaire/codebook was found locally, so that component remains pending direct-source acquisition.
Available English/French/Mongolian files establish wording evidence but do not by themselves prove the language administered to every respondent. Exact sampled ages/grades, component-specific eligibility, and canonical missing reasons also remain unresolved.
## Gate 1 decision
Gate 1 remains **in progress**. This audit resolves the 191-component coverage question and the raw-versus-`_b_` representation issue, but it does not satisfy the population/missing-rule or two-human-reviewer requirements. All records remain provisional and are not approved for confirmatory analysis or anchor construction.
## Reproduction
Run `research/stage1/audit_gshs_primary_outcome_components.py` in the project Python environment. The implementation streams the consolidated CSV and does not load it into pandas memory.
+111 -99
View File
@@ -1,5 +1,5 @@
{
"schema_version": "1.1",
"schema_version": "1.2",
"generated_on": "2026-09-20",
"status": "provisional",
"allowed_uses": [
@@ -16,11 +16,8 @@
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_attemptsui",
"construct": "suicide_attempt",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Attempted suicide in past 12 mos",
"answer_options": {
"1": "0 times",
@@ -30,140 +27,71 @@
"5": "6 or more times"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns",
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_attemptsui",
"construct": "suicide_attempt",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"source_label": "Attempted suicide | Attempted suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"source_url": null,
"page_or_section": "Master_Columns",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_considersui",
"construct": "suicide_ideation",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"source_label": "Seriously considered attempting suicide | Seriously considered attempting suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"source_url": null,
"page_or_section": "Master_Columns",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_plansui",
"construct": "suicide_plan",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"source_label": "Made a plan about how they would attempt suicide | Made a plan about how they would attempt suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"source_url": null,
"page_or_section": "Master_Columns",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
"record_type": "administered_raw_candidate",
"question_text": "During the past 12 months, how many times did you attempt suicide?",
"notes": "Country questionnaires and ordinary codebooks identify this as an administered item. Solomon Islands 2023 direct local source remains pending; population and missing rules are not approved."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_considersui",
"construct": "suicide_ideation",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Considered suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns",
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
"construct": "suicide_ideation",
"record_type": "administered_raw_candidate",
"question_text": "During the past 12 months, did you seriously consider attempting suicide?",
"notes": "Country questionnaires and ordinary codebooks identify this as an administered item. Solomon Islands 2023 direct local source remains pending; population and missing rules are not approved."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_plansui",
"construct": "suicide_plan",
"record_type": "raw_candidate",
"population": "school-attending students; exact component ages pending source review",
"time_window": "past 12 months (from source label; questionnaire verification pending)",
"question_text": null,
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Made a suicide plan in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns",
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "pending_questionnaire_review",
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"notes": "The `_b_` and unprefixed forms remain separate item versions; labels alone do not prove equivalence."
"construct": "suicide_plan",
"record_type": "administered_raw_candidate",
"question_text": "During the past 12 months, did you make a plan about how you would attempt suicide?",
"notes": "Country questionnaires and ordinary codebooks identify this as an administered item. Solomon Islands 2023 direct local source remains pending; population and missing rules are not approved."
},
{
"survey": "YRBS",
@@ -2307,6 +2235,90 @@
}
],
"derived_records_not_independent_items": [
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_attemptsui",
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Attempted suicide | Attempted suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"construct": "suicide_attempt",
"record_type": "derived_or_duplicate_binary_indicator",
"question_text": null,
"derived_from": "raw_mh_attemptsui",
"derivation_rule": "1=Yes when attempt frequency is one or more; 2=No when frequency is zero",
"notes": "Binary Codebooks and exact row-level mapping show this is not an independent administered item."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_considersui",
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Seriously considered attempting suicide | Seriously considered attempting suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"construct": "suicide_ideation",
"record_type": "derived_or_duplicate_binary_indicator",
"question_text": null,
"derived_from": "raw_mh_considersui",
"derivation_rule": "identity binary representation",
"notes": "Binary Codebooks and exact row-level mapping show this is not an independent administered item."
},
{
"survey": "GSHS",
"year": null,
"source_variable": "raw_mh_b_plansui",
"population": "school-attending students; exact component ages/grades pending source review",
"time_window": "past 12 months",
"source_label": "Made a plan about how they would attempt suicide | Made a plan about how they would attempt suicide in past 12 mos",
"answer_options": {
"1": "Yes",
"2": "No"
},
"source_file": "Dataset/GSHS-全球学生健康调查数据/GSHS/02_documentation/GSHS_数据字典.xlsx",
"component_audit_file": "research/stage1/gshs_primary_outcome_component_audit.json",
"source_url": null,
"page_or_section": "Master_Columns plus component-specific questionnaire/codebook references in component audit",
"present_in_local_data": true,
"source_verification_status": "direct_local_sources_verified_for_six_of_seven_observed_components",
"review_status": "provisional",
"reviewer_1": null,
"reviewer_2": null,
"link_status": "not_evaluated",
"construct": "suicide_plan",
"record_type": "derived_or_duplicate_binary_indicator",
"question_text": null,
"derived_from": "raw_mh_plansui",
"derivation_rule": "identity binary representation",
"notes": "Binary Codebooks and exact row-level mapping show this is not an independent administered item."
},
{
"survey": "NSDUH",
"year": 2021,
+4 -4
View File
@@ -1,6 +1,6 @@
# Primary outcome source registry
Version: 1.1 | Status: provisional | Generated: 2026-09-20
Version: 1.2 | Status: provisional | Generated: 2026-09-20
## Scope and decision boundary
@@ -10,7 +10,7 @@ This registry captures source-linked candidates for suicide ideation, plan, and
| Survey | Ideation | Plan | Attempt | Current evidence boundary |
|---|---:|---:|---:|---|
| GSHS | 2 | 2 | 2 | Workbook labels/options only; component questionnaires still require direct review. |
| GSHS | 1 | 1 | 1 | Three administered items occur in 7/191 components; local direct sources cover 6/7, with Solomon Islands pending. |
| YRBS | 17 | 17 | 17 | 51 annual candidates; 2021/2023 data remain quarantined; annual PDFs require direct review. |
| NSDUH | 4 | 4 | 4 | Attempt is administered as YSUI03/YUSUICTRY, but the raw and recoded attempt fields are absent from the local public-use package. |
@@ -18,7 +18,7 @@ Counts are source-record counts, not distinct item families. YRBS counts include
## Material conflicts and exclusions
- GSHS exposes both `_b_` and unprefixed variants. They differ in naming and, for attempt, response form (binary versus frequency). They remain separate until questionnaire/component provenance is verified.
- GSHS ordinary codebooks identify the three unprefixed variables as administered items. Binary Codebooks plus exact row-level relations identify the three `_b_` columns as derived or duplicate representations, not independent items.
- GSHS self-harm variables are not included as suicide-attempt items.
- YRBS metadata contains stale absolute paths from another machine and some automatically extracted neighboring-text contamination. Only exact primary-construct rows were retained, and their direct PDF review is still pending.
- YRBS 2021 and 2023 item metadata are retained for future migration checks, but their respondent data are quarantined pending layout/import validation.
@@ -28,7 +28,7 @@ Counts are source-record counts, not distinct item families. YRBS counts include
## Gate 1 status
Gate 1 remains **in progress**. Before any candidate can enter confirmatory analysis, the project still needs direct official questionnaire/codebook review, exact population and skip-logic verification, canonical response/missing rules, component-specific GSHS provenance, and two actual human reviewers for primary outcomes.
Gate 1 remains **in progress**. Before any candidate can enter confirmatory analysis, the project still needs direct YRBS and Solomon Islands GSHS source review, exact population and skip-logic verification, canonical response/missing rules, and two actual human reviewers for primary outcomes.
## Reproduction