Округление до значительных цифр в numpy
Я пробовал искать это и не могу найти удовлетворительный ответ.
Я хочу взять список/массив чисел и округлить их до n значительных цифр. Я написал функцию, чтобы сделать это, но мне было интересно, есть ли стандартный метод для этого? Я искал, но не могу его найти. Пример:
In: [ 0.0, -1.2366e22, 1.2544444e-15, 0.001222 ], n=2
Out: [ 0.00, -1.24e22, 1.25e-15, 1.22e-3 ]
Спасибо
Ответы
Ответ 1
Сначала критика: вы считаете количество значащих цифр неправильным. В вашем примере вы хотите n = 3, а не 2.
Можно обойти большинство крайних случаев, позволяя простым библиотечным функциям обрабатывать их, если вы используете функцию, которая делает двоичную версию этого алгоритма простой: frexp. В качестве бонуса этот алгоритм также будет работать намного быстрее, потому что он никогда не вызывает функцию log.
#The following constant was computed in maxima 5.35.1 using 64 bigfloat digits of precision
__logBase10of2 = 3.010299956639811952137388947244930267681898814621085413104274611e-1
import numpy as np
def RoundToSigFigs_fp( x, sigfigs ):
"""
Rounds the value(s) in x to the number of significant figures in sigfigs.
Return value has the same type as x.
Restrictions:
sigfigs must be an integer type and store a positive value.
x must be a real value.
"""
if not ( type(sigfigs) is int or type(sigfigs) is long or
isinstance(sigfigs, np.integer) ):
raise TypeError( "RoundToSigFigs_fp: sigfigs must be an integer." )
if sigfigs <= 0:
raise ValueError( "RoundToSigFigs_fp: sigfigs must be positive." )
if not np.isreal( x ):
raise TypeError( "RoundToSigFigs_fp: x must be real." )
xsgn = np.sign(x)
absx = xsgn * x
mantissa, binaryExponent = np.frexp( absx )
decimalExponent = __logBase10of2 * binaryExponent
omag = np.floor(decimalExponent)
mantissa *= 10.0**(decimalExponent - omag)
if mantissa < 1.0:
mantissa *= 10.0
omag -= 1.0
return xsgn * np.around( mantissa, decimals=sigfigs - 1 ) * 10.0**omag
И он обрабатывает все ваши дела правильно, включая бесконечное, nan, 0.0 и субнормальное число:
>>> eglist = [ 0.0, -1.2366e22, 1.2544444e-15, 0.001222, 0.0,
... float("nan"), float("inf"), float.fromhex("0x4.23p-1028"),
... 0.5555, 1.5444, 1.72340, 1.256e-15, 10.555555 ]
>>> eglist
[0.0, -1.2366e+22, 1.2544444e-15, 0.001222, 0.0,
nan, inf, 1.438203867284623e-309,
0.5555, 1.5444, 1.7234, 1.256e-15, 10.555555]
>>> RoundToSigFigs(eglist, 3)
array([ 0.00000000e+000, -1.24000000e+022, 1.25000000e-015,
1.22000000e-003, 0.00000000e+000, nan,
inf, 1.44000000e-309, 5.56000000e-001,
1.54000000e+000, 1.72000000e+000, 1.26000000e-015,
1.06000000e+001])
>>> RoundToSigFigs(eglist, 1)
array([ 0.00000000e+000, -1.00000000e+022, 1.00000000e-015,
1.00000000e-003, 0.00000000e+000, nan,
inf, 1.00000000e-309, 6.00000000e-001,
2.00000000e+000, 2.00000000e+000, 1.00000000e-015,
1.00000000e+001])
Изменение: 2016/10/12 Я обнаружил крайний случай, что исходный код обрабатывается неправильно. Я поместил более полную версию кода в репозиторий GitHub.
Изменение: 2019/03/01 Заменить на перекодированную версию.
Ответ 2
Является numpy.set_printoptions, что вы ищете?
import numpy as np
np.set_printoptions(precision=2)
print np.array([ 0.0, -1.2366e22, 1.2544444e-15, 0.001222 ])
дает:
[ 0.00e+00 -1.24e+22 1.25e-15 1.22e-03]
Edit:
numpy.around, как представляется, решает аспекты этой проблемы, если вы пытаетесь преобразовать данные. Однако он не делает то, что вы хотите, в тех случаях, когда показатель отрицательный.
Ответ 3
Из приведенных номеров примеров, я думаю, вы имеете в виду значимые цифры, а не десятичные знаки (-1.2366e22
до 0 десятичных знаков по-прежнему -1.2366e22
).
Эта часть кода работает для меня, я всегда думал, что должна быть встроенная функция:
def Round_To_n(x, n):
return round(x, -int(np.floor(np.sign(x) * np.log10(abs(x)))) + n)
>>> Round_To_n(1.2544444e-15,2)
1.25e-15
>>> Round_To_n(2.128282321e3, 6)
2130.0
Ответ 4
Хорошо, так разумно безопасно сказать, что это запрещено в стандартных функциях. Чтобы закрыть это, это моя попытка надежного решения. Это довольно уродливое/непитоновое и пробное иллюстрирует лучше, чем все, почему я задал этот вопрос, поэтому, пожалуйста, не стесняйтесь исправлять или бить:)
def round2SignifFigs(vals,n):
"""
(list, int) -> numpy array
(numpy array, int) -> numpy array
In: a list/array of values
Out: array of values rounded to n significant figures
Does not accept: inf, nan, complex
>>> m = [0.0, -1.2366e22, 1.2544444e-15, 0.001222]
>>> round2SignifFigs(m,2)
array([ 0.00e+00, -1.24e+22, 1.25e-15, 1.22e-03])
"""
import numpy as np
if np.all(np.isfinite(vals)) and np.all(np.isreal((vals))):
eset = np.seterr(all='ignore')
mags = 10.0**np.floor(np.log10(np.abs(vals))) # omag's
vals = np.around(vals/mags,n)*mags # round(val/omag)*omag
np.seterr(**eset)
vals[np.where(np.isnan(vals))] = 0.0 # 0.0 -> nan -> 0.0
else:
raise IOError('Input must be real and finite')
return vals
Ближайший я добираюсь до аккуратного не учитывает 0.0, nan, inf или complex:
>>> omag = lambda x: 10**np.floor(np.log10(np.abs(x)))
>>> signifFig = lambda x, n: (np.around(x/omag(x),n)*omag(x))
даяние:
>>> m = [0.0, -1.2366e22, 1.2544444e-15, 0.001222]
>>> signifFig(m,2)
array([ nan, -1.24e+22, 1.25e-15, 1.22e-03])
Ответ 5
У людей довольно слабое понимание "значимых цифр", что является причиной того, что для него мало встроенных языков программирования.
0.00 - это две значимые цифры, поскольку нулевой слева от десятичной точки не является существенным, а два справа.
1.234568e-01 → 0.123500
Номер правой руки имеет 7 значащих цифр, поскольку конечные нули во фракции ARE значительны, если они показаны. Поэтому отображение неверно, так как правильная версия 0.1234568, до 4 sig fig равна 0,1235. Написание его как 0.1234500 означает, что эти последние два нуля являются реальными и точными, что в этом случае они не являются.
В некоторых случаях число сиг-фигов даже не может быть вычислено с учетом числового значения, поэтому история или источник значения должны быть известны. Например, если рН раствора указывается как 7,00, он имеет только два сиг-фига, и они ".00". 7 не имеет значения, так как рН является логарифмической функцией, а 7 - степенью десяти в базовом числе. Например, 1.0e-7, -log10 (1.00e-7) = 7.00. Исходный номер имел 2 sig fig (1.0), у -log10 есть 2 sig fig's, = ( ".00" ), а 7 - не сиг.
На практике иногда число сиг-фига неоднозначно. Если человек пишет 1234000, правила говорят, что 000 является незначительным. Но что, если они действительно значительны? Если писатель хочет сделать это ПРАВИЛЬНО, и НЕПРАВИЛЬНО, они напишут 1.234000e6 (1.234000x10 ^ 6). Эта версия имеет 7-значный рисунок.
12300 3 sig fig (but may be ambiguous to some)
1.23e4 3 sig fig (unambiguous)
1.2300e4 5 sig fig
12300.0 6 sig fig
12300.100 8 sig fig
Ответ 6
Существует простое решение, использующее логику, встроенную в систему форматирования строк pythons:
def round_sig(f, p):
return float(('%.' + str(p) + 'e') % f)
Протестируйте в следующем примере:
for f in [0.01, 0.1, 1, 10, 100, 1000, 1000]:
f *= 1.23456789
print('%e --> %f' % (f, round_sig(f,3)))
который дает:
1.234568e-02 --> 0.012350
1.234568e-01 --> 0.123500
1.234568e+00 --> 1.235000
1.234568e+01 --> 12.350000
1.234568e+02 --> 123.500000
1.234568e+03 --> 1235.000000
1.234568e+03 --> 1235.000000
Удачи!
(Если вам нравится использование лямбда:
round_sig = lambda f,p: float(('%.' + str(p) + 'e') % f)
)
Ответ 7
Мне нравится Грег очень короткая эффективная рутина выше. Тем не менее, он страдает от двух недостатков. Во-первых, это не работает для x<0
, во всяком случае, для меня. (Это np.sign(x)
должен быть удален.) Другое - то, что он не работает, если x
является массивом. Я исправил обе эти проблемы с помощью процедуры ниже. Обратите внимание, что я изменил определение n
.
def Round_n_sig_dig(x, n):
import numpy as np
xr = (np.floor(np.log10(np.abs(x)))).astype(int)
xr=10.**xr*np.around(x/10.**xr,n-1)
return xr
Ответ 8
Там нет встроенного метода.
sround = lambda x,p: float(f'%.{p-1}e'%x)
пример
>>> print( sround(123.45, 2) )
120.0
Ответ 9
Большинство из приведенных здесь решений либо (а) не дают правильных значащих цифр, либо (б) излишне сложны.
Если вашей целью является форматирование отображения, то numpy.format_float_positional напрямую поддерживает желаемое поведение. Следующий фрагмент возвращает число с плавающей точкой x
, отформатированное до 4 значащих цифр, с подавленной научной нотацией.
import numpy as np
x=12345.6
np.format_float_positional(x, precision=4, unique=False, fractional=False, trim='k')
> 12340.