2017-02-08 4 views
0

Ich habe ein Vektor mit vielen Zahlen (> 1E9-Elemente) und wollen abzuleiten die numerische Genauigkeit (Anzahl der Stellen zu erhalten in einer Zahl) und numerische Skala (die Anzahl der Stellen rechts von dem Komma in einer Zahl). Wie kann ich das tun sehr schnell (vektorisiert)?schnelle Art und Weise numerische Genauigkeit und Skalierung (n/o Dezimalstellen) für einen numerischen Vektor

Es existiert eine Frage mit einer Teilantwort (how to return number of decimal places in R), aber die Lösung ist weder schnell (vektorisiert) noch berechnet sie die numerische Genauigkeit.

Beispiel:

# 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 

Optional "Zucker" (später auf diese Frage hinzugefügt - thx to @thc und @gregor):

Wie kann ich vermeiden, über Zählen der Anzahl der Ziffern aufgrund interner Ungenauigkeit, wie Zahlen in Computern gespeichert sind (zB float)?

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

Ihre Eingabe sollte Zeichenketten, nicht Numerik sein. Der Grund ist, dass Floats leicht ungenau sein dürfen. Zum Beispiel: a = 0,15 + 0,15; b = 0,1 + 0,2; a == b ist falsch. – thc

+0

Oder, relevanter für Ihre Beispieldaten: 'x = 54321.1234; Druck (x, Ziffern = 22) ' – Gregor

+0

@thc Sehr guter Punkt! Ich muss eine wichtige Vorbedingung erwähnen: Da ich meine Daten aus einer CSV-Datei in meine 'data.table' eingelesen habe, kann ich (fast ;-)) garantieren, dass ich eine begrenzte Anzahl von Ziffern habe (obwohl eine interne Konvertierung in einen Float kann zerstöre meine Vorbedingung ;-) –

Antwort

3

Hier ist ein Verfahren, Basis R beginnen sie gebunden ist, zu langsam zu sein, aber zumindest berechnet die gewünschten Ergebnisse.

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

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

Für diese Methode würde ich empfehlen, in den colClasses Argument mit data.table ‚s fread Umwandlung in numerische Genauigkeit Probleme in erster Linie zu vermeiden:

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

Es kann notwendig sein, den Vektor während der Eingabe in numerisch zu konvertieren, wie in den Kommentaren erwähnt, zum Beispiel sind einige der Eingabewerte in wissenschaftlicher Notation im Text fi le. In diesem Fall kann die Verwendung einer Formatierungsanweisung oder options(scipen=999) erforderlich sein, um die Konvertierung von diesem Format in das Standard-Dezimalformat zu erzwingen, wie in this answer erwähnt.

+2

Sie sollten 'format (x, scientific = FALSE, ...)' mit anderen Argumenten verwenden, um Fälle wie 'nchar (sub (" . "," ", as.character (10000000000), fixed = TRUE)) == 5' –

+1

@ A.Webb Danke für den Kommentar. Ich habe eine Alternative zur numerischen Numerierung hinzugefügt, die im Hinblick auf das Problem der numerischen Genauigkeit vorzuziehen ist. – lmo

+0

@RichScriven Ah ja. Vielen Dank. Ich vergesse oft, dass die Regex-Funktionen dieses nette Feature haben. – lmo

1

Hier ist die Idee der Math-Version (schneller dann mit Zeichen zu manipulieren). Sie können dies in Funktionen Skala und Präzision setzen, wo in Funktion Präzision Anrufskala Funktion.

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)) 
} 

Ergebnis:

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

Cleverer Algorithmus (das Schätzen der Anzahl der Ziffern des Bruchteils ist wirklich schwierig). Aus praktischer Sicht denke ich, dass diese Lösung für mich zu langsam sein wird, da sie keine Vektorisierung unterstützt (sondern Schleifen über alle Elemente im Vektor). –

0

einfach alle Kommentare und Antworten in eine ready-to-use-Lösung zu konsolidieren, die auch andere Länder (Positionen) und NA poste ich dies als eine Antwort (bitte geben Sie die Auffassung, Kredite an @Imo, @Gregor et al.).

Edit (9. Februar 2017): Added die SQL.precision als Rückgabewert, da sie aus der mathematischen Präzision können unterschiedlich sein.

#' 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)) 
} 
Verwandte Themen