如何舍入到 0.25 或 0.75,而不是 R 中的 0.00 或 0.50?

How to round to 0.25 or 0.75, but not 0.00 or 0.50 in R?

我正在使用 R,我想将我的数据四舍五入到最接近的 0.250.75,但不包括 0.00 或 0.50 .

例如,如果我想四舍五入到最接近的 0.00 或 0.50,我会这样做:

test <- seq(1,10,0.33) 
[1] 1.00 1.33 1.66 1.99 2.32 2.65 2.98 3.31 3.64 3.97 4.30 4.63 4.96 5.29 5.62 5.95 6.28 6.61 6.94 7.27 7.60 7.93 8.26 8.59 8.92 9.25 9.58 9.91

四舍五入到 0.00 或 0.50:

round(test * 2)/2
[1]  1.0  1.5  1.5  2.0  2.5  2.5  3.0  3.5  3.5  4.0  4.5  4.5  5.0  5.5  5.5  6.0  6.5  6.5  7.0  7.5  7.5  8.0  8.5  8.5  9.0  9.0  9.5 10.0

我可以对 0.25 的倍数做同样的事情。我该怎么做才能将这些数字四舍五入为 0.25 或 0.75,不包括 0.50 和 0.00?

round((test + 0.25) * 2) / 2 - 0.25怎么样?对于您的 test,它给出:

# [1] 0.75 1.25 1.75 1.75 2.25 2.75 2.75 3.25 3.75 3.75 4.25 4.75 4.75 5.25 5.75
#[16] 5.75 6.25 6.75 6.75 7.25 7.75 7.75 8.25 8.75 8.75 9.25 9.75 9.75

您也可以round((test - 0.25) * 2) / 2 + 0.25,给予

# [1] 1.25 1.25 1.75 1.75 2.25 2.75 2.75 3.25 3.75 3.75 4.25 4.75 4.75 5.25 5.75
#[16] 5.75 6.25 6.75 6.75 7.25 7.75 7.75 8.25 8.75 8.75 9.25 9.75 9.75

整数的舍入方向行为不同。以1为例。在第一种情况下,它向下舍入为 0.75,而在第二种情况下,它向上舍入为 1.25。这里的整数是一个"grey area";你需要决定你想要的方向。

几年前我写了一些bc functions to allow rounding numbers to the nearest specified value with an optional offset, as well as supporting the six common types of tiebreaking rules。对于您的问题,我已尝试将我的代码移植到 R。

这里有一个与精度相关的重要警告。由于 R 使用浮点编码来表示小数,因此很容易以精度不足而无法正确应用平分规则。这是因为未经调整的舍入结果与舍入间隔之间的差异可能仅由于浮点误差而偏离半个单位,因此根本无法检测是否应应用平分。这对我的原始代码来说不是问题,因为 bc 是一个无限精度的计算器,但它是 R 中的一个问题。我试图通过以宽松的容差进行计算来缓解这个问题,但它并不完美。如果舍入区间的数量级与待舍入值的数量级相差太大,则结果将不正确。但是,应该提到的是,这个警告适用于任何使用浮点运算的解决方案,而不仅仅是我的代码。

无论如何,这是我得到的:

FTOL <- 1e-8;
feq <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),abs(a-b)<=max(abs(a),abs(b))*tol,a==b);
fne <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),abs(a-b)>max(abs(a),abs(b))*tol,a!=b);
fgt <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),a-b>max(abs(a),abs(b))*tol,a>b);
fge <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),a-b>=-max(abs(a),abs(b))*tol,a>=b);
flt <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),b-a>max(abs(a),abs(b))*tol,a<b);
fle <- function(a,b,tol=FTOL) ifelse(is.finite(a) & is.finite(b),b-a>=-max(abs(a),abs(b))*tol,a<=b);

HALFRULE_UP   <- 1L; ## round towards +Inf
HALFRULE_DOWN <- 2L; ## round towards -Inf
HALFRULE_IN   <- 3L; ## round towards zero
HALFRULE_OUT  <- 4L; ## round away from zero
HALFRULE_EVEN <- 5L; ## round to the even multiple of the two multiples of nearest that are tied
HALFRULE_ODD  <- 6L; ## round to the odd multiple of the two multiples of nearest that are tied
nearest <- function(x,nearest=1,offset=0,halfrule=HALFRULE_EVEN) {

    ## ensure correct types
    x <- as.double(x);
    nearest <- as.double(nearest);
    offset <- as.double(offset);
    halfrule <- as.integer(halfrule);

    ## validate
    v <- which(!halfrule%in%c(HALFRULE_UP,HALFRULE_DOWN,HALFRULE_IN,HALFRULE_OUT,HALFRULE_EVEN,HALFRULE_ODD)); if (length(v)>0L) stop(paste0('invalid halfrule: ',halfrule[v[1L]],'.'));

    ## normalize lengths
    len <- max(length(x),length(nearest),length(halfrule));
    x <- rep(x,len=len);
    nearest <- rep(nearest,len=len);
    offset <- rep(offset,len=len);
    halfrule <- rep(halfrule,len=len);

    ## apply offset
    x <- x-offset;

    ## must treat zero nearests different from non-zero
    nonzero <- fne(nearest,0);

    ## start building result
    res <- double(length(x));

    ## nearest zero doesn't really make any sense; but you can consider every possible number to be at the nearest zero
    res[!nonzero] <- x[!nonzero];

    ## simplify subsequent operations to only focus on non-zero nearest
    x <- x[nonzero];
    nearest <- nearest[nonzero];
    halfrule <- halfrule[nonzero];
    offset <- offset[nonzero];

    ## don't care about negativity of nearest
    nearest <- abs(nearest);

    ## get how many times nearest goes into x, truncated
    mult <- as.integer(x/nearest); ## note: can't use %/%, since that actually floors toward -Inf

    ## get round-truncated result
    r.trunc <- mult*nearest;

    ## get absolute excess over r.trunc
    excess <- abs(x - r.trunc);

    ## get half of nearest
    half.of.nearest <- nearest*0.5;

    ## add one to mult if necessary; whether we need to do this in the case of a tie depends on the specified tiebreaker rule and which side of the zero multiple x is on
    adds <- which(
        fgt(excess,half.of.nearest)
        | feq(excess,half.of.nearest) & (
            halfrule==HALFRULE_UP & fgt(x,0)
            | halfrule==HALFRULE_DOWN & flt(x,0)
            | halfrule==HALFRULE_OUT
            | halfrule==HALFRULE_EVEN & mult%%2L!=0L
            | halfrule==HALFRULE_ODD & mult%%2L==0L
        )
    );
    mult[adds] <- mult[adds] + ifelse(flt(x[adds],0),-1,1);

    ## recover target value from mult, and at the same time unshift offset
    res[nonzero] <- nearest*mult+offset;

    res;

}; ## end nearest()
nearest.halfup   <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_UP  );
nearest.halfdown <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_DOWN);
nearest.halfin   <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_IN  );
nearest.halfout  <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_OUT );
nearest.halfeven <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_EVEN);
nearest.halfodd  <- function(x,nearest=1,offset=0) nearest(x,nearest,offset,HALFRULE_ODD );

对于您的示例输入,我们需要四舍五入到最接近的 0.5,偏移量为 0.25:

nearest(seq(1,10,0.33),0.5,0.25);
##  [1] 1.25 1.25 1.75 1.75 2.25 2.75 2.75 3.25 3.75 3.75 4.25 4.75 4.75 5.25 5.75 5.75 6.25
## [18] 6.75 6.75 7.25 7.75 7.75 8.25 8.75 8.75 9.25 9.75 9.75