diff --git a/README.md b/README.md index 30400e7..1c3617e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,66 @@ # labfis.py -Small library (currently only one class) for uncertainty calculations. Made by and for Physics Laboratory students in IFSC, who can't use uncertainties.py because mean absolute deviation. + +## Description + +Small library (currently only one class) for uncertainty calculations and error propagation. + +The uncertainty calculations are in accordance with gaussian’s propagation, as calculated by an analytical method: + +

+ +

+ +Made by and for Physics Laboratory students in IFSC, who can't use uncertainties.py because of mean’s absolute deviation used in its calculation. To get this library on google colaboratory: - + ``` !curl --remote-name \ - -H 'Accept: application/vnd.github.v3.raw' \ - --location https://raw.githubusercontent.com/phisgroup/labfis.py/master/labfis/main.py + +-H 'Accept: application/vnd.github.v3.raw' \ + +--location https://raw.githubusercontent.com/phisgroup/labfis.py/development/labfis/main.py +``` + +## Usage + +Just import with `from labfis import labfloat` and create an *labfloat* object, as this exemple below: + +```py +>>> from labfis import labfloat +>>> a = labfloat(1,3) +>>> b = labfloat(2,4) +>>> a*b +(2 ± 7) ``` +Check the Wiki for more details + +## Instalation + +Intstall main releases with: + +``` +pip install labfis +``` + +Install development version with: + +``` +pip install git+https://github.com/phisgroup/labfis.py/tree/development +``` + +## References + + 1. Kirchner, James. ["Data Analysis Toolkit #5: Uncertainty Analysis and Error Propagation"](http://seismo.berkeley.edu/~kirchner/eps_120/Toolkits/Toolkit_05.pdf) (PDF). _Berkeley Seismology Laboratory_. University of California. Retrieved 22 April 2016. + 2. [Goodman, Leo](https://en.wikipedia.org/wiki/Leo_Goodman "Leo Goodman") (1960). "On the Exact Variance of Products". _Journal of the American Statistical Association_. **55** (292): 708–713. [doi](https://en.wikipedia.org/wiki/Doi_(identifier) "Doi (identifier)"):[10.2307/2281592](https://doi.org/10.2307%2F2281592). [JSTOR](https://en.wikipedia.org/wiki/JSTOR_(identifier) "JSTOR (identifier)") [2281592](https://www.jstor.org/stable/2281592). + 3. Ochoa1,Benjamin; Belongie, Serge ["Covariance Propagation for Guided Matching"](http://vision.ucsd.edu/sites/default/files/ochoa06.pdf) + 4. Ku, H. H. (October 1966). ["Notes on the use of propagation of error formulas"](http://nistdigitalarchives.contentdm.oclc.org/cdm/compoundobject/collection/p16009coll6/id/99848/rec/1). _Journal of Research of the National Bureau of Standards_. **70C** (4): 262. [doi](https://en.wikipedia.org/wiki/Doi_(identifier) "Doi (identifier)"):[10.6028/jres.070c.025](https://doi.org/10.6028%2Fjres.070c.025). [ISSN](https://en.wikipedia.org/wiki/ISSN_(identifier) "ISSN (identifier)") [0022-4316](https://www.worldcat.org/issn/0022-4316). Retrieved 3 October 2012. + 5. Clifford, A. A. (1973). _Multivariate error analysis: a handbook of error propagation and calculation in many-parameter systems_. John Wiley & Sons. [ISBN](https://en.wikipedia.org/wiki/ISBN_(identifier) "ISBN (identifier)") [978-0470160558](https://en.wikipedia.org/wiki/Special:BookSources/978-0470160558 "Special:BookSources/978-0470160558"). + 6. Lee, S. H.; Chen, W. (2009). "A comparative study of uncertainty propagation methods for black-box-type problems". _Structural and Multidisciplinary Optimization_. **37** (3): 239–253. [doi](https://en.wikipedia.org/wiki/Doi_(identifier) "Doi (identifier)"):[10.1007/s00158-008-0234-7](https://doi.org/10.1007%2Fs00158-008-0234-7). + 7. Johnson, Norman L.; Kotz, Samuel; Balakrishnan, Narayanaswamy (1994). _Continuous Univariate Distributions, Volume 1_. Wiley. p. 171. [ISBN](https://en.wikipedia.org/wiki/ISBN_(identifier) "ISBN (identifier)") [0-471-58495-9](https://en.wikipedia.org/wiki/Special:BookSources/0-471-58495-9 "Special:BookSources/0-471-58495-9"). + 8. Lecomte, Christophe (May 2013). "Exact statistics of systems with uncertainties: an analytical theory of rank-one stochastic dynamic systems". _Journal of Sound and Vibrations_. **332** (11): 2750–2776. [doi](https://en.wikipedia.org/wiki/Doi_(identifier) "Doi (identifier)"):[10.1016/j.jsv.2012.12.009](https://doi.org/10.1016%2Fj.jsv.2012.12.009). + 9. ["A Summary of Error Propagation"](http://ipl.physics.harvard.edu/wp-uploads/2013/03/PS3_Error_Propagation_sp13.pdf) (PDF). p. 2. Retrieved 2016-04-04. + 10. ["Propagation of Uncertainty through Mathematical Operations"](http://web.mit.edu/fluids-modules/www/exper_techniques/2.Propagation_of_Uncertaint.pdf) (PDF). p. 5. Retrieved 2016-04-04. + 11. ["Strategies for Variance Estimation"](http://www.sagepub.com/upm-data/6427_Chapter_4__Lee_%28Analyzing%29_I_PDF_6.pdf) (PDF). p. 37. Retrieved 2013-01-18. + 12. Harris, Daniel C. (2003), [_Quantitative chemical analysis_](https://books.google.com/books?id=csTsQr-v0d0C&pg=PA56)(6th ed.), Macmillan, p. 56, [ISBN](https://en.wikipedia.org/wiki/ISBN_(identifier) "ISBN (identifier)") [978-0-7167-4464-1](https://en.wikipedia.org/wiki/Special:BookSources/978-0-7167-4464-1 "Special:BookSources/978-0-7167-4464-1") + 13. ["Error Propagation tutorial"](http://www.foothill.edu/psme/daley/tutorials_files/10.%20Error%20Propagation.pdf) (PDF). _Foothill College_. October 9, 2009. Retrieved 2012-03-01. diff --git a/labfis/__init__.py b/labfis/__init__.py index 3e4e35e..1a61054 100644 --- a/labfis/__init__.py +++ b/labfis/__init__.py @@ -3,7 +3,7 @@ # Copyright © 2020 labfis.py # (see LICENSE for details) -__version__ = '1.0.0' +__version__ = '1.1.4' # Local imports from labfis.main import labfloat diff --git a/labfis/main.py b/labfis/main.py index bfc6d71..53329b7 100644 --- a/labfis/main.py +++ b/labfis/main.py @@ -1,4 +1,4 @@ -from math import floor, ceil, trunc, log, log10 +from math import floor, ceil, trunc, log, log10, sqrt from numbers import Number class LabFloatError(Exception): @@ -26,12 +26,12 @@ def __init__(self, *args, **kwargs): if args: if len(args) == 1: - mean = args[0] + mean = args[0] elif len(args) == 2: mean = args[0] uncertainty = args[1] else: - raise LabFloatError("Too many arguments, expected (val,err), got: ",args) + raise LabFloatError("Too many arguments, expected (val,err) or ([(val,err),...]), got: ",args) self.mean = float(mean) self.uncertainty = abs(float(uncertainty)) @@ -77,29 +77,48 @@ def split(self): else: m, u = self.format() return(["{:g}".format(m),"{:g}".format(u)]) - - def tex(self): - val = self.split() - if len(val) == 1: - m = val[0].split("e") + + def tex(self,*args,**kwargs): + precision = kwargs.get('precision') + if args: + if len(args) == 2: + precision = args + elif len(args) > 2: + raise LabFloatError("Too many arguments, expected: (precision) or (mean precision,err precision) got: ",args) + else: + precision = [args[0],args[0]] + + if self.uncertainty == 0: + if precision: + precision[0] = str(precision[0]) + m = eval("'{:."+precision[0]+"e}'.format(self.mean)") + else: + m = self.split()[0] + m = m.split("e") if len(m) > 1: - m = m[0]+"\cdot 10^{"+m[1]+"}" + m = m[0]+r"\cdot 10^{"+m[1]+"}" else: m = m[0] return("{0}".format(m)) else: - m,u = val + if precision: + precision = (str(precision[0]),str(precision[1])) + m, u = self.format() + m = eval("'{:."+precision[0]+"e}'.format(m)") + u = eval("'{:."+precision[1]+"e}'.format(u)") + else: + m, u = self.split() m = m.split("e") u = u.split("e") if len(m) > 1: - m = m[0]+"\cdot 10^{"+m[1]+"}" + m = m[0]+r"\cdot 10^{"+m[1]+"}" else: m = m[0] if len(u) > 1: - u = u[0]+"\cdot 10^{"+u[1]+"}" + u = u[0]+r"\cdot 10^{"+u[1]+"}" else: u = u[0] - return("({0}\, \pm \,{1})".format(m, u)) + return(r"({0}\, \pm \,{1})".format(m, u)) def __str__(self): val = self.split() @@ -110,6 +129,10 @@ def __str__(self): def __repr__(self): return self.__str__() + + def __getitem__(self, idx): + vals = [self.mean,self.uncertainty] + return vals[idx] def __pos__(self): return self @@ -170,7 +193,7 @@ def __ge__(self,other): def __add__(self, other): if isinstance(other, labfloat): - return labfloat(self.mean + other.mean, self.uncertainty + other.uncertainty) + return labfloat(self.mean + other.mean, sqrt(self.uncertainty ** 2 + other.uncertainty ** 2)) if isinstance(other, Number): return labfloat(self.mean + other, self.uncertainty) @@ -182,13 +205,13 @@ def __iadd__(self, other): def __sub__(self, other): if isinstance(other, labfloat): - return labfloat(self.mean - other.mean, self.uncertainty + other.uncertainty) + return labfloat(self.mean - other.mean, sqrt(self.uncertainty ** 2 + other.uncertainty ** 2)) if isinstance(other, Number): return labfloat(self.mean - other, self.uncertainty) def __rsub__(self, other): if isinstance(other, labfloat): - pass + return labfloat(other.mean - self.mean, sqrt(other.uncertainty ** 2 + self.uncertainty ** 2)) if isinstance(other, Number): return labfloat(other - self.mean, self.uncertainty) @@ -197,9 +220,9 @@ def __isub__(self, other): def __mul__(self, other): if isinstance(other, labfloat): - return labfloat(self.mean * other.mean, self.mean * other.uncertainty + other.mean * self.uncertainty) + return labfloat(self.mean * other.mean, sqrt((other.mean * self.uncertainty) ** 2 + (self.mean * other.uncertainty) ** 2)) if isinstance(other, Number): - return labfloat(self.mean * other, other * self.uncertainty) + return labfloat(self.mean * other, abs(other * self.uncertainty)) def __rmul__(self, other): return self.__mul__(other) @@ -209,9 +232,9 @@ def __imul__(self, other): def __div__(self, other): if isinstance(other, labfloat): - return labfloat(self.mean / other.mean, (self.mean * other.uncertainty + other.mean * self.uncertainty)/other.mean**2) + return labfloat(self.mean / other.mean, sqrt((self.uncertainty / other.mean) ** 2 + (self.mean * other.uncertainty / (other.mean ** 2)) ** 2 )) if isinstance(other, Number): - return labfloat(self.mean / other, self.uncertainty / other) + return labfloat(self.mean / other, abs(self.uncertainty / other)) def __truediv__(self, other): return self.__div__(other) @@ -224,24 +247,24 @@ def __itruediv__(self, other): def __rdiv__(self, other): if isinstance(other, labfloat): - return labfloat(other.mean / self.mean, (other.mean * self.uncertainty + self.mean * other.uncertainty)/self.mean**2) + return labfloat(other.mean / self.mean, sqrt((other.uncertainty / self.mean) ** 2 + (other.mean * self.uncertainty / (self.mean ** 2)) ** 2 )) if isinstance(other, Number): - return labfloat(other / self.mean, other * self.uncertainty / self.mean ** 2) + return labfloat(other / self.mean, abs(other * self.uncertainty / self.mean ** 2)) def __rtruediv__(self, other): return self.__rdiv__(other) def __pow__(self, other): if isinstance(other, labfloat): - raise LabFloatError(0) + return labfloat(self.mean ** other.mean, sqrt((other.mean * self.mean ** (other.mean - 1) * self.uncertainty) ** 2 + (self.mean ** other.mean * log(abs(self.mean)) * other.uncertainty) ** 2)) if isinstance(other, Number): - return labfloat(self.mean ** other, other * self.mean ** (other-1) * self.uncertainty) + return labfloat(self.mean ** other, abs(other * self.mean ** (other - 1) * self.uncertainty)) def __rpow__(self, other): if isinstance(other, labfloat): - raise LabFloatError(0) - if isinstance(other, (float, int, hex, oct, complex)): - return labfloat(other ** self.mean, other ** self.mean * log(other) * self.uncertainty) + return labfloat(other.mean ** self.mean, sqrt((self.mean * other.mean ** (self.mean - 1) * other.uncertainty) ** 2 + (other.mean ** self.mean * log(abs(other.mean)) * self.uncertainty) ** 2)) + if isinstance(other, Number): + return labfloat(other ** self.mean, abs(other ** self.mean * log(abs(other)) * self.uncertainty)) def __ipow__(self, other): return self.__pow__(other)