使用Apply处理数据框时布尔值判断失效?逻辑运算异常排查
为什么我的R代码中逻辑运算符没按预期工作?
先看一下你的代码和遇到的问题:
a = c(TRUE, FALSE, TRUE, FALSE, TRUE, TRUE) b = c('a', 'b', 'c', 'de', 'f', 'g') c = c(1, 2, 3, 4, 5, 6) d = c(0, 0, 0, 0, 0, 1) wtf = data.frame(a, b, c, d) wtf$huh = apply(wtf, 1, function(row) { if (row['a'] == T) { return('we win') } if (row['c'] < 5) { return('hooray') } if (row['d'] == 1) { return('a thing') } return('huh?') })
你预期当row['a']为TRUE时返回'we win',但实际第一行、第三行这些a为TRUE的行却返回了'hooray'——这个坑我之前也踩过!
问题根源:apply()会强制统一数据类型
apply()处理数据框时,会先把整个数据框转换成矩阵,而矩阵只能容纳一种数据类型。你的数据框里有字符列b,所以所有列都会被强制转换成字符型——包括原本的逻辑列a、数值列c和d。
也就是说,你在匿名函数里取row['a']时,它的值不是逻辑值TRUE,而是字符串"TRUE"。这时候row['a'] == T就变成了字符串和逻辑值的跨类型比较,R中这种比较的结果是FALSE,所以第一个条件永远不会触发,程序就走到了第二个条件row['c'] <5——这里row['c']是字符串"1"、"3"等,R会自动把它转成数值,所以1<5、3<5都是TRUE,自然返回'hooray'了。
解决方案
方案1:用向量化操作(推荐,更符合R的风格)
R的核心优势是向量化运算,比apply()更高效也更不容易出错。你可以用基础R的ifelse()嵌套,或者用dplyr的case_when()(代码可读性更好):
基础R版本:
wtf$huh <- with(wtf, ifelse(a, 'we win', ifelse(c < 5, 'hooray', ifelse(d == 1, 'a thing', 'huh?') ) ) )
dplyr版本(代码更清晰):
library(dplyr) wtf <- wtf %>% mutate(huh = case_when( a ~ 'we win', # 第一个满足的条件生效 c < 5 ~ 'hooray', d == 1 ~ 'a thing', TRUE ~ 'huh?' # 所有条件都不满足时的默认值 ))
方案2:如果一定要用apply(),手动转换类型
如果你坚持要用apply(),那就在匿名函数里把每个元素转成正确的类型:
wtf$huh = apply(wtf, 1, function(row) { a_val <- as.logical(row['a']) # 把字符串转成逻辑值 c_val <- as.numeric(row['c']) # 把字符串转成数值 d_val <- as.numeric(row['d']) if (a_val) { return('we win') } if (c_val < 5) { return('hooray') } if (d_val == 1) { return('a thing') } return('huh?') })
验证结果
修改后再查看wtf,就能得到你预期的结果:
> wtf a b c d huh 1 TRUE a 1 0 we win 2 FALSE b 2 0 hooray 3 TRUE c 3 0 we win 4 FALSE de 4 0 hooray 5 TRUE f 5 0 we win 6 TRUE g 6 1 we win
内容的提问来源于stack exchange,提问作者ifly6
相关产品推荐
相关产品推荐

