矢量化简单格式化函数来处理具有NAs的矢量

时间:2017-08-03 10:03:29

标签: r function format vectorization apply

我有一个简单的函数,用于跨图表标签以所需的方式格式化货币:

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 以及示例中格式化的货币值。

注意:

  • 请注意,我对for-loop解决方案
  • 不感兴趣
  • / 解决方案将被接受
  • 理想情况下,我想了解如何在此上下文中正确使用Vectorize
  • 该函数不必处理字符串

3 个答案:

答案 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