|
Hi folks, often times in science one expresses a value (say
1.03789291) and its error (say 0.00089) in a short way by parentheses like so: 1.0379(9) One can vary things a bit, but let's take the simplest case when we only keep 1 digit of the error (and round it of course) and round the value correspondingly. I've been searching around for a simple function that would take 2 float arguments and would return a string but didn't find anything although something tells me it's been done a gazillion times. What would be the simplest such function? Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown -- http://mail.python.org/mailman/listinfo/python-list |
|
On Wed, Feb 15, 2012 at 5:18 PM, Daniel Fetchinson
<[hidden email]> wrote: > Hi folks, often times in science one expresses a value (say > 1.03789291) and its error (say 0.00089) in a short way by parentheses > like so: 1.0379(9) > > One can vary things a bit, but let's take the simplest case when we > only keep 1 digit of the error (and round it of course) and round the > value correspondingly. I've been searching around for a simple > function that would take 2 float arguments and would return a string > but didn't find anything although something tells me it's been done a > gazillion times. > > What would be the simplest such function? Well, this basically works: >>> def format_error(value, error): ... precision = int(math.floor(math.log(error, 10))) ... format = "%%.%df(%%d)" % max(-precision, 0) ... return format % (round(value, -precision), ... int(round(error / 10 ** precision))) ... >>> format_error(1.03789291, 0.00089) '1.0379(9)' Note that "math.floor(math.log(error, 10))" may return the wrong decimal precision due to binary floating point rounding error, which could produce some strange results: >>> format_error(10378929, 1000) '10378900(10)' So you'll probably want to use decimals instead: def format_error(value, error): value = decimal.Decimal(value) error = decimal.Decimal(error) value_scale = value.log10().to_integral(decimal.ROUND_FLOOR) error_scale = error.log10().to_integral(decimal.ROUND_FLOOR) precision = value_scale - error_scale if error_scale > 0: format = "%%.%dE" % max(precision, 0) else: format = "%%.%dG" % (max(precision, 0) + 1) value_str = format % value.quantize(decimal.Decimal("10") ** error_scale) error_str = '(%d)' % error.scaleb(-error_scale).to_integral() if 'E' in value_str: index = value_str.index('E') return value_str[:index] + error_str + value_str[index:] else: return value_str + error_str >>> format_error(1.03789291, 0.00089) '1.0379(9)' >>> format_error(103789291, 1000) '1.03789(1)E+08' I haven't tested this thoroughly, so use at your own risk. :-) Cheers, Ian -- http://mail.python.org/mailman/listinfo/python-list |
|
>> Hi folks, often times in science one expresses a value (say
>> 1.03789291) and its error (say 0.00089) in a short way by parentheses >> like so: 1.0379(9) >> >> One can vary things a bit, but let's take the simplest case when we >> only keep 1 digit of the error (and round it of course) and round the >> value correspondingly. I've been searching around for a simple >> function that would take 2 float arguments and would return a string >> but didn't find anything although something tells me it's been done a >> gazillion times. >> >> What would be the simplest such function? > > Well, this basically works: > >>>> def format_error(value, error): > ... precision = int(math.floor(math.log(error, 10))) > ... format = "%%.%df(%%d)" % max(-precision, 0) > ... return format % (round(value, -precision), > ... int(round(error / 10 ** precision))) > ... >>>> format_error(1.03789291, 0.00089) > '1.0379(9)' > > Note that "math.floor(math.log(error, 10))" may return the wrong > decimal precision due to binary floating point rounding error, which > could produce some strange results: > >>>> format_error(10378929, 1000) > '10378900(10)' > > So you'll probably want to use decimals instead: > > def format_error(value, error): > value = decimal.Decimal(value) > error = decimal.Decimal(error) > value_scale = value.log10().to_integral(decimal.ROUND_FLOOR) > error_scale = error.log10().to_integral(decimal.ROUND_FLOOR) > precision = value_scale - error_scale > if error_scale > 0: > format = "%%.%dE" % max(precision, 0) > else: > format = "%%.%dG" % (max(precision, 0) + 1) > value_str = format % value.quantize(decimal.Decimal("10") ** > error_scale) > error_str = '(%d)' % error.scaleb(-error_scale).to_integral() > if 'E' in value_str: > index = value_str.index('E') > return value_str[:index] + error_str + value_str[index:] > else: > return value_str + error_str > >>>> format_error(1.03789291, 0.00089) > '1.0379(9)' >>>> format_error(103789291, 1000) > '1.03789(1)E+08' > > I haven't tested this thoroughly, so use at your own risk. :-) Thanks a lot, this indeed mostly works, except for cases when the error needs to be rounded up and becomes two digits: >>> format_error( '1.34883', '0.0098' ) '1.349(10)' But in this case I'd like to see 1.35(1) Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown -- http://mail.python.org/mailman/listinfo/python-list |
|
On Thu, Feb 16, 2012 at 1:36 AM, Daniel Fetchinson
<[hidden email]> wrote: >>> Hi folks, often times in science one expresses a value (say >>> 1.03789291) and its error (say 0.00089) in a short way by parentheses >>> like so: 1.0379(9) >>> >>> One can vary things a bit, but let's take the simplest case when we >>> only keep 1 digit of the error (and round it of course) and round the >>> value correspondingly. I've been searching around for a simple >>> function that would take 2 float arguments and would return a string >>> but didn't find anything although something tells me it's been done a >>> gazillion times. >>> >>> What would be the simplest such function? >> >> Well, this basically works: >> >>>>> def format_error(value, error): >> ... precision = int(math.floor(math.log(error, 10))) >> ... format = "%%.%df(%%d)" % max(-precision, 0) >> ... return format % (round(value, -precision), >> ... int(round(error / 10 ** precision))) >> ... >>>>> format_error(1.03789291, 0.00089) >> '1.0379(9)' >> >> Note that "math.floor(math.log(error, 10))" may return the wrong >> decimal precision due to binary floating point rounding error, which >> could produce some strange results: >> >>>>> format_error(10378929, 1000) >> '10378900(10)' >> >> So you'll probably want to use decimals instead: >> >> def format_error(value, error): >> value = decimal.Decimal(value) >> error = decimal.Decimal(error) >> value_scale = value.log10().to_integral(decimal.ROUND_FLOOR) >> error_scale = error.log10().to_integral(decimal.ROUND_FLOOR) >> precision = value_scale - error_scale >> if error_scale > 0: >> format = "%%.%dE" % max(precision, 0) >> else: >> format = "%%.%dG" % (max(precision, 0) + 1) >> value_str = format % value.quantize(decimal.Decimal("10") ** >> error_scale) >> error_str = '(%d)' % error.scaleb(-error_scale).to_integral() >> if 'E' in value_str: >> index = value_str.index('E') >> return value_str[:index] + error_str + value_str[index:] >> else: >> return value_str + error_str >> >>>>> format_error(1.03789291, 0.00089) >> '1.0379(9)' >>>>> format_error(103789291, 1000) >> '1.03789(1)E+08' >> >> I haven't tested this thoroughly, so use at your own risk. :-) > > Thanks a lot, this indeed mostly works, except for cases when the > error needs to be rounded up and becomes two digits: > >>>> format_error( '1.34883', '0.0098' ) > '1.349(10)' > > But in this case I'd like to see 1.35(1) A small adjustment to the scale fixes that. Also tidied up the string formatting part: import decimal def format_error(value, error): value = decimal.Decimal(value) error = decimal.Decimal(error) error_scale = error.adjusted() error_scale += error.scaleb(-error_scale).to_integral().adjusted() value_str = str(value.quantize(decimal.Decimal("1E%d" % error_scale))) error_str = '(%d)' % error.scaleb(-error_scale).to_integral() if 'E' in value_str: index = value_str.index('E') return value_str[:index] + error_str + value_str[index:] else: return value_str + error_str Cheers, Ian -- http://mail.python.org/mailman/listinfo/python-list |
|
On 2/16/12, Ian Kelly <[hidden email]> wrote:
> On Thu, Feb 16, 2012 at 1:36 AM, Daniel Fetchinson > <[hidden email]> wrote: >>>> Hi folks, often times in science one expresses a value (say >>>> 1.03789291) and its error (say 0.00089) in a short way by parentheses >>>> like so: 1.0379(9) >>>> >>>> One can vary things a bit, but let's take the simplest case when we >>>> only keep 1 digit of the error (and round it of course) and round the >>>> value correspondingly. I've been searching around for a simple >>>> function that would take 2 float arguments and would return a string >>>> but didn't find anything although something tells me it's been done a >>>> gazillion times. >>>> >>>> What would be the simplest such function? >>> >>> Well, this basically works: >>> >>>>>> def format_error(value, error): >>> ... precision = int(math.floor(math.log(error, 10))) >>> ... format = "%%.%df(%%d)" % max(-precision, 0) >>> ... return format % (round(value, -precision), >>> ... int(round(error / 10 ** precision))) >>> ... >>>>>> format_error(1.03789291, 0.00089) >>> '1.0379(9)' >>> >>> Note that "math.floor(math.log(error, 10))" may return the wrong >>> decimal precision due to binary floating point rounding error, which >>> could produce some strange results: >>> >>>>>> format_error(10378929, 1000) >>> '10378900(10)' >>> >>> So you'll probably want to use decimals instead: >>> >>> def format_error(value, error): >>> value = decimal.Decimal(value) >>> error = decimal.Decimal(error) >>> value_scale = value.log10().to_integral(decimal.ROUND_FLOOR) >>> error_scale = error.log10().to_integral(decimal.ROUND_FLOOR) >>> precision = value_scale - error_scale >>> if error_scale > 0: >>> format = "%%.%dE" % max(precision, 0) >>> else: >>> format = "%%.%dG" % (max(precision, 0) + 1) >>> value_str = format % value.quantize(decimal.Decimal("10") ** >>> error_scale) >>> error_str = '(%d)' % error.scaleb(-error_scale).to_integral() >>> if 'E' in value_str: >>> index = value_str.index('E') >>> return value_str[:index] + error_str + value_str[index:] >>> else: >>> return value_str + error_str >>> >>>>>> format_error(1.03789291, 0.00089) >>> '1.0379(9)' >>>>>> format_error(103789291, 1000) >>> '1.03789(1)E+08' >>> >>> I haven't tested this thoroughly, so use at your own risk. :-) >> >> Thanks a lot, this indeed mostly works, except for cases when the >> error needs to be rounded up and becomes two digits: >> >>>>> format_error( '1.34883', '0.0098' ) >> '1.349(10)' >> >> But in this case I'd like to see 1.35(1) > > A small adjustment to the scale fixes that. Also tidied up the string > formatting part: > > import decimal > > def format_error(value, error): > value = decimal.Decimal(value) > error = decimal.Decimal(error) > error_scale = error.adjusted() > error_scale += error.scaleb(-error_scale).to_integral().adjusted() > value_str = str(value.quantize(decimal.Decimal("1E%d" % error_scale))) > error_str = '(%d)' % error.scaleb(-error_scale).to_integral() > if 'E' in value_str: > index = value_str.index('E') > return value_str[:index] + error_str + value_str[index:] > else: > return value_str + error_str > > Cheers, > Ian Thanks, it's simpler indeed, but gives me an error for value=1.267, error=0.08: Traceback (most recent call last): File "/home/fetchinson/bin/format_error", line 26, in <module> print format_error( sys.argv[1], sys.argv[2] ) File "/home/fetchinson/bin/format_error", line 9, in format_error error_scale += error.scaleb( -error_scale ).to_integral( ).adjusted( ) File "/usr/lib64/python2.6/decimal.py", line 3398, in scaleb ans = self._check_nans(other, context) File "/usr/lib64/python2.6/decimal.py", line 699, in _check_nans other_is_nan = other._isnan() AttributeError: 'int' object has no attribute '_isnan' Which version of python are you using? Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown -- http://mail.python.org/mailman/listinfo/python-list |
|
On Thu, Feb 16, 2012 at 3:21 PM, Daniel Fetchinson
<[hidden email]> wrote: > Thanks, it's simpler indeed, but gives me an error for value=1.267, error=0.08: > > Traceback (most recent call last): > File "/home/fetchinson/bin/format_error", line 26, in <module> > print format_error( sys.argv[1], sys.argv[2] ) > File "/home/fetchinson/bin/format_error", line 9, in format_error > error_scale += error.scaleb( -error_scale ).to_integral( ).adjusted( ) > File "/usr/lib64/python2.6/decimal.py", line 3398, in scaleb > ans = self._check_nans(other, context) > File "/usr/lib64/python2.6/decimal.py", line 699, in _check_nans > other_is_nan = other._isnan() > AttributeError: 'int' object has no attribute '_isnan' > > Which version of python are you using? 2.7.1. At a guess, it's failing because scaleb() (which was new in 2.6) is buggily expecting a decimal argument, but adjusted() returns an int. Convert the results of the two adjusted() calls to decimals, and I think it should be fine. -- http://mail.python.org/mailman/listinfo/python-list |
|
In reply to this post by Daniel Fetchinson
On 16 fév, 01:18, Daniel Fetchinson <[hidden email]> wrote:
> Hi folks, often times in science one expresses a value (say > 1.03789291) and its error (say 0.00089) in a short way by parentheses > like so: 1.0379(9) > Before swallowing any Python solution, you should realize, the values (value, error) you are using are a non sense : 1.03789291 +/- 0.00089 You express "more precision" in the value than in the error. --- As ex, in a 1.234(5) notation, the "()" is usually used to indicate the accuracy of the digit in "()". Eg 1.345(7) Typographically, the "()" is sometimes replaced by a bold digit ou a subscripted digit. jmf -- http://mail.python.org/mailman/listinfo/python-list |
|
In reply to this post by Ian Kelly-2
>> Thanks, it's simpler indeed, but gives me an error for value=1.267,
>> error=0.08: >> >> Traceback (most recent call last): >> File "/home/fetchinson/bin/format_error", line 26, in <module> >> print format_error( sys.argv[1], sys.argv[2] ) >> File "/home/fetchinson/bin/format_error", line 9, in format_error >> error_scale += error.scaleb( -error_scale ).to_integral( ).adjusted( >> ) >> File "/usr/lib64/python2.6/decimal.py", line 3398, in scaleb >> ans = self._check_nans(other, context) >> File "/usr/lib64/python2.6/decimal.py", line 699, in _check_nans >> other_is_nan = other._isnan() >> AttributeError: 'int' object has no attribute '_isnan' >> >> Which version of python are you using? > > 2.7.1. At a guess, it's failing because scaleb() (which was new in > 2.6) is buggily expecting a decimal argument, but adjusted() returns an int. > Convert the results of the two adjusted() calls to decimals, and I > think it should be fine. Great, with python 2.7 it works indeed! Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown -- http://mail.python.org/mailman/listinfo/python-list |
|
In reply to this post by jmfauth
>> Hi folks, often times in science one expresses a value (say
>> 1.03789291) and its error (say 0.00089) in a short way by parentheses >> like so: 1.0379(9) > > Before swallowing any Python solution, you should > realize, the values (value, error) you are using are > a non sense : > > 1.03789291 +/- 0.00089 > > You express "more precision" in the value than > in the error. My impression is that you didn't understand the original problem: given an arbitrary value to arbitrary digits and an arbitrary error, find the relevant number of digits for the value that makes sense for the given error. So what you call "non sense" is part of the problem to be solved. Cheers, Daniel -- Psss, psss, put it down! - http://www.cafepress.com/putitdown -- http://mail.python.org/mailman/listinfo/python-list |
|
In reply to this post by Daniel Fetchinson
Am 16.02.2012 01:18, schrieb Daniel Fetchinson:
> Hi folks, often times in science one expresses a value (say > 1.03789291) and its error (say 0.00089) in a short way by parentheses > like so: 1.0379(9) Just so that I understand you, the value of the last "digit" is somewhere between 9-9 and 9+9, right? So the value itself is rounded so that its last digit has the same value as the only digit to which the error is rounded. > One can vary things a bit, but let's take the simplest case when we > only keep 1 digit of the error (and round it of course) and round the > value correspondingly. First step is to format the values as decimal string ('{0:f}'.format). Then, pad both values to the same number of digits before and after the radix separator. Then, iterate over the strings, those digits where the error digits are zero are those that are taken verbatim for the value. The following digit is rounded and then added to the result. The according digit in the error is also rounded and added in brackets. Note that you can not compute these "values", since the definition of the rounding depends on a decimal representation. If you have decimal floating point numbers, where 0.1 is representable without loss, you could actually do that. In order to allow common treatment as decimal as required for this algorithm, the first step above was the conversion to a decimal string. Write tests that make sure this works, e.g. I ignored any sign and you also can't use str() to format the value because that could give you "123.45e+5" as a result. Good luck! Uli -- http://mail.python.org/mailman/listinfo/python-list |
|
In reply to this post by jmfauth
On 17 fév, 11:03, Daniel Fetchinson <[hidden email]> wrote:
> >> Hi folks, often times in science one expresses a value (say > >> 1.03789291) and its error (say 0.00089) in a short way by parentheses > >> like so: 1.0379(9) > > > Before swallowing any Python solution, you should > > realize, the values (value, error) you are using are > > a non sense : > > > 1.03789291 +/- 0.00089 > > > You express "more precision" in the value than > > in the error. > > My impression is that you didn't understand the original problem: > given an arbitrary value to arbitrary digits and an arbitrary error, > find the relevant number of digits for the value that makes sense for > the given error. So what you call "non sense" is part of the problem > to be solved. > I do not know where these numbers (value, error) are coming from. But, when the value and the error have not the same "precision", there is already something wrong somewhere. And this, *prior* to any representation of these values/numbers. jmf -- http://mail.python.org/mailman/listinfo/python-list |
| Powered by Nabble | Edit this page |
