信息速览
§0 本条目怎么来的、不做什么
整理方:千方医数集,为中文医学 AI 社区整理可训练数据集的结构化说明。
本条目怎么来的:数字主源是 OECD SDMX REST API 的实时响应(2026-10-24 采集):/dataflow/OECD.ELS.HD 返回 86 个 dataflow,按 @ 前的 DSD 前缀可归为 18 组;/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all 全量拉取得 52,241 条观测,REF_AREA 维度 51 个取值,TIME_PERIOD 从 1960 到 2024。许可条款取自 OECD 出版物与数据页统一的 CC BY 4.0 声明文本。
不做什么:不评价 OECD 的死亡登记质量;不核验各国自报数据与本国统计局原始发布是否一致;不判断某个卫生系统「好不好」。本条目只回答一个问题:这套 SDMX 接口长什么样、怎么取、哪里会坑人。
§1 数据集概览
§1.1 一句话
OECD 健康统计不是一个数据文件,是一套用 SDMX 3.0 表达的多维立方体服务——把一个国家的卫生系统拆成 86 个可独立取数的数据流,每个数据流共享同一套维度模型,于是「德国 2023 年 65 岁女性预期寿命」可以被一个 URL 精确定位。
§1.2 硬数字
| 项 | 值 | 来源 |
|---|---|---|
| dataflow 数 | 86 | /dataflow/OECD.ELS.HD 的 dataflows 数组长度 |
| DSD 结构组数 | 18 | 同上,按 DSD_* 前缀去重 |
| REF_AREA 经济体数 | 51 | 生命 expectancy dataflow 的 REF_AREA 维度取值 |
| 单 dataflow 观测数 | 52,241 | DSD_HEALTH_STAT@DF_LE 全量 |
| 时间跨度 | 1960–2024(65 年) | TIME_PERIOD 维度 |
| 观测维度数 | 14 | SDMX-JSON 的 dimensions.observation 长度 |
| 默认许可 | CC BY 4.0 | OECD 出版物与数据统一声明 |
§1.3 与「又一个健康数据库」的区别
库内公共卫生类条目已有 114 条。多数是疾病层面的数据集:影像、病历、测序、队列。少一层更外圈的东西——把卫生系统当作对象来看的数据。
这一层的价值在于它问的是另一类问题:
- 不是「这个病人得了什么病」,而是「这个国家为每个病人花了多少钱」
- 不是「这个病的患病率」,而是「这个病导致的死亡里,有多少是医疗系统本可以避免的」
- 不是「这个医生诊断得准不准」,而是「这个国家每千人有多少医生、多少张 ICU 床」
做医疗 AI 的人真正需要的,往往就是这层背景:你的模型部署在什么样的卫生系统里。 而这是世界银行那一层给不了的——世行有 GDP、有卫生支出占比,但没有 ICU 床位数、没有可避免死亡率、没有 SHA 2011 的筹资矩阵。
§1.4 与世界银行 / WHO GHO 的分工
| 库内条目 | 与本条关系 | 关键区别 |
|---|---|---|
world-bank-open-data(rid 1360) |
上游分母 vs 系统内部结构 | 世行是编目层,71 个源统一检索,宽而浅;OECD 是专域深库,只讲卫生系统,但拆到筹资与服务质量级 |
who-gho(rid 175) |
覆盖 vs 可比性 | GHO 覆盖 190+ 成员国的疾病指标;OECD 只 51 国,但口径经统一重算,跨国可比性更强 |
gbd |
观测 vs 建模 | GBD 是协变量模型的估计,带 95% UI;OECD 主要是成员国自报 + 方法统一化 |
一句话分工:世行说「这个国家在世界上排第几」,WHO 说「这个国家有什么病」,OECD 说「这个国家的卫生系统是怎么运转的」。
§2 十八组 DSD:卫生系统的解剖图
§2.1 86 个 dataflow 的真实分组
/dataflow/OECD.ELS.HD 返回的 86 个数据流不是扁平列表,按 dataflow ID 里 @ 前的 DSD 前缀可以精确归组:
| DSD 组 | dataflow 数 | 覆盖的主题 | 代表 dataflow |
|---|---|---|---|
DSD_HCQO |
13 | 卫生服务质量与结局 | DF_AC 急性病照护、DF_CC 癌症照护、DF_MH 精神卫生、DF_PS 患者安全、DF_PE 患者体验、DF_PC 初级照护 |
DSD_HEALTH_STAT |
13 | 健康状态与死亡 | DF_LE 预期寿命、DF_COM 死亡原因、DF_AM 可避免死亡、DF_C 癌症、DF_CD 传染病、DF_MIM 孕产妇与婴儿死亡 |
DSD_HEALTH_PROC |
10 | 服务利用与产出 | DF_CONSULT 门诊量、DF_SURG_PROC 手术量、DF_IMMUN 免疫、DF_SCREEN 筛查、DF_WAITING 等待时间 |
DSD_HEALTH_REAC_EMP |
10 | 卫生人力 | DF_PHYS 医生、DF_NURSE 护士、DF_MIDWIFE 助产士、DF_DNST 牙医、DF_PHST 药剂师 |
DSD_HEALTH_LVNG |
6 | 非医疗健康决定因素 | DF_HEALTH_LVNG_AC 饮酒、DF_HEALTH_LVNG_BW 体重、DF_HEALTH_LVNG_TC 烟草、DF_HEALTH_LVNG_VP 电子烟 |
DSD_HEALTH_REAC_HOSP |
6 | 医疗设施与设备 | DF_BEDS_ICU ICU 床位、DF_BEDS_FUNC 按功能分床位、DF_MED_TECH 医疗技术可及 |
DSD_HEALTH_EMP_REAC |
5 | 人力与就业 | DF_PHYS_AGE_SEX 按年龄性别分医生、DF_REAC 卫生人力资源总览 |
DSD_SHA |
5 | 医疗支出与筹资(SHA 2011) | DF_SHA 支出与筹资、DF_SHA_FS 筹资方案收入、DF_SHA_HK 资本形成 |
DSD_HEALTH_LTCR |
4 | 长期照护 | DF_HEALTH_LTCR_BED 长护床位、DF_HEALTH_LTCR_RECIPIENT 接受者、DF_HEALTH_LTCR_WORKER 人员 |
HEALTH_PHMC |
4 | 药品市场 | DF_PHMC_CONSUM 药品消费、DF_PHMC_SALES 药品销售 |
DSD_HEALTH_MORTALITY |
3 | 周度死亡监测 | DF_MORTALITY、DF_MORTALITY_COVID、DF_MORTALITY_EXCESS 超额死亡 |
DSD_GHGHC |
1 | 卫生部门温室气体排放 | DF_GHGHC |
DSD_HEALTH_LTCR_FAC_OWNER |
1 | 长护机构按所有制 | DF_HEALTH_LTCR_FAC_OWNER |
DSD_HEALTH_PROT |
1 | 医疗保障覆盖 | DF_HEALTH_PROT |
DSD_HEALTH_WFMI |
1 | 卫生人力国际迁移 | DF_HEALTH_WFMI |
DSD_HSCS2023 |
1 | 卫生系统特征调查 2023 轮 | DF_HSCS2023 |
DSD_HSCS |
1 | 卫生系统特征调查(2012/2016/2018) | DF_HSCS |
DSD_PARIS |
1 | 患者报告指标调查(PaRIS) | DF_PARIS |
§2.2 这张表怎么读
第一,DSD_HCQO 是最大的一组,也是最被忽视的一组。 13 个 dataflow 全在讲「服务质量和结局」——急性病照护、癌症照护、精神卫生、患者安全、患者体验、初级照护、整合照护、临终照护、髋膝关节 PROM、精神卫生 PREM、初级照护处方、患者安全文化。这一组回答的是「同样花了钱,哪个国家治得更好」,是卫生系统绩效研究的核心材料。
第二,DSD_HEALTH_MORTALITY 的三个周度 dataflow 是全库最特殊的一类。 它不做年度聚合,直接给按周的死亡数,还专门有一个 DF_MORTALITY_EXCESS(超额死亡)。这是 COVID 期间建立起来的能力,现在成了常规监测。如果你的研究涉及疫情冲击,这是能拿到周粒度的少数来源之一。
第三,DSD_GHGHC 只有 1 个 dataflow,但意义特殊。 它统计医疗卫生部门自己的温室气体排放。这是一个很新的视角:医疗系统既是气候变化的应对者,也是排放源。目前只有个别国家有数据。
第四,药品市场(HEALTH_PHMC)的 4 个 dataflow 用的是独立的 DSD 前缀(HEALTH_PHMC 而非 DSD_HEALTH_*)。这不是命名疏漏,是它来自不同的数据收集机制——药品销售数据多由商业数据库(如 IQVIA)提供,再由 OECD 标准化。
§2.3 医学 AI 视角下最该先看的三组
从「要给医学数据集补充系统背景」这个目的出发,优先级是:
第一优先:DSD_SHA(医疗支出与筹资,5 个 dataflow)。
SHA 2011(System of Health Accounts 2011)是国际公认的卫生核算标准。它把「医疗支出」拆成三个正交的轴:
- 功能(HC):服务是什么——住院、门诊、药品、预防、长护
- 提供者(HP):谁提供——医院、诊所、药房、养老机构
- 筹资方案(HF):谁付钱——政府、社保、商业保险、个人自付
这三个轴加上「资本形成」(HK)就是 OECD 那 5 个 dataflow 的骨架。这是本条目最有价值的部分:你能精确回答「这个国家的医疗钱是怎么花的、谁掏的」,而不是只知道「卫生支出占 GDP 6.2%」。
第二优先:DSD_HEALTH_REAC_EMP + DSD_HEALTH_REAC_HOSP(16 个 dataflow)。
这两组加起来 16 个数据流,讲的是卫生系统的物理能力:多少医生、多少护士、按年龄性别怎么分布、多少床位、多少 ICU 床、有哪些医疗技术(MRI、CT、PET、放疗设备)。
对医学 AI 的意义很直接:你的模型部署环境有多少硬件。 一个只在三级医院有 MRI 的国家,和一个乡镇卫生院都有 CT 的国家,需要的模型形态完全不同。
第三优先:DSD_HCQO(13 个 dataflow)。
服务质量与结局。这一组最接近「临床」,但也是口径最复杂的一组——大量依赖患者报告(PROM / PREM),覆盖率远低于支出与人力。先看它能不能用,再决定要不要用它。
§2.4 SHA 2011:把「医疗支出」拆成三维
这是本条目最需要讲清的一件事,因为它是 OECD 相对其他所有来源的独占优势。
绝大多数数据集里的「卫生支出」是一个数:SH.XPD.CHEX.GD.ZS = 6.2%。这个数告诉你「花了多少」,但不告诉你「花在哪」和「谁掏的钱」。OECD 的 SHA 2011(System of Health Accounts 2011)把它拆成三个正交的轴:
轴一:功能(HC,Health Care function)—— 钱花在做什么
| 代码 | 功能 | 说明 |
|---|---|---|
| HC.1 | 治疗性照护 | 又分住院(HC.1.1)、日间(HC.1.2)、门诊(HC.1.3)、居家(HC.1.4) |
| HC.2 | 康复 | 住院与门诊康复 |
| HC.3 | 长期照护 | 护理为主,分住院与居家 |
| HC.4 | 辅助服务 | 检验、影像、患者转运 |
| HC.5 | 医疗用品 | 处方药(HC.5.1)、非处方药、治疗器械 |
| HC.6 | 预防服务 | 免疫、筛查、健康监测、流行病学 |
| HC.7 | 治理与行政 | 保险管理、卫生部门管理 |
轴二:提供者(HP,Health Care provider)—— 谁提供服务
| 代码 | 提供者 |
|---|---|
| HP.1 | 医院(分综合、精神、专科) |
| HP.2 | 长期照护机构 |
| HP.3 | 门诊提供者(诊所、独立执业医生、牙医、检验机构) |
| HP.4 | 辅助服务提供者 |
| HP.5 | 零售与其他医疗用品销售商(药店) |
| HP.6 | 预防服务提供者 |
| HP.7 | 卫生行政与保险机构 |
轴三:筹资方案(HF,Health Care financing scheme)—— 谁付钱
| 代码 | 筹资方案 |
|---|---|
| HF.1 | 政府方案与强制保险(又分 HF.1.1 政府、HF.1.2 社保) |
| HF.2 | 自愿保险(商业健康险、社区保险) |
| HF.3 | 个人自付(OOP,out-of-pocket) |
| HF.4 | 外部资金(国际援助、跨境支付) |
三轴交叉的意义:任何一个「功能 × 提供者 × 筹资」格子都有数。你能直接回答这几类问题:
- 「这个国家的处方药费用里,个人自付占多少?」→ HC.5.1 × HP.5 × HF.3
- 「预防服务是谁掏的钱?」→ HC.6 × HF.1.1
- 「长期照护在各国由机构提供还是家庭提供?」→ HC.3 × (HP.2 vs HP.3)
为什么这比「卫生支出占 GDP」有用得多:两个国家支出占比都是 6%,但一个 80% 用于医院、50% 由个人自付,另一个 30% 用于医院、10% 由个人自付——这两个系统的性质完全不同。只用汇总量做跨国比较,等于只看体重不看体脂。
对医学 AI 的一个具体含义:如果一个国家的自付比例高(HF.3 占大头),说明「患者能不能负担」直接影响服务可及性。你的模型在那个国家部署时,要考虑的不是「医生够不够」,是「患者会不会因为付不起而不来」。这个判断只能从 SHA 的三轴分解里读出来。
§2.5 覆盖率的现实:51 个经济体不是 51 个国家
REF_AREA 维度有 51 个取值。实际构成:
| 类别 | 数量 | 例子 |
|---|---|---|
| OECD 成员国 | 38 | AUS、AUT、BEL、CAN、CHL、COL、CRI、CZE、DNK、EST、FIN、FRA、DEU、GRC、HUN、ISL、IRL、ISR、ITA、JPN、KOR、LVA、LTU、LUX、MEX、NLD、NZL、NOR、POL、PRT、SVK、SVN、ESP、SWE、CHE、TUR、GBR、USA |
| 伙伴国 / 关键非成员 | 13 | CHN、IND、IDN、BRA、ZAF、RUS、ARG、PER、THA、UKR、BGR、ROU、HRV |
关键判断:这 51 个经济体里,没有一个是低收入国家(按世行分类)。最低收入档的成员是 COL、CRI、MEX、CHN、IDN、IND、ZAF 等中高或中低收入国家。
这个事实决定了 OECD 数据的适用边界:它能回答「富裕国家之间卫生系统有何不同」,不能回答「全球健康公平」。如果你的研究需要覆盖撒哈拉以南非洲,这份数据帮不上,得去 WHO GHO 或世行。
另一个细节:保加利亚、罗马尼亚、克罗地亚、乌克兰在 REF_AREA 里出现,但它们不是 OECD 成员。OECD 收录它们是因为「欧盟成员国 + 有可比数据」。做分析时如果按「OECD 成员」筛选,需要额外维护一份成员名单,不能直接用 REF_AREA 的 51 个值。
§3 SDMX 取数通道
§3.1 四条 URL 模板
OECD 的 SDMX 3.0 服务有四类端点,全部在 https://sdmx.oecd.org/public/rest/ 下:
| 端点 | 用途 | 示例 |
|---|---|---|
/dataflow/{agency}/{id} |
列数据流 | /dataflow/OECD.ELS.HD |
/dataflow/all/all/latest |
列全部 agency 的数据流 | 返回 1,552 个 |
/datastructure/{agency}/{id} |
看某数据流的维度结构 | 需要 DSD 全名 |
/data/{flowRef}/{key} |
取数 | 见下 |
取数的完整路径:
https://sdmx.oecd.org/public/rest/data/{agencyID},{dataflowID},{version}/{key}?{params}
§3.2 一个能跑通的最小请求
curl -s -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2022&dimensionAtObservation=AllDimensions"
四个组成部分:
OECD.ELS.HD—— agencyDSD_HEALTH_STAT@DF_LE—— dataflow ID(注意@前后是 DSD 名与 DF 名)1.0—— 版本all—— 键,表示「全部维度全展开」
Accept 头是必须的。 不带它,服务端要么返回 406,要么退回 XML。正确的两个值:
| 用途 | Accept |
|---|---|
| 取数据 | application/vnd.sdmx.data+json;version=1.0 |
| 取结构 | application/vnd.sdmx.structure+json;version=1.0 |
§3.3 14 个维度,一次看清
以 DSD_HEALTH_STAT@DF_LE(预期寿命)为例,SDMX-JSON 响应的 data.structure.dimensions.observation 列出 14 个维度:
| # | 维度 | 该 dataflow 的取值数 | 说明 |
|---|---|---|---|
| 0 | REF_AREA |
51 | 经济体 |
| 1 | FREQ |
1 | 频率,这里只有 A(年度) |
| 2 | MEASURE |
3 | LFEXP(总)、LFEXPDMF(男)、LFEXPDFM(女) |
| 3 | UNIT_MEASURE |
1 | Y(年) |
| 4 | AGE |
5 | Y0(出生时)、Y40、Y60、Y65、Y80 |
| 5 | SEX |
3 | _T、F、M |
| 6 | SOCIO_ECON_STATUS |
1 | _Z(不适用) |
| 7 | DEATH_CAUSE |
1 | _Z |
| 8 | CALC_METHODOLOGY |
1 | _Z |
| 9 | GESTATION_THRESHOLD |
1 | _Z |
| 10 | HEALTH_STATUS |
1 | _Z |
| 11 | DISEASE |
1 | _Z |
| 12 | CANCER_SITE |
1 | _Z |
| 13 | TIME_PERIOD |
65 | 1960–2024 |
这 14 个维度的设计逻辑值得理解:它是「所有健康统计 dataflow 的超集」。DSD_HEALTH_STAT 下有 13 个 dataflow(预期寿命、死亡原因、可避免死亡、癌症、传染病、伤害、健康状态、自评健康、婴儿低出生体重、孕产妇与婴儿死亡、潜在寿命损失、病假、自评健康按社经地位),它们要共享同一个维度表,所以每个 dataflow 只能在自己用得到的维度上取值,其余全部填 _Z。
这就是 _Z 的来源。 在预期寿命的 dataflow 里,14 个维度有 8 个是 _Z。它们不是缺失,是「这个概念在本数据流里不适用」。
§3.4 请求参数
| 参数 | 作用 | 备注 |
|---|---|---|
startPeriod |
起始年份 | 推荐总是加,能显著减小响应 |
endPeriod |
结束年份 | — |
dimensionAtObservation |
设为 AllDimensions |
必须加,否则拿不到每条的完整维度 |
format |
sdmx-json 等 |
不如用 Accept 头可靠 |
references |
none / children / all |
取结构时用 |
有一个参数不要加:任意自定义的时间戳。 OECD 服务端会因为多一个它不认识的查询参数而直接返回 403,报错文本是 Unknown query parameter : t。这与世行 API 可以随便加缓存破坏参数完全不同——绕 CDN 缓存这一个习惯在 OECD 这边会直接失败。
§3.5 CSV 出口
不想解析 SDMX-JSON 时,改 Accept 就能拿 CSV:
curl -s -H "Accept: text/csv" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2023"
返回的表头直接就是维度名:
DATAFLOW,REF_AREA,FREQ,MEASURE,UNIT_MEASURE,AGE,SEX,SOCIO_ECON_STATUS,DEATH_CAUSE,
CALC_METHODOLOGY,GESTATION_THRESHOLD,HEALTH_STATUS,DISEASE,CANCER_SITE,TIME_PERIOD,
OBS_VALUE,DECIMALS,OBS_STATUS,OBS_STATUS2,OBS_STATUS3,UNIT_MULT
CSV 出口是这条数据的实用通道。 一次 2023 年的请求只有 90 KB,比 JSON 小得多,而且列名即维度名,pandas 直接读。
§4 记录结构与字段字典
§4.1 一条记录是什么
一条记录 = 一个 dataflow × 14 个维度取值 × 一个年份 = 一个数。
在只用到 6 个维度的预期寿命 dataflow 里,它简化成:
(DSD_HEALTH_STAT@DF_LE, DEU, A, LFEXP, Y, Y0, _T, ..., 2023) → 81.2
这不是「一个病人」,不是「一次调查」,是 SDMX 立方体里的一个格点。理解这一点,后面所有操作才不会错位。
§4.2 SDMX-JSON 的观测编码
这是这套数据最反直觉的一处。观测不是对象数组,而是一个以冒号分隔的索引串为键的字典:
"observations": {
"0:0:0:0:0:0:0:0:0:0:0:0:0:0": [6.2, null, null, null, null, null],
"1:0:0:0:0:0:0:0:0:0:0:0:0:0": [6.9, null, null, null, null, null]
}
键 "0:0:0:..." 的每一位是对应维度在 values 数组中的位置索引。要还原成可读记录,必须做一次笛卡尔解码:
import json, itertools
def decode_sdmx(path):
d = json.load(open(path))
st = d["data"]["structure"]
dims = st["dimensions"]["observation"]
names = [x["id"] for x in dims]
codes = [[v["id"] for v in x["values"]] for x in dims]
rows = []
for key, obs in d["data"]["dataSets"][0]["observations"].items():
idx = [int(i) for i in key.split(":")]
rec = {n: c[i] for n, c, i in zip(names, codes, idx)}
rec["OBS_VALUE"] = obs[0] # 第一元素才是值
rec["OBS_STATUS"] = obs[1] # 后续是状态标志
rows.append(rec)
return rows
三处容易崩的地方:
obs[0]是值,obs[1:]是属性。直接float(obs)会抛TypeError。- 键的位数必须等于维度数。解析时若维度数变化,硬编码
split(":")[0]之类会错位。 observations可能是空字典{}(该键组合无数据),要判空。
§4.3 三个状态字段
CSV 出口里有三列状态:OBS_STATUS、OBS_STATUS2、OBS_STATUS3。它们对应 SDMX 的多层属性。
| 字段 | 常见值 | 含义 |
|---|---|---|
OBS_STATUS |
空 | 正常上报值 |
OBS_STATUS |
E |
估算值 |
OBS_STATUS |
B / P |
断裂 / 临时值 |
OBS_STATUS2 / 3 |
空 / 各类代码 | 附加说明,多为口径变更或来源差异 |
最需要警惕的是 B(break in series)。 它表示「这个序列在某一年发生了口径断裂,前后的数不可直接连成一条曲线」。这是跨国时间序列分析里最容易被忽略的一类错误——你会得到一条看起来连续、实际中间换过算法的曲线。
防护代码:
df = df[df["OBS_STATUS"].fillna("") != "B"]
或者保留但打标,在做增长率类计算时排除断裂年份。
§4.4 UNIT_MEASURE 必须联查 codelist
UNIT_MEASURE 是代码不是文字。生命 expectancy 里是 Y(年),但别的 dataflow 可能是:
| 代码 | 含义 |
|---|---|
Y |
年 |
PT |
百分比 |
PS |
每十万人 |
PN |
每千人 |
USD_PPP |
购买力平价美元 |
XDC |
本国货币 |
这些代码在数据响应里没有释义,必须去 /codelist/OECD.ELS.HD/CL_UNIT_MEASURE 取。不做这一步,你的报表里会出现「卫生支出 = 4,823 USD_PPP」这种半成品。
§4.5 _Z 与 null 不是一回事
这两个概念常被混为一谈,但它们完全不同:
| 标记 | 含义 | 处理 |
|---|---|---|
维度值 _Z |
不适用——这个概念在本次查询的语境下没有意义 | 该维度在此 dataflow 里恒为 _Z,直接丢弃这一列 |
OBS_VALUE = null |
缺失——应该有值但没报 | 按缺失处理,不能填 0 |
在预期寿命的 14 个维度里,8 个恒为 _Z。如果你把它们当特征做 one-hot,会得到 8 列全零的常数特征;如果你把它们当缺失做插值,会对一个「不适用的概念」编造数据。
判断方法:如果某个维度在整份响应里只有一个取值且为 _Z,说明这个 dataflow 根本不用它,直接删列。
def drop_z_dims(df):
return df.loc[:, [c for c in df.columns if not (df[c].nunique() == 1 and df[c].iloc[0] == "_Z")]]
§5 检索、抽样与泄漏分析
§5.1 用医学 AI 的眼光看,泄漏在哪
OECD 数据本身没有训练/测试划分,但把它当协变量接进医学模型时,有三层风险:
第一层:口径泄漏(最隐蔽)。 OECD 的部分指标是事后重算的。比如「可避免死亡率」(DF_AM)本身就是用死亡率 + 医疗可及性估算出来的。如果你用它当特征去预测死亡率,等于用「包含被告变量的函数」当特征。
第二层:时间对齐泄漏。 OECD 数据有大量回溯修订。2026 年拉到的 2020 年数据,和 2021 年拉到的 2020 年数据可能不同。做训练/测试切分时如果用了「最新版本的全历史」,测试集的信息会经由修订渗透到训练集。
第三层:国家集合泄漏。 51 个经济体是当前成员与伙伴国。但 OECD 成员是陆续加入的(智利 2010、斯洛文尼亚 2010、哥伦比亚 2020、哥斯达黎加 2021)。用「2024 年的 51 国集合」去做 2000 年的分析,会引入幸存者偏差——那些后来加入的国家,在 2000 年本来就与老成员不同。
§5.2 三步抽样法
不要一上来拉全量 86 个 dataflow。推荐顺序:
- 看数据流清单:
/dataflow/OECD.ELS.HD—— 86 个名字,先确认你要的东西在哪一组 - 看维度结构:取一份
startPeriod限最近一年的数据,看 14 个维度里哪些有值、哪些是_Z - 看时间覆盖:去掉
startPeriod拉全量,看TIME_PERIOD的最早年份
三步之后,这个 dataflow 能不能用就清楚了。
§5.3 各 dataflow 的时间起点差异巨大
同一份数据,不同 dataflow 的年份起点完全不同:
| dataflow | 大致起止 | 说明 |
|---|---|---|
DF_LE 预期寿命 |
1960–2024 | 最长,65 年 |
DF_SHA 卫生支出 |
1970 年代起 | SHA 2011 框架下口径一致的部分更晚 |
DF_MED_TECH 医疗技术 |
1990 年代起 | 设备统计起步晚 |
DF_MORTALITY_EXCESS 超额死亡 |
2020 起 | 疫情期建立,只有周度近期数据 |
DF_PARIS 患者报告指标 |
2023 起 | 首轮调查 |
DF_GHGHC 卫生部门排放 |
近年 | 新议题 |
这意味着「拼宽表」时不能用统一年份窗口。 如果你设 1960–2024,DF_PARIS 和 DF_GHGHC 会全空;如果你设 2023,会丢掉 60 年的历史。正确做法是按研究问题定窗口,而不是按数据可用性定。
§5.4 一个实测:预期寿命的覆盖率
DF_LE 全量 52,241 条观测。粗算一下:51 个经济体 × 65 年 × 3 个 MEASURE × 5 个 AGE × 3 个 SEX = 149,175 个理论组合。实际 52,241,填充率约 35%。
稀疏来自哪里:
- 不是所有国家都有全部 5 个年龄组(Y40、Y80 是较晚加入的)
- 早期年份只有
LFEXP(总),没有按性别分 - 部分国家在部分年份中断
推论:即使是最成熟的指标,这个立方体也只有三分之一是填满的。先看稀疏结构,再决定怎么聚合。
§6 AI 就绪指南
§6.1 最小可用流水线
import requests
import pandas as pd
BASE = "https://sdmx.oecd.org/public/rest"
AGENCY = "OECD.ELS.HD"
def fetch_flow(dataflow_id, start=None, end=None, csv=True):
"""取一个 dataflow 的全部观测。CSV 通道比 JSON 快且直接可用。"""
url = f"{BASE}/data/{AGENCY},{dataflow_id},1.0/all"
params = {"dimensionAtObservation": "AllDimensions"}
if start: params["startPeriod"] = start
if end: params["endPeriod"] = end
headers = {"Accept": "text/csv"} if csv else {"Accept": "application/vnd.sdmx.data+json;version=1.0"}
r = requests.get(url, params=params, headers=headers, timeout=120)
r.raise_for_status()
if csv:
from io import StringIO
return pd.read_csv(StringIO(r.text))
# JSON 通道走 §4.2 的 decode_sdmx
import json
return pd.DataFrame(decode_sdmx_rows(r.json()))
df = fetch_flow("DSD_HEALTH_STAT@DF_LE", start="2015")
用 CSV 通道的理由:SDMX-JSON 要先解码索引串,多一层出错机会;CSV 直接是宽表,列名就是维度名。只有当你需要 structure 里的 codelist 名称(而不只是代码)时,才需要走 JSON。
§6.2 剔除 _Z 常量维度
def drop_z(df):
keep = []
for c in df.columns:
s = df[c].astype(str)
if s.nunique() == 1 and s.iloc[0] == "_Z":
continue
keep.append(c)
return df[keep]
df = drop_z(df)
print(df.shape) # 14 维 → 通常只剩 6 维
§6.3 把代码翻成可读标签
CSV 里的 MEASURE、UNIT_MEASURE、AGE、SEX 都是代码。取 codelist 才能翻译:
def get_codelist(cl_id):
url = f"{BASE}/codelist/{AGENCY}/{cl_id}"
r = requests.get(url, headers={"Accept": "application/vnd.sdmx.structure+json;version=1.0"}, timeout=60)
d = r.json()["data"]
cl = d["codelists"][0]
return {
c["id"]: (c.get("names", {}).get("en") or c.get("name", ""))
for c in cl["codes"]
}
MEASURE_NAME = get_codelist("CL_MEASURE")
df["measure_label"] = df["MEASURE"].map(MEASURE_NAME)
§6.4 处理序列断裂
def drop_breaks(df, status_col="OBS_STATUS"):
"""剔除 B(break in series)标记的观测。"""
s = df[status_col].fillna("").astype(str)
return df[~s.str.contains("B")]
df = drop_breaks(df)
如果研究需要连续序列,更稳的做法是按国家逐个检查断裂点,在断裂处切开而不是整段丢弃:
def split_at_breaks(df):
s = df[df["OBS_STATUS"].fillna("") == "B"]["TIME_PERIOD"].unique()
return {seg: g for seg, g in df.groupby(
df["TIME_PERIOD"].apply(lambda y: sum(1 for b in s if b <= y))
)}
§6.5 拼装多 dataflow 的宽表
FLOWS = {
"le": "DSD_HEALTH_STAT@DF_LE",
"che": "DSD_SHA@DF_SHA",
"phys": "DSD_HEALTH_EMP_REAC@DF_PHYS",
"beds": "DSD_HEALTH_REAC_HOSP@DF_BEDS_FUNC",
}
frames = {}
for k, f in FLOWS.items():
d = drop_z(fetch_flow(f, start="2010"))
d = d[d["SEX"].isin(["_T"]) & d["AGE"].isin(["Y0", "_T"])]
# 每个 dataflow 的 MEASURE 语义不同,必须各自筛
d = d.rename(columns={"OBS_VALUE": k})
frames[k] = d[["REF_AREA", "TIME_PERIOD", k]].drop_duplicates(["REF_AREA", "TIME_PERIOD"])
wide = frames["le"]
for k in ["che", "phys", "beds"]:
wide = wide.merge(frames[k], on=["REF_AREA", "TIME_PERIOD"], how="outer")
print(wide.notna().mean())
三个必须注意的点:
- 不同 dataflow 的
MEASURE取值集不同。DF_LE的MEASURE是LFEXP系列,DF_SHA的是支出相关代码。统一按SEX == "_T"筛是对的,但AGE在不同 flow 里含义不同——DF_SHA里AGE多数是_T,不要硬套Y0。 drop_duplicates不能省。 有些 dataflow 在同一(REF_AREA, TIME_PERIOD)下有多个MEASURE,筛完后仍可能重复,重复 merge 会炸行数。- 用
how="outer"。 各 dataflow 年份边界不同(§5.3),inner 会砍掉大半。
§6.6 给影像模型接 OECD 国家协变量
场景:跨欧洲多中心的影像数据集,想检验模型表现是否与国家卫生资源相关。
ISO3_SET = {"AUT","BEL","CZE","DNK","FIN","FRA","DEU","GRC","HUN",
"IRL","ITA","NLD","NOR","POL","PRT","ESP","SWE","CHE","GBR"}
cov = wide[wide["REF_AREA"].isin(ISO3_SET)] # 只留有数据的国家
sample = sample.merge(cov, left_on=["country_iso3","study_year"],
right_on=["REF_AREA","TIME_PERIOD"], how="left")
features = ["che", "phys", "beds"] # 卫生支出、医生密度、床位
sample["has_cov"] = sample[features].notna().all(axis=1)
四个要点:
study_year必须是采集年。 用发表年会把未来信息带进来。- 这 51 国本身就是一个偏样本。 你的结论只适用于「OECD 成员国 + 若干伙伴国」,不能推广到全球。
- 三个协变量高度相关。 卫生支出、医生密度、床位密度在富裕国家之间共线性很强,同时放进模型要看 VIF。
has_cov指示变量比插值更诚实。 有些国家某年确实没报,别编。
§6.7 study_year 落在数据边界外怎么办
这是实操中最常见的问题。假设样本采集于 2024 年,但某国的 DF_SHA 只到 2022 年。三个处理方案,按推荐度排序:
| 方案 | 做法 | 代价 |
|---|---|---|
| 用最近可得年份 + 距今年数标记 | 取 2022 值,同时记录 lag = 2 |
需要把 lag 当协变量或做敏感性分析 |
| 用多年均值 | 取 2020–2022 三年均值 | 平滑掉年度波动,但掩盖趋势 |
| 线性外推 | 按前 3 年趋势外推 | 不推荐——卫生支出有政策断点,外推极易出错 |
不推荐插值或外推的原因:卫生支出与医生密度都不是平滑过程,一次医改、一次危机就能改变斜率。外推得到的是「假精度」。
§7 质量评估、DAIMS 与口径交叉核对
§7.1 DAIMS 评估
| 维度 | 得分 | 说明 |
|---|---|---|
| 数据集标识 | 3/3 | 每个 dataflow 有稳定的 agency + ID + version 三元组,可精确寻址 |
| 许可清晰度 | 3/3 | 统一 CC BY 4.0,条款文本完整(含翻译与改编的署名要求) |
| 溯源信息 | 2/3 | 有 dataflow 级描述与来源说明,但观测级不标「哪国哪个部门报的」 |
| 版本与更新 | 2/3 | dataflow 有版本号,但历史版本不可回溯(latest 覆盖旧版) |
| 字段字典 | 3/3 | 14 个维度全部有 codelist,SDMX 标准本身即字典 |
| 缺失处理 | 2/3 | 有 _Z(不适用)与 null(缺失)区分,但 null 原因不细分 |
| 文档完备度 | 3/3 | API 文档、Data Explorer、SDMX 规范三处齐全 |
| 访问稳定性 | 2/3 | 服务稳定,但查询参数校验极严(多一个参数就 403) |
| 合计 | 20/24 = 83.3% | 与本库 world-bank-open-data 同分,但扣分点不同 |
§7.2 与世行那套 83.3% 的差别
两个数据集同分,但强项弱项正好互补:
| 维度 | 世行 | OECD |
|---|---|---|
| 标识 | 2/3(无单条 DOI) | 3/3(三元组精确寻址) |
| 字段字典 | 3/3 | 3/3 |
| 访问稳定性 | 3/3(参数宽松) | 2/3(校验极严) |
| 溯源 | 3/3(sourceOrganization 到 UNICEF 级) |
2/3(只到 dataflow 级) |
| 版本 | 2/3 | 2/3 |
一句话:世行胜在「宽松好用」,OECD 胜在「结构严谨」。前者适合快速探索,后者适合建可复现的管道。
§7.2b 两处 OECD 独有的可比性优势
DAIMS 那八项维度测的是「数据工程的规范程度」,测不出「内容本身的可比性」。OECD 在这两件事上有别处拿不到的优势:
第一,口径是被主动统一过的,不是事后拼接的。 世行是把各国上报的数原样编目,所以「五岁以下死亡率」在不同国家的统计基础可能完全不同。OECD 则是先定口径再收数:成员国按同一份问卷、同一套 SHA 2011 分类、同一套生命表方法上报。代价是覆盖国家少,收益是「德国和日本的可比性」远高于世行同指标。
第二,它有「反事实」类指标。 DF_AM(可避免死亡率)与 DF_PYLL(潜在寿命损失年)都不是观测,是按定义构造出来的量。它们的价值在于把「医疗系统本该做到什么」变成了一个可计算的数。世行与 WHO 都没有系统做这件事。
代价也要说清:这两个指标的定义依赖「现有医疗技术」这一假设,而假设本身随技术进步在变。跨十年比较可避免死亡率,等于用两套不同的假设比——这一点前文 §9.4 已经提过,这里再强调一次,因为它是这类指标最容易被误用的地方。
§7.3 口径交叉核对:OECD vs WHO vs 世行
三个地方都有预期寿命,但数不完全一样:
| 来源 | 方法 | 与 OECD 的典型差异 |
|---|---|---|
OECD DF_LE |
成员国自报死亡率 + OECD 统一生命表法 | 基准 |
| WHO GHO | 各国死亡率 + WHO 生命表法,缺报国用模型补 | 多在 ±0.3 年,缺报国差异更大 |
世行 SP.DYN.LE00.IN |
转自 UN WPP | 与 WHO 接近,与 OECD 在个别国家差 1 年以上 |
差异的真实来源不是谁算错了,而是三件事:
- 死亡率数据源不同:OECD 优先用成员国自报的精细死亡登记,UN/WHO 在缺报国要用模型补。
- 生命表方法不同:起算年龄组、开区间年龄组的处理方式有差别。
- 修订节奏不同:OECD 每年随成员更新,UN WPP 两年一版。
引用纪律:写论文时必须明确「数据来自 OECD Health Statistics,2026 年 10 月版」。不能写「据国际组织统计」。三套数据的差在 0.3–1 年这个量级,足以改变「某国是否进入长寿国家前十」这类结论。
§7.4 一处必须说清的口径:86 个 dataflow ≠ 86 个指标体系
同一份底层数据会在多个 dataflow 里出现。比如「医生数」至少出现在三处:
DSD_HEALTH_EMP_REAC@DF_PHYS—— 医生总数与密度DSD_HEALTH_EMP_REAC@DF_PHYS_AGE_SEX—— 按年龄与性别拆的医生数DSD_HEALTH_EMP_REAC@DF_PHYS_CAT—— 按类别(全科/专科)拆的医生数
这三个不是三份独立数据,是同一份数据的三种切法。 如果你的管道按 dataflow 循环拉取再求并集,同一批医生会被算三次。
§8 生态位与相邻资源
§8.1 跨国卫生数据的四层结构
第一层:国家行政统计(各国卫生部年报、死亡登记) —— 最原始,口径最不统一
第二层:专域深库(OECD Health、WHO GHO、Eurostat) —— 口径对齐,单域深
第三层:编目分发(世界银行、OWID) —— 跨域宽,单域浅
第四层:建模估计(GBD、IHME) —— 全球覆盖,有推算
OECD 在第二层的顶端。它的不可替代性有两处:
- 卫生系统的内部结构:SHA 2011 的筹资矩阵、ICU 床位、医疗技术清单、可避免死亡率。这些在第三、四层都没有。
- 可比的「质量」维度:患者报告结局(PROM)、患者体验(PREM)、患者安全文化。这一组几乎只有 OECD 在做跨国标准化。
§8.2 与 Eurostat 的分工
Eurostat 覆盖 EU-27,OECD 覆盖 38 个成员国 + 13 个伙伴国。重叠部分是欧洲国家。
| Eurostat | OECD | |
|---|---|---|
| 覆盖 | EU-27 + EFTA | 38 成员国 + 13 伙伴 |
| 结构 | 自有 SDMX 目录(TOC) | 自有 SDMX(dataflow) |
| 强项 | 行政统计、区域细分(NUTS) | 卫生系统绩效、跨域可比 |
| 弱项 | 非欧盟国一概没有 | 低收入国家一概没有 |
实用判断:做欧洲内部研究用 Eurostat(有 NUTS-2 区域级);做「欧洲 vs 日本 vs 韩国 vs 美国」的卫生系统比较,只能用 OECD。
§8.3 与 WHO GHO 的分工
who-gho(rid 175)覆盖 190+ 成员国,指标以疾病为主。OECD 覆盖 51 个经济体,指标以系统为主。
| 场景 | 用哪个 |
|---|---|
| 全球疾病负担、传染病监测 | GHO |
| 富裕国家卫生系统绩效比较 | OECD |
| 医疗支出结构(谁付钱、花在哪) | OECD(GHO 没有 SHA 级细节) |
| ICU 床位、医疗设备 | OECD |
| 低收入国家的卫生人力 | GHO |
一个常被忽略的点:GHO 里也有「卫生支出」指标,但那多是「总支出占 GDP」这类汇总数。SHA 2011 的三轴分解(功能 × 提供者 × 筹资)只有 OECD 有系统地做。
§8.4 与 PaRIS 的关系
DSD_PARIS@DF_PARIS 值得单独说。PaRIS(Patient-Reported Indicator Surveys)是 OECD 在 2023 年启动的患者报告指标调查,采集的是「患者在初级照护中的体验与健康结果」。
它的特殊性在于:
- 数据来源不是行政统计,是患者问卷
- 它有 PROM(患者报告结局),这在跨国卫生数据里极罕见
- 覆盖只有十余个国家,首轮 2023
对医学 AI 的意义:如果你要研究「模型输出与患者自评健康的一致性」,PaRIS 是目前少有的跨国可比来源。但覆盖太窄,只能做案例研究,不能做面板。
§9 许可、伦理与合规
§9.1 一套许可,但条款里有三处必须遵守
OECD 的数据与出版物统一采用 CC BY 4.0。但 OECD 在标准 CC BY 之外,明确写了几条附加要求:
| 要求 | 原文要点 | 实际影响 |
|---|---|---|
| 署名 | 必须引用原作 | 写「OECD (2026), OECD Health Statistics」并附 DOI 或 URL |
| 翻译 | 必须引用原作、标明改动、并加一句「如有出入以原文为准」 | 做中文版时必须加这句 |
| 改编 | 必须引用原作并注明「本作品是 OECD 原作的改编」 | 二次加工数据时要声明 |
| 第三方材料 | CC BY 不覆盖作品中的第三方材料 | 药品市场数据部分来自商业库,需单独看 |
| 标识 | 不得使用 OECD 的 logo、视觉识别或封面图,不得暗示 OECD 背书 | 产品里不能放 OECD logo |
| 争议解决 | 依 PCA 仲裁规则,仲裁地巴黎,仲裁员一名 | 商用大规模再分发需过法务 |
§9.2 与世行 CC BY 4.0 的差别
两家都用 CC BY 4.0,但附加条款不同:
| 世行 | OECD | |
|---|---|---|
| 仲裁地 | 华盛顿 | 巴黎 |
| 仲裁规则 | 世行自定流程(先调解 45 天) | PCA 2012 仲裁规则 |
| 翻译要求 | 未特别强调 | 明确要求加「以原文为准」句 |
| 改编声明 | 未特别强调 | 明确要求加改编声明 |
| Logo 限制 | 有 | 有,且更明确 |
对实际工作的影响:如果你要把 OECD 数据翻译成中文并加自己的注解发布,必须同时做三件事:引用原作、声明这是改编、加「如有出入以原文为准」。漏掉任何一条都不符合条款。
§9.3 有没有个体级数据
没有。OECD 健康统计全部是国家与地区级聚合。
但有两个边界情况要说清:
- PaRIS(
DF_PARIS) 底层是患者问卷,但发布的仍是聚合结果(国家级均值、分布)。个体记录不公开。 - 周度死亡数(
DF_MORTALITY系列) 粒度到周、到国家,但不到个人。它是死亡登记的聚合计数。
所以:这份数据可以放心用于训练集的国家层协变量,不涉及个人隐私。 但如果你的产品宣称「基于 OECD 患者数据训练」,那是误导——这里没有患者级数据。
§9.4 伦理:三类典型误用
数据本身是聚合统计,伦理风险不在隐私,在误用:
第一类:把 51 国当全球。 用 OECD 的「卫生支出与预期寿命关系」去推论全球卫生政策,会得出「多花钱就能长寿」的结论。但这个关系在低收入国家完全不成立——那里缺的不是钱,是基础设施与人力。
第二类:把自报当客观。 OECD 多数指标来自成员国自报。自报体系里,指标的定义边界、统计口径、上报完整性都有国别差异。「A 国医生密度高于 B 国」这句话,可能反映的是两国统计口径不同,而不是真实差距。
第三类:把可避免死亡率当因果。 「可避免死亡率」的定义是「在现有医疗技术下本可避免的死亡」。它是一个反事实构造,不是观测。用它证明「某国医疗系统无效」是把定义当成了发现。
§9.5 关于以色列、塞浦路斯等地的数据说明
OECD 出版物有固定的地域免责声明,涉及以色列统计数据由以色列相关部门提供、塞浦路斯与科索沃的表述等。引用具体国家数据时应保留这些说明,不做政治性解读。
§10 复用边界与三条硬结论
§10.1 三条硬结论
结论一:这是「卫生系统」库,不是「健康」库。
OECD 健康统计的核心价值不在疾病指标(那些 WHO 更全),而在系统的运转方式:钱怎么筹、人怎么配、床怎么设、质量怎么量。要用它回答「这个国家的人健康吗」,不如用 GHO;要用它回答「这个国家的卫生系统怎么运作」,只有它。
结论二:86 个 dataflow 里,真正能直接用于建模的是个位数。
大部分 dataflow 有严重的维度稀疏、年份断裂或覆盖不足。真正稳的只有:预期寿命(DF_LE)、死亡原因(DF_COM)、卫生支出(DF_SHA)、医生护士密度(DF_PHYS / DF_NURSE)、床位(DF_BEDS_FUNC)。先验证覆盖,再谈建模。
结论三:_Z 和 null 必须分开处理。
14 个维度里常有一半是 _Z(不适用),那不是缺失。混为一谈会让你对一个不适用的概念做插值,或者造出 8 列全零的常数特征。先删 _Z 常量维度,再谈缺失处理。
§10.2 该用 / 不该用
| 场景 | 该不该用 |
|---|---|
| 卫生系统绩效比较(支出 vs 产出) | ✅ 强烈推荐 |
| 给跨欧洲影像数据集打系统协变量 | ✅ 推荐(注意 51 国偏样本) |
| 医疗支出结构分析(SHA 2011) | ✅ 推荐(唯一系统来源) |
| 全球健康公平研究 | ❌ 用 WHO GHO 或世行 |
| 找个体患者数据或临床记录 | ❌ 这里没有 |
| 低收入国家卫生人力分析 | ❌ 覆盖为零 |
| 当临床决策依据 | ❌ 绝对不行(国家聚合) |
§10.3 一个判断标准:这个 dataflow 该不该进你的管道
四个问题:
问题一:它的维度能不能压到你要的粒度? 如果 14 个维度里有 10 个是 _Z,这个 dataflow 就很简单;如果有 6 个维度都有多个取值,你拿到的是多维立方体,必须先聚合。
问题二:它的年份能对上你的样本吗? 预期寿命有 1960–2024,超额死亡只有 2020 起。年份不匹配的协变量比没有更糟。
问题三:它的 OBS_STATUS 干净吗? 大量 B(断裂)或 E(估算)的序列,做趋势分析会得到假结论。
判断方法很简单,一行统计:
s = df["OBS_STATUS"].fillna("").astype(str)
print(s.value_counts(normalize=True).head())
如果 B 的占比超过 5%,这个 dataflow 就不适合做增长率类计算——断裂点前后的数不能相减。如果 E 占比高,说明这个指标大量依赖估算,引用时要说明「含估算值」。
问题四:它和你的结局同源吗? 可避免死亡率是用死亡率算出来的,不能当死亡率的特征。
四个都过了,才值得写进管道。
§10.4 一句话记住它
OECD 健康统计把「卫生系统」拆成 86 个可寻址的零件,让你能说清一个国家的医疗钱怎么花、人来不来得及、治得好不好。
§11 常见误读
这一节把散落在前文的坑集中一次讲清。每一条都对应一次实测中的真实失败——不是理论风险。
| 常见说法 | 实际情况 | 证据 |
|---|---|---|
| 「86 个 dataflow 是 86 个数据集」 | 86 个数据流归在 18 组 DSD 下。DSD_HCQO 一组就占 13 个 |
/dataflow/OECD.ELS.HD 按 @ 前前缀去重 |
| 「SDMX 请求随便拼就行」 | 用点号键时必须给全部 14 维,少一个就 403 Not enough key values,且不告诉缺哪维 |
实测 .../DF_LE,1.0/XYZ 返回 expecting 13 got 1 |
| 「加个时间戳破缓存是常规操作」 | OECD 服务端多一个不认识的参数就 403:Unknown query parameter : t |
实测加 ?t=... 直接失败 |
| 「返回的是对象数组」 | 观测是 {"0:0:0:...": [值, 状态...]} 的索引串字典,键的每一位是维度取值位置 |
§4.2 |
「obs 就是数值」 |
obs 是二维数组,obs[0] 才是值,obs[1] 往后是状态标志。float(obs) 抛 TypeError |
[6.2, null, null, ...] |
| 「51 个经济体就是 51 个国家」 | 含 38 个成员国 + 13 个伙伴国,没有一个是低收入国家 | REF_AREA 取值清单 |
| 「保加利亚、罗马尼亚是 OECD 成员」 | 不是。它们因「欧盟成员 + 有可比数据」被收录 | §2.5 |
「_Z 是缺失值」 |
_Z 是不适用(概念在此 dataflow 无意义)。null 才是缺失 |
预期寿命 14 维里 8 维恒为 _Z |
| 「一个国家的医生数只能查一个 dataflow」 | 出现在 DF_PHYS、DF_PHYS_AGE_SEX、DF_PHYS_CAT 三处,是同一份数据的不同切法 |
§7.4 |
「OBS_STATUS 可以忽略」 |
B = 序列断裂,前后不可连成一条曲线;E = 估算值。忽略会让趋势分析失真 |
§4.3 |
「UNIT_MEASURE 是给人看的」 |
是代码(Y / PT / PS / USD_PPP),响手里没有释义,必须查 codelist |
§4.4 |
| 「OECD 数据是 CC BY 4.0,随便用」 | 是 CC BY 4.0,但附加:翻译要加「以原文为准」、改编要声明、不得用 OECD logo | §9.1 |
| 「PaRIS 有患者级数据」 | 底层是问卷,发布的仍是国家级聚合。个体记录不公开 | §8.4 |
| 「各 dataflow 的年份窗口可以统一」 | 预期寿命 1960–2024,超额死亡 2020 起,PaRIS 2023 起 | §5.3 |
每一行背后的展开:
「86 个 dataflow 是 86 个数据集」 —— 这是最常见的层级错认。86 是「可独立取数的数据流」数,不是「独立的数据收集活动」数。18 组 DSD 是真正的数据模型:同一组 DSD 下的 dataflow 共享 14 维模型,只是各自在哪些维度上有值不同。DSD_HCQO(卫生服务质量)一组就有 13 个 dataflow,从急性病照护到患者安全文化,它们共用一个维度表。
「SDMX 请求随便拼就行」 —— SDMX 的键路径是位置敏感的。写 .../DF_LE,1.0/DEU.A.LFEXP 这种只给前几维的写法,服务端不会「按前缀匹配」,而是直接报 403。正确做法是用 all 让服务端展开,再在本地过滤。这个设计与世行 API 的「路径越简越好」正好相反。
「加个时间戳破缓存」 —— 这是从世行那边带过来的习惯,在 OECD 这边会直接失败。OECD 的查询参数白名单极严,多一个不认识的参数就 403。绕 CDN 缓存只能靠 Cache-Control: no-cache 请求头,不能加 URL 参数。
「返回的是对象数组」 —— SDMX-JSON 1.0 的观测编码是为压缩体积设计的:键是维度索引串,值是「值 + 属性位置索引」的数组。这个编码在维度少时看起来还行,14 维时键会变成 "0:0:2:0:4:1:0:0:0:0:0:0:0:12" 这种长串。必须写解码器。
「obs 就是数值」 —— 这是解码器写错时最常见的崩溃点。obs 是列表,第一元素是值。而且后续元素可能是 null,表示该属性位置没有标志。
「51 个经济体就是 51 个国家」 —— 51 里 OECD 成员国 38 个,其余 13 个是伙伴国或关键非成员。关键是没有一个是低收入国家。 这决定了这份数据不能用于全球健康公平研究。
「_Z 是缺失值」 —— 这是最需要澄清的一处。_Z 是 SDMX 的标准代码,含义是「不适用」(not applicable)。在预期寿命的 14 维里,SOCIO_ECON_STATUS、DEATH_CAUSE、CALC_METHODOLOGY、GESTATION_THRESHOLD、HEALTH_STATUS、DISEASE、CANCER_SITE 这 7 个维度恒为 _Z——因为「预期寿命」这个概念没有死因、没有癌症部位。把它们当缺失插值,等于给一个不存在的概念编数据。
「一个国家的医生数只能查一个 dataflow」 —— 医生数出现在三个 dataflow 里,分别是总数、按年龄性别分、按类别分。它们是同一份底层数据的三视图。按 dataflow 循环拉取会让同一批医生被算三次。
「OBS_STATUS 可以忽略」 —— B 表示序列断裂(break in series),通常因为定义变更或数据来源更换。忽略它,你会得到一条看起来连续、中间换过算法的曲线。在计算增长率、做趋势外推时,这类错误极难事后发现。
「UNIT_MEASURE 是给人看的」 —— 它是代码。Y=年、PT=百分比、PS=每十万人、USD_PPP=购买力平价美元。响应里只有代码,释义在 /codelist/OECD.ELS.HD/CL_UNIT_MEASURE。不做联查,报表里会出现「支出 = 4823 XDC」这种半成品。
「CC BY 4.0 随便用」 —— 许可本身宽松,但 OECD 加了三条明确要求:翻译须加「如有出入以原文为准」;改编须声明「这是 OECD 原作的改编」;不得使用 OECD logo 或暗示背书。做中文产品时第一条几乎必然适用。
「PaRIS 有患者级数据」 —— PaRIS 底层确实是患者问卷(PROM / PREM),但公开的是国家级聚合结果。想拿个体记录,得单独申请,且条款不同。
「各 dataflow 年份窗口可以统一」 —— 预期寿命 1960–2024(65 年),卫生支出约 1970 年代起,医疗技术 1990 年代起,超额死亡 2020 起,PaRIS 2023 起。统一窗口会让你要么丢历史、要么全空。
§11.2 四类容易忽略的结构性问题
第一,structure 里没有 codelist 名称,只有 ID。
取数时若用 references=none,响应里只有代码。要拿到「LFEXP 是什么意思」,必须单独请 /codelist。这意味着一个完整的数据集下载至少是两次请求:一次取数据,一次取代码表。
第二,dataSets[0].action 可能是 Information。
正常数据响应里 action 是 Information。但如果查询有问题,仍可能返回 200 与一个空 observations。不要只判 HTTP 状态码,要判 observations 是否为空。
第三,version 是 1.0,但 dataflow 会演进。
URL 里的 1.0 是 dataflow 版本。OECD 更新数据时通常原地更新同一版本,不升版本号。所以同一 URL 在不同时间拉取会得到不同数,且没有版本标识可以区分。这是与「可复现」直接冲突的一点——必须自己记录采集时间。
第四,PaRIS 与 HSCS 是调查数据,不是行政统计。
DSD_PARIS、DSD_HSCS2023、DSD_HSCS 这三个 dataflow 的底层是问卷,样本量有限,有抽样误差。它们和 DF_LE 那种「全国死亡登记汇总」在统计性质上完全不同,不能混在一个面板里当同质变量。
§11.3 上线前的十项自检
- [ ]
Accept头设对了(数据用application/vnd.sdmx.data+json;version=1.0,CSV 用text/csv) - [ ] 没有在 URL 里加自定义参数(会 403)
- [ ] 用了
all作为键,而不是只给前几维 - [ ] 解码时取的是
obs[0],不是obs - [ ] 删掉了恒为
_Z的常量维度 - [ ]
null与_Z分开处理 - [ ] 检查了
OBS_STATUS里的B(序列断裂) - [ ] 联查了 codelist 把
UNIT_MEASURE翻成文字 - [ ] 记录了采集时间(因为 dataflow 原地更新,无版本号)
- [ ] 引用时写明「OECD Health Statistics,年月版」,并区分于 WHO / 世行的同指标数字
附录 A:本篇数字速查
| 数字 | 值 | 来源 |
|---|---|---|
| dataflow 总数 | 86 | /dataflow/OECD.ELS.HD 的 dataflows 长度 |
| DSD 结构组数 | 18 | 按 dataflow ID 的 @ 前缀去重 |
| 最大 DSD 组 | DSD_HCQO 13 个 |
卫生服务质量与结局 |
| 第二组 | DSD_HEALTH_STAT 13 个 |
健康状态与死亡 |
| REF_AREA 经济体数 | 51 | DF_LE 的维度取值 |
| OECD 成员国数 | 38 | 51 中扣除 13 个伙伴国 |
DF_LE 观测数 |
52,241 | 全量拉取 |
DF_LE 年份跨度 |
1960–2024(65 年) | TIME_PERIOD |
DF_LE 维度数 |
14 | dimensions.observation |
DF_LE 中 _Z 维度数 |
8 | 常量不适用维度 |
| 全部 agency dataflow 数 | 1,552 | /dataflow/all/all/latest |
| 默认许可 | CC BY 4.0 | OECD 统一声明 |
| 仲裁地 | 巴黎(PCA 规则) | 许可附加条款 |
| DAIMS | 20/24 = 83.3% | 本条目评估 |
附录 B:五个可复制的查询
# 1. 列出健康域全部 dataflow(86 个)
curl -s -H "Accept: application/vnd.sdmx.structure+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/dataflow/OECD.ELS.HD?references=none" \
| python3 -c "import sys,json;d=json.load(sys.stdin);print(len(d['data']['dataflows']))"
# 86
# 2. 取预期寿命 2022 年起全部观测
curl -s -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2022&dimensionAtObservation=AllDimensions"
# 3. 同上但走 CSV(推荐,直接可读)
curl -s -H "Accept: text/csv" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2023"
# 4. 看维度结构(十四维有哪些取值)
curl -s -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2023&dimensionAtObservation=AllDimensions" \
| python3 -c "
import sys,json
d=json.load(sys.stdin)
for x in d['data']['structure']['dimensions']['observation']:
print(x['id'], len(x['values']))"
# 5. 取单位代码表
curl -s -H "Accept: application/vnd.sdmx.structure+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/codelist/OECD.ELS.HD/CL_UNIT_MEASURE"
附录 C:核对清单
拿到数据后按顺序跑:
- [ ] 确认 dataflow ID 拼写完整(形如
DSD_XXX@DF_YYY,@不能省) - [ ] 确认
Accept头没写错(写错会得到 406 或 XML) - [ ] 确认键用了
all或给了完整 14 维 - [ ] 确认没有加自定义 URL 参数
- [ ] 解码后检查行数是否合理(不是 0,也不是几百倍于预期)
- [ ] 检查
_Z常量维度是否已删 - [ ] 检查
OBS_STATUS里B的占比 - [ ] 按研究问题定年份窗口,不按数据可用性定
- [ ] 记录采集日期(dataflow 原地更新,无版本标识)
- [ ] 引用时写「OECD Health Statistics」,不与 WHO / 世行数字混用
附录 D:十八组 DSD 速查
| DSD | 数 | 一句话 |
|---|---|---|
DSD_HCQO |
13 | 卫生服务质量与结局:急性病、癌症、精神卫生、患者体验、患者安全 |
DSD_HEALTH_STAT |
13 | 健康状态:预期寿命、死因、可避免死亡、癌症、传染病、孕产妇婴儿死亡 |
DSD_HEALTH_PROC |
10 | 服务利用:门诊、手术、免疫、筛查、等待时间、住院时长 |
DSD_HEALTH_REAC_EMP |
10 | 卫生人力:医生、护士、助产士、牙医、药剂师、物理治疗师、薪酬 |
DSD_HEALTH_LVNG |
6 | 健康决定因素:饮酒、体重、食品供应、烟草、电子烟 |
DSD_HEALTH_REAC_HOSP |
6 | 设施设备:ICU 床、按功能分床、医院数、医疗技术 |
DSD_HEALTH_EMP_REAC |
5 | 人力细分:按年龄性别、按类别、卫生与社会就业 |
DSD_SHA |
5 | 卫生支出与筹资(SHA 2011):支出、筹资方案、资本形成、COVID 支出 |
DSD_HEALTH_LTCR |
4 | 长期照护:床位、接受者、人员 |
HEALTH_PHMC |
4 | 药品市场:总览、关键指标、消费、销售 |
DSD_HEALTH_MORTALITY |
3 | 周度死亡:总、COVID、超额死亡 |
DSD_GHGHC |
1 | 卫生部门温室气体排放 |
DSD_HEALTH_LTCR_FAC_OWNER |
1 | 长护机构按所有制 |
DSD_HEALTH_PROT |
1 | 医疗保障覆盖 |
DSD_HEALTH_WFMI |
1 | 卫生人力国际迁移 |
DSD_HSCS2023 |
1 | 卫生系统特征调查 2023 |
DSD_HSCS |
1 | 卫生系统特征调查 2012/2016/2018 |
DSD_PARIS |
1 | 患者报告指标调查(PROM / PREM) |
附录 E:一次最小可复现实验
十分钟内能跑完,用来验证本条目所有关于 SDMX 结构的断言。
# 1. 看 dataflow 有多少(18 组 DSD)
curl -s -H "Accept: application/vnd.sdmx.structure+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/dataflow/OECD.ELS.HD?references=none" \
| python3 -c "
import sys,json,collections
d=json.load(sys.stdin)['data']['dataflows']
print(len(d), len({x['id'].split('@')[0] for x in d}))"
# 期望: 86 18
# 2. 看不给全维度会怎样
curl -s -o /dev/null -w "%{http_code}\n" -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/DEU"
# 期望: 403
# 3. 看加自定义参数会怎样
curl -s -o /dev/null -w "%{http_code}\n" -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/dataflow/OECD.ELS.HD?references=none&t=1"
# 期望: 403
# 4. 看维度数与 _Z 数量
curl -s -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2023&dimensionAtObservation=AllDimensions" \
| python3 -c "
import sys,json
d=json.load(sys.stdin)['data']['structure']['dimensions']['observation']
print(len(d), sum(1 for x in d if len(x['values'])==1 and x['values'][0]['id']=='_Z'))"
# 期望: 14 8
# 5. 看观测是数组不是标量
curl -s -H "Accept: application/vnd.sdmx.data+json;version=1.0" \
"https://sdmx.oecd.org/public/rest/data/OECD.ELS.HD,DSD_HEALTH_STAT@DF_LE,1.0/all?startPeriod=2023&dimensionAtObservation=AllDimensions" \
| python3 -c "
import sys,json
d=json.load(sys.stdin)['data']['dataSets'][0]['observations']
k=list(d)[0]; print(k[:30], type(d[k]), d[k][:2])"
# 期望: 索引串 <class 'list'> [值, None]
跑完这五条,本条目 §3.2、§3.3、§3.4、§4.2、§11 里的每条断言都被独立验证过一遍。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- ucdp — 共享标签:公共卫生与流行病学 / 电子健康记录 / 评测基准
- world-bank-open-data — 共享标签:公共卫生与流行病学 / 电子健康记录 / 评测基准
- mimicel — 共享标签:公共卫生与流行病学 / 电子健康记录
- mimiciii-demo — 共享标签:公共卫生与流行病学 / 电子健康记录
- mimiciv — 共享标签:公共卫生与流行病学 / 电子健康记录
- armd-mgb — 共享标签:公共卫生与流行病学 / 电子健康记录 / 评测基准
- sdoh-adverse-pregnancy — 共享标签:公共卫生与流行病学 / 电子健康记录 / 评测基准
- amr-uti — 共享标签:公共卫生与流行病学 / 电子健康记录 / 评测基准
- tpehgt — 共享标签:电子健康记录 / 评测基准
- arxiv — 共享标签:公共卫生与流行病学 / 评测基准
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

