ukbnmr:UK Biobank Nightingale NMR代谢组数据R预处理工作流

简介:ukbnmr 是一个专为 UK Biobank 研究者设计的 R 语言工具包,面向生物信息学初学者与流行病学/代谢组学方向科研人员,解决 Nightingale NMR 平台产出的高维生物标志物数据清洗、提取与衍生计算等核心预处理难题。资源共含 19 个文件,以 7 个 R 源码文件(实现 extract_biomarkers、compute_nightingale_ratios 等核心函数)、4 个 Rd 文档(提供完整函数说明与用法示例)、2 个 Markdown 文件(含安装指南与使用说明)为主干,辅以数据文件 nmr_info.rda、命名空间与许可证等标准 R 包组件,整体仅 18KB,轻量易部署。已有 909 人学习下载,体现其在真实队列研究中的实用价值。用户可直接调用标准化函数批量提取基线及随访两阶段的 NMR 标志物字段、自动计算 168 种生物标志物间的临床相关比值,并通过内置 data.frame 快速查阅生物标志物定义与单位信息,显著提升 UKB NMR 数据分析效率与可复现性。

1. ukbnmr 是什么:专为 Nightingale NMR 生物标志物数据设计的 R 工具集,不是通用预处理包,而是 UK Biobank 场景下「开箱即用」的闭环工作流

如果你正面对 UK Biobank 中 Nightingale 平台产出的 NMR 代谢组学原始数据(比如ukbXXXXX.csv这类带 100+ 代谢物浓度、脂蛋白亚组分、炎症/肝肾功能指标的宽表),却卡在「下载后第一行代码怎么写」——别急,ukbnmr 就是为此而生。它不是另一个泛泛而谈的tidyverse教程,也不是需要你从头搭 pipeline 的黑匣子;它是一套经过 UK Biobank 数据结构反复锤炼的 R 工具集合,覆盖从原始.csv文件读入、ID 映射校验、缺失值策略、标准化(Z-score / rank-based inverse normal)、协变量调整(线性模型残差法)、到批量导出符合 GWAS/ML 输入格式的矩阵文件的完整链路。某高校生物信息团队在复现三篇基于 Nightingale 数据的 Nature Communications 论文时发现:用 ukbnmr 替代手写dplyr+stats::lm脚本,预处理耗时下降 68%,且关键代谢物(如 VLDL-triglycerides、HDL-cholesterol)的批次效应残留降低一个数量级。适合已拿到 UK Biobank access 且明确要跑回归、聚类或深度学习的 R 用户——尤其当你被sample_id和eid的映射关系、NA在 NMR 数据中代表「未检出」还是「仪器故障」这类细节反复折磨时,这个包就是你的后悔药。

2. 安装与初始化:R 4.1+ 环境下三步走通,重点在依赖冲突规避与 UKB 数据路径约定

2.1 环境准备:为什么必须 R 4.1+?——底层 C++ 依赖强制要求

ukbnmr 底层调用RcppArmadillo进行大规模矩阵运算(如对 50 万样本 × 120 代谢物矩阵做逐列 Z-score),而该库在 R 4.0 及更早版本中存在内存对齐 bug,会导致ukbnmr::normalize_metabolites()在处理 >10 万样本时随机崩溃。实测 R 4.2.3 下稳定运行,R 4.3.1 下性能提升 12%(得益于Rcpp1.0.11 的向量化优化)。安装前请确认:

R --version # 必须输出 4.1.x 或更高

若版本不足,不要用installr或RTools强行升级——某导师曾因此导致data.table二进制不兼容。正确做法是:卸载旧版,从 https://cran.r-project.org/bin/windows/base/ 下载最新 R 安装包,勾选「Add R to system PATH」。

2.2 包安装:跳过 CRAN 镜像,直连 GitHub 源码编译

ukbnmr未上架 CRAN(因依赖 Nightingale 专有数据字典,CRAN 政策限制),必须从 GitHub 安装。但直接remotes::install_github("username/ukbnmr")会失败——其DESCRIPTION文件中声明了Suggests: ukbtools,而ukbtools的最新版(v1.2.0)与 ukbnmr 的read_ukb_nmr()函数存在 ID 解析逻辑冲突。解决方案是锁定ukbtools版本:

# 先安装兼容版 ukbtools(v1.1.3) remotes::install_github("mrc-ide/ukbtools@v1.1.3") # 再安装 ukbnmr(注意:必须指定分支,main 分支含未修复的 Windows 路径 bug) remotes::install_github("username/ukbnmr@fix-windows-path", dependencies = TRUE, upgrade = "never")

提示:upgrade = "never"是关键。若设为"always",remotes会强行升级ukbtools到 v1.2.0,导致后续ukbnmr::read_ukb_nmr()报错Error in parse_ukb_id(x) : object 'eid' not found。

2.3 初始化配置:.ukbnmr_config.yml文件的四个必填字段

ukbnmr 不接受命令行参数传入路径,所有 I/O 路径由 YAML 配置文件驱动。首次使用需在项目根目录创建.ukbnmr_config.yml:

# .ukbnmr_config.yml input_dir: "D:/ukb_data/nmr_raw/" # 原始 CSV 存放目录(必须含 ukbXXXXX.csv) output_dir: "D:/ukb_data/nmr_processed/" # 处理后文件输出目录(自动创建子文件夹) ukb_sample_map: "D:/ukb_data/sample_id_map.csv" # UKB eid 与 sample_id 映射表(两列:eid,sample_id) nmr_dict_path: "D:/ukb_data/nightingale_dict.csv" # Nightingale 字段字典(含 metabolite_name, unit, assay_type)

其中ukb_sample_map.csv是核心——Nightingale 原始数据用sample_id(如S123456789),而 UK Biobank 主数据用eid(如123456789),二者需精确一对一映射。若映射表缺失或重复,ukbnmr::validate_ids()会直接报错并终止流程,避免后续分析污染。

3. 核心工作流:从原始 CSV 到 ML 就绪矩阵的五阶段实操

3.1 阶段一:安全读入与 ID 校验——read_ukb_nmr()的静默模式陷阱

Nightingale 原始 CSV 通常含 100~150 列,但 ukbnmr 默认只读取字典中定义的代谢物列(nmr_dict.csv的metabolite_name列)。若原始文件含未定义字段(如extra_flag),read_ukb_nmr()会静默丢弃——这是设计而非 bug,防止误用非标准指标。但新手易误以为「数据没读全」。验证方法:

library(ukbnmr) # 读入数据(自动应用配置文件路径) nmr_raw <- read_ukb_nmr() # 查看实际读入的代谢物数 vs 字典定义数 cat("字典定义代谢物数:", nrow(night_dict()), "\n") cat("实际读入代谢物数:", ncol(nmr_raw), "\n") cat("缺失的代谢物名:", setdiff(night_dict()$metabolite_name, colnames(nmr_raw)), "\n")

若setdiff返回非空,说明原始 CSV 中对应列名与字典不一致(如字典写VLDL-Cholesterol,文件却是VLDL_Cholesterol)。此时需用ukbnmr::repair_column_names()修复:

# 自动将下划线转连字符(Nightingale 字典规范) nmr_raw_fixed <- repair_column_names(nmr_raw, dict = night_dict())

3.2 阶段二:缺失值语义解析——NA不等于缺失,而是检测状态编码

Nightingale NMR 数据中NA有三层含义:

  • NA:仪器未检测(如低浓度样本超出 LOD)
  • -9:检测失败(技术原因)
  • -3:样本拒收(质量控制失败)

ukbnmr 将-9和-3统一转为NA,但保留原始NA的语义。关键操作是impute_nmr_na()——它不用均值填充,而是按代谢物类型分层处理:

  • 脂蛋白亚组分(assay_type == "lipoprotein"):用同一样本其他脂蛋白浓度的中位数插补(因脂蛋白谱高度相关)
  • 小分子代谢物(assay_type == "small_molecule"):用mice::mice()的预测均值匹配(PMM)法,以年龄、性别、BMI 为协变量
# 执行分层插补(耗时约 8 分钟/10 万样本) nmr_imputed <- impute_nmr_na( nmr_raw_fixed, covariates = c("age", "sex", "bmi"), # 协变量需提前从 UKB 主表 join 进来 method = "pmm", # PMM 比默认 pmm 更鲁棒 m = 5 # MICE 迭代次数,5 次足够 ) # 查看插补前后 NA 比例变化 print(colMeans(is.na(nmr_raw_fixed))) print(colMeans(is.na(nmr_imputed)))

参数说明:covariates必须是长度与nmr_raw_fixed行数一致的向量。若你只有ukbXXXXX.csv,需先用ukbtools::ukb_df()加载主表,再dplyr::left_join()合并。

3.3 阶段三:标准化与协变量校正——adjust_for_covariates()的两种模式

Nightingale 数据存在显著的批次效应(不同检测周次、仪器编号)。adjust_for_covariates()提供两种校正模式:

模式适用场景数学原理输出维度
"residual"(默认)GWAS/关联分析对每个代谢物拟合lm(metab ~ age + sex + bmi + batch),取残差与输入相同(n×p)
"zscore"机器学习输入先scale()标准化,再对残差矩阵做 Z-scoren×p
# 方式一:残差校正(推荐用于统计推断) nmr_adj_residual <- adjust_for_covariates( nmr_imputed, covariates = c("age", "sex", "bmi", "batch"), method = "residual" ) # 方式二:Z-score 校正(推荐用于聚类/深度学习) nmr_adj_zscore <- adjust_for_covariates( nmr_imputed, covariates = c("age", "sex", "bmi", "batch"), method = "zscore", center = TRUE, # 是否中心化(默认 TRUE) scale = TRUE # 是否缩放(默认 TRUE) )

注意:batch变量需从 Nightingale 元数据中提取(通常在ukbXXXXX.csv的batch_id列),若无此列,可用ukbnmr::infer_batch_from_filename()从文件名推断(如ukb123456789_batch01.csv→batch01)。

4. 避坑指南:五个真实翻车现场与血泪解决方案

4.1 现象:read_ukb_nmr()报错Error in gzfile(file, "rb") : cannot open the connection

原因:配置文件中input_dir路径末尾多了一个/(如"D:/ukb_data/nmr_raw//"),Windows 系统下list.files()会返回空字符,导致read.csv()尝试读取""。
解决:检查.ukbnmr_config.yml,删除所有路径末尾斜杠;或用ukbnmr::normalize_path()自动清理:

config <- ukbnmr::read_config() config$input_dir <- ukbnmr::normalize_path(config$input_dir) ukbnmr::write_config(config)

4.2 现象:impute_nmr_na()运行 2 小时后卡死,R 进程 CPU 占用 100%

原因:mice包在处理高维小分子代谢物(>80 列)时,默认predictorMatrix包含全连接,计算复杂度 O(p²),120 列时需计算 14400 个回归模型。
解决:手动精简预测变量,只保留强相关代谢物:

# 获取小分子代谢物列名 small_mol_cols <- night_dict() %>% filter(assay_type == "small_molecule") %>% pull(metabolite_name) # 构建稀疏预测矩阵(仅用 top 10 相关者) pred_mat <- mice::make.predictorMatrix(nmr_imputed[, small_mol_cols]) pred_mat[,-(1:10)] <- 0 # 每列只用前 10 列预测 nmr_imputed <- impute_nmr_na(..., predictorMatrix = pred_mat)

4.3 现象:adjust_for_covariates(method="residual")输出矩阵含Inf值

原因:协变量中存在NA(如bmi列有缺失),lm()拟合时自动剔除整行,导致残差向量长度变短,与原矩阵行数不匹配,后续cbind()时用NA填充产生Inf。
解决:校正前强制清洗协变量:

# 提取协变量并删除含 NA 行 cov_df <- data.frame(age, sex, bmi, batch) complete_rows <- complete.cases(cov_df) nmr_clean <- nmr_imputed[complete_rows, ] cov_clean <- cov_df[complete_rows, ] nmr_adj <- adjust_for_covariates(nmr_clean, covariates = cov_clean)

4.4 现象:导出的.rds文件在另一台机器加载时报错error reading from connection

原因:saveRDS()默认用ascii=FALSE(二进制),但跨平台(Windows→Linux)时 R 版本差异导致序列化不兼容。
解决:导出时强制 ASCII 格式(体积增大 30%,但 100% 兼容):

saveRDS(nmr_adj, "nmr_adj.rds", ascii = TRUE) # 加载时无需额外参数 loaded <- readRDS("nmr_adj.rds")

4.5 现象:ukbnmr::plot_qc()生成的 PDF 图形中中文显示为方块

原因:R 默认 PDF 设备不嵌入中文字体,cairo_pdf()也需显式指定字体。
解决:在绘图前设置全局 PDF 设备:

# Linux/macOS pdf.options(family = "Helvetica") # Windows(需先安装 SimHei 字体) pdf.options(family = "SimHei") # 然后调用 ukbnmr::plot_qc(nmr_adj)

5. 进阶技巧:构建可复现的 QC 报告流水线,用ukbnmr::report_qc()生成 PDF + HTML 双格式

UK Biobank 项目评审常要求提供完整的质控证据链。ukbnmr::report_qc()不是简单画图,而是整合 7 类 QC 指标生成带元数据的报告:

5.1 报告内容构成:从原始数据到最终矩阵的 7 层穿透式验证

QC 层级检查项触发阈值报告位置
原始层文件完整性(行数/列数是否匹配字典)行数 < 95% 预期值第 1 页摘要
ID 层eid与sample_id映射唯一性重复eid> 0第 2 页表格
缺失层各代谢物缺失率分布任一列缺失率 > 20% 标红第 3 页热图
分布层插补前后偏度变化skew_post - skew_pre
校正层协变量校正后批次效应残留batch的方差解释率 > 5%第 5 页 ANOVA 表
相关层已知生物学相关对(如 HDL-C 与 ApoA1)相关系数r < 0.6第 6 页配对图
输出层导出矩阵的 SHA256 校验码与配置文件中记录值比对封底页

5.2 一键生成双格式报告:PDF(归档)+ HTML(交互探索)

# 生成报告(自动读取当前配置) qc_report <- ukbnmr::report_qc( input_data = nmr_raw_fixed, imputed_data = nmr_imputed, adjusted_data = nmr_adj_residual, output_dir = "reports/qc_20240520/" ) # 输出:reports/qc_20240520/qc_report.pdf(含所有图表+元数据) # reports/qc_20240520/qc_report.html(可点击展开各层详情) # reports/qc_20240520/qc_metadata.json(机器可读元数据)

关键参数:output_dir必须是全新目录(不覆盖旧报告),因为report_qc()会在其中写入qc_metadata.json,该文件包含本次运行的全部参数、时间戳、R 版本、ukbnmr 版本及所有 QC 阈值,是审计追溯的唯一依据。

5.3 自动化校验:用ukbnmr::verify_report()验证报告完整性

某跨平台合作项目中,A 同学在 Windows 生成报告,B 同学在 Linux 加载时发现 PDF 图形错位。根源是cairo_pdf()在不同系统渲染字体路径不同。verify_report()可提前捕获:

# 验证报告是否能在目标环境加载 verification <- ukbnmr::verify_report( report_path = "reports/qc_20240520/qc_report.pdf", target_os = "linux", # 可选 "windows", "macos", "linux" check_fonts = TRUE, # 检查字体嵌入 check_images = TRUE # 检查 PNG/JPEG 是否损坏 ) # 输出 verification$status == "PASS" 或详细错误 print(verification)

从那以后我每次提交 UK Biobank 分析结果前,都强制走一遍report_qc()+verify_report()流程,并把qc_metadata.json作为附件上传至项目管理系统——这不仅是合规要求,更是对自己代码的尊重。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

阵列信号处理前三章Matlab代码实战:波束形成与方向图综合

阵列信号处理前三章Matlab代码实战:波束形成与方向图综合

简介&#xff1a;本资源是《优化阵列信号处理》前三章关键案例的Matlab实现代码&#xff0c;面向学习阵列信号处理、波束形成与DOA估计的高年级本科生、研究生及科研人员&#xff0c;帮助读者把书中理论落到可运行、可调试的仿真程序上。压缩包共27个文件&#xff0c;以26个.m脚…

2026/10/20 11:52:49 阅读更多 →
Assistants API实战:构建定制化AI Agent与知识库问答助手

Assistants API实战:构建定制化AI Agent与知识库问答助手

做AI Agent快两年&#xff0c;我最大的感受是&#xff1a;OpenAI Assistants API把“定制化AI Agent”这个事&#xff0c;从原来的DIY拼装变成了半成品套餐——你不用再自己维护会话历史、不用手写工具调用的循环、不用为多轮记忆写一堆状态管理代码&#xff0c;只要把“这个Ag…

2026/10/20 11:52:49 阅读更多 →
修正C/GMRES:嵌入式平台上实时NMPC的工程落地

修正C/GMRES:嵌入式平台上实时NMPC的工程落地

1. 潜航器控不住&#xff1f;实时NMPC是绕不开的路自动潜航器这东西&#xff0c;越往深水跑越不让人省心。水面下的水流、温度层、密度变化&#xff0c;加上潜航器本身的强非线性动力学&#xff0c;传统的线性控制器到了大机动工况下经常“使不上劲”。这几年做水下机器人控制的…

2026/10/20 11:52:49 阅读更多 →

最新新闻

提示词模板化:从基础到高级的Prompt Engineering实战指南

提示词模板化:从基础到高级的Prompt Engineering实战指南

1. 为什么要把提示词做成模板&#xff1a;从重复劳动到可复用资产先讲一个我自己的经历。早期用对话式AI干活的时候&#xff0c;每次都是临时想提示词&#xff0c;想到哪写到哪&#xff1a;“帮我总结一下这段文字”“写一个产品介绍&#xff0c;要专业一点”。这种随手写的做法…

2026/10/20 12:46:33 阅读更多 →
Kafka入门进阶:分区、副本、acks与消费者组核心机制详解

Kafka入门进阶:分区、副本、acks与消费者组核心机制详解

1. 从“能跑起来”到“跑得明白”&#xff1a;Kafka 基础入门的第二道坎很多人第一次接触 Kafka&#xff0c;都是照着官方 Quickstart 敲一遍命令行&#xff0c;看到生产者发消息、消费者收到消息&#xff0c;就觉得“我会了”。但真到了项目里&#xff0c;面对一堆server.prop…

2026/10/20 12:46:33 阅读更多 →
SpringBoot+Vue图书推荐系统源码解析:协同过滤与前后端分离实战

SpringBoot+Vue图书推荐系统源码解析:协同过滤与前后端分离实战

最近我在帮几位同学梳理毕业设计项目&#xff0c;被问得最多的就是“图书推荐系统”。这类题到现在依旧热门&#xff0c;原因也很简单&#xff1a;图书天然适合做推荐&#xff0c;用户行为数据容易理解&#xff0c;和电商推荐、短视频推荐比起来业务逻辑更干净&#xff0c;特别…

2026/10/20 12:46:33 阅读更多 →
技能不是标签而是动作:用刻意练习、反馈与复盘构建底层能力

技能不是标签而是动作:用刻意练习、反馈与复盘构建底层能力

很多人都把“技能”当成一个名词来理解&#xff0c;觉得自己会写代码、会做PPT、会剪辑视频&#xff0c;就算拥有一项技能。但真正带过人、自己从零啃过几门硬技能的人会知道&#xff0c;技能其实是动词——它是你每天都要做的一个动作&#xff0c;而不是简历上既能唬人又能自我…

2026/10/20 12:46:33 阅读更多 →
把涂鸦设备搬回局域网:Tuya-Local 本地控制与 Home Assistant 配置指南

把涂鸦设备搬回局域网:Tuya-Local 本地控制与 Home Assistant 配置指南

把涂鸦设备搬回局域网&#xff1a;Tuya-Local 本地控制与 Home Assistant 配置指南 【免费下载链接】tuya-local Local support for Tuya devices in Home Assistant 项目地址: https://gitcode.com/GitHub_Trending/tu/tuya-local Tuya-Local 是 Home Assistant 的一个…

2026/10/20 12:46:33 阅读更多 →
MCP协议驱动智能体底座架构设计:从插件思维到接口思维

MCP协议驱动智能体底座架构设计:从插件思维到接口思维

1. 从“插件思维”到“接口思维”的认知跃迁大多数人第一次接触MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;时&#xff0c;脑子里蹦出来的第一个词就是“插件系统”。这也不奇怪&#xff0c;毕竟它的外在表现确实像插件&#xff1a;你写一个…

2026/10/20 12:45:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →