使用mutate赋值差异变量时dysreg_index超出0-1范围的问题排查
解决血液分析物失调指数计算中重复计数的问题
问题说明
需要基于z评分血液分析物数据集创建四个失调指数:
- metab_index:包含alt_zscore、alb_zscore、alp_zscore、calcium_zscore、uc_ratio_zscore、sodium_zscore、phos_zscore、pot_zscore、pcv_zscore,以及低值的glob_zscore(低于25%分位数)
- imm_index:包含baso_per_ul_zscore、esino_per_ul_zscore、lympho_per_ul_zscore、mono_per_ul_zscore、neutro_per_ul_zscore,以及高值的glob_zscore(高于75%分位数)
- neuro_index:仅包含cortisol_zscore(不在20%-80%分位数区间)
- dysreg_index:包含所有上述分析物,glob_zscore无论高低值均需计入(即符合任一条件则计1)
指数计算规则:每行中符合失调条件的分析物数量 ÷ 该行非NA分析物总数,结果需在0-1之间。原代码因重复定义glob_zscore的判断函数,导致dysreg_index计算时对glob_zscore重复计数,结果超出合理范围。
修正后的代码
library(dplyr) # 预计算所有分析物的分位数,避免重复计算且保证一致性 quantiles_df <- df %>% summarize( across(ends_with("_zscore"), list( q20 = ~quantile(., 0.2, na.rm = TRUE), q80 = ~quantile(., 0.8, na.rm = TRUE), q25 = ~quantile(., 0.25, na.rm = TRUE), q75 = ~quantile(., 0.75, na.rm = TRUE) ), .names = "{col}_{fn}") ) # 定义各分析物的失调判断函数,拆分glob的两个条件 funcs <- list( # 代谢类9项分析物 alt_zscore = function(z) !is.na(z) & !between(z, quantiles_df$alt_zscore_q20, quantiles_df$alt_zscore_q80), alb_zscore = function(z) !is.na(z) & !between(z, quantiles_df$alb_zscore_q20, quantiles_df$alb_zscore_q80), alp_zscore = function(z) !is.na(z) & !between(z, quantiles_df$alp_zscore_q20, quantiles_df$alp_zscore_q80), calcium_zscore = function(z) !is.na(z) & !between(z, quantiles_df$calcium_zscore_q20, quantiles_df$calcium_zscore_q80), uc_ratio_zscore = function(z) !is.na(z) & z < quantiles_df$uc_ratio_zscore_q25, sodium_zscore = function(z) !is.na(z) & !between(z, quantiles_df$sodium_zscore_q20, quantiles_df$sodium_zscore_q80), phos_zscore = function(z) !is.na(z) & !between(z, quantiles_df$phos_zscore_q20, quantiles_df$phos_zscore_q80), pot_zscore = function(z) !is.na(z) & !between(z, quantiles_df$pot_zscore_q20, quantiles_df$pot_zscore_q80), pcv_zscore = function(z) !is.na(z) & z > quantiles_df$pcv_zscore_q75, # glob的两个独立判断条件 glob_low = function(z) !is.na(z) & z < quantiles_df$glob_zscore_q25, glob_high = function(z) !is.na(z) & z > quantiles_df$glob_zscore_q75, # 免疫类5项分析物 baso_per_ul_zscore = function(z) !is.na(z) & z > quantiles_df$baso_per_ul_zscore_q75, esino_per_ul_zscore = function(z) !is.na(z) & z > quantiles_df$esino_per_ul_zscore_q75, lympho_per_ul_zscore = function(z) !is.na(z) & z > quantiles_df$lympho_per_ul_zscore_q75, mono_per_ul_zscore = function(z) !is.na(z) & z > quantiles_df$mono_per_ul_zscore_q75, neutro_per_ul_zscore = function(z) !is.na(z) & z > quantiles_df$neutro_per_ul_zscore_q75, # 神经内分泌类 cortisol_zscore = function(z) !is.na(z) & !between(z, quantiles_df$cortisol_zscore_q20, quantiles_df$cortisol_zscore_q80) ) # 计算各失调指数 df <- df %>% mutate( # 代谢指数:9项基础分析物 + glob低值判断 metab_index = { base_cols <- names(funcs[1:9]) numerator <- c( mapply(function(fn, x) fn(x), funcs[1:9], pick(all_of(base_cols))), funcs$glob_low(pick(glob_zscore)) ) denominator <- rowSums(!is.na(pick(all_of(c(base_cols, "glob_zscore"))))) rowSums(numerator) / denominator }, # 免疫指数:5项基础分析物 + glob高值判断 imm_index = { base_cols <- names(funcs[12:16]) numerator <- c( mapply(function(fn, x) fn(x), funcs[12:16], pick(all_of(base_cols))), funcs$glob_high(pick(glob_zscore)) ) denominator <- rowSums(!is.na(pick(all_of(c(base_cols, "glob_zscore"))))) rowSums(numerator) / denominator }, # 神经指数:仅cortisol_zscore neuro_index = { numerator <- funcs$cortisol_zscore(pick(cortisol_zscore)) denominator <- as.integer(!is.na(pick(cortisol_zscore))) numerator / denominator }, # 总失调指数:所有分析物,glob高低值任一符合即计1 dysreg_index = { all_unique_cols <- names(df) # 计算各分析物的失调标记 dysreg_flags <- c( # 代谢9项 mapply(function(fn, x) fn(x), funcs[1:9], pick(all_of(names(funcs[1:9])))), # glob:高低值任一符合则为1 list(glob_flag = !is.na(pick(glob_zscore)) & (funcs$glob_low(pick(glob_zscore)) | funcs$glob_high(pick(glob_zscore)))), # 免疫5项 mapply(function(fn, x) fn(x), funcs[12:16], pick(all_of(names(funcs[12:16])))), # cortisol funcs$cortisol_zscore(pick(cortisol_zscore)) ) denominator <- rowSums(!is.na(pick(all_of(all_unique_cols)))) rowSums(dysreg_flags) / denominator } )
修正核心要点
- 拆分glob判断逻辑:将原重复定义的
glob_zscore函数拆分为glob_low和glob_high两个独立函数,避免函数覆盖导致的逻辑丢失。 - 预计算分位数:提前统一计算所有分析物的分位数,确保各函数使用的分位数一致,同时提升代码运行效率。
- 分母去重统计:计算总失调指数时,分母统计所有独特分析物的非NA数量(glob仅算一次),保证分子(符合条件的数量)不会超过分母,结果始终在0-1区间内。
- glob总失调逻辑:总失调指数中,glob_zscore只要符合低值或高值任一条件即计为1,避免重复计数问题。
内容的提问来源于stack exchange,提问作者burphound
相关产品推荐
相关产品推荐

