web-dev-qa-db-ja.com

Pythonで数値を有効数字に丸める方法

UIに表示されるようにフロートを丸める必要があります。例えば、ある重要な数字へ:

1234-> 1000

0.12-> 0.1

0.012-> 0.01

0.062-> 0.06

6253-> 6000

1999-> 2000

Pythonライブラリを使用してこれを行う良い方法はありますか、それとも自分で記述する必要がありますか?

122
Peter Graham

負の数を使用して整数を丸めることができます。

>>> round(1234, -3)
1000.0

したがって、最上位桁のみが必要な場合:

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

おそらく、floatが1より大きい場合、floatを整数に変換する必要があります。

130
Evgeny

文字列フォーマットの%gは、いくつかの有効数字に丸められた浮動小数点をフォーマットします。科学表記法 'e'を使用する場合があるため、丸められた文字列を浮動小数点数に変換してから、%s文字列の書式設定を行います。

>>> '%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'
93
Peter Graham

有効な10進数以外(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
46
indgar
print('{:g}'.format(float('{:.1g}'.format(12.345))))

このソリューションは、他のすべてのソリューションとは異なります。

  1. itexactlyはOPの質問を解決します
  2. notは必要ありません追加パッケージ
  3. それはnotユーザー定義補助関数または数学演算

任意の数のnの有効数字に対して、次を使用できます。

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

テスト:

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']

:このソリューションでは、後続のゼロの数が異なる数を区別する標準的な方法がないため、入力から有効数字の数を動的に調整することはできません(3.14 == 3.1400)。そうする必要がある場合は、 to-precision パッケージで提供されているような非標準関数が必要です。

13
Falken

私はパッケージを作成しました to-precision それはあなたが望むことをします。これにより、数字を多かれ少なかれ重要な数字にすることができます。

また、指定された有効数字で標準、科学、および工学表記を出力します。

受け入れられた答えには次の行があります

>>> round_to_1(1234243)
1000000.0

それは実際に8つのsigイチジクを指定します。番号1234243の場合、ライブラリには1つの重要な数字のみが表示されます。

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

また、最後の有効数字を丸め、表記法が指定されていない場合に使用する表記法を自動的に選択できます。

>>> to_precision(599, 2)
'600'
>>> to_precision(1164, 2)
'1.2e3'
6
William Rusnack

整数を有効数字1桁に丸めるための基本的な考え方は、小数点の前に1桁の浮動小数点に変換し、それを丸めてから元の整数サイズに戻すことです。

これを行うには、整数よりも小さい10のべき乗の最大値を知る必要があります。これにはlog 10関数のfloorを使用できます。

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

Indgarのソリューションを変更して、負の数と小さな数(ゼロを含む)を処理しました。

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)
4
ryan281
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

うまくいけば、上記のすべての答えのベストを取ります(マイナス1行のラムダとしてそれを置くことができること;))。まだ検討していないので、この回答を自由に編集してください。

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

文字列を使用せずに丸めたい場合は、上記のコメントにあるリンクを見つけました。

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

最高のように私を打つ。次に、文字列フォーマット記述子を使用して印刷すると、妥当な出力が得られ、数値表現を他の計算目的に使用できます。

リンクのコードは、def、doc、およびreturnの3つのライナーです。バグがあります。対数の爆発をチェックする必要があります。それは簡単だ。入力をsys.float_info.minと比較します。完全なソリューションは次のとおりです。

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

任意のスカラー数値に対して機能し、何らかの理由で応答をシフトする必要がある場合、nはfloatになります。実際に制限を次のようにプッシュできます。

sys.float_info.min*sys.float_info.epsilon

何らかの理由で極小値を操作している場合、エラーを引き起こすことなく。

3
getting_sleepy

質問に直接答えるために、これは R function からの命名を使用した私のバージョンです:

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)

この回答を投稿する主な理由は、「0.075」が0.08ではなく0.07に丸められるという不満のコメントです。これは、「初心者C」が指摘しているように、 有限精度と基数2表現 の両方を持つ浮動小数点演算の組み合わせによるものです。実際に表現できる0.075に最も近い数値はわずかに小さいため、単純に予想されるよりも丸め方が異なります。

また、これは非10進浮動小数点演算の使用にも適用されることに注意してください。 CとJavaの両方に同じ問題があります。

より詳細に表示するために、Pythonに数値を「hex」形式でフォーマットするように依頼します。

0.075.hex()

0x1.3333333333333p-4が得られます。これを行う理由は、通常の10進数表現には丸めが含まれることが多いため、コンピューターが実際に数値を「見る」方法ではないためです。この形式に慣れていない場合、いくつかの便利なリファレンスは Python docsC standard です。

これらの数値がどのように機能するかを示すために、次のようにして開始点に戻ることができます。

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

0.075が出力されるはずです。 16**13は、小数点の後に13桁の16進数があるためであり、2**-4は、16進数の指数が2進数であるためです。

これで、フロートがどのように表されるかについてのアイデアが得られました。decimalモジュールを使用して、精度を高め、何が起こっているかを示すことができます。

from decimal import Decimal

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

与える:0.07499999999999999722444243844およびround(0.075, 2)0.07に評価される理由をうまく説明する

2
Sam Mason

これをすぐに処理できるものは考えられません。しかし、浮動小数点数ではかなり適切に処理されます。

>>> round(1.2322, 2)
1.23

整数は扱いにくいです。それらはメモリに10進数として保存されないので、重要な場所は自然なことではありません。ただし、文字列になったら実装するのはかなり簡単です。

または整数の場合:

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

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

任意の数を処理する関数を作成する場合、私の好みはそれらを文字列に変換し、何をすべきかを決定するために小数点以下の桁を探すことです。

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

別のオプションは、タイプを確認することです。これははるかに柔軟性が低く、おそらくDecimalオブジェクトなどの他の数値とうまく動作しません。

>>> def roundall2(n, sigfigs):
...   if type(n) is int: return intround(n, sigfigs)
...   else: return round(n, sigfigs)
2
Tim McNamara

別の質問を追加しないのはなぜですか?

上記の多くは匹敵しますが、これは私の美学に少し良く合っています

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)

これは個々の数値とnumpy配列で機能し、負の数値でも正常に機能するはずです。

追加できるステップが1つあります。np.round()は、roundedが整数の場合でも10進数を返します(つまり、significantFigures = 2の場合、-460に戻ることが期待されますが、代わりに-460.0になります)。このステップを追加して、それを修正できます。

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

残念なことに、この最後の手順は一連の数字に対しては機能しません。必要な場合は、読者の皆様にご理解いただけるようにそれをお任せします。

0
Zephyr

この関数は、数値が10 **(-decimal_positions)よりも大きい場合に通常のラウンドを行います。そうでない場合は、意味のある小数位置の数に達するまでさらに小数を追加します。

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)

それが役に立てば幸い。

0
Ettore Galli

python 2.6+を使用する 新しいスタイルのフォーマット (%-styleは非推奨であるため):

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

python 2.7+では、先頭の0sを省略できます。

0
eddygeek

これは文字列を返すので、小数部分のない結果と、そうでなければE表記で表示される小さな値が正しく表示されます。

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))
0
Gnubie

https://stackoverflow.com/users/1391441/gabriel 、次はrnd(.075、1)に関する懸念に対処しますか?警告:値をfloatとして返します

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
0
Don Mclachlan

私もこれに遭遇しましたが、丸めタイプを制御する必要がありました。したがって、値、丸めの種類、および必要な有効数字を考慮に入れることができるクイック関数(以下のコードを参照)を作成しました。

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))
0
drew.ray