OECD 健康统计 — 86 个 dataflow,把「卫生系统」拆成可比的零件

它不是一份数据,是一套 SDMX 3.0 的机器可读接口:生命 expectancy、医疗支出与筹资、医生护士密度、病床与 ICU、住院与手术量、癌症与传染病死亡、可避免死亡、药品市场——全部放在同一套维度模型下。做卫生系统比较研究时,你真正需要的那张「国家 × 年 × 指标」表,这里有 18 组数据流等着被拼起来

来源 OECD Health Statistics(OECD.ELS.HD,经 OECD Data Explorer 分发) url: https://data-explorer.oecd.org/发布时间: 2026-10-24最后更新: 2026-10-24 阅读 7
OECD 健康统计 — 86 个 dataflow,把「卫生系统」拆成可比的零件

信息速览

数据集名称OECD 健康统计 — 86 个 dataflow,把「卫生系统」拆成可比的零件
数据类型86 个 dataflow,18 组 DSD,51 个经济体,1960–2024,SDMX 3.0,CC BY 4.0,卫生系统绩效
规模不适用(国家与地区级聚合统计;PaRIS 患者报告指标调查在独立的 dataflow,为抽样调查聚合结果,不含个体记录)
接入方式OECD Health Statistics(OECD.ELS.HD,经 OECD Data Explorer 分发) url: https://data-explorer.oecd.org/
AI 就绪度

§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 —— agency
  • DSD_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

三处容易崩的地方:

  1. obs[0] 是值,obs[1:] 是属性。直接 float(obs) 会抛 TypeError。
  2. 键的位数必须等于维度数。解析时若维度数变化,硬编码 split(":")[0] 之类会错位。
  3. 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。推荐顺序:

  1. 看数据流清单:/dataflow/OECD.ELS.HD —— 86 个名字,先确认你要的东西在哪一组
  2. 看维度结构:取一份 startPeriod 限最近一年的数据,看 14 个维度里哪些有值、哪些是 _Z
  3. 看时间覆盖:去掉 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())

三个必须注意的点:

  1. 不同 dataflow 的 MEASURE 取值集不同。 DF_LE 的 MEASURE 是 LFEXP 系列,DF_SHA 的是支出相关代码。统一按 SEX == "_T" 筛是对的,但 AGE 在不同 flow 里含义不同——DF_SHA 里 AGE 多数是 _T,不要硬套 Y0。
  2. drop_duplicates 不能省。 有些 dataflow 在同一 (REF_AREA, TIME_PERIOD) 下有多个 MEASURE,筛完后仍可能重复,重复 merge 会炸行数。
  3. 用 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)

四个要点:

  1. study_year 必须是采集年。 用发表年会把未来信息带进来。
  2. 这 51 国本身就是一个偏样本。 你的结论只适用于「OECD 成员国 + 若干伙伴国」,不能推广到全球。
  3. 三个协变量高度相关。 卫生支出、医生密度、床位密度在富裕国家之间共线性很强,同时放进模型要看 VIF。
  4. 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 年以上

差异的真实来源不是谁算错了,而是三件事:

  1. 死亡率数据源不同:OECD 优先用成员国自报的精细死亡登记,UN/WHO 在缺报国要用模型补。
  2. 生命表方法不同:起算年龄组、开区间年龄组的处理方式有差别。
  3. 修订节奏不同: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 在第二层的顶端。它的不可替代性有两处:

  1. 卫生系统的内部结构:SHA 2011 的筹资矩阵、ICU 床位、医疗技术清单、可避免死亡率。这些在第三、四层都没有。
  2. 可比的「质量」维度:患者报告结局(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 — 共享标签:公共卫生与流行病学 / 评测基准

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

返回 AI-Ready 数据集