在R(或RStudio)中,我想将带有单位的字段转换为数字,但我收到了错误的警告



使用R,我得到的数据看起来像:

A,   B
=  =====
x,"  120"
y," 2300"
z,"1.2 M"
x," 4500"
x," 42 M"

这是使用构建的

A <- c("x","y","z","x","x")
B <- c("  120", " 2300", "1.2 M", " 4500", " 42 M")
data <- data.frame(A, B)

我想将第二列转换为一个有效的数字("M"表示数百万(。我拥有的是:

df <- data %>%
mutate( B = ifelse( grepl("^ *[0-9]+$", .$B), 
as.numeric(.$B),
1000000 * as.numeric(sub(" *([0-9]+) M", "\1", .$B))))

即。它寻找一个带有"M"的字段,如果找到,则在sustricg上做as.numeric并乘以1000000。

这很好,除非我收到警告:

Warning messages:
1: Problem with `mutate()` input `B`.
i NAs introduced by coercion
i Input `B` is `ifelse(...)`. 
2: In ifelse(grepl("^ *[0-9]+$", .$B), as.numeric(.$B), 1e+06 *  :
NAs introduced by coercion

但当我使用df %>% filter(is.na(B))检查数据时,没有NAs

我不想完全禁用警告,但我确实想在";无效";。

有什么建议吗?阿美,我错过什么了?

得到警告的原因是因为ifelse/if_else/case_whenyesno部分都传递了整个向量。稍后将根据条件决定要返回的输出。

因此,我们认为我们只将as.numeric(B)应用于那些满足条件的数字,但实际上,它与应用as.numeric(data$B)相同,后者给出了相同的警告,即B中很少有值不能转换为数字。因此,您需要以一种根本不会为任何值生成警告的方式使用ifelse(@r2evans已经显示了这一点(,或者在没有ifelse的情况下单独应用操作。

data$C <- NA_real_
inds <- grepl("^ *[0-9]+$", data$B)
data$C[inds] <- as.numeric(B[inds])
data$C[!inds] <- 1000000 * as.numeric(sub(" *([0-9]+) M", "\1", data$B[!inds]))

我有一个未完全测试的函数,它是我编写的另一个函数的伴侣(它松散地基于utils:::format.object_size(。

unKMG <- function(s, standard = "SI") {
known_bases <- c(legacy = 1024, IEC = 1024, SI = 1000)
known_units <- list(SI = c("", "k", "M", "G", "T", "P", 
"E", "Z", "Y"), IEC = c("", "Ki", "Mi", "Gi", 
"Ti", "Pi", "Ei", "Zi", "Yi"), legacy = c("", "K", 
"M", "G", "T", "P"))
standard <- match.arg(standard, c("auto", names(known_bases)))
powers <- known_bases[[standard]] ^
(setNames(seq_along(known_units[[standard]]), known_units[[standard]])-1)
sapply(strsplit(trimws(s), "[[:space:]]+"),
function(z) {
nums <- suppressWarnings(as.numeric(z))
prod(nums[!is.na(nums)], powers[names(powers) %in% z[is.na(nums)]])
})
}
unKMG(dat$B)
# [1]      120     2300  1200000     4500 42000000

但如果你想要一次机会,试试

as.numeric(gsub(".*\b(-?[0-9]+\.?[0-9]*)\b.*", "\1", dat$B)) * 
ifelse(grepl("M", dat$B), 1000000, 1)
# [1]      120     2300 12000000     4500 42000000
dat %>%
mutate(
B = as.numeric(gsub(".*\b(-?[0-9]+\.?[0-9]*)\b.*", "\1", B)) * 
ifelse(grepl("M", B), 1000000, 1)
)

正则表达式的解释:

  • .*任何东西(不包括任何东西(
  • \b一个字边界,不消耗(包含(;例如,这确保了我们不会在较大的数字中间开始或结束一个数字
  • (...)组,在第二个自变量中被引用为\1
  • 负符号的-?0或1
  • [0-9]+1位或1位以上
  • 小数点的\.?0或1(对于不同的语言环境,替换为,[,.](
  • [0-9]*0位或更多数字

(如果你没有注意到的话,我把我的数据命名为dat……当我把data放错地方时,我已经被invalid 'type' (closure) of argument的错误咬了太多次了,现在我倾向于使用其他与基本R函数不冲突的名称。(

最新更新