Significant figures calculation

H

Harold

Hi,

I am looking for an easy way to do significant figure calculations in
python (which I want to use with a class that does unit calculations).
Significant figure calculations should have the semantics explained,
e.g., here: http://chemistry.about.com/od/mathsciencefundamentals/a/sigfigures.htm

My hope was that the decimal module would provide this functionality,
but:
False # actually not sure how the semantics should be

I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
that did not lead to the correct behavior. Google and this list didn't
yield a good answer yet... so I'd be happy to get a good
recommendations or pointers.

P.S. I am aware that significant figure calculation is controversial
and makes implicit assumptions on the probability distributions of the
variables. I am simply looking for an implementation of the (well
defined) arithmetics as defined on the cited website.
 
S

Steven D'Aprano

Hi,

I am looking for an easy way to do significant figure calculations in
python (which I want to use with a class that does unit calculations).
Significant figure calculations should have the semantics explained,
e.g., here:
http://chemistry.about.com/od/mathsciencefundamentals/a/sigfigures.htm

My hope was that the decimal module would provide this functionality,
but:

False # actually not sure how the semantics should be

I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
that did not lead to the correct behavior.

Really? It works for me.

Decimal('1.0')

The only thing you have to watch out for is this:
True
 
J

Jerry Hill

Really? It works for me.

Decimal('49')

I'm curious. Is there a way to get the number of significant digits
for a particular Decimal instance? I spent a few minutes browsing
through the docs, and didn't see anything obvious. I was thinking
about setting the precision dynamically within a function, based on
the significance of the inputs.
 
S

steve+comp.lang.python

Jerry said:
I'm curious. Is there a way to get the number of significant digits
for a particular Decimal instance? I spent a few minutes browsing
through the docs, and didn't see anything obvious. I was thinking
about setting the precision dynamically within a function, based on
the significance of the inputs.

Not officially, so far as I know, but if you're willing to risk using a
private implementation detail that is subject to change:
'12345000'

However, sometimes you may need to inspect the exponent as well:
-8
 
H

Harold

I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
Really? It works for me.

You are right, I did something wrong when attempting to set the
precision.
And the trick with rounding the decimal with the unary + is neat.
It's the first time for me to play with decimals, so bare with me if I
miss
the obvious.

However, this approach forces you to know the number of significant
figures
beforehand -- which is precisely what the arithmetics should do for
you.
What would indeed be nice, is Jerry's suggestion to obtain the number
of
significant bits and write a small wrapper around the number protocoll
implementation that accounts significance and have __str__/__repr__
set
the context dynamically.

I haven't seen anything obvious in the docs, though it might be
possible
to use log10 of the length of some normalized string representation.
 
C

Chris Torek

I'm curious. Is there a way to get the number of significant digits
for a particular Decimal instance?

Yes:

def sigdig(x):
"return the number of significant digits in x"
return len(x.as_tuple()[1])

import decimal
D = decimal.Decimal

for x in (
'1',
'1.00',
'1.23400e-8',
'0.003'
):
print 'sigdig(%s): %d' % (x, sigdig(D(x)))
 
H

Harold

I'm curious.  Is there a way to get the number of significant digits
for a particular Decimal instance?

Yes:

def sigdig(x):
    "return the number of significant digits in x"
    return len(x.as_tuple()[1])

Great! that's exactly what I needed.
thanks Chris!
 
D

Dave Angel

(You top-posted your reply, instead of writing your response following
the part you were quoting)

In numerical analysis there is this concept of machine zero, which is
computed like this:

e=1.0
while 1.0+e> 1.0:
e=e/2.0
print e

The number e will give you the precision of floating point numbers.

Lalitha Prasad

That particular algorithm is designed for binary floating point. The OP
was asking about Decimal instances. So you'd want to divide by 10.0
each time. And of course you'd want to do it with Decimal objects.
DaveA
 
H

Harold

I'm curious.  Is there a way to get the number of significant digits
for a particular Decimal instance?

Yes:

def sigdig(x):
    "return the number of significant digits in x"
    return len(x.as_tuple()[1])

Great, Chris, this is (almost) exactly what I needed.
To make it work for numbers like 1200, that have four digits but only
two of them being significant, I changed your snippet to the
following:

class Empirical(Decimal) :
@property
def significance(self) :
t = self.as_tuple()
if t[2] < 0 :
return len(t[1])
else :
return len(''.join(map(str,t[1])).rstrip('0'))

5

now it's only about overriding the numerical operators :)
 
E

Ethan Furman

Harold said:
I'm curious. Is there a way to get the number of significant digits
for a particular Decimal instance?
Yes:

def sigdig(x):
"return the number of significant digits in x"
return len(x.as_tuple()[1])

Great, Chris, this is (almost) exactly what I needed.
To make it work for numbers like 1200, that have four digits but only
two of them being significant, I changed your snippet to the
following:

class Empirical(Decimal) :
@property
def significance(self) :
t = self.as_tuple()
if t[2] < 0 :
return len(t[1])
else :
return len(''.join(map(str,t[1])).rstrip('0'))

5

What about when 1200 is actually 4 significant digits? Or 3?

~Ethan~
 
E

Ethan Furman

Harold said:
Hi Ethan,


Then you'd simply write 1.200e3 and 1.20e3, respectively.
That's just how the rules are defined.

But your code is not following them:

Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit
(Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
--> from decimal import Decimal
--> class Empirical(Decimal) :
.... @property
.... def significance(self) :
.... t = self.as_tuple()
.... if t[2] < 0 :
.... return len(t[1])
.... else :
.... return len(''.join(map(str,t[1])).rstrip('0'))
....
--> Empirical('1.200E+3').significance
2 # should be four
--> Empirical('1.20E+3').significance
2 # should be three
--> Empirical('1.20E+4').significance
2 # should be three

The negatives appear to work, though:
--> Empirical('1.20E-4').significance
3
--> Empirical('1.2819E-3').significance
5
--> Empirical('1.2819E-1').significance
5
--> Empirical('1.281900E-1').significance
7

~Ethan~
 
S

Steven D'Aprano

Harold wrote: [...]

Well, that's completely wrong. It should be 4.
What about when 1200 is actually 4 significant digits? Or 3?

Then you shouldn't write it as 1200.0. By definition, zeros on the right are
significant. If you don't want zeroes on the right to count, you have to
not show them.

Five sig figures: 1200.0
Four sig figures: 1200
Three sig figures: 1.20e3
Two sig figures: 1.2e3
One sig figure: 1e3
Zero sig figure: 0
 
S

Steven D'Aprano

Is 0.0 one sig fig or two? (Just vaguely curious. Also curious as to
whether a zero sig figures value is ever useful.)

Two. I was actually being slightly silly about zero fig figures.

Although, I suppose, if you genuinely had zero significant figures, you
couldn't tell what the number was at all, so you'd need to use a NaN :)
 
M

Mel

Erik said:
That's not really zero significant figures; without further
qualification, it's one.


Yes. They're order of magnitude estimates. 1 x 10^6 has one
significant figure. 10^6 has zero.

By convention, nobody ever talks about 1 x 9.97^6 .

Mel.
 
M

Mel

Erik said:
Not sure what the relevance is, since nobody had mentioned any such thing.

If it was intended as a gag, I don't catch the reference.

I get giddy once in a while.. push things to limits. It doesn't really mean
anything. The point was that it's only the 2 in a number like 2e6 that is
taken to have error bars. The 6 is always an absolute number. As is the 10
in 2*10**6. The thought also crossed my mind of a kind of continued
fraction in reverse -- 2e1.3e.7 . I managed to keep quiet about that one.

Mel.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,233
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top