我有一个简单的函数,用于跨图表标签以所需的方式格式化货币:
curr_fmt <- function(x) {
f <- function(x) {
if (is.numeric(x)) {
if (x >= 1e4) {
x <- x / 1e3
paste0(intToUtf8(163), scales::comma(x), "K")
} else {
paste0(intToUtf8(163), scales::comma(x))
}
} else {
x
}
}
vf <- Vectorize(f, "x")
vf(x)
}
当应用于数字向量时,它会产生所需的结果:
>> curr_fmt(c(1, 2e4, 3e4, 100))
[1] "£1" "£20K" "£30K" "£100"
如果向量包含 NA
值,则不会产生所需的结果:
>> curr_fmt(c(1, 2e4, 3e4, 100, NA))
在
if (x >= 10000) {
中重新运行调试错误:缺少值 需要TRUE/FALSE
问题必须与矢量化有关,因为函数 f
以理想的方式处理 NA
值:
>> f(NA)
[1] NA
>> f(3e4)
[1] "£30K"
curr_fmt(c(1, 2e4, 3e4, 100, NA))
[1] "£1" "£20K" "£30K" "£100" NA
x
向量的长度相对应, NA
值返回为 NA
以及示例中格式化的货币值。答案 0 :(得分:3)
我们需要更改if
条件,以便仅使用非NA值
curr_fmt <- function(x) {
f <- function(x) {
if (is.numeric(x) & !is.na(x)) { # changed here
if (x >= 1e4) {
x <- x / 1e3
paste0(intToUtf8(163), scales::comma(x), "K")
} else {
paste0(intToUtf8(163), scales::comma(x))
}
} else {
x
}
}
vf <- Vectorize(f, "x")
vf(x)
}
curr_fmt(c(1, 2e4, 3e4, 100, NA))
#[1] "£1" "£20K" "£30K" "£100" NA
curr_fmt(c(1, 2e4, 3e4, 100))
#[1] "£1" "£20K" "£30K" "£100"
答案 1 :(得分:3)
装饰者模式的完美情况:
robustify <- function(func)
{
function(...)
{
z = (...)
x = Filter(Negate(is.na), z)
z[!is.na(z)] = func(x)
z
}
}
> robustify(curr_fmt)(c(1, 2e4, 3e4, NA, 23))
[1] "£1" "£20K" "£30K" NA "£23"
资产:可重复使用,功能齐全,您无需修改原始功能,只需更改行为即可。
答案 2 :(得分:3)
既然akrun的回答被接受了它就不再重要了,但是这里是一个不需要if/else
的矢量化解决方案。
curr_fmt2 <- function(x){
y <- character(length(x))
isna <- is.na(x)
isnum <- is.numeric(x)
y[isna] <- NA
y[!isna & !isnum] <- x[!isna & !isnum]
inx <- !isna & isnum & x < 1e4
y[inx] <- paste0(intToUtf8(163), scales::comma(x[inx]))
inx <- !isna & isnum & x >= 1e4
x <- x / 1e3
y[inx] <- paste0(intToUtf8(163), scales::comma(x[inx]), "K")
y
}
x1 <- c(1, 2e4, 3e4, 100)
x2 <- c(1, 2e4, 3e4, 100, NA)
curr_fmt2(x1)
[1] "£1" "£20K" "£30K" "£100"
curr_fmt2(x2)
[1] "£1" "£20K" "£30K" "£100" NA