build(stage1): create primary outcome response layer

This commit is contained in:
Jinotech
2026-09-20 09:11:16 +12:00
parent 2bc0062aef
commit 89c20f32c7
7 changed files with 435 additions and 7 deletions
+29
View File
@@ -2,6 +2,35 @@
Command failures and integration errors.
---
## [ERR-20260920-040] system-python-missing-project-package
**Logged**: 2026-09-20
**Priority**: low
**Status**: resolved
**Area**: environment
### Summary
The first response-long build was launched with system Python, which does not contain the locked project package set.
### Error
`ModuleNotFoundError: No module named 'duckdb'`
### Context
The import failed before source data were read or an output table was created. The project environment is `.venv-research`.
### Suggested Fix
Run research builders with `.venv-research/Scripts/python.exe` and reserve system Python for dependency-free diagnostics.
### Metadata
- Reproducible: yes
- Related Files: `research/stage1/build_responses_long.py`, `research/audit/environment_audit.md`
- Pattern-Key: environment.wrong-python-runtime
### Resolution
- **Resolved**: 2026-09-20
- **Notes**: Re-ran with the locked project Python; the long table and independent checks completed.
---
## [ERR-20260920-026] sas-input-block-comment-collision
+19 -5
View File
@@ -1,9 +1,9 @@
# 项目进度档案
版本:1.14
版本:1.15
建立日期:2026-09-20
当前总状态:`in_progress`
当前阶段:阶段 1 `in_progress`84 个主要结局题目实例与编码/双审包已形成,YRBS 1991–2023 共 17 年完整表均通过来源与值域检查;`responses_long.parquet`设计复核和实际双人审核尚未完成
当前阶段:阶段 1 `in_progress`84 个主要结局题目实例及 919,863 行回答长表已形成并通过结构校验,YRBS 17 年完整表可用;设计复核和实际双人审核尚未完成
## 1. 管理规则
@@ -28,7 +28,7 @@
| 阶段 | 名称 | 状态 | 当前判定 |
|---|---|---|---|
| 0 | 冻结研究问题、范围与版本 | `passed` | 四项交付物齐备,Gate 0 已通过;研究工具链补强与锁定完成 |
| 1 | 有来源证据的题库和回答层 | `in_progress` | 主要结局结构化包已生成,YRBS 2021/2023 已按官方 SAS 布局解除导入隔离;回答长表、设计复核和实际双重审核尚未完成 |
| 1 | 有来源证据的题库和回答层 | `in_progress` | 主要结局题库、回答字典和回答长表已生成并通过结构校验;设计复核和实际双重审核尚未完成 |
| 2 | 可行性、可识别性和连接审计 | `not_started` | 等待 Gate 1 |
| 3 | 冻结验证协议与已知真值模拟 | `not_started` | 等待 Gate 2 |
| 4 | 直接协调和传统基线 | `not_started` | 等待 Gate 3 |
@@ -775,6 +775,20 @@ Gate 0 当前判定:`passed`。四项交付物齐备;目标总体政策、
- 推送状态:`pushed`;研究增量提交 `a6df01d` 已推送至 `origin/main`
- 下一步:用当前题库/回答字典生成跨调查 `responses_long.parquet`,保留 eligibility、observed status 与各类缺失;同时准备实际双人审核安排。
### A-20260920-061:生成并验证主要结局回答长表
- 时间:2026-09-20
- 阶段:1(有来源证据的题库和回答层)
- 动作:行动前完整读取工作流与进度档案并核对干净 Git 状态;以 `item_bank.csv``response_dictionary.csv` 为唯一映射源,流式处理 GSHS,读取 17 个 YRBS 年度完整表,并将 NSDUH 限定为官方 `CATAG2=1`(12–17 岁)青少年,生成 Gate 1 回答长表。
- 输出:新增 `build_responses_long.py``responses_long_manifest.json``responses_long_audit.md`;生成忽略于 Git 的 `Dataset/可直接分析数据包_NSDUH_YRBS/harmonization/responses_long.parquet`
- 检查:输出 919,863 行且“受访者×题目”键 919,863 个,重复为 0;覆盖 80 个有本地回答数据的实例。按调查为 GSHS 64,599、YRBS 764,028、NSDUH 91,236 行,均与从 7 个 GSHS 组件、17 个 YRBS 年度表及 NSDUH 青少年人数独立推导的期望行数一致。867,995 行为有效观测;其余保留为 unknown、refused、dont_know、ordinary_missing 或 data_error;必填标识/状态无空值,所有有效观测均有 canonical response。
- 纳入边界:GSHS 仅含 7 个实际施测组件;YRBS 含 17 个已验证年度;NSDUH 只含 12–17 岁青少年的意念和计划。4 个 `YUSUICTRY` 实例仍在题库但没有回答行,因为公用微数据未提供该字段,未从其他题推导或伪造。
- 可复现性:输出 SHA-256 为 `0260386444fd2d4a9b0183ef79b171b4fa47ab1d7c1706b05619c14cc8f8a197`;生成物由脚本重建,不纳入 Git 大文件历史。
- 工程记录:首次误用系统 Python 导致 DuckDB 不可用,在读取数据前失败;切换到锁定的 `.venv-research` 后完成,记录为已解决 `ERR-20260920-040`
- 质量边界:结构校验通过不等于条目内容双审或抽样设计批准。所有题目仍为 `provisional`Gate 1 保持 `in_progress`
- 推送状态:`push_pending`;完成差异检查、提交和远端推送后更新。
- 下一步:逐调查核验权重、PSU、stratum 和方差方法的官方来源;随后落实两名实际审核者签署。
## 9. 剩余工作量估算(2026-09-20 基线)
| 阶段 | 主要工作 | 预计人周 | 主要波动来源 |
@@ -797,6 +811,6 @@ Gate 0 当前判定:`passed`。四项交付物齐备;目标总体政策、
阶段 1 下一行动:
1. 以题库和回答字典为唯一映射源,为当前可用且通过检查的数据生成 `responses_long.parquet`,显式保存 eligibility、observed status 与缺失类型
2. 复核 GSHS、YRBS、NSDUH 的年度/组件设计字段与官方方差说明,禁止凭字段名推断方差方法
1. 复核 GSHS、YRBS、NSDUH 的年度/组件设计字段与官方方差说明,禁止凭字段名推断方差方法
2. 核对长表各题的阳性/缺失计数与原始审计,形成回答层验收摘要
3. 安排两名实际审核者逐项处理 `primary_outcome_human_review_queue.csv`;审核完成前保持全部主要结局为 `provisional`
@@ -430,7 +430,7 @@ Generated from the direct-source registry and audits by `build_primary_outcome_p
- source conflicts or evidence gaps: {conflicts}
- human reviewers recorded: 0
The package does not satisfy Gate 1 by itself. Two actual human reviewers must independently sign every primary-outcome item; `responses_long.parquet` is not yet built; NSDUH youth attempt is absent from local public-use microdata; and several design and administered-language fields remain provisional.
The package does not satisfy Gate 1 by itself. `responses_long.parquet` has passed structural validation, but two actual human reviewers must independently sign every primary-outcome item; NSDUH youth attempt is absent from local public-use microdata; and several design and administered-language fields remain provisional.
"""
(STAGE1 / "primary_outcome_package.md").write_text(text, encoding="utf-8")
+241
View File
@@ -0,0 +1,241 @@
"""Build the Gate-1 long response table from source-verified primary outcomes."""
from __future__ import annotations
import csv
import hashlib
import json
from collections import Counter
from pathlib import Path
from typing import Any
import duckdb
import pyarrow as pa
import pyarrow.parquet as pq
ROOT = Path(__file__).resolve().parents[2]
STAGE1 = ROOT / "research" / "stage1"
GSHS = ROOT / "Dataset" / "GSHS-全球学生健康调查数据" / "GSHS" / "01_data" / "GSHS.csv"
YRBS_DIR = ROOT / "Dataset" / "可直接分析数据包_NSDUH_YRBS" / "YRBS" / "full_by_year"
NSDUH = ROOT / "Dataset" / "可直接分析数据包_NSDUH_YRBS" / "NSDUH" / "nsduh_2021_2024_full.parquet"
OUTPUT = ROOT / "Dataset" / "可直接分析数据包_NSDUH_YRBS" / "harmonization" / "responses_long.parquet"
MANIFEST = STAGE1 / "responses_long_manifest.json"
REPORT = STAGE1 / "responses_long_audit.md"
SCHEMA = pa.schema([
("respondent_id", pa.string()),
("survey_id", pa.string()),
("component_id", pa.string()),
("survey_year", pa.int16()),
("item_version_id", pa.string()),
("raw_response", pa.string()),
("canonical_response", pa.string()),
("eligibility", pa.string()),
("observed_status", pa.string()),
("missing_type", pa.string()),
("source_row", pa.int64()),
])
def load_csv(path: Path) -> list[dict[str, str]]:
with path.open(encoding="utf-8-sig", newline="") as handle:
return list(csv.DictReader(handle))
def normalized_code(value: Any) -> str:
if value is None:
return "<missing>"
if isinstance(value, float) and value.is_integer():
return str(int(value))
text = str(value).strip()
if not text:
return "<missing>"
if text.endswith(".0"):
try:
return str(int(float(text)))
except ValueError:
pass
return text
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
class LongWriter:
def __init__(self, path: Path, response_map: dict[tuple[str, str], dict[str, str]]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
self.writer = pq.ParquetWriter(path, SCHEMA, compression="zstd")
self.response_map = response_map
self.buffer: list[dict[str, Any]] = []
self.rows_by_survey: Counter[str] = Counter()
self.rows_by_item: Counter[str] = Counter()
self.observed_status: Counter[str] = Counter()
self.missing_types: Counter[str] = Counter()
def add(self, *, respondent_id: str, survey_id: str, component_id: str, year: int,
item_version_id: str, raw_value: Any, eligibility: str, source_row: int) -> None:
raw_code = normalized_code(raw_value)
key = (item_version_id, raw_code)
if key not in self.response_map:
raise ValueError(f"No response dictionary entry for {key}")
mapping = self.response_map[key]
observed = mapping["is_observed_response"] == "true"
missing_type = mapping["missing_type"]
status = "observed" if observed else (missing_type or "missing")
survey = item_version_id.split("::", 1)[0]
self.buffer.append({
"respondent_id": respondent_id,
"survey_id": survey_id,
"component_id": component_id,
"survey_year": year,
"item_version_id": item_version_id,
"raw_response": None if raw_code == "<missing>" else raw_code,
"canonical_response": mapping["canonical_code"] or None,
"eligibility": eligibility,
"observed_status": status,
"missing_type": missing_type or None,
"source_row": source_row,
})
self.rows_by_survey[survey] += 1
self.rows_by_item[item_version_id] += 1
self.observed_status[status] += 1
if missing_type:
self.missing_types[missing_type] += 1
if len(self.buffer) >= 50_000:
self.flush()
def flush(self) -> None:
if self.buffer:
self.writer.write_table(pa.Table.from_pylist(self.buffer, schema=SCHEMA))
self.buffer.clear()
def close(self) -> None:
self.flush()
self.writer.close()
def main() -> None:
item_rows = load_csv(STAGE1 / "item_bank.csv")
dictionary_rows = load_csv(STAGE1 / "response_dictionary.csv")
item_index = {(r["survey"], r["component_id"], r["source_variable"].lower()): r for r in item_rows}
response_map = {(r["item_version_id"], r["raw_code"]): r for r in dictionary_rows}
writer = LongWriter(OUTPUT, response_map)
expected_rows: dict[str, int] = {}
# GSHS: stream the large CSV, retaining only the seven source-verified components.
gshs_components = {r["component_id"] for r in item_rows if r["survey"] == "GSHS"}
gshs_vars = ["raw_mh_considersui", "raw_mh_plansui", "raw_mh_attemptsui"]
with GSHS.open(encoding="utf-8-sig", newline="") as handle:
reader = csv.DictReader(handle)
for source_row, row in enumerate(reader, start=1):
component = row["dataset_id"]
if component not in gshs_components:
continue
year = int(str(row["survey_year"])[:4])
survey_id = next(r["survey_id"] for r in item_rows if r["survey"] == "GSHS" and r["component_id"] == component)
respondent = f"{component}::{row['id']}"
for variable in gshs_vars:
item = item_index[("GSHS", component, variable)]
writer.add(respondent_id=respondent, survey_id=survey_id, component_id=component,
year=year, item_version_id=item["item_version_id"], raw_value=row[variable],
eligibility="eligible_administered_component", source_row=source_row)
gshs_audit = json.loads((STAGE1 / "gshs_primary_outcome_component_audit.json").read_text(encoding="utf-8"))
expected_rows["GSHS"] = sum(c["observed_rows"] for c in gshs_audit["components"] if c["primary_outcomes_observed"]) * 3
# YRBS: all 17 annual full tables have passed source/layout and value-domain checks.
yrbs_items = [r for r in item_rows if r["survey"] == "YRBS"]
yrbs_respondents = 0
for year in sorted({int(r["year"]) for r in yrbs_items}):
annual_items = [r for r in yrbs_items if int(r["year"]) == year]
columns = ["source_row", *[r["source_variable"].lower() for r in annual_items]]
table = pq.read_table(YRBS_DIR / f"yrbs_{year}_full.parquet", columns=columns).to_pydict()
yrbs_respondents += len(table["source_row"])
for offset, source_row in enumerate(table["source_row"]):
respondent = f"YRBS::{year}::{source_row}"
for item in annual_items:
variable = item["source_variable"].lower()
writer.add(respondent_id=respondent, survey_id=item["survey_id"], component_id=item["component_id"],
year=year, item_version_id=item["item_version_id"], raw_value=table[variable][offset],
eligibility="eligible_sampled_student", source_row=int(source_row))
expected_rows["YRBS"] = yrbs_respondents * 3
# NSDUH: restrict to CATAG2=1 (12-17); attempt is documented but absent from public-use data.
nsduh_items = [r for r in item_rows if r["survey"] == "NSDUH" and r["source_variable"] in {"YUSUITHK", "YUSUIPLN"}]
nsduh_table = pq.read_table(NSDUH, columns=["YEAR", "QUESTID2", "CATAG2", "YUSUITHK", "YUSUIPLN"]).to_pydict()
nsduh_youth = 0
for offset, age_group in enumerate(nsduh_table["CATAG2"]):
if normalized_code(age_group) != "1":
continue
nsduh_youth += 1
year = int(nsduh_table["YEAR"][offset])
annual_items = [r for r in nsduh_items if int(r["year"]) == year]
respondent = f"NSDUH::{year}::{nsduh_table['QUESTID2'][offset]}"
for item in annual_items:
variable = item["source_variable"]
writer.add(respondent_id=respondent, survey_id=item["survey_id"], component_id=item["component_id"],
year=year, item_version_id=item["item_version_id"], raw_value=nsduh_table[variable][offset],
eligibility="eligible_age_12_17", source_row=offset + 1)
expected_rows["NSDUH"] = nsduh_youth * 2
writer.close()
con = duckdb.connect()
path_sql = str(OUTPUT).replace("'", "''")
totals = con.execute(f"SELECT count(*), count(DISTINCT respondent_id || '|' || item_version_id), count(DISTINCT item_version_id) FROM read_parquet('{path_sql}')").fetchone()
null_checks = con.execute(f"SELECT count(*) FILTER (WHERE respondent_id IS NULL OR item_version_id IS NULL OR eligibility IS NULL OR observed_status IS NULL), count(*) FILTER (WHERE observed_status='observed' AND canonical_response IS NULL) FROM read_parquet('{path_sql}')").fetchone()
bad_codes = con.execute(f"SELECT count(*) FROM read_parquet('{path_sql}') WHERE observed_status='observed' AND raw_response IS NULL").fetchone()[0]
con.close()
if totals[0] != totals[1]:
raise AssertionError("respondent_id × item_version_id keys are not unique")
if null_checks != (0, 0) or bad_codes:
raise AssertionError(f"Long-table required-field/canonical checks failed: {null_checks=} {bad_codes=}")
if totals[2] != 80:
raise AssertionError(f"Expected responses for 80 locally analyzable items, found {totals[2]}")
if dict(writer.rows_by_survey) != expected_rows:
raise AssertionError(f"Source-derived row totals do not match output: {expected_rows=} actual={dict(writer.rows_by_survey)}")
manifest = {
"schema_version": "1.0", "generated_on": "2026-09-20", "status": "provisional",
"output_file": str(OUTPUT.relative_to(ROOT)).replace("\\", "/"),
"sha256": sha256(OUTPUT), "bytes": OUTPUT.stat().st_size,
"rows": totals[0], "unique_response_keys": totals[1], "item_instances_with_rows": totals[2],
"rows_by_survey": dict(sorted(writer.rows_by_survey.items())),
"source_derived_expected_rows": dict(sorted(expected_rows.items())),
"rows_by_item": dict(sorted(writer.rows_by_item.items())),
"observed_status_counts": dict(sorted(writer.observed_status.items())),
"missing_type_counts": dict(sorted(writer.missing_types.items())),
"required_field_nulls": null_checks[0], "observed_rows_without_canonical_response": null_checks[1],
"eligibility_rules": {
"GSHS": "Rows only from the seven components where the primary items were administered.",
"YRBS": "All respondents in each verified national annual full table; blank source responses remain unknown missing.",
"NSDUH": "CATAG2=1 (official label: 12-17 years old); only YUSUITHK and YUSUIPLN are locally available.",
},
"excluded_item_instances": [
r["item_version_id"] for r in item_rows
if r["survey"] == "NSDUH" and r["source_variable"] == "YUSUICTRY"
],
"gate_boundary": "No human reviewer fields are inferred. Four NSDUH attempt items have no response rows because the fields are absent from local public-use microdata.",
}
MANIFEST.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
REPORT.write_text(
"# Primary-outcome response long-table audit\n\n"
"Status: **provisional; structural validation passed**.\n\n"
f"- rows: {manifest['rows']:,}\n"
f"- unique respondent × item keys: {manifest['unique_response_keys']:,}\n"
f"- item instances with local response data: {manifest['item_instances_with_rows']} of 84\n"
f"- rows by survey: `{manifest['rows_by_survey']}`\n"
f"- observed/missing statuses: `{manifest['observed_status_counts']}`\n"
f"- SHA-256: `{manifest['sha256']}`\n\n"
"The output row totals equal counts independently derived from the seven GSHS components, all 17 YRBS annual full tables, and NSDUH respondents with CATAG2=1. Required identifiers/status fields are complete, respondent × item keys are unique, and every observed response has a canonical response. The four NSDUH attempt instances remain in the item bank without response rows because the public-use microdata omit those fields. Human double review is still required.\n",
encoding="utf-8",
)
print(json.dumps({k: manifest[k] for k in ["rows", "unique_response_keys", "item_instances_with_rows", "rows_by_survey", "observed_status_counts", "missing_type_counts", "sha256"]}, indent=2))
if __name__ == "__main__":
main()
+1 -1
View File
@@ -10,4 +10,4 @@ Generated from the direct-source registry and audits by `build_primary_outcome_p
- source conflicts or evidence gaps: 32
- human reviewers recorded: 0
The package does not satisfy Gate 1 by itself. Two actual human reviewers must independently sign every primary-outcome item; `responses_long.parquet` is not yet built; NSDUH youth attempt is absent from local public-use microdata; and several design and administered-language fields remain provisional.
The package does not satisfy Gate 1 by itself. `responses_long.parquet` has passed structural validation, but two actual human reviewers must independently sign every primary-outcome item; NSDUH youth attempt is absent from local public-use microdata; and several design and administered-language fields remain provisional.
+12
View File
@@ -0,0 +1,12 @@
# Primary-outcome response long-table audit
Status: **provisional; structural validation passed**.
- rows: 919,863
- unique respondent × item keys: 919,863
- item instances with local response data: 80 of 84
- rows by survey: `{'GSHS': 64599, 'NSDUH': 91236, 'YRBS': 764028}`
- observed/missing statuses: `{'data_error': 177, 'dont_know': 458, 'observed': 867995, 'ordinary_missing': 172, 'refused': 6544, 'unknown': 44517}`
- SHA-256: `0260386444fd2d4a9b0183ef79b171b4fa47ab1d7c1706b05619c14cc8f8a197`
The output row totals equal counts independently derived from the seven GSHS components, all 17 YRBS annual full tables, and NSDUH respondents with CATAG2=1. Required identifiers/status fields are complete, respondent × item keys are unique, and every observed response has a canonical response. The four NSDUH attempt instances remain in the item bank without response rows because the public-use microdata omit those fields. Human double review is still required.
@@ -0,0 +1,132 @@
{
"schema_version": "1.0",
"generated_on": "2026-09-20",
"status": "provisional",
"output_file": "Dataset/可直接分析数据包_NSDUH_YRBS/harmonization/responses_long.parquet",
"sha256": "0260386444fd2d4a9b0183ef79b171b4fa47ab1d7c1706b05619c14cc8f8a197",
"bytes": 4405969,
"rows": 919863,
"unique_response_keys": 919863,
"item_instances_with_rows": 80,
"rows_by_survey": {
"GSHS": 64599,
"NSDUH": 91236,
"YRBS": 764028
},
"source_derived_expected_rows": {
"GSHS": 64599,
"NSDUH": 91236,
"YRBS": 764028
},
"rows_by_item": {
"GSHS::2023_MAR_GSHS_v01::mar_fez2023::raw_mh_attemptsui": 4172,
"GSHS::2023_MAR_GSHS_v01::mar_fez2023::raw_mh_considersui": 4172,
"GSHS::2023_MAR_GSHS_v01::mar_fez2023::raw_mh_plansui": 4172,
"GSHS::GHA_2022_GSHS_v01::gha_sek_tak2022::raw_mh_attemptsui": 3796,
"GSHS::GHA_2022_GSHS_v01::gha_sek_tak2022::raw_mh_considersui": 3796,
"GSHS::GHA_2022_GSHS_v01::gha_sek_tak2022::raw_mh_plansui": 3796,
"GSHS::IND_2022_GSHS_v01::ind_jaipur_2022::raw_mh_attemptsui": 4299,
"GSHS::IND_2022_GSHS_v01::ind_jaipur_2022::raw_mh_considersui": 4299,
"GSHS::IND_2022_GSHS_v01::ind_jaipur_2022::raw_mh_plansui": 4299,
"GSHS::JAM_2023_GSHS_v01::jam_st_cath_2023::raw_mh_attemptsui": 3041,
"GSHS::JAM_2023_GSHS_v01::jam_st_cath_2023::raw_mh_considersui": 3041,
"GSHS::JAM_2023_GSHS_v01::jam_st_cath_2023::raw_mh_plansui": 3041,
"GSHS::MNG_2023_GSHS_v01::mng2023::raw_mh_attemptsui": 3311,
"GSHS::MNG_2023_GSHS_v01::mng2023::raw_mh_considersui": 3311,
"GSHS::MNG_2023_GSHS_v01::mng2023::raw_mh_plansui": 3311,
"GSHS::SLB_2023_GSHS_v01::slb_2023::raw_mh_attemptsui": 1995,
"GSHS::SLB_2023_GSHS_v01::slb_2023::raw_mh_considersui": 1995,
"GSHS::SLB_2023_GSHS_v01::slb_2023::raw_mh_plansui": 1995,
"GSHS::WLF_2023_GSHS_v01::wlf_2023::raw_mh_attemptsui": 919,
"GSHS::WLF_2023_GSHS_v01::wlf_2023::raw_mh_considersui": 919,
"GSHS::WLF_2023_GSHS_v01::wlf_2023::raw_mh_plansui": 919,
"NSDUH::2021::YUSUIPLN": 10743,
"NSDUH::2021::YUSUITHK": 10743,
"NSDUH::2022::YUSUIPLN": 11969,
"NSDUH::2022::YUSUITHK": 11969,
"NSDUH::2023::YUSUIPLN": 11572,
"NSDUH::2023::YUSUITHK": 11572,
"NSDUH::2024::YUSUIPLN": 11334,
"NSDUH::2024::YUSUITHK": 11334,
"YRBS::1991::Q19": 12272,
"YRBS::1991::Q20": 12272,
"YRBS::1991::Q21": 12272,
"YRBS::1993::Q24": 16296,
"YRBS::1993::Q25": 16296,
"YRBS::1993::Q26": 16296,
"YRBS::1995::Q22": 10904,
"YRBS::1995::Q23": 10904,
"YRBS::1995::Q24": 10904,
"YRBS::1997::Q22": 16263,
"YRBS::1997::Q23": 16263,
"YRBS::1997::Q24": 16263,
"YRBS::1999::Q23": 15349,
"YRBS::1999::Q24": 15349,
"YRBS::1999::Q25": 15349,
"YRBS::2001::Q24": 13601,
"YRBS::2001::Q25": 13601,
"YRBS::2001::Q26": 13601,
"YRBS::2003::Q24": 15214,
"YRBS::2003::Q25": 15214,
"YRBS::2003::Q26": 15214,
"YRBS::2005::Q24": 13917,
"YRBS::2005::Q25": 13917,
"YRBS::2005::Q26": 13917,
"YRBS::2007::Q24": 14041,
"YRBS::2007::Q25": 14041,
"YRBS::2007::Q26": 14041,
"YRBS::2009::Q24": 16410,
"YRBS::2009::Q25": 16410,
"YRBS::2009::Q26": 16410,
"YRBS::2011::Q25": 15425,
"YRBS::2011::Q26": 15425,
"YRBS::2011::Q27": 15425,
"YRBS::2013::Q27": 13583,
"YRBS::2013::Q28": 13583,
"YRBS::2013::Q29": 13583,
"YRBS::2015::Q27": 15624,
"YRBS::2015::Q28": 15624,
"YRBS::2015::Q29": 15624,
"YRBS::2017::Q26": 14765,
"YRBS::2017::Q27": 14765,
"YRBS::2017::Q28": 14765,
"YRBS::2019::Q26": 13677,
"YRBS::2019::Q27": 13677,
"YRBS::2019::Q28": 13677,
"YRBS::2021::Q26": 17232,
"YRBS::2021::Q27": 17232,
"YRBS::2021::Q28": 17232,
"YRBS::2023::Q27": 20103,
"YRBS::2023::Q28": 20103,
"YRBS::2023::Q29": 20103
},
"observed_status_counts": {
"data_error": 177,
"dont_know": 458,
"observed": 867995,
"ordinary_missing": 172,
"refused": 6544,
"unknown": 44517
},
"missing_type_counts": {
"data_error": 177,
"dont_know": 458,
"ordinary_missing": 172,
"refused": 6544,
"unknown": 44517
},
"required_field_nulls": 0,
"observed_rows_without_canonical_response": 0,
"eligibility_rules": {
"GSHS": "Rows only from the seven components where the primary items were administered.",
"YRBS": "All respondents in each verified national annual full table; blank source responses remain unknown missing.",
"NSDUH": "CATAG2=1 (official label: 12-17 years old); only YUSUITHK and YUSUIPLN are locally available."
},
"excluded_item_instances": [
"NSDUH::2021::YUSUICTRY",
"NSDUH::2022::YUSUICTRY",
"NSDUH::2023::YUSUICTRY",
"NSDUH::2024::YUSUICTRY"
],
"gate_boundary": "No human reviewer fields are inferred. Four NSDUH attempt items have no response rows because the fields are absent from local public-use microdata."
}