人类死亡率数据库 — 41 国死亡账本,回溯到 1751 年

41 个国别页面 / 50 个 population 包 / 1751–2024 / 单岁×性别×年 / 死亡数、暴露人口、生命表

来源 https://www.mortality.org/发布时间: 2026-10-29最后更新: 2026-10-29 阅读 10
人类死亡率数据库 — 41 国死亡账本,回溯到 1751 年

信息速览

数据集名称人类死亡率数据库 — 41 国死亡账本,回溯到 1751 年
数据类型元数据,原始数据链接,文献引用
规模不适用(国家×年龄×性别×年份的聚合统计,不含任何个体记录;41 个国别页面、50 个可下载 population 包)
接入方式https://www.mortality.org/
AI 就绪度

「死亡是每个国家都在记录、却几乎没有两个国家用同一种口径记录的事件。」人类死亡率数据库(Human Mortality Database,下称 HMD)就是为这句话建的。它由德国马克斯·普朗克人口研究所、美国加州大学伯克利分校人口学系与法国国家人口研究所三方共建,官方自述为「发达国家死亡率领域领先的科学数据资源」,做法是把各国统计局的出生数、死亡数、人口数收上来,用同一套方法协议重建成年龄×性别×年份的单一网格。

它给的不是死因,而是死亡本身:某一年、某个年龄、某个性别,死了多少人、暴露了多少人年、死亡率是多少、由此推得的生命表与预期寿命是多少。全库最早的一条序列来自瑞典,死亡数与暴露人口都自 1751 年起,出生数自 1749 年起;最近的一端到 2024 年,部分国家的人口规模已到 2025 年 1 月 1 日。全库共 41 个国别页面,对应 50 个可下载的 population 包——德国分东西、法国分全体与平民、新西兰分全体与毛利/非毛利、英国分五套,这些子人口各自成包。

它是免费的,但要注册;它是开放的,但只开放自己算出来的那一半。HMD 团队构造的暴露人口、死亡率与生命表按 CC BY 4.0 发布,各国统计局提供的输入数据仍归各国所有,不得商用、不得转发布。一屏速览见下表。

维度 内容
中文名 / 英文名 人类死亡率数据库 / Human Mortality Database(HMD)
共建机构 马克斯·普朗克人口研究所(MPIDR)、加州大学伯克利分校人口学系、法国国家人口研究所(INED)
起始年份 2000 年立项;数据序列最早回溯至 1751 年(瑞典)
覆盖范围 41 个国家与地区,50 个可下载 population 包
时间跨度 因国而异,瑞典 1751–2024 最长;多数欧洲国家 19 世纪中至 2024
最小记录单元 国家(population)× 年龄(单岁 0–109,110+)× 年份 × 性别
年龄上限处理 110 岁为开放区间,110+ 合并为一格
核心统计量 死亡数、暴露人口、人口规模、出生数、年龄别死亡率、标准化死亡率、生命表、出生预期寿命
网格切法 1×1、1×5、1×10、5×1、5×5、5×10 六种,另有 Lexis 三角文件
文件格式 制表符分隔的 ASCII 文本(.txt);输入库为逗号分隔
子库 STMF(周死亡数,38 个人口单位)、HCD(固定死因列表,18 国)
获取方式 官网注册后下载;按统计量打包、按国别打包两条路径
许可 构造估计值 CC BY 4.0;输入数据不可商用、不可转发布
更新节奏 按日期打版本,2026 年 2–8 月发布 8 个版本,每版带独立 DOI
一句话定位 全世界唯一一套把「各年龄死多少、活多久」对齐到可直接算生命表的跨国长序列

§1 它要解决的具体麻烦

1.1 各国都在记死亡,但没有两国记法相同

人口学研究里最反直觉的一件事是:死亡登记的历史比统计学本身还长,可比性却比任何临床指标都差。同一个「死亡率」,在瑞典是教区簿记里逐日登记的死亡人数除以年末人口,在俄国是户籍册上的死亡登记除以估算人口,在日本是户籍与住民票两套系统交叉核对后的结果。年龄的分组方式不同(单岁、五岁一组、0 岁与 1–4 岁合并),死亡发生的时间点不同(登记日还是发生日),人口的分母不同(常住人口、法定人口、平民人口、去军事化人口),统计的边界也不同(是否含死胎、是否含境外死亡的本国公民)。

HMD 的存在理由就是把这一步一次性做掉。它不生产原始数据——原始数据永远在各国统计局手里;它生产的是一套重述:把各国的原始件按统一协议拆解、补齐、对齐,输出成同一套行列结构。你在自己的代码里读到的每一个数字,背后都对应着协议里的一条规则;规则的例外,写在每个国家那份几十页的说明 PDF 里。

这个定位决定了它的边界。它不是死亡登记库,所以不告诉你死因;它不是抽样调查,所以没有个体属性;它是分母与分子的对齐工程,所以当一国的原始登记本身不可靠时,它能做的只是标注警示,而不是变出数据。

1.2 三条常见的错误期待

第一条,期待它覆盖全球。它的官方措辞是「发达国家死亡率」,41 个国别页面里,欧洲占了大半,加上北美两国、澳新、日本、韩国、香港、台湾、智利、以色列。撒哈拉以南非洲、南亚、东南亚大部、拉美大部都不在其中。想算全球疾病负担,它给不了你要的国家,硬用会造成系统性缺口。

第二条,期待它给死因。HMD 主库只有全因死亡。死因在 HCD 子库里,覆盖 18 个国家,且是按固定列表重建的版本——把 ICD-8、ICD-9、ICD-10 的不同编码映射到一套不变的分类上,才能跨年份比较。没做过这层映射的人,直接拿各国原始死因表拼时间序列,会在 ICD 改版的年份看到假的跳变。

第三条,期待它是个下载即用的干净 CSV。它的文件是定宽文本、带两行表头、缺失用点号表示、年龄列写「110+」、年份列在领土变更年份会写「1914-」和「1914+」。第一次读进来如果不去处理这些,pandas 会把整列判成字符串,后续所有计算静默出错。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
某国某年各年龄的死亡率 单岁 0–109 加 110+ 的 mx,六种网格任选 够,且是官方平滑后的版本
自己算生命表与预期寿命 mx、qx、ax、lx、dx、Lx、Tx、ex 全套已算好,也可拿 mx 自己重算 够,两条路都给
按出生年看一代人的死亡经历 世代表(cohort),但要求该队列已连续观测满 30 年 部分够,末端队列大量留空
比较两国婴儿死亡率的历史 统一口径的出生数与婴儿死亡数 够,但早期段有质量警示
死因结构与死因别死亡率 主库没有;HCD 子库有 18 国,且是重建版 部分够,国家数骤减
周级别的超额死亡监测 STMF 子库,38 个人口单位 够,但另一套口径
个体级死亡记录 无 不够,且不可能有
亚国家(省、市)死亡率 无;只有少数国家在包内拆了子人口 不够,需转亚国家库
全球南方国家的死亡水平 无 不够,须转 WHO GHO 或联合国人口展望
医疗干预、病程、合并症 无 不够,这是聚合死亡库的天然边界

1.4 你的处境与它能帮到的事

你的处境 它能帮你什么 下一步该做什么
要给一个跨国的生存分析模型找基线风险 直接给 41 国的年龄别死亡率曲线,按年取用 先定年龄网格(1×1 还是 5×1),再按人口代码批量取 Mx
要评估某国某年的超额死亡 年度序列可做年均基线,STMF 可做周基线 年度用主库,周用 STMF,两者不要混在同一条序列里
要做养老金或寿险的精算假设 周期生命表加出生预期寿命,可直接用于定价 注意周期表与世代表的差别,定价通常用周期表
要把死亡率当协变量接进影像或电子病历模型 国家×年龄×年份的一列数值,键值干净 按受试者的国家、年龄、年份做左连接,注意年份缺口
要研究 19 世纪的人口转变 瑞典、丹麦、法国、意大利等有长序列 先读该国那份 background PDF,确认早期段能否用
要做死因结构的时间序列 HCD 的固定列表可跨 ICD 改版比较 只用重建序列,别与原始分类序列混拼
要给论文补一张各国预期寿命对比图 官方另有四张汇总表,直接是整理好的指标 用汇总表比自己拼快,但要注意它滞后于主库
要复现某篇论文的死亡率数字 每版带 DOI,可精确回溯到某一日的快照 记下 DOI 里的日期串,按该日期重下

1.5 一句话判断

如果你的问题里有年龄、性别、年份、国家这四个维度,且不涉及死因,HMD 基本是默认答案;只要缺其中任何一个,或者要问「死于什么」,它就不是。

1.6 不适合谁:四条边界

第一条,做全球健康估计的人不适合把它当主数据源。41 个国家里没有一个人口大国在非洲,印度也不在。用它做全球汇总,等于用欧洲的曲线去外推七十亿人。

第二条,做死因分析的人不适合只拿主库。主库的全因死亡是一个总数,任何「心血管占多少」「癌症占多少」的问题都必须转 HCD,而 HCD 只有 18 国,重建序列的起点普遍在 1950 年代之后。

第三条,做个体风险预测的人不适合把它当标签源。它没有个体记录,也没有任何协变量,你能用它做的只有按国家年龄年份取一个基线率,剩下的个体差异必须靠别的数据。

第四条,要求最新一年数据即可用的人不适合在发布当月取数。主库末端年份各国不一,最新一年常是暂定值,下一版会修订;把暂定值写进已发表的表里,等于给未来的自己埋一个不可复现的坑。

§2 数据构成与规格

2.1 三层结构:输入库、完整序列、派生指标

HMD 的数据不是一个平面,而是三层叠起来的。最底层是输入库(Input Data),放的是各国统计局给的原始件:出生数、死亡数、人口数,加上领土调整因子;每个 population 另配四份 PDF——背景与说明、逐点注释、数据来源、参考文献与编码对照。中间层是完整序列(Complete Data Series),是 HMD 用方法协议从输入库算出来的成品:出生数、死亡数、1 月 1 日人口规模、暴露人口(周期与世代)、死亡率(周期与世代)、生命表(周期与世代)。最上层是派生指标,官方单独做了四张汇总表——婴儿死亡率、活到 65 岁的概率、出生预期寿命、人口与暴露——给只要速览数字的人用。

层 内容 谁改得动 出问题时的表现
输入库 出生数、死亡数、人口数、领土调整因子、四份 PDF 只有各国统计局 原始登记漏报,HMD 只能标注,无法补出人
完整序列 暴露人口、死亡率、标准化死亡率、生命表 HMD 团队 协议改版时整条序列会整体平移
派生指标 婴儿死亡率、活到 65 岁概率、出生预期寿命、人口 HMD 团队 更新滞后于主库,个别国家缺行
子库 STMF 按周、按年龄组的死亡数 HMD 团队 覆盖人口单位少,末端周不完整
子库 HCD 固定列表的死因别死亡数与死亡率 HMD 团队 只有 18 国,重建序列起点晚

2.2 字段与记录的完整字典

下表按文件族给出主要字段。完整序列文件普遍是两行表头:第一行写统计量与性别,第二行写单位或注释;数据从第三行起,制表符分隔。输入库则是逗号分隔、表头单行、字段名全大写。

文件族 主键字段 数值字段 类型 取值与单位 常见陷阱
Births.txt Year Female、Male、Total 数值 活产数,人 部分年份是估计值,出现小数
Deaths_1x1.txt Year、Age Female、Male、Total 数值 死亡数,人 早期年份为拆分估计,非整数
Deaths_lexis.txt Year、Age、Cohort Female、Male、Total 数值 死亡数,人 三角内出生年相邻,聚合时易重复计数
Exposures_1x1.txt Year、Age Female、Male、Total 数值 人年 与人口规模不是一回事,不能互相替代
Population.txt Year、Age Female、Male、Total 数值 每年 1 月 1 日的存量人数 领土变更年出现「1914-」「1914+」两行
Mx_1x1.txt Year、Age Female、Male、Total 数值 年龄别死亡率,无量纲 值可以大于 1,不是概率
sdr_5x1.txt Year、Age Female、Male、Total 数值 fltper_1x1.txt Year、Age
cMx_1x1.txt Cohort、Age Female、Male、Total 数值 世代死亡率 主键是出生年,不是日历年
E0per.txt Year Female、Male、Total 数值 出生预期寿命,年 单年值,不含置信区间
输入库 birth.txt Year、Month、Day Count 整数 原始出生登记 月份列可空,需按年汇总后再用
输入库 death.txt Year、Month、Day、Age Count 整数 原始死亡登记 年龄编码与完整序列不同,需查对照表
输入库 pop.txt Year、Age Count 整数 原始人口数 普查年与估计年混排

2.3 一条记录的完整解剖

拿瑞典 2024 年 70 岁女性这一格来看。README 里说的「一行」,指的是某个文件里由年龄与年份定位的那一行,但真正的最小记录单元是那一行里的三个性别列之一,再加上文件本身携带的 population 与统计量语义。

Deaths_1x1.txt 的开头(制表符分隔,两行表头):
Year	Age	Female	Male	Total
		Deaths	Deaths	Deaths
1751	0	4635.4	5102.7	9738.1
1751	1	1798.3	1979.0	3777.3
1751	110+	0.0	0.0	0.0

Exposures_1x1.txt 的同位置:
1751	0	24390.6	25417.3	49807.9

Mx_1x1.txt 的同位置(= 死亡数 ÷ 暴露人口):
1751	0	0.190049	0.200755	0.195527

fltper_1x1.txt 的一行(女性周期生命表,八列):
2024	70	0.013755	0.013661	0.480259	94988.12	1297.94	94358.36	1753936.4	18.464

这条链解释了为什么 HMD 不能「只下一个文件」。只下 Mx 就丢了分母,只下生命表就丢了可复算性,只下死亡数就什么都算不了。要么按统计量把六个文件都拉下来,要么按国别把整个包拉下来。

#-- 读一个文件并确认三件事:表头、缺失编码、年龄列类型
import pandas as pd

def read_hmd(path, value_name="Total"):
    #-- 前两行是表头与单位,跳过;"." 是缺失
    df = pd.read_csv(path, sep=r"\s+", skiprows=2, na_values=".")
    df.columns = [c.strip() for c in df.columns]
    #-- 年龄列含 "110+",统一转成整数 110 并打上开放区间标记
    df["Age"] = df["Age"].astype(str).str.replace("+", "", regex=False)
    df["Age"] = pd.to_numeric(df["Age"], errors="coerce").astype("Int64")
    df["Year"] = df["Year"].astype(str).str.rstrip("-+")
    df["Year"] = pd.to_numeric(df["Year"], errors="coerce").astype("Int64")
    return df[["Year", "Age", value_name]].rename(columns={value_name: "value"})

#-- 用法:分母与分子各读一次,按 (Year, Age) 对齐后相除
deaths = read_hmd("SWE/STATS/Deaths_1x1.txt", "Female")
expos  = read_hmd("SWE/STATS/Exposures_1x1.txt", "Female")
mx = deaths.merge(expos, on=["Year", "Age"], suffixes=("_d", "_e"))
mx["mx_recomputed"] = mx["value_d"] / mx["value_e"]
print(mx.tail(8))

2.4 六种年龄×时间的切法

死亡数与暴露人口可以按年龄与时间的粒度切六种网格,命名规则是「年龄粒度×时间粒度」。这不是六份不同的数据,是同一份数据被重新聚合了六次:1×1 最细,5×10 最粗。选哪一种取决于你要做的分析,而不是哪一种更「全」。

网格 年龄粒度 时间粒度 典型用途 代价
1×1 单岁 单年 精细建模、生命表复算、机器学习特征 文件最大,早期年份含大量拆分估计
1×5 单岁 五年 平滑随机波动、跨十年比较 边界年归属需明确
1×10 单岁 十年 长期趋势、世纪尺度对比 小国的高龄段信息被抹平
5×1 五岁组 单年 面板回归、跨国年度比较 0 岁与 1–4 岁合并后婴儿死亡率失真
5×5 五岁组 五年 与多数国际统计口径直接对齐 无法重建单岁结构
5×10 五岁组 十年 历史人口转变的长周期分析 粒度最粗,仅适合宏观

另有一份 Lexis 三角文件,它的行主键是年龄、年份与出生队列三者,等于把每个「年龄×年份」的方格沿对角线切成上下两个三角。这个切法的意义在于:同一个年龄格里,年初出生的人和年末出生的人,经历的日历事件(比如某年冬天的流感)并不相同。要做世代分析、要正确估计某一年份的年龄别死亡率在队列维度上的归属,就必须用三角数据。反过来,如果你只做年度横截面,三角数据是多余的一层。

2.5 与同类资源的字段差异

资源 最小单元 有没有分母 有没有生命表 死因 最深回溯
HMD 国家×年龄×年份×性别 有,暴露人年 有,周期与世代 仅 HCD 子库 18 国 1751
WHO 死亡数据库 国家×年份×年龄×性别×ICD 死因 多数国家无 无 有,按 ICD 原版 1950 年代
世界银行开放数据 国家×年份×指标 无 无 无 1960
OECD 健康统计 国家×年份×指标 部分指标有 无 有,按 OECD 口径 1960
联合国人口展望 国家×年份×五岁组 有,但为模型估计 有,为模型生命表 无 1950(含预测)
GBD 结果库 国家×年份×年龄×性别×死因 有,为模型估计 有 有,按 GBD 列表 1990

差别最大的一列是有没有分母。WHO 死亡数据库给的是死亡数,多数国家不配套暴露人口,因此你拿到的是分子;要算率,得另找分母,而分母的来源与分子的来源往往不是同一套口径。HMD 的全部设计都围绕一件事:让分子与分母在同一条规则下生成。这一个设计选择,决定了它只有 41 个国家,也决定了它在这 41 个国家里几乎无可替代。

§3 核心层 A:定位——它统计的是死亡本身,不是死因

3.1 一个反直觉的取舍:把死因全部丢掉

任何第一次打开 HMD 的人都会愣一下:这么大一个数据库,居然没有死因。四十一国、两百七十余年、单岁年龄、分性别,却连一个 ICD 编码都找不到。这不是遗漏,是设计上的第一取舍。

理由在于可加性。全因死亡是一个可以相加的量:把各年龄的死亡数加起来,等于总死亡数;把各国加起来,等于一个区域的总死亡数(前提是分母也对齐)。死因不是这样的量。同一个人在死亡证明上只能填一个根本死因,各国的填报规则不同、编码版本不同、甚至同一份证明在不同年份会被归到不同类别。世卫组织的死亡数据库保留各国原始编码,因此可以问「某国某年缺血性心脏病死了多少」,但很难直接问「过去七十年里,这个国家的死因结构怎么变的」,因为 ICD 改版会在序列上留下台阶。

HMD 的选择是:把不可加的那一维砍掉,把可加的那一维做到极致。砍掉死因之后,剩下的工作全部围绕两件事——准确计数死亡,准确估计暴露。做到极致之后,它就变成了唯一一套可以直接算生命表的跨国长序列。要死因的人去 HCD,要全球覆盖的人去别处,它不为这两类需求做妥协。

3.2 41 个国别页面,50 个 population 包

官网首页列出的国别页是 41 个。但下载页面上按国别打包的 zip 是 50 个。差的九个不是新国家,是同一个国家内部的子人口:德国分全体、东德、西德三包;法国分全体与平民人口两包;新西兰分全体、毛利、非毛利三包;英国分全体、英格兰与威尔士全体、英格兰与威尔士平民、苏格兰、北爱尔兰五包。四国相加,比按国家计数多出九个包。

区域 国家与地区(人口代码) 该组的结构特点
西欧与南欧 意大利 ITA、西班牙 ESP、葡萄牙 PRT、希腊 GRC、法国 FRATNP / FRACNP、比利时 BEL、荷兰 NLD、卢森堡 LUX、瑞士 CHE、奥地利 AUT、爱尔兰 IRL 法国拆全体与平民两包,平民序列在战争年份与全体差异明显
北欧 瑞典 SWE、丹麦 DNK、挪威 NOR、芬兰 FIN、冰岛 ISL 登记最连续的一组,瑞典与丹麦的序列可上溯至 18 世纪
中欧与东欧 德国 DEUTNP / DEUTE / DEUTW、波兰 POL、捷克 CZE、斯洛伐克 SVK、匈牙利 HUN、斯洛文尼亚 SVN、克罗地亚 HRV、保加利亚 BGR 德国拆三包;战后边界变动与社会主义时期登记口径是主要断点
前苏联与波罗的海 俄罗斯 RUS、白俄罗斯 BLR、乌克兰 UKR、立陶宛 LTU、拉脱维亚 LVA、爱沙尼亚 EST 1991 年前后的统计体系切换,是这一组最需要读说明的地方
英语圈 美国 USA、加拿大 CAN、澳大利亚 AUS、新西兰 NZL_NP / NZL_MA / NZL_NM、英国 GBR_NP / GBRTENW / GBRCENW / GBR_SCO / GBR_NIR 英国拆五包,英格兰与威尔士另有平民序列
东亚 日本 JPN、韩国 KOR、香港 HKG、台湾 TWN 登记完整度高,但可用起点普遍晚于欧洲
其他 智利 CHL、以色列 ISR 拉美与中东各只有一个人口单位,外推需谨慎

这份名单最该记住的不是国家数,而是它的偏向:欧洲中心、英语圈次之、东亚三点、拉美与中东各一点、非洲为零。任何以 HMD 为基础的跨国结论,事实上都以「今日的发达经济体」为总体,而不是「人类」。

3.3 时间深度不是一条线,是一把参差的梳子

「HMD 覆盖 1751 年至今」这句话容易造成误解,仿佛所有国家都从 1751 年开始。事实是每个国家的起点各不相同,且同一个国家的不同统计量起点也不同。瑞典的死亡数与暴露人口都从 1751 年起,出生数从 1749 年起,人口规模到 2025 年 1 月 1 日;而韩国的可用序列大体从 20 世纪后半段才开始,香港、台湾、智利的起点更晚。

国家与地区 死亡序列起点 说明
瑞典 1751 全库最长,出生数自 1749 年起
丹麦 1835 北欧第二个长序列
法国 1816 另有平民人口序列
意大利 1872 统一后才有全国口径
日本 1947 战后人口普查体系建立之后
美国 1933 进入出生死亡登记完整区之后
韩国 20 世纪后半段 与户籍电子化进程相关
俄罗斯 1959 苏联时期统计体系
香港、台湾、智利 20 世纪后半段 起点晚,但登记质量高

3.4 两级可信度:输入是别人报的,估计是自己算的

HMD 内部有一条明确的责任分界。输入库里的数字,责任在各国统计局;HMD 只保证「如实转录」并配上来源文件。完整序列里的数字,责任在 HMD;它由方法协议生成,协议改版时整条序列会变。

判断对象 责任方 出错的典型形态 应对
输入库死亡数 各国统计局 某年漏报、某年只覆盖部分人口 读该国注释,必要时剔除该年
输入库人口数 各国统计局 普查间隔长、战时估算粗糙 看是否有领土调整因子
子人口 subpopulation 同一国家内按地域或身份拆出的人口,单独成包
标准化死亡率 age-standardized death rate 消除年龄结构影响后的死亡率,用于跨人群比较
出生队列 birth cohort 同一年出生的一批人,世代数据的主键
暴露人口 HMD 出生日分布假设与真实不符 高龄段与婴儿段最敏感
年龄别死亡率 HMD 分母过小导致率失真 高龄段看官方是否已平滑
生命表 HMD 高龄 qx 依赖平滑外推 80 岁以上不要当观测值用
世代表 HMD 末端队列不完整 只用已闭合的队列

§4 核心层 B:质量——一套协议如何把各国的脏数据洗干净

4.1 六步法:从统计局的原件到一张生命表

方法协议把建库过程拆成固定的六步,每一步的输入输出都写死。这六步不是六个工序,而是六个必须按顺序解决的口径问题;任何一步的假设变了,后面全部要重算。

步骤 做什么 关键假设 出错后果
一、收集出生数 按年、按月收集活产登记 活产定义与联合国口径一致 婴儿死亡率的分母偏移
二、整理死亡数 按性别、单岁年龄、出生年归位 死亡发生年优先于登记年 年末死亡被推到下一年
三、估计人口规模 以普查为锚,内插出每年 1 月 1 日人口 普查间的人口变化可由出生死亡净迁移解释 战争与移民潮年份偏差最大
四、计算暴露人口 由相邻年初人口与当年死亡合成人年 生日在年内均匀分布 婴儿段与高龄段失真
五、算年龄别死亡率 死亡数除以暴露人口 分子分母覆盖同一人群 率可以大于 1
六、生成生命表 由 mx 转 qx,再推 lx、dx、Lx、Tx、ex 高龄段 mx 需平滑 高龄预期寿命被系统性高估

第三步是整套方法里最吃技巧的一步。两次普查之间的人口不是线性的:出生与死亡在一年内不均匀,移民可能集中在某几个月,战争可能让整段登记消失。协议给出的做法是分段使用不同技术——普查间隔内用存活比法从上一次普查推下一次,间隔外用出生死亡平衡法,边界年做特殊处理。这些技术在协议里有页码,但每个国家在实施时的取舍写在各自的 background PDF 里。

4.2 三处必须做的修正

第一处是生日分布。暴露人口不是「年初人口加年末人口除以二」这么简单。一个人在年内活过的时长,取决于他的生日在年内的位置;如果假设所有人生日都在 1 月 1 日,婴儿段的暴露会被系统性高估,因为当年出生的婴儿实际只暴露了几个月。协议用出生数的月度分布来修正这一点,这也是为什么输入库里有一份按月的出生文件。

第二处是死亡时点。死亡登记年与死亡发生年常常不一致,年末死亡被推到次年登记是普遍现象。协议统一按发生年归位,不与登记年混用。跨年度的流感季、热浪这类事件,如果按登记年统计,峰值会被抹平并后移。

第三处是领土变更。国界变过的地方,同一年的「人口」可以有两个值:变更前的旧边界与变更后的新边界。协议的处理是在年份后加后缀——带减号的指变更前的领土,带加号的指变更后的领土,两套估计都给。用法国的 1914 年举例,1914- 覆盖的是 1913 年底的旧领土,1914+ 则按 1914 年 1 月 1 日的边界计算。直接按年份去重会把其中一套丢掉,导致该年前后出现人口断崖。

4.3 高龄段:80 岁以上的数字是平滑出来的

协议规定:周期生命表里,从 80 岁起拟合一条 logistic 曲线,但只在死亡数降到百人以下的最低年龄(限定在 80 至 95 岁之间)之后,才用拟合值替换观测值;世代表不做平滑,概率直接由数据算。

理由很实在。到了 90 岁以上,每年死亡人数可能只有几十人,暴露人口也很小,观测到的率会剧烈跳动;某些年龄甚至出现死亡数大于暴露人口(率大于 1),或暴露为零(率无定义)的情况。这些数直接代进生命表公式,会算出负的存活人数或无穷大的预期寿命。平滑的作用是把这些病态值拉回可用范围,代价是这一段含模型成分,只能当模型值用。

4.4 案例:瑞典 1751——一条跑了 274 年的连续序列

瑞典是这个库的样板,也是理解 HMD 全部设计意图的最佳入口。在瑞典的国别页面上,死亡数与暴露人口的可用年份都是 1751–2024,死亡数的 Lexis 三角版本同样是 1751–2024,人口规模到 2025 年 1 月 1 日,出生数从 1749 年起。生命周期表覆盖 1751–2024,分女、男、合计三套;世代暴露人口覆盖 1676–1994,世代生命表覆盖 1751–1933。

这组年份值得逐个读。死亡数从 1751 年开始,意味着这条序列经历了两次霍乱大流行、1867–1868 年的北欧饥荒、1918 年流感、两次世界大战、1970 年代的心血管病高峰,以及 2020 年的新冠。它没有断过。世代暴露从 1676 年开始,意味着最早的一批出生队列在 1751 年时已七十五岁,他们七十六岁以后的死亡经历可以被观察到;这正是官方那条规则的由来——一个队列要连续观测满三十年才给世代率。世代生命表只到 1933 年,是因为更晚出生的队列还没有全部死亡,给不出完整的世代表。

官方在页面顶部放了一句警示:1751–1860 年的数据质量低于其后年份,使用需谨慎。之所以质量低,是因为那个年代的死亡登记来自教区簿记,覆盖的是教区人口而非现代意义上的国家人口,年龄申报存在明显的堆积(把年龄报到整十),婴儿死亡的漏报也更严重。这段数据可以用来观察长期趋势与量级,不适合用来做精细的年龄别建模。

§4.4 的案例还有一层方法学意义:它示范了「什么是好序列」。一条好序列不是起点早,而是起点早且断点少、口径变更有记录、异常年份有注释。瑞典三者俱全,所以它成了所有跨国死亡率研究的事实基准——任何新方法在别的国家跑出反直觉结果时,第一步都是拿到瑞典上试一遍,看是不是方法本身的问题。

§5 核心层 C:取数——三道门与五段代码

5.1 三条取数路径,各有各的适用面

官网给了三条路。第一条是浏览器点单:国别页面上一格一格的下载链接,适合只要一两个文件的人。第二条是按统计量打包:把同一种统计量在所有国家的文件打成一个 zip,适合要横向比较四十国的人。第三条是按国别打包:把一国所有文件打成一个 zip,适合做单国纵深研究的人。

路径 入口 体积量级 适合谁 代价
国别页面单选 各国 Country 页 单文件几十 KB 到几 MB 只要一两个统计量 点几十次,无法脚本化
按统计量 Zipped Data Files 页 出生数最轻,暴露人口最重 跨国横截面研究 只含完整序列,不含输入库
按国别 同上,By country 段 单国几 MB,全库数百 MB 单国纵深、要输入库 一次拿一堆用不上的文件
全库单包 All HMD countries 统计量版约一百八十余 MB,国别版约三百六十 MB 要建镜像或做全量分析 下载时间长,且需登录
汇总表 首页四张 xlsx 每张不到一 MB 只想要指标图 粒度粗,滞后于主库

5.2 第一道门:注册墙

下载地址不是公开的。直接请求任意一个 zip,服务器会以 302 跳到登录页。这一点用一条命令就能确认。

#-- 直接请求 zip,观察状态码与跳转目标
curl -sSL -o /dev/null -w '%{http_code} %{url_effective}\n' \
  'https://www.mortality.org/File/GetDocument/hmd.v6/zip/by_country/SWE.zip'
#-- 预期输出:200 https://www.mortality.org/Account/Login
#-- 也就是说拿到的是登录页,不是 zip

正确做法是先在浏览器里注册并登录,再从浏览器导出 Cookie,交给脚本复用。注册是免费的,不需要机构邮箱,也没有审批流程。但要注意:账号是给人用的,批量爬取会触发限制,也不符合官方在用户协议里的请求——它明确希望使用者把链接转给同行,而不是转发数据副本。

#-- 复用浏览器会话下载单个 population 包
import requests

#-- 从浏览器开发者工具里复制 Cookie 头,整串贴进来
COOKIE = "ASP.NET_SessionId=replace_with_the_value_from_your_browser"

s = requests.Session()
s.headers.update({"User-Agent": "Mozilla/5.0", "Cookie": COOKIE})

url = "https://www.mortality.org/File/GetDocument/hmd.v6/zip/by_country/SWE.zip"
r = s.get(url, timeout=120, allow_redirects=True)
#-- 若被踢回登录页,响应会是 HTML 而不是 zip,必须先判断再落盘
assert r.headers.get("Content-Type", "").startswith("application/zip"), "会话失效,重新导出 Cookie"
open("SWE.zip", "wb").write(r.content)
print(len(r.content), "bytes written")

判断是否真的拿到了数据,靠 Content-Type 与文件头两个信号。拿到 zip 时前两个字节是 PK;拿到登录页时开头是 HTML。这条断言能挡掉绝大多数静默失败——最坏的情况是把登录页存成 zip,后面解压时才报错,而错误信息指向解压器,不指向会话。

5.3 第二道门:文件结构

解开一个国别包,目录按统计量与输入库两分。STATS 下是完整序列的文本文件,InputDB 下是原始件与四份 PDF。

#-- 先看清结构再动手,避免把 InputDB 当 STATS 读
unzip -l SWE.zip | head -40
#-- 典型结构:
#--   SWE/STATS/Deaths_1x1.txt
#--   SWE/STATS/Exposures_1x1.txt
#--   SWE/STATS/Mx_1x1.txt
#--   SWE/STATS/fltper_1x1.txt
#--   SWE/STATS/Deaths_lexis.txt
#--   SWE/InputDB/SWEdeath.txt
#--   SWE/InputDB/SWEcom.pdf
unzip -q SWE.zip -d hmd_sweden
ls hmd_sweden/SWE/STATS | sort

文件名有一套固定的构词法。前缀 flt、mlt、blt 分别指女性、男性、合计的生命表;后缀 per 指周期、coh 指世代;中间的 1x1、1x5、5x1 是网格。记不住这套构词法的人,会在几十个文件里靠猜;记住之后,文件名本身就是元数据。

5.4 第三道门:从率到生命表

官方已经把生命表算好了,但自己算一遍有它的价值:可以验证读进来的数对不对,也可以在需要非标准网格(比如自定义年龄分组)时自己生成。

#-- 由 mx 生成周期生命表:核心只有三行公式
import numpy as np
import pandas as pd

def period_life_table(mx: pd.Series, ax: float = 0.5) -> pd.DataFrame:
    mx = mx.astype(float).to_numpy()
    n = np.ones_like(mx)
    n[-1] = np.inf                       #-- 最后一格是 110+ 开放区间
    qx = 1.0 - np.exp(-n * mx)           #-- 由中心死亡率转死亡概率
    qx[-1] = 1.0                         #-- 开放区间内必定死亡
    lx = np.zeros_like(mx)
    lx[0] = 100000.0                     #-- 生命表基数
    for i in range(1, len(mx)):
        lx[i] = lx[i - 1] * (1.0 - qx[i - 1])
    dx = lx * qx
    Lx = np.where(np.isinf(n), lx / np.maximum(mx, 1e-12), n * (lx - (1.0 - ax) * dx))
    Tx = np.cumsum(Lx[::-1])[::-1]
    ex = Tx / np.maximum(lx, 1e-12)
    return pd.DataFrame({"mx": mx, "qx": qx, "lx": lx, "dx": dx, "Lx": Lx, "Tx": Tx, "ex": ex})

df = pd.read_csv("hmd_sweden/SWE/STATS/Mx_1x1.txt", sep=r"\s+",
                 skiprows=2, na_values=".")
df = df[df.Year == 2024][["Age", "Female"]].dropna()
df["Age"] = df["Age"].astype(str).str.replace("+", "", regex=False).astype(int)
df = df.sort_values("Age")
lt = period_life_table(df["Female"])
print(round(lt["ex"].iloc[0], 3), "岁")   #-- 应接近官方 E0per.txt 里 2024 年女性值

拿 §4.4 的案例里那条序列来试,这段代码的三个关键点恰好对应三处最常见的错误。第一,nx 在最后一格是无穷大,若统一按 1 处理,110+ 的 qx 会被算成小于 1 的数。第二,qx 不能直接等于 mx:官方按 qx 等于 mx 除以 1 加(1 减 ax)乘 mx 转换,本段代码用等力假设的 1 减 exp(-n·mx),两者低死亡率段一致,高龄段会偏离。第三,lx 的累积必须按存活概率连乘,用 1 减累积 qx 会在高龄段算出负数。

5.5 Lexis 三角:什么时候必须用它

三角文件的行主键是年龄、年份和出生队列三者。同一个年龄格里,出生在年初的人与出生在年末的人分属不同的队列,落在不同的三角里。年度横截面分析不需要它;世代分析、事件史分析、以及需要把某一年的事件精确归到队列的地方,必须用它。

#-- 把 Lexis 三角聚合成 1x1 网格,并与官方 Deaths_1x1 对账
import pandas as pd

lex = pd.read_csv("hmd_sweden/SWE/STATS/Deaths_lexis.txt", sep=r"\s+",
                  skiprows=2, na_values=".")
lex.columns = [c.strip() for c in lex.columns]
for col in ("Year", "Age", "Cohort"):
    lex[col] = pd.to_numeric(lex[col].astype(str).str.replace("+", "", regex=False),
                             errors="coerce")
agg = (lex.groupby(["Year", "Age"], as_index=False)["Female"].sum()
          .rename(columns={"Female": "from_lexis"}))

ref = pd.read_csv("hmd_sweden/SWE/STATS/Deaths_1x1.txt", sep=r"\s+",
                  skiprows=2, na_values=".")
ref = ref[["Year", "Age", "Female"]].rename(columns={"Female": "official"})
ref["Age"] = pd.to_numeric(ref["Age"].astype(str).str.replace("+", "", regex=False),
                           errors="coerce")

chk = agg.merge(ref, on=["Year", "Age"], how="outer")
chk["diff"] = (chk["from_lexis"] - chk["official"]).abs()
print(chk["diff"].max())   #-- 应为一个极小的舍入误差,而不是几十上百

这段对账是判断「三角文件有没有读对」最省时间的办法。如果最大差值是量级性的,八成是把 Cohort 列当成了字符串导致排序错乱,或者把两个三角重复计了一次。

5.6 STMF:另一套口径的周数据

STMF 与主库同源不同口径。它给的是按国家、年、周、性别、五个年龄组的死亡数,覆盖 38 个人口单位,最早的一条是芬兰自 1990 年起,多数国家自 2000 年起,末端到 2026 年的第 41 周。文件是单一 csv,也提供 xlsx 版本。

#-- 用 STMF 算某国的周死亡 P-score(相对基线的超额百分比)
import pandas as pd

stmf = pd.read_csv("stmf.csv")
stmf.columns = [c.strip() for c in stmf.columns]
#-- 列名以官方 csv 为准,先确认再取,不同版本可能有细微差别
total_col = [c for c in stmf.columns if c.lower() in ("dtotal", "d_total")][0]

swe = stmf[(stmf["CountryCode"] == "SWE") & (stmf["Sex"] == "b")].copy()
swe["Week"] = pd.to_numeric(swe["Week"], errors="coerce")
swe = swe.dropna(subset=[total_col, "Week"])

base = swe[(swe["Year"] >= 2015) & (swe["Year"] <= 2019)]
baseline = base.groupby("Week")[total_col].mean()

swe = swe[swe["Year"] == 2020].set_index("Week")
swe["pscore"] = (swe[total_col] / baseline.reindex(swe.index) - 1.0) * 100
print(swe["pscore"].round(1).head(12))

用 STMF 有三个坑。第一,性别标识用单字母,合计是 b,不是 Total。第二,周编号按 ISO 周,年末的第 52 或 53 周在不同国家可能被合并或拆分,跨年比较时要把这两年单独处理。第三,也是最重要的:STMF 的死亡数是原始登记数,没有经过主库那套暴露人口修正,所以它算出来的是「死亡数的相对变化」,不是「死亡率的相对变化」。人口在增长的国家,同样的死亡率会给出更高的死亡数 P-score。

§6 与库内条目的关系

6.1 关系图谱:一条死亡,六种记法

同一件事——人死了——在库内至少被六类资源记录过。分清它们的不是「谁更全」,而是「记的是哪一个侧面」。HMD 记的是死亡发生的年龄与性别,加上分母;WHO 死亡数据库记的是死亡的原因;WHO 全球卫生观察记的是各国上报的汇总指标;世界银行开放数据记的是与发展相关的横截面指标;GHDx 记的是「这些数据在哪里、能不能拿到」;OECD 健康统计记的是成员国卫生系统的可比指标;NVSS 与 CDC WONDER 记的是美国的个体级与聚合级生命登记。

资源 记的是哪一面 最小单元 与 HMD 的关系 库内状态
WHO 死亡数据库 死于什么(按 ICD 编码) 国家×年×年龄×性别×死因 互补,不是替代:它给分子不给分母 本次检索未见独立词条
WHO 全球卫生观察 各国上报的汇总指标 国家×年×指标 上游汇总,粒度粗一到两级 已收录
世界银行开放数据 与发展相关的国别指标 国家×年×指标 只在宏观层面重叠,无年龄结构 已收录
GHDx 数据目录与可获取性登记 数据集词条 目录层,HMD 可能是它的一条记录 已收录
OECD 健康统计 成员国卫生系统指标 国家×年×指标 有死亡与预期寿命,但为各国上报口径 已收录
NVSS 美国的出生死亡个体记录 个体记录(受限) 美国一国的上游原始件,HMD 的美国序列源自此类 已收录
CDC WONDER 美国的聚合查询 县×年×死因 美国国内的死因聚合,与 HCD 部分重叠 已收录
IHME 结果库 全球疾病负担的模型估计 国家×年×年龄×性别×死因 模型估计,HMD 常是它的输入之一 本次检索未见独立词条
GBD 类全球估计 全球死因与风险归因 同上 覆盖全球但为估计值,HMD 是可验证的登记底座 见上

6.2 关键区隔:WHO 死亡数据库是「死因」,HMD 是「分母加存活曲线」

这两个库最容易被混为一谈,因为它们名字里都有 mortality,都按国家、年、年龄、性别给数。但只要问一句「能不能直接算生命表」,区别立刻显形。

WHO 死亡数据库的结构是:国家、年份、性别、年龄组、ICD 编码、死亡数。它的核心价值在 ICD 编码这一列——你可以问某个国家某年缺血性心脏病死了多少人,也可以按 ICD 的大类重组成心血管、肿瘤、外因。它的问题是:只有分子,没有分母。多数国家不配套提供同口径的暴露人口,要用率就得另找人口数,而人口数的来源、口径、年份覆盖往往与死亡登记不是一套。于是「率」这一步,实际上由使用者自己承担,且各人算出的率不可比。

HMD 的结构是:国家、年份、性别、年龄、死亡数、暴露人口、死亡率、生命表。它没有 ICD 编码这一列,但把分母做到了与分子同源:暴露人口由同一套出生死亡人口数据推出,分子分母覆盖同一人群、同一时期、同一边界。于是率可以直接算,生命表可以直接算,预期寿命可以直接算,且四十一个国家的算法完全一致。

判据 WHO 死亡数据库 HMD
维度 死因是核心维度 死因维度不存在
分子 有,按 ICD 原始编码 有,全因合计
分母 多数国家缺,需另找 有,且与分子同源
能否直接算生命表 不能,缺 qx 所需的分母与年龄细分 能,官方已算好且给了公式
国家数 远多于 41,覆盖全球大部分地区 41 个,集中于发达经济体
时间深度 多数自 1950 年代起 最早 1751 年
年龄粒度 多为五岁组,婴儿段常合并 单岁 0–109 加 110+
数据性质 各国上报的登记数,未做口径统一 按统一协议重建的估计值
典型问题 「这个国家的心血管死亡占多少」 「这个国家各年龄的死亡水平与预期寿命」

两者最合理的用法是串联,而不是二选一。第一步,用 HMD 拿到该国该年的全因死亡水平、年龄别死亡率与预期寿命,作为可信的底座;第二步,用 WHO 死亡数据库拿到死因结构,即各死因占全因死亡的比例;第三步,把比例乘回 HMD 的全因死亡率,得到「死因别死亡率」。这条路绕了一圈,但它避开了两条捷径的陷阱:直接用 WHO 的死亡数除以随便找的人口数,会得到不可比的率;直接用 HMD 的全因曲线去解释死因变化,会得出没有因果内容的结论。

HCD 子库是这两条路的官方接缝。它做的事正是把 ICD 的变动抹平:用固定的短、中、长三套死因列表,把 1950 年代以来的原始编码重建成跨年份可比的序列,覆盖 18 个国家。也就是说,HMD 团队自己也承认「死因维度不可丢」,只是把它放在一个受控的子库里,用重建的方式保证可比性。需要死因又需要长时序的人,正确的入口是 HCD,不是 WHO 原始表。

6.3 检索路径:从问题到入口

你的问题 该去哪个库 为什么不是 HMD
某国某年各年龄死亡率与预期寿命 HMD 这就是它的本职
某国某年心血管病死亡占多少 WHO 死亡数据库或 HCD 主库无死因维度
某国某年婴儿死亡率是多少 HMD 或 WHO 全球卫生观察 两者都有,HMD 的可复算性更强
全球 190 个国家的死亡率 WHO 全球卫生观察或全球疾病负担估计 HMD 只有 41 国
某国的死因序列跨越 ICD 改版 HCD 主库无死因,WHO 原表不可比
某国 2020 年第 15 周的死亡数 STMF 主库是年度粒度
美国某县的死因别死亡数 CDC WONDER HMD 不拆到县
想找某个冷门国家的生命登记原始件 GHDx 它是目录,HMD 只收 41 国
论文里要一句「某国预期寿命 2023 年为多少」 HMD 汇总表 主库也能给,但汇总表更快

§7 质量评估与局限性

7.0 阅读本节前的一个前置校正

先纠一个几乎人人都会犯的错位:把 HMD 的数字当成「观测值」。它的大部分数字是估计值,而且是有方法假设的估计值。死亡数在早期年份是拆分的、在晚期年份是暂定的;暴露人口全部是构造的;80 岁以上的死亡率经过平滑;110+ 是一个把所有人合并的开放区间。这些处理都不是瑕疵,是让跨国长序列得以存在的必要代价,但它们决定了一件事:HMD 的数字适合做「水平与趋势」的判断,不适合做「某一个格子精确到个位」的判断。

7.1 五维评分:DAIMS 八项检查

DAIMS 是千方医数集对数据集做的八项 AI 就绪检查,每项 0 到 3 分,满分 24。评分对象是这份公开数据本身,不含任何第三方加工版本。

检查项 得分 理由
数据集标识 3/3 每个发布版带独立 DOI(形如 10.4054/HMD.Countries.加日期),可精确回溯到某一日快照
许可清晰度 3/3 构造估计值 CC BY 4.0,输入数据另有限制,两类分界写在用户协议里,不靠猜
溯源信息 3/3 每个数字可追到来源文件;每个 population 配四份 PDF,含逐点注释与编码对照
版本与更新 2/3 按日期打版本且列出历史版本,但没有语义化版本号,协议版本与数据版本是两套编号
字段字典 3/3 说明页把每个统计量、每列生命表参数、每种网格都定义清楚,含公式指向
缺失处理 2/3 缺失统一用点号,语义单一;缺的是「为什么缺」的机器可读标记,原因只在 PDF 里
文档完备度 3/3 方法协议、说明页、各国背景件、子库方法注记四层齐全
机器可读接口 1/3 无 API,只有静态文本文件,且下载需登录会话,自动化取数成本高
合计 20/24 内容与文档接近满分,扣分集中在接口与缺失语义

一句话读这张表:它的短板不在数据质量,在工程接口。一个没有 API、还要登录的数据源,在任何自动化流水线里都是摩擦点;而它的文档质量又高到足以抵消这种摩擦——几乎所有接口缺失带来的问题,都能通过读文档解决。

7.2 一处不提供的量:不确定性区间

官方给出的每一个死亡率、每一个预期寿命都是点估计,既没有置信区间,也没有标准误列。这不是疏漏:它的不确定性主要来自输入数据本身,而不是抽样。想量化不确定性,只能自己做敏感性分析——换分母假设(年初人口与年中人口)、换高龄平滑的处理方式、换领土边界的取舍,看结果摆动多大。摆动通常不改结论。

7.3 已知的、可枚举的偏差

偏差 发生在哪 方向 严重度 应对
婴儿死亡漏报 19 世纪及以前的多数国家 低估死亡、高估预期寿命 高 只做趋势不做水平;对照该国注释
年龄申报堆积 早期年份,尤其 60 岁以上 单岁曲线出现锯齿 中 用五岁组网格,或用 1×5 平滑
战争年份登记中断 两次世界大战期间的交战国 该年死亡数偏低 高 剔除该年或单独建模
领土变更年双行 法国、德国、俄国等 按年份去重会丢一套 高 保留减号加号后缀,显式选择
高龄平滑 80 至 95 岁间由死亡数定起点 曲线偏光滑、极端值被压 中 当模型值用,不做生物学推断
末端暂定值 各国最新一至两年 下版会修订 中 建模时截断到倒数第三年
移民未观测 移民大国(澳、加、美、以色列) 暴露人口与队列归属偏移 中 优先用周期表而非世代表
子人口混用 德、法、新西兰、英国 同一国家两条曲线并存 中 在图上标明人口代码,不合并

7.4 外部验证矩阵

判断一份数据可不可信,最省力的办法是拿它和别的来源在同一格上对一次。下表是四类可做的对账,前两类可以自己跑,后两类需要读对方的文档。

对账对象 对什么 预期结果 不一致时的解读
官方 E0per.txt 与自己算的生命表 出生预期寿命 差异应在小数点后两位内 差得多说明 nx 或 qx 公式用错
Deaths_lexis 聚合与 Deaths_1x1 同一年龄年份的死亡数 差异应接近零 有量级差异说明三角重复计数
与 WHO 全球卫生观察的同国同年预期寿命 水平 通常相差零点几岁 差一岁以上要查分母口径与年份定义
与美国 NVSS 的同年死亡数 计数 HMD 常略高或略低,因调整不同 差异稳定可接受,方向突变说明登记变更
与 OECD 健康统计的预期寿命 水平 成员国应高度接近 若 OECD 更高,多为分母用了年中人口
与各国统计局公布的年度报告 单年死亡数 应基本一致 不一致时以 HMD 的调整为线索去读注释

§4.4 的案例之所以年年被拿来当基准,正是因为它在这张表的每一行上都经得起对账。

7.5 它为何能持续:因为它服务的是一个不能没有基准的学科

一个数据项目能活二十六年,通常靠三样东西:稳定的机构、不可替代的位置、以及足够低的使用门槛。HMD 三样都有,但最关键的还是第二样。

人口学的几乎所有方法都需要一个「已知正确答案」的基准:新模型要先在数据质量最好的群体上验证,新估计要先和登记完整国家的观测曲线对上,跨国比较要先有一个口径统一的参照系。这个位置只能有一个占据者,因为占两个就意味着两套基准。HMD 从 2000 年起就在这个位置上,后面的方法、模型、估计都围绕着它长出来,替换成本高到没有人愿意承担。

门槛低是第三根支柱。免费注册、无需申请、无需伦理审批、无机构限制,任何人几分钟内就能拿到瑞典两百七十年的死亡序列。这个低门槛换来的不是收入,是引用量;而引用量反过来成了它向各国统计局索取数据的筹码。这是一个正循环,也是它比多数政府主导的统计门户更持久的原因。

§8 生态位与历史

8.1 二十六年,三次扩张

HMD 的历史不长,但每一步都对应着一个明确的需求缺口。它 2000 年立项,最早的动机很具体:老龄化研究需要长时段的死亡率序列,而当时没有任何一份跨国的、口径统一的、可以一直回溯到 19 世纪的数据库。学者们各自维护自己国家的小表,跨国比较只能靠拼。

阶段 时间 做了什么 补的是哪个缺口
立项 2000 年 伯克利与马普所联合启动,确立方法协议与六步流程 跨国可比的长序列不存在
定型 2000 年代 覆盖扩展到三十余国,协议迭代至第六版,确立输入库与完整序列双层 口径统一与可复算性
深化 2010 年代 补齐东欧与前苏联国家,加入 Lexis 三角与世代数据 队列视角与事件史分析
应急扩张 2020 年 建立 STMF,按周发布死亡数 疫情把年度粒度打得不够用
死因重建 2020 年代 HCD 以固定列表重建 18 国死因序列 ICD 改版导致长序列断裂
版本化 2020 年代中 每次打包配 DOI,列出历史版本 可复现性与引用规范

8.2 主库、STMF、HCD 的分工

三者共用一套机构与一套引用规范,但口径互不相同。把它们当成一个库的三个表来 join,是最常见的误用。

维度 主库 STMF HCD
时间粒度 年 周 年
年龄粒度 单岁至 110+ 五个年龄组 按死因列表与年龄组
覆盖 41 个国别页面 38 个人口单位 18 个国家
起点 最早 1751 最早 1990(芬兰) 重建序列多在 1950 年代后
死因 无 无 有,固定列表
更新 约每月至两月一版 更频繁,末端到 2026 年第 41 周 每季至半年
用在哪 水平、趋势、生命表 短期超额死亡 死因结构的长序列

8.3 竞争与替代:为什么没有第二个 HMD

理论上有三类潜在替代。第一类是各国统计局自己发布的生命表——最权威,但口径各异、格式各异、语言各异,做四十国比较的工作量等于重做一遍 HMD。第二类是世界卫生组织与联合国的人口估计——覆盖全球,但多数国家是模型估计,且年龄分组粗,无法支撑单岁分析。第三类是学术机构自己重建的国别序列——质量参差,且没有统一的更新机制。

§9 使用指引

9.1 三种角色的路径

同一个库,做流行病学的人、做精算的人、做机器学习的人,走的是三条不同长度的路。

角色 最短路径 该避开的弯路 大概要花的时间
流行病学研究者 注册 → 下一个国别包 → 读 Mx_5x1 与 E0per → 画趋势图 不要一上来下全库;不要用 1×1 做长期趋势,噪声太大 半小时内可出第一张图
精算与养老金 注册 → 下目标国的 fltper_1x1 与 bltper_1x1 → 取 qx 与 lx 不要用世代表定价;不要把周期表当预测 一小时内可完成定价表
机器学习与建模 注册 → 按统计量下全部 → 拼成国家×年×年龄×性别的长表 不要用主库做标签;不要把 110+ 当真实年龄 一天内可完成特征工程

9.2 一条最短的可用流程

下面的流程假设你已经把某个国别包解开在本地。它做四件事:读死亡率、对齐年龄、算两张表、输出一张可用于建模的长表。

#-- 从原始文本到建模可用的长表,全流程
import pandas as pd

POP, ROOT = "SWE", "hmd_sweden/SWE/STATS"

def load(name, value="Total"):
    df = pd.read_csv(f"{ROOT}/{name}", sep=r"\s+", skiprows=2, na_values=".")
    df.columns = [c.strip() for c in df.columns]
    df["Age"] = pd.to_numeric(df["Age"].astype(str).str.replace("+", "", regex=False),
                              errors="coerce").astype("Int64")
    df["Year"] = pd.to_numeric(df["Year"].astype(str).str.rstrip("-+"),
                               errors="coerce").astype("Int64")
    #-- 长表化:宽表的三列性别变成一行一个观测
    long = df.melt(id_vars=["Year", "Age"], value_vars=["Female", "Male", "Total"],
                   var_name="sex", value_name=value)
    long["pop"] = POP
    long["stat"] = name.split("_")[0].replace(".txt", "")
    return long.dropna(subset=["Year", "Age"])

mx = load("Mx_1x1.txt", "mx")
ex = load("E0per.txt", "e0")          #-- 该文件只有 Year 与三列性别,Age 为空
print(mx.shape, mx["Year"].min(), mx["Year"].max())
print(ex.groupby("sex")["e0"].last())

#-- 存成 parquet,后续按 (pop, year, age, sex) 索引
mx.to_parquet("swe_mx.parquet", index=False)

流程里有三个容易踩的坑,都写在了注释的边上;§4.4 的案例那条序列恰好三坑俱全。第一,E0per 这类文件没有年龄列,套用同一个加载函数会得到全空的 Age,必须单独处理。第二,melt 之后行数会变成三倍,若后面还要 merge 别的文件,键必须带上 sex,否则会笛卡尔积。第三,年份列在领土变更年带减号或加号,直接转数值会得到空值,必须先剥后缀。

9.3 引用方式

官方的引用要求写得比其他数据源都细:不只是「注明来源」,而是给了一段可以直接抄的完整引文,并明确要求下载时记下日期,因为数据会修订。

场景 该怎么引 注意
使用主库估计值 引 HMD 三方机构全称加官网 官方给了固定句式,逐字照抄最稳
使用 STMF 单独引 STMF 数据系列 不要只引主库,两者是不同系列
使用 HCD 单独引 HCD 数据系列 同上,且要说明是重建序列还是原始分类
使用输入库原始数据 引该国统计局,并说明经 HMD 整理 输入数据版权属原提供方
复现某一版结果 引该版的 DOI DOI 里含日期串,可精确锁定快照
商业产品中使用 只能用构造估计值 输入数据不得商用,需原提供方许可

官方的引文句式是:HMD. Human Mortality Database. Max Planck Institute for Demographic Research (Germany), University of California, Berkeley (USA), and French Institute for Demographic Studies (France). Available at www.mortality.org. 三方机构的全称、国别括注、网址都在里面,改成中文或简写都会漏掉要素。子库另有两段对应句式,用哪个系列就换哪一段。

§10 AI 使用声明

项目 声明
数据性质 国家×年龄×性别×年份的聚合统计,不含任何个体记录,不涉及个人信息保护问题
可用于训练的部分 仅 HMD 构造的估计值(暴露人口、死亡率、生命表、标准化死亡率)
不可用于训练的部分 各国统计局提供的输入数据,除非取得原提供方许可,不得商用、不得再分发
许可义务 再分发或衍生发布须署名三方机构,保留 CC BY 4.0 声明,并注明取数日期与版本 DOI
典型建模用途 生存分析基线风险、死亡率预测模型(Lee-Carter 系)的基准序列、跨国协变量、生命表生成
不应支持的推断 死因归因、个体风险预测、亚国家差异、全球南方国家的死亡水平
已知的模型风险 末端暂定值会被当作稳定信号;110+ 合并格会被误当真实年龄;80 岁以上曲线含平滑成分
生成式使用提示 若用本库数字生成文本或图表,须标注版本日期,且不得把周期预期寿命表述为某代人的实际寿命

常见误读(避坑清单)

常见说法 实际 证据
「HMD 覆盖全世界」 只覆盖 41 个国别页面、50 个 population 包,且几乎全是发达经济体 官网首页国别列表;无任何非洲国家,印度不在其中
「有了 HMD 就不需要死因数据」 主库只有全因死亡,死因在 HCD 子库且仅 18 国 说明页列出的完整序列六类数据里没有死因项
「1751 年起所有国家都有数据」 1751 是瑞典一条序列的起点,各国起点差异可达两百年 瑞典页面的可用年份;日本 1947、美国 1933
「110+ 是一个年龄」 它是把所有 110 岁及以上合并的开放区间 说明页明确写 0 到 109 单岁加 110+ 开放区间
「暴露人口就是人口数」 前者是人年,后者是时点存量 两个文件同时存在,数值不同,前者用于算率
「mx 是死亡概率」 mx 是中心死亡率,可以大于 1 高龄段存在率大于 1 的格子,概率不可能大于 1
「数据可以直接 CSV 读入」 制表符分隔、两行表头、缺失为点号、年龄带加号 文件头部结构;直接读会把年龄列判成字符串
「下载是公开匿名的」 zip 请求会 302 跳转到登录页 直接请求任意 zip,落点是 /Account/Login
「最新一年可以直接用」 末端年份多为暂定值,下一版会修订 每版 DOI 不同,历史版本可对比出修订
「HMD 的数字都是原始观测」 大部分是方法协议生成的估计值,高龄段还经过平滑 协议对 80 岁以上平滑的规定;早期年份为拆分估计

FAQ

Q1:HMD 和 WHO 死亡数据库有什么不同?
WHO 死亡数据库是按 ICD 死因分类的登记数,回答「死于什么」;HMD 是按年龄与性别的全因死亡数加暴露人口,回答「各年龄死多少、活多久」。前者覆盖国家多但口径杂、多数国家没有配套暴露人口;后者只覆盖 41 个发达经济体,却把分子分母对齐到可以直接算生命表。两者互补,常常一个提供死因结构、一个提供分母与存活曲线。

Q2:不做人口学,能直接用吗?
能,但只用得上两成。最常见的非人口学用法是把国家×年龄×年份的死亡率当作协变量或基线风险,接进流行病学模型、保险精算表或生存分析模块。若要自己算生命表,官方已给出 mx 到 qx 的转换规则,二十行代码即可,不需要读完整本方法协议。

Q3:为什么下载会被跳转到登录页?
zip 下载地址会以 302 跳转到 /Account/Login,属于注册墙而非付费墙。注册免费,注册后浏览器会话即可下载;命令行工具若不携带会话 Cookie,拿到的永远是登录页的 HTML 而不是压缩包。判断是否拿到真数据,看响应头里的 Content-Type 与前两个字节是不是 PK。

Q4:1751 年这个起点是怎么来的?
来自瑞典。瑞典的人口登记在世界范围内罕见地连续,该国页面上死亡数与暴露人口的可用年份都是 1751 到 2024,出生数从 1749 年起,人口规模到 2025 年 1 月 1 日。1751 因此是全库最早的一条死亡序列起点,而不是所有国家的共同起点。理解这一点,才不会把「某年某国没有数据」误读成「该国该年死亡率很低」。

Q5:能不能拿到个体级死亡记录?
不能。它从设计上就是聚合库,最小单元格是国家×年龄×年份×性别。需要个体死亡记录的研究要转向各国微观死亡登记,HMD 只能提供分母与基准率。这一点也决定了它不涉及任何个人信息合规问题。

Q6:STMF 是不是 HMD 的一部分?
是同一团队维护的独立子库,覆盖 38 个人口单位,给的是周死亡数,用于识别短期超额死亡。它与主库的年度序列口径不同、更新节奏更快,混用时要说明用的是哪一个。尤其注意它给的是死亡数的相对变化,不是死亡率的相对变化,人口增长的国家会被高估。

Q8:世代数据和周期数据该怎么选?
看你的问题跟的是人还是年。周期表回答「如果某年的年龄别死亡率一直保持不变,一个新生儿能活多久」,用于当期水平比较;世代表回答「某一批出生的人实际活了多少年」,用于队列分析。做养老金与寿险定价通常用周期表,做人口转变与队列研究用世代表。世代表有三十年连续观测的门槛,末端队列大量留空。

Q9:为什么高龄段的数字看着太光滑?
因为 80 岁以上的中心死亡率经过平滑。到了 90 岁以上,每年的死亡人数与暴露人口都很小,观测率会剧烈跳动,甚至出现死亡数大于暴露人口的情况,直接代入生命表公式会算出负的存活人数。平滑把这些病态值拉回可用范围,代价是这一段含有模型成分,不能当纯观测做生物学推断。

Q10:可以把它用于商业产品吗?
可以,但只能用 HMD 构造的那一半。暴露人口、死亡率、生命表按 CC BY 4.0 授权,商用允许,需署名。各国统计局提供的输入数据不行,那部分保留原提供方许可,明确不得商用、不得转发布。做商业产品时,取数流程里要把输入库的文件排除掉,避免无意中把受限数据打进产品。

Q12:怎么保证我三年后还能复现今天的结果?
记下两样东西:打包文件名里的日期,以及对应的 DOI。每个发布版都配了 DOI,形如 10.4054/HMD.Countries.加日期;历史版本在下载页上单独列出,可以按日期取回。只写「数据来自 HMD」是不够的,因为下一版会修订末端年份,同一个查询会给出不同的数。

术语表

术语 英文 含义
暴露人口 exposure to risk 一段时期内真正暴露于死亡风险的人年数,是死亡率的分母
中心死亡率 central death rate, mx 某年龄区间内死亡数除以人年数,可以大于 1
死亡概率 probability of death, qx 活到年龄 x 的人在进入下一区间前死亡的概率,介于 0 与 1
平均存活年数 average years lived, ax 在区间内死亡者平均活过的年数,用于在 lx 与 Lx 之间转换
存活人数 survivors, lx 假设起点为十万人时,活到确切年龄 x 的人数
人年数 person-years, Lx 区间内活过的总人年数
剩余人年数 total person-years, Tx 从年龄 x 起到全部死亡为止的总人年数
预期寿命 life expectancy, ex 剩余人年数除以存活人数,即平均还能活多少年
周期生命表 period life table 用某一年的年龄别死亡率构造的假想队列生命表
世代生命表 cohort life table 跟踪某一批出生的人直到全部死亡的真实经历
Lexis 图 Lexis diagram 以年龄为纵轴、时间为横轴的坐标系,死亡事件落在方格内
Lexis 三角 Lexis triangle 把年龄×年份的方格沿对角线分成上下两个三角,对应不同出生队列
网格 1×1 1x1 grid 单岁年龄×单年时间的最细切法
标准化死亡率 age-standardized death rate 消除年龄结构影响后的死亡率,用于跨人群比较
输入库 Input Database 各国统计局提供的原始出生、死亡、人口数
完整序列 Complete Data Series HMD 用方法协议从输入库算出的成品数据
人口代码 population code 三字母加后缀的标识,如 SWE、DEUTNP、GBR_NP

附录 A:核心数字核对表

项 值 来源 来源层级
国别页面数 41 官网首页国别列表 官方页面
可下载 population 包 50 下载页按国别段 官方页面
最早死亡序列起点 1751(瑞典) 瑞典国别页可用年份 官方页面
最早出生数起点 1749(瑞典) 同上 官方页面
人口规模末端 2025 年 1 月 1 日(瑞典) 同上 官方页面
年龄上限 110+,开放区间 说明页 官方页面
网格切法 6 种加 Lexis 三角 说明页 官方页面
生命表列数 8(mx 至 ex) 说明页 官方页面
世代数据门槛 连续观测 30 年 说明页 官方页面
高龄平滑起点 80 岁 说明页 官方页面
STMF 人口单位数 38 STMF 页面 官方页面
HCD 国家数 18 HCD 页面 官方页面
STMF 最早起点 1990(芬兰) STMF 页面 官方页面
全库国别包体积 约三百六十 MB 下载页标注 官方页面
2026 年发布版本数 8(2 月至 8 月) 历史版本列表 官方页面
许可 CC BY 4.0(估计值) 用户协议 官方页面

附录 B:数字的来源分层

层级 含义 本篇涉及的数字
官方页面 官网首页、说明页、下载页、子库页、用户协议直接写明 41、50、1751、1749、110+、六种网格、八列、三十年、80 岁、38、18、1990、体积、版本数、CC BY 4.0
官方文档 方法协议与各国 PDF 中的规定,本篇只引用其被说明页复述的部分 六步流程、平滑规则、领土变更双行、缺失点号
论文摘要 未直接引用,STMF 的方法学出处见其页面链接的期刊文章 无
未核实 官方未给出单一汇总口径、本篇以区间或概数表述 各国起点年份的完整清单(仅举八例)、每版之间平均间隔

有一处口径需要说明:官网首页的国别列表是 41 个,下载页的 population 包是 50 个,两个数字都对,差额来自德、法、新西兰、英国四国的子人口。官方没有在任何一处把这两个数字并排列出,本篇的并置属于核对后的推论,不是官方表述。

附录 C:完整参考文献

序号 文献 用途
1 Human Mortality Database 官网首页(www.mortality.org) 国别清单、机构、定位表述
2 官网 Zipped Data Files 页 打包体积、两条下载路径、历史版本与 DOI
3 官网 Explanatory Notes 页 六类数据、网格定义、生命表八列、平滑与三十年规则
4 官网 User Agreement 页 许可分界、引用句式、副本转发的请求
5 瑞典国别页(Country?cntr=SWE) 1751 起点、各统计量可用年份、质量警示
6 官网 STMF 页 38 个人口单位、周粒度、起止周、文件格式
7 官网 HCD 页 18 国、重建与原始分类两套序列、三档死因列表
8 Methods Protocol 第六版(2025-08-05 修订,81 页) 六步流程、暴露人口构造、生命表公式出处
9 官网 Citation Guidelines 页 引用与版本记录要求

附录 D:任务代码

把「某国某年的单岁死亡率曲线」变成「一张可直接进模型的宽表」,是最高频的一个小任务。下面这段代码把三步合成一步,并显式处理了三处口径问题。

#-- 生成国家×年份×年龄的宽表,性别作为列,缺失显式标注
import pandas as pd

def build_panel(root, pop, years):
    mx = pd.read_csv(f"{root}/{pop}/STATS/Mx_1x1.txt", sep=r"\s+",
                     skiprows=2, na_values=".")
    mx.columns = [c.strip() for c in mx.columns]
    mx["Age"] = pd.to_numeric(mx["Age"].astype(str).str.replace("+", "", regex=False),
                              errors="coerce")
    #-- 年份列可能带 - 或 + 后缀(领土变更年),先剥掉再转数值
    mx["Year"] = pd.to_numeric(mx["Year"].astype(str).str.rstrip("-+"), errors="coerce")
    mx = mx.dropna(subset=["Age", "Year"])
    mx = mx[mx["Year"].isin(years)]
    wide = mx.pivot_table(index=["Year", "Age"], values=["Female", "Male"],
                          aggfunc="first").reset_index()
    wide.columns = ["year", "age", "mx_f", "mx_m"]
    wide["pop"] = pop
    #-- 110+ 单独打标,避免被当成单岁年龄参与回归
    wide["is_open_interval"] = wide["age"] >= 110
    return wide.sort_values(["year", "age"])

panel = build_panel("hmd", "SWE", range(1950, 2025))
print(panel.shape, panel["is_open_interval"].sum())
panel.to_csv("swe_panel.csv", index=False)

附录 E:如果只记五件事

序号 要点
一 它是全因死亡加暴露人口,不是死因库;死因在 HCD,只有 18 国
二 41 个国别页面、50 个 population 包,差额来自德法新英四国的子人口
三 1751 是瑞典一条序列的起点,不是全库统一起点;各国起点相差可达两百年
四 下载需免费注册,zip 请求会跳登录页;命令行取数必须复用浏览器会话
五 构造估计值 CC BY 4.0 可用可商用,输入数据不可商用、不可转发布

编辑说明

核验时间为 2026 年 10 月 29 日,对象是官网在架的公开页面、说明文档与方法协议正文,未登录、未下载数据包,体积与版本信息取自页面标注。

未覆盖的内容有三类:一是各国起点年份的完整清单,官网需逐国进入页面才能读到,本次只抽取了八个国家作示例;二是各版本之间的修订幅度,需要至少两个版本的数据包才能比对,本次未下载;三是方法协议正文的逐节细节,本次只引用了说明页复述的部分。

若需引用本篇的数字做二次发布,建议同时记录官网上当时的版本 DOI,因为 41 与 50 这两个数字都可能随国家级数据包的增减而变化,而版本 DOI 是唯一能锁定当时状态的东西。


相关数据集导航

以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:

  • hmd — 共享标签:公共卫生与流行病学 / 死亡登记 / 公共卫生统计 / 全球健康
  • who-mortality-db — 共享标签:公共卫生与流行病学 / 死亡登记 / 公共卫生统计 / 全球健康
  • ghdx — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • cvss-nvss — 共享标签:公共卫生与流行病学 / 死亡登记 / 公共卫生统计
  • gbd-results — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • wpp — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • usgs-comcat — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • mswep-v3 — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • who-gho — 共享标签:公共卫生与流行病学 / 公共卫生统计 / 全球健康
  • hfa-europe — 共享标签:公共卫生与流行病学 / 死亡登记 / 公共卫生统计

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

返回 AI-Ready 数据集