scinum¶
This page contains only API docs. For more info, visit scinum on GitHub or open the example.ipynb notebook on binder:
Contents
Scientific numbers with multiple uncertainties and correlationaware, gaussian propagation and numpy support.
Classes¶
Number
¶

class
Number
(nominal=0.0, uncertainties={})¶ Implementation of a scientific number, i.e., a nominal value with named uncertainties. uncertainties mist be a dict or convertable to a dict with strings as keys. If a value is an int or float, it is interpreted as an absolute, symmetric uncertainty. If it is a tuple, it is interpreted in different ways. Examples:
from scinum import Number, REL, ABS, UP, DOWN num = Number(2.5, { "sourceA": 0.5, # absolute 0.5, both up and down "sourceB": (1.0, 1.5), # absolute 1.0 up, 1.5 down "sourceC": (REL, 0.1), # relative 10%, both up and down "sourceD": (REL, 0.1, 0.2), # relative 10% up, 20% down "sourceE": (1.0, REL, 0.2), # absolute 1.0 up, relative 20% down "sourceF": (REL, 0.3, ABS, 0.3) # relative 30% up, absolute 0.3 down }) # get the nominal value via direct access num.nominal # => 2.5 # get the nominal value via __call__() (same as get()) num() # => 2.5 num(direction="nominal") # => 2.5 # get uncertainties num.get_uncertainty("sourceA") # => (0.5, 0.5) num.get_uncertainty("sourceB") # => (1.0, 1.5) num.get_uncertainty("sourceC") # => (0.25, 0.25) num.get_uncertainty("sourceD") # => (0.25, 0.5) num.get_uncertainty("sourceE") # => (1.0, 0.5) num.get_uncertainty("sourceF") # => (0.75, 0.3) # get shifted values via __call__() (same as get()) num(UP, "sourceA") # => 3.0 num(DOWN, "sourceB") # => 1.0 num(UP, ("sourceC", "sourceD")) # => 2.854... num(UP) # => 4.214... (all uncertainties) # get only the uncertainty (unsigned) num(DOWN, ("sourceE", "sourceF"), unc=True) # => 0.583... # get the uncertainty factor (unsigned) num(DOWN, ("sourceE", "sourceF"), factor=True) # => 1.233... # combined num(DOWN, ("sourceE", "sourceF"), unc=True, factor=True) # => 0.233...
When uncertainties is not a dictionary, it is interpreted as the default uncertainty, named
Number.DEFAULT
.This class redefines most of Python’s magic functions to allow transparent use in standard operations like
+
,*
, etc. Gaussian uncertainty propagation is applied automatically. When operations connect two number instances, their uncertainties are combined assuming there is no correlation. For correlationaware operations, please refer to methods such asadd()
ormul()
below. Examples:num = Number(5, 1) print(num + 2) # > '7.0 + 1.0' print(num * 3) # > '15.0 + 3.0' num2 = Number(2.5, 1.5) print(num + num2) # > '7.5 + 1.80277563773' print(num * num2) # > '12.5 + 7.90569415042' num.add(num2, rho=1) print(num) # > '7.5 + 2.5'
See
str()
for information on string formatting.
classattribute
default_format
¶ The default format string (
"%s"
) that is used instr()
when no format string was passed.

classattribute
DEFAULT
¶ Constant that denotes the default uncertainty (
"default"
).

classattribute
ALL
¶ Constant that denotes all uncertainties (
"all"
).

classattribute
REL
¶ Constant that denotes relative errors (
"rel"
).

classattribute
ABS
¶ Constant that denotes absolute errors (
"abs"
).

classattribute
NOMINAL
¶ Constant that denotes the nominal value (
"nominal"
).

classattribute
UP
¶ Constant that denotes the up direction (
"up"
).

classattribute
DOWN
¶ Constant that denotes the down direction (
"down"
).

nominal
¶ 
type: float
The nominal value.

uncertainties
¶ 
type: dictionary
The uncertainty dictionary that maps names to 2tuples holding absolute up/down effects.

is_numpy
¶ 
type: bool

readonly
Whether or not a NumPy array is wrapped.

shape
¶ 
type: tuple
The shape of the wrapped NumPy array or None, depending on what type is wrapped.

dtype
¶ 
type: type
The default dtype to use when a NumPy array is wrapped. The initial value is
numpy.float32
when NumPy is available, None otherwise.

get_uncertainty
(name='default', direction=None, default=None)¶ Returns the absolute up and down variaton in a 2tuple for an uncertainty name. When direction is set, the particular value is returned instead of a 2tuple. In case no uncertainty was found and default is not None, that value is returned.

u
(*args, **kwargs)¶ Shorthand for
get_uncertainty()
.

set_uncertainty
(name, value)¶ Sets the uncertainty value for an uncertainty name. value should have one of the formats as described in
uncertainties()
.

clear
(nominal=None, uncertainties=None)¶ Removes all uncertainties and sets the nominal value to zero (float). When nominal and uncertainties are given, these new values are set on this instance.

str
(format=None, unit=None, scientific=False, si=False, labels=True, style='plain', styles=None, force_asymmetric=False, **kwargs)¶ Returns a readable string representiation of the number. format is used to format nonNumPy nominal and uncertainty values. It can be a string such as
"%d"
, a function that is called with the value to format, or a rounding function as accepted byround_value()
. When None (the default),default_format
is used. All keyword arguments except wildcard kwargs are only used to format nonNumPy values. In case of NumPy objects, kwargs are passed to numpy.array2string.When unit is set, it is appended to the end of the string. When scientific is True, all values are represented by their scientific notation. When scientific is False and si is True, the appropriate SI prefix is used. labels controls whether uncertainty labels are shown in the string. When True, uncertainty names are used, but it can also be a list of labels whose order should match the uncertainty dict traversal order. style can be
"plain"
,"latex"
, or"root"
. styles can be a dict with fields"space"
,"label"
,"unit"
,"sym"
,"asym"
,"sci"
to customize every aspect of the format style on top ofstyle_dict
. Unless force_asymmetric is True, an uncertainty is quoted symmetric if it yields identical values in both directions.Examples:
n = Number(17.321, {"a": 1.158, "b": 0.453}) n.str() # > '17.321 + 1.158 (a) + 0.453 (b)' n.str("%.1f") # > '17.3 + 1.2 (a) + 0.5 (b)' n.str("publication") # > '17.32 + 1.16 (a) + 0.45 (b)' n.str("pdg") # > '17.3 + 1.2 (a) + 0.5 (b)' n = Number(8848, 10) n.str(unit="m") # > "8848.0 + 10.0 m" n.str(unit="m", force_asymmetric=True) # > "8848.0 +10.010.0 m" n.str(unit="m", scientific=True) # > "8.848 + 0.01 x 1E3 m" n.str("%.2f", unit="m", scientific=True) # > "8.85 + 0.01 x 1E3 m" n.str(unit="m", si=True) # > "8.848 + 0.01 km" n.str("%.2f", unit="m", si=True) # > "8.85 + 0.01 km" n.str(unit="m", style="latex") # > "8848.0 \pm 10.0\,m" n.str(unit="m", style="latex", si=True) # > "8.848 \pm 0.01\,km" n.str(unit="m", style="root") # > "8848.0 #pm 10.0 m" n.str(unit="m", style="root", si=True) # > "8.848 #pm 0.01 km"

repr
(*args, **kwargs)¶ Returns the unique string representation of the number.

copy
(nominal=None, uncertainties=None)¶ Returns a deep copy of the number instance. When nominal or uncertainties are set, they overwrite the fields of the copied instance.

get
(direction=NOMINAL, names=ALL, unc=False, factor=False)¶ Returns different representations of the contained value(s). direction should be any of NOMINAL, UP or DOWN. When not NOMINAL, names decides which uncertainties to take into account for the combination. When unc is True, only the unsigned, combined uncertainty is returned. When False, the nominal value plus or minus the uncertainty is returned. When factor is True, the ratio w.r.t. the nominal value is returned.

add
(other, rho=1.0, inplace=True)¶ Adds an other
Number
orDeferredResult
instance, propagating all uncertainties. Uncertainties with the same name are combined with the correlation rho, which can either be aCorrelation
instance, a dict with correlations defined per uncertainty, or a plain float. When inplace is False, a new instance is returned.

sub
(other, rho=1.0, inplace=True)¶ Subtracts an other
Number
orDeferredResult
instance, propagating all uncertainties. Uncertainties with the same name are combined with the correlation rho, which can either be aCorrelation
instance, a dict with correlations defined per uncertainty, or a plain float. When inplace is False, a new instance is returned.

mul
(other, rho=1.0, inplace=True)¶ Multiplies by an other
Number
orDeferredResult
instance, propagating all uncertainties. Uncertainties with the same name are combined with the correlation rho, which can either be aCorrelation
instance, a dict with correlations defined per uncertainty, or a plain float. When inplace is False, a new instance is returned.Unlike the other operations, other can also be a
Correlation
instance, in which case aDeferredResult
is returned to resolve the combination of uncertainties later on.

div
(other, rho=1.0, inplace=True)¶ Divides by an other
Number
orDeferredResult
instance, propagating all uncertainties. Uncertainties with the same name are combined with the correlation rho, which can either be aCorrelation
instance, a dict with correlations defined per uncertainty, or a plain float. When inplace is False, a new instance is returned.

pow
(other, rho=1.0, inplace=True)¶ Raises by the power of an other
Number
orDeferredResult
instance, propagating all uncertainties. Uncertainties with the same name are combined with the correlation rho, which can either be aCorrelation
instance, a dict with correlations defined per uncertainty, or a plain float. When inplace is False, a new instance is returned.

classattribute
Correlation
¶

class
Correlation
([default, ]**rhos)¶ Container class describing correlations to be applied to equally named uncertainties when combining two
Number
instances through an operator.A correlation object is therefore applied to a number by means of multiplication or matrix multiplication (i.e.
*
or@
), resulting in aDeferredResult
object which is used subsequently by the actual combination operation with an other number. SeeDeferredResult
for more examples.Correlation coefficients can be defined per named source of uncertainty via rhos. When a coefficient is retrieved (by
get()
) with a name that was not defined before, a default value is used, which itself defaults to one.
get
(name, default=None)¶ Returns a correlation coefficient rho named name. When no coefficient with that name exists and default is set, which itself defaults to
default
, this value is returned instead. Otherwise, a KeyError is raised.

DeferredResult
¶

class
DeferredResult
(number, correlation)¶ Class that wraps a
Number
instance number and aCorrelation
instance correlation that is automatically produced as a result of a multiplication or matrix multiplication between the two. Internally, this is used for the deferred resolution of uncertainty correlations when combined with an otherNumber
. Example:n = Number(2, 5) n * Correlation(1) * n # > '25.0 + 20.0' (the default) n * Correlation(0) * n # > '25.00 + 14.14' # note the multiplication n * c, which creates the DeferredResult n**(n * c) # > '3125.00 + 11842.54'

number
¶ 
type: Number
The wrapped number object.

correlation
¶ 
type: Correlation
The wrapped correlation object.

ops
¶

class
ops
¶ Numberaware replacement for the global math (or numpy) module. The purpose of the class is to provide operations (e.g. pow, cos, sin, etc.) that automatically propagate the uncertainties of a
Number
instance through the derivative of the operation. Example:num = ops.pow(Number(5, 1), 2) print(num) # > 25.00 (+10.00, 10.00)

classmethod
register
(function=None, name=None, py_op=None, ufuncs=None)¶ Registers a new math function function with name and returns an
Operation
instance. A math function expects aNumber
as its first argument, followed by optional (keyword) arguments. When name is None, the name of the function is used. The returned object can be used to set the derivative (similar to property). Example:@ops.register def my_op(x): return x * 2 + 1 @my_op.derive def my_op(x): return 2 num = Number(5, 2) print(num) # > 5.00 (+2.00, 2.00) num = ops.my_op(num) print(num) # > 11.00 (+4.00, 4.00)
Please note that there is no need to register simple functions as in the particular example above as most of them are just composite operations whose derivatives are already known.
When the registered operation is a member of
operators
and thus capable of propagating uncertainties with two operands, py_op should be set to the symbol of the operation (e.g."*"
, see_py_ops
).To comply with NumPy’s ufuncs (https://numpy.org/neps/nep0013ufuncoverrides.html) that are dispatched by
Number.__array_ufunc__()
, an operation might register the ufuncs objects that it handles. When strings, they are interpreted as a name of a NumPy function.

classmethod
get_operation
(name)¶ Returns an operation that was previously registered with name.

classmethod
op
(*args, **kwargs)¶ Shorthand for
get_operation()
.

classmethod
get_ufunc_operation
(ufunc)¶ Returns an operation that was previously registered to handle a NumPy ufunc, which can be a string or the function itself. None is returned when no operation was found to handle the function.

classmethod
rebuilt_ufunc_cache
()¶ Rebuilts the internal cache of ufuncs.

classmethod
Operation
¶

class
Operation
(function, derivative=None, name=None, py_op=None, ufuncs=None)¶ Wrapper around a function and its derivative.

function
¶ 
type: function
The wrapped function.

derivative
¶ 
type: function
The wrapped derivative.

name
¶ 
type: string

readonly
The name of the operation.

py_op
¶ 
type: None, string

readonly
The symbol referring to an operation that implements uncertainty propagation combining two operands.

ufuncs
¶ 
type: list

readonly
List of ufunc objects that this operation handles.

typed
¶

class
typed
(fparse=None, setter=True, deleter=True, name=None)¶ Shorthand for the most common property definition. Can be used as a decorator to wrap around a single function. Example:
class MyClass(object): def __init__(self): self._foo = None @typed def foo(self, foo): if not isinstance(foo, str): raise TypeError("not a string: {}".format(foo)) return foo myInstance = MyClass() myInstance.foo = 123 # > TypeError myInstance.foo = "bar" # > ok print(myInstance.foo) # > prints "bar"
In the exampe above, set/get calls target the instance member
_foo
, i.e. “_<function_name>”. The member name can be configured by setting name. If setter (deleter) is True (the default), a setter (deleter) method is booked as well. Prior to updating the member when the setter is called, fparse is invoked which may implement sanity checks.
Functions¶
combine_uncertainties
¶

combine_uncertainties
(op, unc1, unc2, nom1=None, nom2=None, rho=0.0)¶ Combines two uncertainties unc1 and unc2 according to an operator op which must be either
"+"
,""
,"*"
,"/"
, or"**"
. The three latter operators require that you also pass the nominal values nom1 and nom2, respectively. The correlation can be configured via rho.
calculate_uncertainty
¶

calculate_uncertainty
(terms, rho=0.0)¶ Generically calculates the uncertainty of a quantity that depends on multiple terms. Each term is expected to be a 2tuple containing the derivative and the uncertainty of the term. Correlations can be defined via rho. When rho is a numner, all correlations are set to this value. It can also be a mapping of a 2tuple, the two indices of the terms to describe, to their correlation coefficient. In case the indices of two terms are not included in this mapping, they are assumed to be uncorrelated. Example:
calculate_uncertainty([(3, 0.5), (4, 0.5)]) # uncorrelated # > 2.5 calculate_uncertainty([(3, 0.5), (4, 0.5)], rho=1) # fully correlated # > 3.5 calculate_uncertainty([(3, 0.5), (4, 0.5)], rho={(0, 1): 1}) # fully correlated # > 3.5 calculate_uncertainty([(3, 0.5), (4, 0.5)], rho={(1, 2): 1}) # no rho value defined for pair (0, 1), assumes zero correlation # > 2.5
ensure_number
¶
ensure_nominal
¶
is_numpy
¶

is_numpy
(x)¶ Returns True when numpy is available on your system and x is a numpy type.
is_ufloat
¶

is_ufloat
(x)¶ Returns True when the “uncertainties” package is available on your system and x is a
ufloat
.
parse_ufloat
¶

parse_ufloat
(x, default_tag='default')¶ Takes a
ufloat
object x from the “uncertainties” package and returns a tuple with two elements containing its nominal value and a dictionary with its uncertainties. When the error components of x contain multiple uncertainties with the same name, they are combined under the assumption of full correlation. When an error component is not tagged, default_tag is used.
infer_math
¶

infer_math
(x)¶ Returns the numpy module when
is_numpy()
for x is True, and the math module otherwise.
make_list
¶

make_list
(obj, cast=True)¶ Converts an object obj to a list and returns it. Objects of types tuple and set are converted if cast is True. Otherwise, and for all other types, obj is put in a new list.
split_value
¶

split_value
(val)¶ Splits a value val into its significand and decimal exponent (magnitude) and returns them in a 2tuple. val might also be a numpy array. Example:
split_value(1) # > (1.0, 0) split_value(0.123) # > (1.23, 1) split_value(42.5) # > (4.25, 1) a = np.array([1, 0.123, 42.5]) split_value(a) # > ([1.0, 1.23, 4.25], [0, 1, 1])
The significand will be a float while magnitude will be an integer. val can be reconstructed via
significand * 10**magnitude
.
match_precision
¶

match_precision
(val, ref, force_float=False, **kwargs)¶ Returns a string version of a value val matching the significant digits as given in ref. val might also be a numpy array. Unless force_float is True, the returned string might represent an integer in case the decimal digits are removed. All remaining kwargs are forwarded to
Decimal.quantize
. Example:match_precision(1.234, "0.1") # > "1.2" match_precision(1.234, "1.0") # > "1" match_precision(1.234, "0.1", decimal.ROUND_UP) # > "1.3" a = np.array([1.234, 5.678, 9.101]) match_precision(a, "0.1") # > ["1.2", "5.7", "9.1"]
round_uncertainty
¶

round_uncertainty
(unc, method=1, precision=None, **kwargs)¶ Rounds an uncertainty unc following a specific method and returns a 3tuple containing the significant digits as a string, the decimal magnitude that is required to recover the uncertainty, and the precision (== number of significant digits). unc might also be a numpy array. Possible values for the rounding method are:
"pdg"
: Rounding rules as defined by the PDG."pdg+1"
: Same rules as for"pdg"
with an additional significant digit."publication"
,"pub"
: Same rules as for``”pdg+1”`` but without the rounding of the first three significant digits above 949 to 1000.positive integer: Enforces a fixed number of significant digits.
By default, the target precision is derived from the rounding method itself. However, a value can be defined to enfore a certain number of significant digits after the rounding took place. This is only useful for methods that include fixed rounding thresholds (
"pdg"
). All remaining kwargs are forwarded tomatch_precision()
which is performing the rounding internally.Example:
round_uncertainty(0.123, 1) # > ("1", 1, 1) round_uncertainty(0.123, "pub") # > ("123", 3, 3) round_uncertainty(0.123, "pdg") # > ("12", 2, 2) round_uncertainty(0.456, 1) # > ("5", 1, 1) round_uncertainty(0.456, "pub") # > ("46", 2, 2) round_uncertainty(0.456, "pdg") # > ("5", 1, 1) round_uncertainty(9.87, 1) # > ("1", 1, 1) round_uncertainty(9.87, "pub") # > ("99", 1, 2) round_uncertainty(9.87, "pdg") # > ("10", 0, 2) # enfore higher precision round_uncertainty(0.987, "pub", precision=3) # > ("990", 3, 3) round_uncertainty(0.987, "pdg", precision=3) # > ("100", 2, 3) # numpy array support a = np.array([0.123, 0.456, 0.987]) round_uncertainty(a, "pub") # > (["123", "46", "987"], [3, 2, 3])
round_value
¶

round_value
(val, unc=None, method=0, align_precision=True, **kwargs)¶ Rounds a number val with an uncertainty unc which can be a single float or array (symmetric) or a 2tuple (asymmetric up / down) of floats or arrays. It also supports a list of these values for simultaneous evaluation. When val is a
Number
instance, its uncertainties are used in their default iteration order. Returns a 3tuple containing:The string representation of the central value.
The string representation(s) of uncertainties. The structure is identical to the one passed on unc.
The decimal magnitude.
method controls the behavior of the rounding:
When
"pdg"
,"pdg+1"
,"publication"
, or"pub"
, uncertainties are required and internallyround_uncertainty()
is used to infer the precision based on the smallest uncertainty.When a formatting string is passed, it should have the (default) pattern
"%*.<N>f"
, and N is interpreted as the number of digits after the decimal point.When a negative integer or zero (the default) is passed, the value is interpreted as the number of digits after the decimal point (similar to passing a format string).
When a positive number is passed, it is interpreted as the amount of significant digits to keep, evaluated on the smallest number among either the nominal or uncertainty values.
In case multiple uncertainties are given and the rounding method is uncertaintybased (1. above), the precision is derived based on the smallest uncertainty as a reference and then enforced to the nominal value and all other uncertainties when align_precision is True. Otherwise, values are allowed to have different precisions. All remaining kwargs are forwarded to
match_precision()
which is performing the rounding internally.Examples:
# differnt uncertainty structures round_value(1.23, 0.456, 1) # > ("12", "5", 1) round_value(1.23, [0.456], 1) # > ("12", ["5"], 1) round_value(1.23, (0.456, 0.987), 1) # > ("12", ("5", "10"), 1) round_value(1.23, [(0.456, 0.987)], 1) # > ("12", [("5", "10")], 1) round_value(1.23, [0.456, 0.987], 1) # > ("12", ["5", "10"], 1) # different rounding methods round_value(125.09, (0.56, 0.97)) # > ("125", ("1", "1"), 0) round_value(125.09, (0.56, 0.97), "pub") # > ("12509", ("56", "97"), 2) round_value(125.09, (0.56, 0.97), "%.2f") # > ("12509", ("56", "97"), 2) round_value(125.09, (0.56, 0.97), 2) # > ("12509", ("56", "97"), 2) round_value(125.09, (0.56, 0.97), 3) # > ("125090", ("560", "970"), 3) # without uncertainties round_value(125.09, method=2) # > ("13", None, 1) round_value(125.09, method=2) # > ("12509", None, 2) round_value(125.09, method="%.2f") # > ("12509", None, 2) round_value(125.09, method="pdg") # > Exception, "pdg" is uncertainty based # array support vals = np.array([1.23, 4.56]) uncs = np.array([0.45678, 0.078]) round_value(vals, uncs, 2) # > (["123", "4560"], ["46", "78"], [2, 3])
infer_si_prefix
¶

infer_si_prefix
(f)¶ Infers the SI prefix of a value f and returns the string label and decimal magnitude in a 2tuple. Example:
infer_si_prefix(1) # > ("", 0) infer_si_prefix(25) # > ("", 0) infer_si_prefix(4320) # > ("k", 3)
create_hep_data_representer
¶

create_hep_data_representer
(method=None, force_asymmetric=False, force_float=False, **kwargs)¶ Creates a PyYAML representer function that encodes a
Number
as a data structure that is compatible to the HEPData format for values in data files.import yaml import scinum as sn yaml.add_representer(sn.Number, sn.create_hep_data_representer())
For documentation of the rounding method, see
round_uncertainty()
. When None, the default_format of the number instance is used in case it is not a python format string. Otherwise"pdg+1"
is assumed. When the up and down variations of an uncertainty are identical after rounding, they are encoded as a symmetric uncertainty unless force_asymmetric is True. Also, when all decimal digits are removed during rounding, the final value is encoded as an integer value unless force_float is True.All remaining kwargs are forwarded to
match_precision()
which is performing the rounding internally.
Other attributes¶

style_dict
¶ 
type: dict
Dictionaly containing formatting styles for
"plain"
,"latex"
and"root"
styles which are used inNumber.str()
. Each style dictionary contains 6 fields:"space"
,"label"
,"unit"
,"sym"
,"asym"
, and"sci"
. As an example, the plain style is configured as{ "space": " ", "label": "({label})", "unit": " {unit}", "sym": "+ {unc}", "asym": "+{up}{down}", "sci": "x 1E{mag}", }

HAS_NUMPY
¶ 
type: bool
A flag that is True when NumPy is available on your system, False otherwise.

HAS_UNCERTAINTIES
¶ 
type: bool
A flag that is True when the uncertainties package is available on your system, False otherwise.

HAS_YAML
¶ 
type: bool
A flag that is True when PyYAML is available on your system, False otherwise.

NOMINAL
¶ 
type: string
Shorthand for
Number.NOMINAL
.

DOWN
¶ 
type: string
Shorthand for
Number.DOWN
.

REL
¶ 
type: string
Shorthand for
Number.REL
.

ABS
¶ 
type: string
Shorthand for
Number.ABS
.