R语言用ifelse基于多列生成新变量结果异常如何解决?
基于洪灾数据集生成风险分级变量的异常问题
背景
我尝试基于tu_aftermatthew数据集的三个字段tu_aftermatthew$FLD_ZONE、tu_aftermatthew$SFHA_TF、tu_aftermatthew$ZONE_SUBTY生成新的风险分级变量,各字段交叉统计结果如下:
> with(tu_aftermatthew,table(FLD_ZONE, SFHA_TF, useNA = "always")) SFHA_TF FLD_ZONE F T <NA> A 0 2494 0 AE 0 286245 0 AH 0 621 0 AO 0 669 0 AREA NOT INCLUDED 8257 0 0 VE 0 5710 0 X 12626557 0 0 <NA> 0 0 7241489 > with(tu_aftermatthew,table(ZONE_SUBTY, SFHA_TF, useNA = "always")) SFHA_TF ZONE_SUBTY F T <NA> 0.2 PCT ANNUAL CHANCE FLOOD HAZARD 159698 0 0 1 PCT ANNUAL CHANCE FLOOD HAZARD CONTAINED IN CHANNEL 0 13 0 1 PCT CONTAINED IN STRUCTURE, COMMUNITY ENCROACHMENT 8 0 0 1 PCT CONTAINED IN STRUCTURE, FLOODWAY 6 0 0 1 PCT FUTURE CONDITIONS 4192 0 0 1 PCT FUTURE CONDITIONS CONTAINED IN STRUCTURE 1 0 0 AREA OF MINIMAL FLOOD HAZARD 12459618 0 0 AREA WITH REDUCED FLOOD RISK DUE TO LEVEE 3034 0 0 COMMUNITY ENCROACHMENT AREA 0 1007 0 FLOODWAY 0 17059 0 STATE ENCROACHMENT AREA 0 294 0 <NA> 8257 277366 7241489
新变量FLOOD_RISK生成规则
- 当
SFHA_TF取值为"T"且FLD_ZONE不为"VE"时,赋值为"SFHA_INLAND",总计数应为290029 - 当
FLD_ZONE取值为"VE"时,赋值为"SFHA_COASTAL",总计数应为5710 - 当
ZONE_SUBTY取值为"0.2 PCT ANNUAL CHANCE FLOOD HAZARD"时,赋值为"500_FLOOD",该类记录与SFHA_TF=="T"的记录无重叠,不会和前两类冲突
预期输出结果
> table(tu_aftermatthew$FLOOD_RISK) 500_FLOOD SFHA_INLAND SFHA_COASTAL 159698 90029 5710
问题现象
我使用ifelse语句实现上述逻辑时结果不符合预期,代码如下:
> tu_aftermatthew$FLOOD_RISK <- ifelse(tu_aftermatthew$SFHA_TF == "T", "SFHA_INLAND", NA) > tu_aftermatthew$FLOOD_RISK <- ifelse(tu_aftermatthew$FLD_ZONE == "VE", "SFHA_COASTAL", tu_aftermatthew$FLOOD_RISK) > tu_aftermatthew$FLOOD_RISK <- ifelse(tu_aftermatthew$ZONE_SUBTY == "0.2 PCT ANNUAL CHANCE FLOOD HAZARD", "500_FLOOD", tu_aftermatthew$FLOOD_RISK) > > table(tu_aftermatthew$FLOOD_RISK, useNA = "always" ) 500_FLOOD SFHA_INLAND <NA> 159698 18373 19993971
拆分步骤验证发现,第三个ifelse语句似乎覆盖了前一步生成的SFHA_INLAND、SFHA_COASTAL赋值,但按照数据特征,0.2%概率洪泛区的记录和SFHA_TF为T的记录没有重叠,不该出现该问题。我构造了如下测试数据复现逻辑,运行却完全正常,目前无法定位问题原因:
FLD<-c("A","A","V","X","X") SFHA<-c("T","T","T","F","F") DESC<-c("1","1","1","0", ".2") df<-data.frame(FLD, SFHA, DESC) df df$FLOOD_RISK <- ifelse(df$SFHA == "T", "SFHA_INLAND", NA) df$FLOOD_RISK <- ifelse(df$FLD == "V", "SFHA_COASTAL", df$FLOOD_RISK) df$FLOOD_RISK <- ifelse(df$DESC == ".2", "500_FLOOD", df$FLOOD_RISK) df FLD SFHA DESC FLOOD_RISK 1 A T 1 SFHA_INLAND 2 A T 1 SFHA_INLAND 3 V T 1 SFHA_COASTAL 4 X F 0 <NA> 5 X F .2 500_FLOOD
问题根因
问题核心是R中ifelse函数对NA判断的处理逻辑:当判断条件的计算结果为NA时,ifelse会直接返回NA,而非你期望的保留原有值。
你的原数据中存在大量ZONE_SUBTY为NA的记录:对于已经被赋值为SFHA_INLAND、SFHA_COASTAL的记录,在第三个ifelse判断时,ZONE_SUBTY == "0.2 PCT ANNUAL CHANCE FLOOD HAZARD"的结果为NA,因此这些记录会被错误覆盖为NA。你构造的测试数据中没有NA值,因此不会复现该问题。
修复方案
将第三个ifelse的判断条件补充NA校验即可,修改后代码如下:
tu_aftermatthew$FLOOD_RISK <- ifelse(tu_aftermatthew$SFHA_TF == "T", "SFHA_INLAND", NA) tu_aftermatthew$FLOOD_RISK <- ifelse(tu_aftermatthew$FLD_ZONE == "VE", "SFHA_COASTAL", tu_aftermatthew$FLOOD_RISK) # 补充NA判断,只有明确满足条件时才赋值,否则保留原有值 tu_aftermatthew$FLOOD_RISK <- ifelse(!is.na(tu_aftermatthew$ZONE_SUBTY) & tu_aftermatthew$ZONE_SUBTY == "0.2 PCT ANNUAL CHANCE FLOOD HAZARD", "500_FLOOD", tu_aftermatthew$FLOOD_RISK)
也可以使用更易读的dplyr::case_when实现,避免多层ifelse嵌套的逻辑问题:
library(dplyr) tu_aftermatthew <- tu_aftermatthew %>% mutate(FLOOD_RISK = case_when( FLD_ZONE == "VE" ~ "SFHA_COASTAL", SFHA_TF == "T" ~ "SFHA_INLAND", ZONE_SUBTY == "0.2 PCT ANNUAL CHANCE FLOOD HAZARD" ~ "500_FLOOD", TRUE ~ NA_character_ ))
内容的提问来源于stack exchange,提问作者tchoup
相关产品推荐
相关产品推荐

