【问题标题】:Fast way to get numeric precision and scale (n/o decimal points) for a numeric vector获取数字向量的数字精度和比例(无小数点)的快速方法
【发布时间】:2017-02-08 19:41:54
【问题描述】:

我有一个包含许多数字(> 1E9 个元素)的向量,并且想要导出 数值精度(数字中的位数)和数值刻度(数字中小数点右侧的位数)。

我怎样才能做到这一点非常快(矢量化)?

存在一个带有部分答案的问题 (how to return number of decimal places in R),但解决方案既不快速(矢量化)也不计算数值精度。

例子:

# small example vector with numeric data
x <- c(7654321, 54321.1234, 321.123, 321.123456789)

> numeric.precision(x)  # implementation is the answer
[1] 7, 9, 6, 12

> numeric.scale(x)      # implementation is the answer
[1] 0, 4, 3, 9

可选的“糖”(稍后添加到此问题 - 感谢 @thc 和 @gregor):

如何避免由于计算机中数字存储方式的内部不精确性(例如浮点数)而过度计算位数?

> x = 54321.1234
> as.character(x)
[1] "54321.1234"
> print(x, digits = 22)
[1] 54321.12339999999676365

【问题讨论】:

  • 您的输入应该是字符串,而不是数字。原因是允许浮点数稍微不精确。例如:a=0.15+0.15; b=0.1+0.2; a==b 是假的。
  • 或者,与您的示例数据更相关:x = 54321.1234; print(x, digits = 22)
  • 获取小数点左边位数的好方法是trunc(log10(abs(x))) + 1。我把它留在这里,以防它在完整答案中有用。我不确定它将如何将速度方面与转换为字符进行比较。
  • 如果你真的可以“保证我的位数有限”,那么在format(x, digits = maxp, scientific = FALSE) 上使用lmo 的nchar(sub()) 方法,其中maxp 是您期望的数据的最大精度。
  • 是的,但您说过您可以保证有限位数。我们已经证明 22 过多 - format(x, digits = 14, scientific = F, trim = T, drop0trailing = T) 适用于您的示例并且有一点缓冲。在这个例子中,你最多可以放 16 个。

标签: r


【解决方案1】:

这里是一个base R方法,入手肯定是太慢了,但至少能计算出想要的结果。

# precision
nchar(sub(".", "", x, fixed=TRUE))
[1]  7  9  6 12

# scale
nchar(sub("\\d+\\.?(.*)$", "\\1", x))
[1] 0 4 3 9

对于这种方法,我建议在 data.tablefread 中使用 colClasses 参数,以避免首先转换为数值精度问题:

x <- unlist(fread("7654321
54321.1234
321.123
321.123456789", colClasses="character"), use.names=FALSE)

在输入过程中可能需要将向量转换为数字,如 cmets 中所述,例如某些输入值在文本文件中采用科学计数法。在这种情况下,可能需要使用格式化语句或options(scipen=999) 强制从该格式转换为标准十进制格式,如this answer 中所述。

【讨论】:

  • 您可能应该将format(x,scientific=FALSE,...) 与其他必要的参数一起使用,以防止出现nchar(sub(".", "", as.character(10000000000), fixed=TRUE)) == 5 等情况
  • @A.Webb 感谢您的评论。我添加了数字强制的替代方法,这在数字精度问题方面可能更可取。
  • @RichScriven 啊,是的。谢谢。我经常忘记正则表达式函数有这个不错的功能。
  • @Imo 我将接受您的回答作为最佳解决方案。您是否介意添加options(scipen=999)(来自stackoverflow.com/a/5352328/4468078)以禁用导致计算精度不足的科学记数法?我的测试向量是:x &lt;- c(7654321, 54321.1234, 321.123, 321.123456789, 54321.1234, 100000000000, 1E4)
  • 请注意,NAs 当前计数错误(精度 2 和小数位数 2)。如果向量仅包含 1 位数字和 NA,则结果将(略微)错误。
【解决方案2】:

这是数学版本的想法(比使用字符操作更快)。你可以把它放在函数scale和precision中,在functionprecision中调用scale函数。

for (i in 1:length(x)) {
     after <- 0
     while(x[i]*(10^after) != round(x[i]*(10^after))) 
     { after <- after + 1 }
     cat(sprintf("Scale: %s\n", after))
     before <- floor(log10(abs(x[i])))+1
     cat(sprintf("Precision: %s\n", before+after))
 }

结果:

Scale: 0
Precision: 7
Scale: 4
Precision: 9
Scale: 3
Precision: 6
Scale: 9
Precision: 12

【讨论】:

  • 聪明的算法(估计小数部分的位数真的很难)。从实际的角度来看,我认为这个解决方案对我来说太慢了,因为它不支持向量化(但循环遍历向量中的所有元素)。
【解决方案3】:

只是为了将所有 cmets 和答案整合到一个即用型解决方案中,该解决方案还考虑了不同的国家(地区)和 NA 我将其发布为答案(请感谢 @Imo、@Gregor 等人。) .

编辑(2017 年 2 月 9 日):添加了 SQL.precision 作为返回值,因为它可能与数学精度不同。

#' Calculates the biggest precision and scale that occurs in a numeric vector
#'
#' The scale of a numeric is the count of decimal digits in the fractional part (to the right of the decimal point).
#' The precision of a numeric is the total count of significant digits in the whole number,
#' that is, the number of digits to both sides of the decimal point. 
#'
#' To create a suitable numeric data type in a SQL data base use the returned \code{SQL.precision} which
#' is defined by \code{max(precision, non.fractional.precision + scale)}.
#'
#' @param x numeric vector
#'
#' @return A list with four elements:
#'         precision (total number of significant digits in the whole number),
#'         scale (number of digits in the fractional part),
#'         non.fractional.precision (number of digits at the left side and SQL precision.
#'
#' @details NA will be counted as precision 1 and scale 0!
#'
#' @examples
#'
#' \preformatted{
#' x <- c(0, 7654321, 54321.1234, 321.123, 321.123456789, 54321.1234, 100000000000, 1E4, NA)
#' numeric.precision.and.scale(x)
#' numeric.precision.and.scale(c(10.0, 1.2))   # shows why the SQL.precision is different
#' }
numeric.precision.and.scale <- function(x) {

  # Remember current options
  old.scipen <- getOption("scipen")

  # Overwrite options
  options(scipen = 999)   # avoid scientific notation when converting numerics to strings

  # Extract the decimal point character of the computer's current locale
  decimal.sign <- substr( 1 / 2, 2, 2)

  x.string <- as.character(x[!is.na(x)])

  if (length(x.string) > 0) {
    # calculate
    precision <- max(nchar(sub(decimal.sign, "", x.string, fixed = TRUE)))
    scale <- max(nchar(sub(paste0("\\d+\\", decimal.sign, "?(.*)$"), "\\1", x.string)))
    non.fractional.precision <- max(trunc(log10(abs(x))) + 1, na.rm = TRUE)
    SQL.precision <- max(precision, non.fractional.precision + scale)

    # Reset changed options
    options(scipen = old.scipen)
  } else {
    precision <- 1
    scale <- 0
    non.fractional.precision <- 1
    SQL.precision <- 1
  }

  return(list(precision = precision,
              scale = scale,
              non.fractional.precision = non.fractional.precision,
              SQL.precision = SQL.precision))
}

【讨论】:

    猜你喜欢
    • 2015-02-03
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 2014-07-20
    • 1970-01-01
    • 2018-09-17
    相关资源
    最近更新 更多