So runden Sie eine Zahl in Python auf signifikante Zahlen

148

Ich muss einen Float runden, um in einer Benutzeroberfläche angezeigt zu werden. ZB zu einer signifikanten Zahl:

1234 -> 1000

0,12 -> 0,1

0,012 -> 0,01

0,062 -> 0,06

6253 -> 6000

1999 -> 2000

Gibt es eine gute Möglichkeit, dies mit der Python-Bibliothek zu tun, oder muss ich es selbst schreiben?

Peter Graham
quelle
2
Formatieren Sie nur die Ausgabe? Fragen Sie danach? docs.python.org/library/stdtypes.html#string-formatting oder so? docs.python.org/library/string.html#string-formatting
S.Lott
Welche Leistung erwarten Sie für 0.062 und 6253?
Lamirap
Das Präzisionspaket macht das jetzt. Meine gepostete Antwort beschreibt, wie dies gilt.
William Rusnack

Antworten:

145

Sie können negative Zahlen verwenden, um ganze Zahlen zu runden:

>>> round(1234, -3)
1000.0

Wenn Sie also nur die höchstwertige Ziffer benötigen:

>>> from math import log10, floor
>>> def round_to_1(x):
...   return round(x, -int(floor(log10(abs(x)))))
... 
>>> round_to_1(0.0232)
0.02
>>> round_to_1(1234243)
1000000.0
>>> round_to_1(13)
10.0
>>> round_to_1(4)
4.0
>>> round_to_1(19)
20.0

Sie müssen wahrscheinlich darauf achten, dass float in eine Ganzzahl umgewandelt wird, wenn es größer als 1 ist.

Evgeny
quelle
2
Dies ist die richtige Lösung. Die Verwendung log10ist der einzig richtige Weg, um zu bestimmen, wie es gerundet werden soll.
Wolph
73
round_to_n = Lambda x, n: rund (x, -int (Boden (log10 (x))) + (n - 1))
Roy Hyunjin Han
28
Sie sollten verwenden log10(abs(x)), sonst werden negative Zahlen fehlschlagen (und x == 0natürlich separat behandeln )
Tobias Kienzler
2
Ich habe ein Paket erstellt, das dies jetzt tut und wahrscheinlich einfacher und robuster als dieses ist. Post Link , Repo Link . Hoffe das hilft!
William Rusnack
2
round_to_n = lambda x, n: x if x == 0 else round(x, -int(math.floor(math.log10(abs(x)))) + (n - 1))schützt vor x==0und x<0danke @RoyHyunjinHan und @TobiasKienzler. Schützt nicht gegen undefinierte wie math.inf oder Müll wie None etc
AJP
98

% g in der Zeichenfolgenformatierung formatiert einen Float, der auf eine bestimmte Anzahl von signifikanten Zahlen gerundet ist. Manchmal wird die wissenschaftliche 'e'-Notation verwendet. Konvertieren Sie daher die abgerundete Zeichenfolge zurück in einen Gleitkommawert und dann durch% s Zeichenfolgenformatierung.

>>> '%s' % float('%.1g' % 1234)
'1000'
>>> '%s' % float('%.1g' % 0.12)
'0.1'
>>> '%s' % float('%.1g' % 0.012)
'0.01'
>>> '%s' % float('%.1g' % 0.062)
'0.06'
>>> '%s' % float('%.1g' % 6253)
'6000.0'
>>> '%s' % float('%.1g' % 1999)
'2000.0'
Peter Graham
quelle
7
Das OP forderte, dass 1999 als „2000“ und nicht als „2000.0“ formatiert wird. Ich kann keinen trivialen Weg finden, Ihre Methode zu ändern, um dies zu erreichen.
Tim Martin
1
Es ist genau das, was ich immer wollte! wo hast du das gefunden
Djhaskin987
12
Beachten Sie, dass das Verhalten von% g nicht immer korrekt ist. Insbesondere werden nachgestellte Nullen immer abgeschnitten, auch wenn sie signifikant sind. Die Zahl 1.23400 hat 6 signifikante Stellen, aber "% .6g"% (1.23400) führt zu "1.234", was falsch ist. Weitere Details in diesem Blog-Beitrag: randlet.com/blog/python-significant-figures-format
randlet
3
Genau wie die Methode in Evgeny Antwort versagt diese richtig rund 0.075zu 0.08. Es kehrt 0.07stattdessen zurück.
Gabriel
1
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)Mit dieser Option können Sie die Anzahl der signifikanten Stellen anpassen!
Denizb
49

Wenn Sie eine andere als eine signifikante Dezimalstelle haben möchten (ansonsten dieselbe wie Evgeny):

>>> from math import log10, floor
>>> def round_sig(x, sig=2):
...   return round(x, sig-int(floor(log10(abs(x))))-1)
... 
>>> round_sig(0.0232)
0.023
>>> round_sig(0.0232, 1)
0.02
>>> round_sig(1234243, 3)
1230000.0
Indgar
quelle
8
round_sig (-0.0232) -> Mathe-Domänenfehler, möchten Sie vielleicht eine abs () hinzufügen;)
dgorissen
2
Genau wie die Methoden in Evgeny und Peter Grahams Antworten versagt diese richtig rund 0.075zu 0.08. Es kehrt 0.07stattdessen zurück.
Gabriel
3
Auch für round_sig (0) schlägt dies fehl.
Yuval Atzmon
2
@Gabriel Dies ist eine integrierte "Funktion" von Python, die auf Ihrem Computer ausgeführt wird und sich in ihrem Verhalten der Funktion manifestiert round. docs.python.org/2/tutorial/floatingpoint.html#tut-fp-issues
Novice C
1
@ Gabriel Ich habe eine Antwort hinzugefügt, die erklärt, warum Sie erwarten sollten , 0,7 von der Rundung "0,075" zurück zu bekommen! siehe stackoverflow.com/a/56974893/1358308
Sam Mason
30
f'{float(f"{i:.1g}"):g}'
# Or with Python <3.6,
'{:g}'.format(float('{:.1g}'.format(i)))

Diese Lösung unterscheidet sich von allen anderen, weil:

  1. es löst genau die OP-Frage
  2. es ist nicht braucht jedes zusätzliches Paket
  3. es ist nicht braucht jede benutzerdefinierte Hilfsfunktion oder mathematische Operation

Für eine beliebige Anzahl nvon signifikanten Zahlen können Sie verwenden:

print('{:g}'.format(float('{:.{p}g}'.format(i, p=n))))

Prüfung:

a = [1234, 0.12, 0.012, 0.062, 6253, 1999, -3.14, 0., -48.01, 0.75]
b = ['{:g}'.format(float('{:.1g}'.format(i))) for i in a]
# b == ['1000', '0.1', '0.01', '0.06', '6000', '2000', '-3', '0', '-50', '0.8']

Hinweis : Mit dieser Lösung ist es nicht möglich, die Anzahl der signifikanten Ziffern dynamisch von der Eingabe anzupassen, da es keine Standardmethode gibt, um Zahlen mit unterschiedlicher Anzahl von nachgestellten Nullen ( 3.14 == 3.1400) zu unterscheiden. Wenn Sie dies tun müssen, werden nicht standardmäßige Funktionen benötigt, wie sie im Präzisionspaket enthalten sind.

Falken
quelle
Zu Ihrer Information: Ich habe diese Lösung unabhängig von Eddygeek gefunden, als ich versucht habe, das gleiche Problem in einem meiner Codes zu lösen. Jetzt merke ich, dass meine Lösung offensichtlich fast identisch mit seiner ist (ich habe gerade die fehlerhafte Ausgabe bemerkt und mich nicht darum gekümmert, den Code zu lesen, mein Fehler). Wahrscheinlich hätte ein kurzer Kommentar unter seiner Antwort anstelle einer neuen Antwort gereicht ... Der einzige (Schlüssel-) Unterschied ist die doppelte Verwendung des :gFormatierers, bei dem ganze Zahlen erhalten bleiben.
Falken
Wow, deine Antwort muss wirklich von oben nach unten gelesen werden;) Dieser Double-Cast-Trick ist schmutzig, aber ordentlich. (Beachten Sie, dass 1999 so formatiert wurde , dass es 5 signifikante Ziffern 2000.0 vorschlägt , sodass es {:g}erneut durchlaufen werden muss.) Im Allgemeinen sind Ganzzahlen mit nachgestellten Nullen in Bezug auf signifikante Ziffern nicht eindeutig, es sei denn, es wird eine Technik (wie die Überschrift über der letzten signifikanten Ziffer ) verwendet.
Tomasz Gandor
8

Ich habe das Paket präzise erstellt, das genau das tut, was Sie wollen. Sie können Ihren Zahlen mehr oder weniger wichtige Zahlen geben.

Es gibt auch Standard-, wissenschaftliche und technische Notationen mit einer bestimmten Anzahl von signifikanten Zahlen aus.

In der akzeptierten Antwort steht die Zeile

>>> round_to_1(1234243)
1000000.0

Das spezifiziert tatsächlich 8 Sig Feigen. Für die Nummer 1234243 zeigt meine Bibliothek nur eine signifikante Zahl an:

>>> from to_precision import to_precision
>>> to_precision(1234243, 1, 'std')
'1000000'
>>> to_precision(1234243, 1, 'sci')
'1e6'
>>> to_precision(1234243, 1, 'eng')
'1e6'

Es wird auch die letzte signifikante Zahl gerundet und kann automatisch auswählen, welche Notation verwendet werden soll, wenn keine Notation angegeben ist:

>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'
William Rusnack
quelle
Jetzt suche ich das gleiche aber bewarb mich bei einem Pandas df
mhoff
@mhoff Sie können wahrscheinlich Pandas Karte mit einem Lambda verwenden. lambda x: to_precision(x, 2)
William Rusnack
Fügen Sie dies zu (PyPI) [ pypi.org/] hinzu . Soweit ich das beurteilen kann, gibt es dort nichts Vergleichbares.
Morgoth
Dies ist ein großartiges Paket, aber ich denke, die meisten Funktionen befinden sich jetzt im sigfig-Modul
HyperActive
1
es hat einen Fehler: std_notation (9.999999999999999e-05, 3) gibt an: '0,00010', was nur 2 signifikante Stellen sind
Boris Mulder
5

Um eine Ganzzahl auf eine signifikante Zahl zu runden, besteht die Grundidee darin, sie in einen Gleitkomma mit einer Ziffer vor dem Punkt umzuwandeln und diese zu runden und dann wieder in ihre ursprüngliche Ganzzahlgröße umzuwandeln.

Dazu müssen wir die größte Potenz von 10 weniger als die ganze Zahl kennen. Wir können dafür den Boden der log 10-Funktion verwenden.

from math import log10, floor
def round_int(i,places):
    if i == 0:
        return 0
    isign = i/abs(i)
    i = abs(i)
    if i < 1:
        return 0
    max10exp = floor(log10(i))
    if max10exp+1 < places:
        return i
    sig10pow = 10**(max10exp-places+1)
    floated = i*1.0/sig10pow
    defloated = round(floated)*sig10pow
    return int(defloated*isign)
Cris Stringfellow
quelle
1
Plus eine für eine Lösung, die ohne Python-Runde (.., Ziffern) und ohne Bedingungen funktioniert!
Steve Rogers
5

Um die Frage direkt zu beantworten, ist hier meine Version mit Namen aus der R-Funktion :

import math

def signif(x, digits=6):
    if x == 0 or not math.isfinite(x):
        return x
    digits -= math.ceil(math.log10(abs(x)))
    return round(x, digits)

Mein Hauptgrund für die Veröffentlichung dieser Antwort sind die Kommentare, in denen ich mich beschwere, dass "0,075" auf 0,07 statt 0,08 rundet. Dies ist, wie von "Novice C" hervorgehoben, auf eine Kombination von Gleitkomma-Arithmetik zurückzuführen, die sowohl eine endliche Genauigkeit als auch eine Basis-2-Darstellung aufweist . Die Zahl, die 0,075 am nächsten kommt und tatsächlich dargestellt werden kann, ist etwas kleiner, daher wird die Rundung anders ausgeführt, als Sie es naiv erwarten könnten.

Beachten Sie auch, dass dies für jede Verwendung von nicht-dezimaler Gleitkomma-Arithmetik gilt, z. B. haben C und Java beide das gleiche Problem.

Um dies genauer zu zeigen, bitten wir Python, die Zahl im "Hex" -Format zu formatieren:

0.075.hex()

was uns gibt : 0x1.3333333333333p-4. Der Grund dafür ist, dass die normale Dezimaldarstellung häufig eine Rundung beinhaltet und daher nicht so ist, wie der Computer die Zahl tatsächlich "sieht". Wenn Sie an dieses Format nicht gewöhnt sind, sind einige nützliche Referenzen die Python-Dokumente und der C-Standard .

Um zu zeigen, wie diese Zahlen ein wenig funktionieren, können wir zu unserem Ausgangspunkt zurückkehren, indem wir Folgendes tun:

0x13333333333333 / 16**13 * 2**-4

was sollte ausdrucken 0.075. 16**13Dies liegt daran, dass nach dem Dezimalpunkt 13 hexadezimale Ziffern stehen und dass hexadezimale 2**-4Exponenten die Basis 2 sind.

Jetzt haben wir eine Vorstellung davon, wie Floats dargestellt werden. Wir können das decimalModul verwenden, um uns mehr Präzision zu verschaffen und uns zu zeigen, was los ist:

from decimal import Decimal

Decimal(0x13333333333333) / 16**13 / 2**4

Geben: 0.07499999999999999722444243844und hoffentlich erklären, warum zu round(0.075, 2)bewertet0.07

Sam Mason
quelle
1
Dies ist eine großartige Erklärung dafür, warum 0,075 auf Codeebene auf 0,07 abgerundet wird , aber wir (in den Naturwissenschaften) haben gelernt, immer aufzurunden und nicht abzurunden. Das erwartete Verhalten soll also tatsächlich 0,08 haben, ungeachtet der Probleme mit der Gleitkommapräzision.
Gabriel
1
Ich bin mir nicht sicher, wo Ihre Verwirrung liegt: Wenn Sie 0,075 eingeben, geben Sie tatsächlich ~ 0,07499 (wie oben) ein, was nach normalen mathematischen Regeln rundet. Wenn Sie einen Datentyp (wie einen Dezimal-Gleitkomma ) verwenden, der 0,075 darstellen könnte, sollte er tatsächlich auf 0,08 gerundet werden
Sam Mason
Ich bin nicht verwirrt. Wenn ich 0,075 eingebe, gebe ich tatsächlich 0,075 ein. Was auch immer in der Gleitkomma-Mathematik im Code passiert, ist mir egal.
Gabriel
@Gabriel: Und wenn Sie hatte absichtlich eingegeben 0.074999999999999999, was man erwarten würde in diesem Fall zu bekommen?
Mark Dickinson
@ MarkDickinson das kommt darauf an. Eine signifikante Zahl: 0,07, zwei: 0,075.
Gabriel
4
def round_to_n(x, n):
    if not x: return 0
    power = -int(math.floor(math.log10(abs(x)))) + (n - 1)
    factor = (10 ** power)
    return round(x * factor) / factor

round_to_n(0.075, 1)      # 0.08
round_to_n(0, 1)          # 0
round_to_n(-1e15 - 1, 16) # 1000000000000001.0

Hoffentlich nehmen Sie das Beste aus allen obigen Antworten (abzüglich der Möglichkeit, es als einzeiliges Lambda zu formulieren;)). Noch nicht erforscht, zögern Sie nicht, diese Antwort zu bearbeiten:

round_to_n(1e15 + 1, 11)  # 999999999999999.9
AJP
quelle
4

Ich habe Indgars Lösung so modifiziert, dass sie mit negativen und kleinen Zahlen (einschließlich Null) umgeht.

from math import log10, floor
def round_sig(x, sig=6, small_value=1.0e-9):
    return round(x, sig - int(floor(log10(max(abs(x), abs(small_value))))) - 1)
ryan281
quelle
Warum nicht einfach testen ob x == 0? Wenn Sie einen Einzeiler lieben, einfach return 0 if x==0 else round(...).
pjvandehaar
2
@pjvandehaar, Sie sind für den allgemeinen Fall korrekt und ich hätte das einfügen sollen. Zusätzlich erhalten wir für die numerischen Berechnungen, die ich durchführen muss, gelegentlich Zahlen wie 1e-15. In unserer Anwendung möchten wir, dass ein Vergleich zweier kleiner Zahlen (von denen eine Null sein kann) als gleich angesehen wird. Einige Leute möchten auch kleine Zahlen (es könnte 1e-9, 1e-15 oder sogar 1e-300 sein) auf Null runden.
Ryan281
1
Interessant. Danke, dass du das erklärt hast. In diesem Fall gefällt mir diese Lösung sehr gut.
pjvandehaar
@ Morgoth Dies ist ein interessantes und schwieriges Problem. Wie Sie bereits betont haben, zeigt der gedruckte Wert nicht die 3 signifikanten Ziffern an, aber der Wert ist korrekt (z 0.970 == 0.97. B. ). Ich denke, Sie könnten einige der anderen Drucklösungen verwenden, f'{round_sig(0.9701, sig=3):0.3f}'wenn Sie möchten, dass die Null gedruckt wird.
Ryan281
3

Wenn Sie ohne Zeichenfolgen runden möchten, ist der Link, den ich gefunden habe, in den obigen Kommentaren vergraben:

http://code.activestate.com/lists/python-tutor/70739/

kommt mir am besten vor. Wenn Sie dann mit Zeichenfolgenformatierungsdeskriptoren drucken, erhalten Sie eine angemessene Ausgabe, und Sie können die numerische Darstellung für andere Berechnungszwecke verwenden.

Der Code unter dem Link besteht aus drei Zeilen: def, doc und return. Es hat einen Fehler: Sie müssen nach explodierenden Logarithmen suchen. Das ist einfach. Vergleichen Sie die Eingabe mit sys.float_info.min. Die Komplettlösung lautet:

import sys,math

def tidy(x, n):
"""Return 'x' rounded to 'n' significant digits."""
y=abs(x)
if y <= sys.float_info.min: return 0.0
return round( x, int( n-math.ceil(math.log10(y)) ) )

Es funktioniert für jeden skalaren numerischen Wert, und n kann ein Wert sein, floatwenn Sie die Antwort aus irgendeinem Grund verschieben müssen. Sie können das Limit tatsächlich verschieben auf:

sys.float_info.min*sys.float_info.epsilon

ohne einen Fehler zu provozieren, wenn Sie aus irgendeinem Grund mit winzigen Werten arbeiten.

schläfrig werden
quelle
2

Ich kann mir nichts vorstellen, was dies sofort bewältigen könnte. Aber es ist ziemlich gut für Gleitkommazahlen gehandhabt.

>>> round(1.2322, 2)
1.23

Ganzzahlen sind schwieriger. Sie werden nicht als Basis 10 im Speicher gespeichert, daher sind wichtige Orte keine Selbstverständlichkeit. Es ist jedoch ziemlich trivial zu implementieren, sobald sie eine Zeichenfolge sind.

Oder für ganze Zahlen:

>>> def intround(n, sigfigs):
...   n = str(n)
...   return n[:sigfigs] + ('0' * (len(n)-(sigfigs)))

>>> intround(1234, 1)
'1000'
>>> intround(1234, 2)

Wenn Sie eine Funktion erstellen möchten, die eine beliebige Zahl verarbeitet, würde ich es vorziehen, beide in Zeichenfolgen zu konvertieren und nach einer Dezimalstelle zu suchen, um zu entscheiden, was zu tun ist:

>>> def roundall1(n, sigfigs):
...   n = str(n)
...   try:
...     sigfigs = n.index('.')
...   except ValueError:
...     pass
...   return intround(n, sigfigs)

Eine andere Möglichkeit besteht darin, nach Typ zu suchen. Dies ist weitaus weniger flexibel und spielt wahrscheinlich nicht gut mit anderen Zahlen wie DecimalObjekten:

>>> def roundall2(n, sigfigs):
...   if type(n) is int: return intround(n, sigfigs)
...   else: return round(n, sigfigs)
Tim McNamara
quelle
Nur mit Strings herumzuspielen, rundet die Zahlen nicht ab. 1999 auf 1 signifikante Zahl gerundet ist 2000, nicht 1000.
Peter Graham
Es gibt eine gute Diskussion über dieses Problem, das unter ActiveState code.activestate.com/lists/python-tutor/70739
Tim McNamara
2

Die veröffentlichte Antwort war die beste verfügbare, wenn sie gegeben wurde, hat jedoch eine Reihe von Einschränkungen und liefert keine technisch korrekten signifikanten Zahlen.

numpy.format_float_positional unterstützt das gewünschte Verhalten direkt. Das folgende Fragment gibt den xauf 4 signifikante Zahlen formatierten Float zurück , wobei die wissenschaftliche Notation unterdrückt ist.

import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.
Herbst
quelle
Die Dokumentation (verschoben nach numpy.org/doc/stable/reference/generated/… ) besagt, dass diese Funktion den Dragon4-Algorithmus implementiert (von Steele & White 1990, dl.acm.org/doi/pdf/10.1145/93542.93559 ). Es erzeugt störende Ergebnisse, z print(*[''.join([np.format_float_positional(.01*a*n,precision=2,unique=False,fractional=False,trim='k',pad_right=5) for a in [.99, .999, 1.001]]) for n in [8,9,10,11,12,19,20,21]],sep='\n'). Ich habe Dragon4 selbst nicht überprüft.
Rainald62
0

Ich bin auch darauf gestoßen, aber ich brauchte Kontrolle über den Rundungstyp. Daher habe ich eine Schnellfunktion geschrieben (siehe Code unten), die Wert, Rundungstyp und gewünschte signifikante Ziffern berücksichtigen kann.

import decimal
from math import log10, floor

def myrounding(value , roundstyle='ROUND_HALF_UP',sig = 3):
    roundstyles = [ 'ROUND_05UP','ROUND_DOWN','ROUND_HALF_DOWN','ROUND_HALF_UP','ROUND_CEILING','ROUND_FLOOR','ROUND_HALF_EVEN','ROUND_UP']

    power =  -1 * floor(log10(abs(value)))
    value = '{0:f}'.format(value) #format value to string to prevent float conversion issues
    divided = Decimal(value) * (Decimal('10.0')**power) 
    roundto = Decimal('10.0')**(-sig+1)
    if roundstyle not in roundstyles:
        print('roundstyle must be in list:', roundstyles) ## Could thrown an exception here if you want.
    return_val = decimal.Decimal(divided).quantize(roundto,rounding=roundstyle)*(decimal.Decimal(10.0)**-power)
    nozero = ('{0:f}'.format(return_val)).rstrip('0').rstrip('.') # strips out trailing 0 and .
    return decimal.Decimal(nozero)


for x in list(map(float, '-1.234 1.2345 0.03 -90.25 90.34543 9123.3 111'.split())):
    print (x, 'rounded UP: ',myrounding(x,'ROUND_UP',3))
    print (x, 'rounded normal: ',myrounding(x,sig=3))
zeichnete
quelle
0

Verwenden der neuen Formatierung von Python 2.6+ (da% -Style veraltet ist):

>>> "{0}".format(float("{0:.1g}".format(1216)))
'1000.0'
>>> "{0}".format(float("{0:.1g}".format(0.00356)))
'0.004'

In Python 2.7+ können Sie die führenden 0s weglassen .

Eddygeek
quelle
Mit welcher Version von Python? Python 3.6.3 | Anaconda, Inc. | (Standard, 13. Oktober 2017, 12:02:49) hat das gleiche alte Rundungsproblem. "{0}". Format (float ("{0: .1g}". Format (0.075))) ergibt '0.07', nicht '0.08'
Don Mclachlan
@ DonMclachlan Ich habe eine Erklärung hinzugefügt, warum dies in stackoverflow.com/a/56974893/1358308
Sam Mason
0

Diese Funktion führt eine normale Runde durch, wenn die Zahl größer als 10 ** ist (- decimal_positions). Andernfalls wird mehr Dezimalzahl hinzugefügt, bis die Anzahl der aussagekräftigen Dezimalstellen erreicht ist:

def smart_round(x, decimal_positions):
    dp = - int(math.log10(abs(x))) if x != 0.0 else int(0)
    return round(float(x), decimal_positions + dp if dp > 0 else decimal_positions)

Ich hoffe es hilft.

Ettore Galli
quelle
0

https://stackoverflow.com/users/1391441/gabriel , geht das Folgende auf Ihre Besorgnis über rnd (.075, 1) ein? Vorsichtsmaßnahme: Gibt den Wert als Float zurück

def round_to_n(x, n):
    fmt = '{:1.' + str(n) + 'e}'    # gives 1.n figures
    p = fmt.format(x).split('e')    # get mantissa and exponent
                                    # round "extra" figure off mantissa
    p[0] = str(round(float(p[0]) * 10**(n-1)) / 10**(n-1))
    return float(p[0] + 'e' + p[1]) # convert str to float

>>> round_to_n(750, 2)
750.0
>>> round_to_n(750, 1)
800.0
>>> round_to_n(.0750, 2)
0.075
>>> round_to_n(.0750, 1)
0.08
>>> math.pi
3.141592653589793
>>> round_to_n(math.pi, 7)
3.141593
Don Mclachlan
quelle
0

Dies gibt eine Zeichenfolge zurück, sodass Ergebnisse ohne Bruchteile und kleine Werte, die sonst in der E-Notation erscheinen würden, korrekt angezeigt werden:

def sigfig(x, num_sigfig):
    num_decplace = num_sigfig - int(math.floor(math.log10(abs(x)))) - 1
    return '%.*f' % (num_decplace, round(x, num_decplace))
Gnubie
quelle
0

Wenn eine Frage so gründlich beantwortet wurde, warum nicht eine weitere hinzufügen?

Dies passt etwas besser zu meiner Ästhetik, obwohl viele der oben genannten vergleichbar sind

import numpy as np

number=-456.789
significantFigures=4

roundingFactor=significantFigures - int(np.floor(np.log10(np.abs(number)))) - 1
rounded=np.round(number, roundingFactor)

string=rounded.astype(str)

print(string)

Dies funktioniert für einzelne Zahlen und Numpy-Arrays und sollte für negative Zahlen gut funktionieren.

Es gibt noch einen zusätzlichen Schritt, den wir hinzufügen könnten: np.round () gibt eine Dezimalzahl zurück, auch wenn gerundet eine Ganzzahl ist (dh für signifikante Zahlen = 2 erwarten wir möglicherweise, dass wir -460 zurückbekommen, aber stattdessen erhalten wir -460.0). Wir können diesen Schritt hinzufügen, um dies zu korrigieren:

if roundingFactor<=0:
    rounded=rounded.astype(int)

Leider funktioniert dieser letzte Schritt für eine Reihe von Zahlen nicht - das überlasse ich Ihnen, lieber Leser, um herauszufinden, ob Sie es brauchen.

Zephyr
quelle
0

Das sigfig- Paket / die Bibliothek behandelt dies. Nach der Installation können Sie Folgendes tun:

>>> from sigfig import round
>>> round(1234, 1)
1000
>>> round(0.12, 1)
0.1
>>> round(0.012, 1)
0.01
>>> round(0.062, 1)
0.06
>>> round(6253, 1)
6000
>>> round(1999, 1)
2000
HyperActive
quelle
0
import math

  def sig_dig(x, n_sig_dig):
      num_of_digits = len(str(x).replace(".", ""))
      if n_sig_dig >= num_of_digits:
          return x
      n = math.floor(math.log10(x) + 1 - n_sig_dig)
      result = round(10 ** -n * x) * 10 ** n
      return float(str(result)[: n_sig_dig + 1])


    >>> sig_dig(1234243, 3)
    >>> sig_dig(243.3576, 5)

        1230.0
        243.36
LetzerWille
quelle