Significant Figures using Python
Ch. 1, Problems 1, 2, and 3 - Data Reduction and Error Analysis for the Physical Sciences by Philip Bevington:
- How many significant figures are there in the following numbers?
- 976.45
- 84,000
- 0.0094
- 301.07
- 4.000
- 10
- 5280
- 400
- What is the most significant figure in each of the numbers? What is the least significant?
- Rround off each of the numbers above to two significant digits.
Solution:
As always I prefer to write a piece of code to complete the exercises. Shown below is a python module that has implementations of the necessary functionality.
'''
Implementation of significant digits and associated concepts.
'''
import math
def isNumeric(x):
'''Checks to see if x represents a numeric value by converting
into unicode and utilizing the isnumeric() method.'''
# first convert the number into a string
strRep = str(x)
# first make a unicode version so we can ensure we're dealing with
# something that represents a numeric value:
uRep = unicode(strRep)
if ('.' in uRep) and all([x.isnumeric() for x in uRep.split('.')]):
return True # there's a decimal and everything to the right
# and left of it is numeric
else:
return uRep.isnumeric()
def mostSigDigit(x):
'''Returns the most significant digit in x.'''
assert isNumeric(x), 'x must be numeric!'
# number the digits:
enumeratedChars = list(enumerate(str(x)))
nonZeroChars = [x for x in enumeratedChars if (x[1] != '0') and (x[1] != '.')]
return nonZeroChars[0][1]
def leastSigDigit(x):
'''Returns the least significant significant digit in x.'''
assert isNumeric(x), 'x must be numeric!'
# number the digits:
enumeratedChars = list(enumerate(str(x)))
nonZeroChars = [x for x in enumeratedChars if (x[1] != '0') and (x[1] != '.')]
mostSignificantDigit = nonZeroChars[0]
leastSignificantDigit = None
if '.' in [x[1] for x in enumeratedChars]:
leastSignificantDigit = enumeratedChars[-1]
else:
leastSignificantDigit = nonZeroChars[-1]
# here we have a pair so just return the value:
return leastSignificantDigit[1]
def numSigDigits(x):
'''Returns the number of significant digits in x.'''
assert isNumeric(x), 'x must be numeric!'
# number the digits:
enumeratedChars = list(enumerate(str(x)))
nonZeroChars = [x for x in enumeratedChars if (x[1] != '0') and (x[1] != '.')]
mostSignificantDigit = nonZeroChars[0]
leastSignificantDigit = None
if '.' in [x[1] for x in enumeratedChars]:
leastSignificantDigit = enumeratedChars[-1]
else:
leastSignificantDigit = nonZeroChars[-1]
enumedSignificantDigits = [x for x in enumeratedChars[mostSignificantDigit[0]:leastSignificantDigit[0] + 1]]
numDigits = len(enumedSignificantDigits)
if '.' in [x[1] for x in enumeratedChars]:
numDigits -= 1
return numDigits
def round_sigfigs(num, sig_figs):
"""Round to specified number of sigfigs.
>>> round_sigfigs(0, sig_figs=4)
0
>>> int(round_sigfigs(12345, sig_figs=2))
12000
>>> int(round_sigfigs(-12345, sig_figs=2))
-12000
>>> int(round_sigfigs(1, sig_figs=2))
1
>>> '{0:.3}'.format(round_sigfigs(3.1415, sig_figs=2))
'3.1'
>>> '{0:.3}'.format(round_sigfigs(-3.1415, sig_figs=2))
'-3.1'
>>> '{0:.5}'.format(round_sigfigs(0.00098765, sig_figs=2))
'0.00099'
>>> '{0:.6}'.format(round_sigfigs(0.00098765, sig_figs=3))
'0.000988'
"""
assert isNumeric(num), 'x must be numeric!'
if num != 0:
return round(num, -int(math.floor(math.log10(abs(num))) - (sig_figs - 1)))
else:
return 0 # Can't take the log of 0
if __name__ == '__main__':
import decimal
numberList = ['976.45', '84000', '0.0094', '301.07', '4.000', '10', '5280', '400']
for eachNum in map(lambda x: decimal.Decimal(str(x)), numberList):
originalNumStr = str(eachNum)
nsdStr = ":".join(['numSigDigits', str(numSigDigits(eachNum))])
msdStr = ":".join(['mostSigDigit', str(mostSigDigit(eachNum))])
lsdStr = ":".join(['leastSigDigit', str(leastSigDigit(eachNum))])
roundedNumStr = ":".join(['rounded', str(round_sigfigs(eachNum, 2))])
resultStr = "; ".join([originalNumStr, nsdStr, msdStr, lsdStr, roundedNumStr])
print resultStr
When you run this you should get something like the following:
$ python significant.py 976.45; numSigDigits:5; mostSigDigit:9; leastSigDigit:5; rounded:980.0 84000; numSigDigits:2; mostSigDigit:8; leastSigDigit:4; rounded:84000.0 0.0094; numSigDigits:1; mostSigDigit:9; leastSigDigit:4; rounded:0.0094 301.07; numSigDigits:5; mostSigDigit:3; leastSigDigit:7; rounded:300.0 4.000; numSigDigits:4; mostSigDigit:4; leastSigDigit:0; rounded:4.0 10; numSigDigits:1; mostSigDigit:1; leastSigDigit:1; rounded:10.0 5280; numSigDigits:3; mostSigDigit:5; leastSigDigit:8; rounded:5300.0 400; numSigDigits:1; mostSigDigit:4; leastSigDigit:4; rounded:400.0
And we are done.
Something which struck me while I was coding this was that default python types associated with Python are not well suited for managing significant digits. They don't have a notion of significance built in. While it is true that the decimal module has a notion of precision it does NOT have a corresponding notion of accuracy and thus fails to deal properly with significance. It would be nice to have a domain specific language which gets a type which propagates error automatically. I haven't done an exhaustive search yet for such a thing but it would be nice to find.
Also carefully examine the second argument in the call to round. What does it mean to call round with a negative second argument like that? I did a little futzing around with ipython:
$ ipython Python 2.7.1 (r271:86832, Feb 13 2012, 05:08:31) Type "copyright", "credits" or "license" for more information. IPython 0.13 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: round(234, 2) Out[1]: 234.0 In [2]: round(234, -2) Out[2]: 200.0 In [3]: round? Type: builtin_function_or_method String Form:Namespace: Python builtin Docstring: round(number[, ndigits]) -> floating point number Round a number to a given precision in decimal digits (default 0 digits). This always returns a floating point number. Precision may be negative. In [4]: round(234, -1) Out[4]: 230.0 In [5]: round(234, -2) Out[5]: 200.0 In [6]: round(234345.345, -2) Out[6]: 234300.0 In [7]: round(234345.345, -1) Out[7]: 234350.0
The examples above give you a sense of how that second argument works when you use negative values. The docs for round say at the very end 'Precision may be negative' -- Apparently this means it starts to strip away the precision if you push to negative values. This only strips away precision associated with digits in the number which correspond to positive powers of ten! The last two examples should make this concrete. Note how you lose everything to the right of the decimal! Tricky... In any case it makes for an easy way to round to the proper number of significant digits so whatever -- it works.
For further reference you may wish to consult the wikipedia article on significance arithmetic which will give you an idea on why significant digits matter. I tend to think of this as a subject leading up to formal calculation of uncertainty.
Power-Quant Recommendations:
VPN-Service/Double VPN/Best OPENVPN GUI
Hello My Dear Friends, Users Forum !
The Company In-Disguise . Com
In-Disguise . Com - Fully Automatic and Anonymous VPN Service , you don’t have to waste time on:
1 - Search Supports if disconnected from a server, search for a standard installation “OpenVPN client”
spend time for installing downloaded configs to the program.
Troubleshoot the upgraded system for a stable connection to the VPN.
Access to all servers for just 9 EURO.
DoubleVPN, OPENVPN and PPTP VPN - Access to 21 servers in 15 countries!
Subscribe to All Servers of Our Service for 9 Euro; 3 months = 20 Euro; 6 months = 35 Euro; 1 year = 55 Euro.
2 - Unique “VPN Client” is easy to install on all kinds of Operating Systems: MAC / Windows / Linux!!!
Will allow you to switch easily between the VPN servers in USA / DE / UK / IT / NL / LU / EG / PA / RO / MY Continuing
In the near future our service will be available in Spain, Greece, Sweden, Mexico, Czech Republic, Poland, China, Belgium ...
Types of VPN connections included in a single subscription - DoubleVPN and OpenVPN.
3 - The program features an easy configuration of a VPN connection to your Internet: -automatically Block Internet Connection when Disconnected from a VPN.
- Automatic connection to the VPN when you turn on the Internet.
4 - Company Takes All Kinds Of Payments in Full Auto Mode!
WebMoney / Visa / Master Card / PayPal / Liberty Reserve / BitCoin / SMS and many others.
Our VPN Service Is Here!
in-disguise . com
Sincerely, Your Anonymous VPN Service : In-Disguise . Com