def add(self, data_tuple): """ Adds a data-row to table :param data_tuple: Tuple[Optional[int, float, Const, Data]] = data for each column of table :return: void """ type_tuple = type(data_tuple) if type_tuple == list: data_tuple = tuple(data_tuple) elif type_tuple != tuple: raise ValueError(f"Expected a tuple, get {type_tuple} instead") if len(data_tuple) == self.columns: for index, data in enumerate(data_tuple): if isinstance(data, (Data, Const)): if data.unit != self.units[index] and data.unit != Unit( ""): raise Exception( f"Can´t fill column of {self.units[index]} with {data.unit}" ) elif data.unit == Unit(""): data_tuple[index].unit = self.units[index] else: data_tuple[index].unit = Unit() self.datas.append(data_tuple) else: raise ValueError( f"Expected an tuple of {self.columns}, get tuple of {len(data_tuple)} instead" )
def __init__(self, column_names=[], columns=2, signs=[]): """ Initialize a Table. :param column_names: List[str] = names of the columns :param columns: int = number of columns :param signs: List[Union[Unit, str]] = sign or unit of each column """ self.datas = [] self.columns = columns self.column_names = [] self.units = [] # create units if not signs: self.units = [Unit("") for i in range(self.columns)] else: for sign in signs: if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] unit = Unit(sign[0], sign[1]) else: unit = Unit(sign[0]) else: unit = sign self.units.append(unit) # create column names if not column_names: self.column_names = [f"Column {i}" for i in range(self.columns)] else: self.column_names = column_names
def calc_unit(self, value_dict): """ Determine the unit of the result will have. :param value_dict: Dict[str: Union[int, float, Data, Const]] = Dict of the variables for the formula :return: Unit = Unit of the result of the formula """ type_dict = {key: type(value_dict[key]) for key in value_dict} dummy = { "unit": self.__create_formula(type_dict), "cur_unit": 1, "Symbol": Symbol, "sympy": sympy } exec("from sympy.functions import *", dummy) for key in value_dict: if not (type_dict[key] in [int, float]): numerator = value_dict[key].unit.numerator denominator = value_dict[key].unit.denominator for unit in numerator.keys(): exec(f"{unit} = Symbol('{unit}')", dummy) exec(f"cur_unit *= {unit} ** {numerator[unit]}", dummy) for unit in denominator.keys(): exec(f"{unit} = Symbol('{unit}')", dummy) exec(f"cur_unit /= ({unit} ** {denominator[unit]})", dummy) exec(f"{key} = Symbol('{key}')", dummy) exec(f"unit = unit.subs({key},cur_unit)", dummy) dummy["cur_unit"] = 1 units = self.__get_units(sympy.nsimplify(dummy["unit"])) if not units: return Unit("") numerator = {} denominator = {} for unit, power in units: if power > 0: numerator[unit] = power else: denominator[unit] = -1 * power unit = Unit() unit.denominator = denominator unit.numerator = numerator return unit
def modus(self): """ calculate modus of each column. modus: most common value in column :return: List[Optional[float, Const]] """ modes = [] for i in range(self.columns): column_data = [ data[i].value if isinstance(data[i], (Data, Const)) else data[i] for data in self.datas ] mode = column_data[0] counter = column_data.count(mode) for i2 in range(counter): column_data.remove(mode) while len(column_data) > 0: data = column_data[0] new_counter = column_data.count(data) if new_counter > counter: mode = data counter = new_counter for i2 in range(new_counter): column_data.remove(data) if self.units[i] != Unit(): mode = Const(value=mode, sign=self.units[i]) modes.append(mode) return modes
def __add__(self, other): """ Addition with other Const. :param other: Union[Const, Data, int, float] = other object to add with :return: Union[Const, Data, int, float] = result of the subtraction """ if isinstance(other, Const): if self.unit == other.unit: return Const(self.value + other.value, sign=self.unit) else: raise ArithmeticError( "Addition of Data with different units is not possible") elif isinstance(other, Data): if self.unit == other.unit: return Data(str(self.value + other.value), str(other.error), n=other.n, sign=self.unit) else: raise ArithmeticError( "Addition of Data and Const with different units is not possible" ) elif isinstance(other, (int, float)): if self.unit == Unit(): return self.value + other else: raise ArithmeticError( "Addition of values with different units is not possible") else: raise TypeError( f"unsupported operand '+' for Const and {type(other)}")
def test_truediv(self): self.assertEqual(str(a / b), "m/s") self.assertEqual(str(a / a), "") self.assertEqual(str(c / a), "m") self.assertEqual(str(f / f), "") self.assertEqual(str(c / a), "m") self.assertEqual(str(d / a), "s") self.assertEqual(e / f, Unit("kg;s^2", "N"))
def __number_sub(self, other): """ Helper function for subtraction of a Data and an Union[int, float]. :param other: Union[int, float] = value to subtract :return: Data = result of the subtraction """ if self.unit == Unit(""): data = Data(str(self.value - other), str(self.error), n=self.n) data.power = self.power return data
def __init__(self, value: str, error: str, sign: Union[str, Unit] = "", power: int = 0, n: int = 0): """ Initiate new value with uncertainty :param value: str = value :param error: str = uncertainty :param sign: Union[str, Unit] = unit of the value **sign-EBNF:** S := '"' units '"' | '"' units '/' units '"' units := unit | unit ';' units unit := string | string '^' integer :param power: int = power of the value (for dimensions like mV => power = -3 and unit = 'V') :param n: int = significant digits of the error (if 0 (Default): get digits from error: str) """ type_check((value, str), (error, str)) if n == 0: self.n = digits(error) else: self.n = n if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] self.unit = Unit(sign[0], sign[1]) else: self.unit = Unit(sign[0]) else: self.unit = sign self.power = power self.error = float(error) * 10**self.power self.value = float(value) * 10**self.power round_data(self)
def __init__(self, value, sign): """ Initalize a constant with a unit. :param value: Union[int, float] = constant value :param sign: Union[str, Unit] = String carrying the unit. **sign-EBNF:** S := '"' units '"' | '"' units '/' units '"' units := unit | unit ';' units unit := string | string '^' integer """ if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] self.unit = Unit(sign[0], sign[1]) else: self.unit = Unit(sign[0]) else: self.unit = sign self.value = float(value)
def insert(self, line, column, value): """ insert a value at line, column in table :param line: int; line in the table :param column: int; column in the table :param value: Optional[float, int, Data, Const]; new value for slot :return: void """ if isinstance(value, Data): value = Data(value=str(value.value), error=str(value.error), sign=Unit(""), n=value.n, power=value.power) elif isinstance(value, Const): value = Const(value=value.value, sign=Unit("")) data_l = self.datas[line][:] data_l = list(data_l) data_l[column] = value data_l = tuple(data_l) self.datas[line] = data_l return True
def add_column(self, name="", sign=""): """ Adds a column to table. :param name: str = name of the new table :param unit: Union[str, Unit] = unit of the new column :return: void """ if name == "": self.column_names.append(f"Column {self.columns}") else: self.column_names.append(name) self.columns += 1 if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] unit = Unit(sign[0], sign[1]) else: unit = Unit(sign[0]) self.units.append(unit) elif isinstance(sign, Unit): self.units.append(sign) else: raise TypeError( f"Expected unit or string got {type(sign)} instead") for index in range(len(self.datas)): element = self.datas[index][:] element = list(element) element += [""] element = tuple(element) self.datas[index] = element return True
def drop(self, line_index): """ Return and delete data-row at line_index :param line_index: int = index for row to delete :return: Tuple[Optional[int, float, Const, Data]] = row to drop """ results = self.datas.pop(line_index) new_results = [] for index, result in enumerate(results): if self.units[index] != Unit(): if isinstance(result, (Data, Const)): result.unit = self.units[index] new_results.append(result) else: new_result = Const(value=result, sign=self.units[index]) new_results.append(new_result) else: new_results.append(result) return new_results
def export(self, path, name, replace_dot=False): """ Exports data in .csv format :param path: path to .csv file (.csv in name needed) :param replace_dot: difference between english and german floats "3.11", "3,11" :return: void """ # check if any value in a column has an error (is a Data) data_cols = [ any([isinstance(row[i], Data) for row in self.datas]) for i in range(self.columns) ] with open(path + "/" + name, "w", encoding="UTF-8") as doc: # write table headers doc.write("".join([ f"{h_str};;" if data_col else f"{h_str};" for data_col, h_str in zip(data_cols, [ f"{column_name} [{unit}]" if unit != Unit("") else column_name for column_name, unit in zip(self.column_names, self.units) ]) ]) + "\n") doc.write("".join([ "Bestwert; Fehler;" if data_col else "Bestwert;" for data_col in data_cols ]) + "\n") # write data for row in self.datas: data_string = "".join([ f"{data.value};{data.error};" if data_col else f"{data.value};" if type(data) == Const else f"{data};" for data, data_col in zip(row, data_cols) ]) + "\n" if replace_dot: data_string = data_string.replace(".", ",") doc.write(data_string)
import unittest from pyrror.unit import Unit a = Unit("m") b = Unit("s") c = Unit("m^2") d = Unit("m;s") e = Unit("s;m") f = Unit("N;m", "kg;s") g = Unit() class MyTestCase(unittest.TestCase): def test_truediv(self): self.assertEqual(str(a / b), "m/s") self.assertEqual(str(a / a), "") self.assertEqual(str(c / a), "m") self.assertEqual(str(f / f), "") self.assertEqual(str(c / a), "m") self.assertEqual(str(d / a), "s") self.assertEqual(e / f, Unit("kg;s^2", "N")) def test_mul(self): self.assertEqual((d * a == e * a), True) self.assertEqual((d * a == e), False) def test_eq(self): self.assertEqual((d == e), True) self.assertEqual((a == a), True) self.assertEqual((a == b), False) self.assertEqual((c == a * a), True)
class Const: """ Class for constants and values with units if they carry no uncertainty (or a neglected one). """ def __init__(self, value, sign): """ Initalize a constant with a unit. :param value: Union[int, float] = constant value :param sign: Union[str, Unit] = String carrying the unit. **sign-EBNF:** S := '"' units '"' | '"' units '/' units '"' units := unit | unit ';' units unit := string | string '^' integer """ if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] self.unit = Unit(sign[0], sign[1]) else: self.unit = Unit(sign[0]) else: self.unit = sign self.value = float(value) @instancemethod def __str__(self): """ Creates a string representation of the Const. :return: str = representation of the Const """ string = str(self.value) unit_string = str(self.unit) if unit_string != "": string += " " + unit_string return string @instancemethod def __repr__(self): return self.__str__() @instancemethod def __mul__(self, other): """ Multiplication with other. :param other: Union[Data, Const, int, float] = other object to multiply with :return: Union[Data, Const] = Result type depends on the other object """ if isinstance(other, (int, float)): value = self.value * other unit = self.unit return Const(value, sign=unit) elif isinstance(other, Const): value = self.value * other.value unit = self.unit * other.unit return Const(value, sign=unit) elif isinstance(other, Data): value = self.value * other.value unit = self.unit * other.unit n = other.n error = self.value * other.error return Data(str(value), str(error), n=n, sign=unit) else: raise TypeError( f"unsupported operand '*' for Const and {type(other)}") @instancemethod def __rmul__(self, other): """ Multiplication with other. :param other: Union[Data, Const, int, float] = other object to multiply with :return: Union[Data, Const] = result type depends on the other object """ return self.__mul__(other) @instancemethod def __add__(self, other): """ Addition with other Const. :param other: Union[Const, Data, int, float] = other object to add with :return: Union[Const, Data, int, float] = result of the subtraction """ if isinstance(other, Const): if self.unit == other.unit: return Const(self.value + other.value, sign=self.unit) else: raise ArithmeticError( "Addition of Data with different units is not possible") elif isinstance(other, Data): if self.unit == other.unit: return Data(str(self.value + other.value), str(other.error), n=other.n, sign=self.unit) else: raise ArithmeticError( "Addition of Data and Const with different units is not possible" ) elif isinstance(other, (int, float)): if self.unit == Unit(): return self.value + other else: raise ArithmeticError( "Addition of values with different units is not possible") else: raise TypeError( f"unsupported operand '+' for Const and {type(other)}") @instancemethod def __radd__(self, other): return self.__add__(other) @instancemethod def __sub__(self, other): """ Subtraction with other Const. :param other: Union[Const, Data, int, float] = other object to add with :return: Union[Const, Data] = result of the subtraction """ return self.__add__(-1 * other) @instancemethod def __rsub__(self, other): neg_self = -1 * self return neg_self.__add__(other) @instancemethod def __truediv__(self, other): """ Division with other object. :param other: Union[Data, Const, int, float] = other object to add with :return: Union[Data, Const] = Result type depends on the other object """ if isinstance(other, (int, float)): value = self.value / other unit = self.unit return Const(value, sign=unit) elif isinstance(other, Const): value = self.value / other.value unit = self.unit / other.unit return Const(value, sign=unit) elif isinstance(other, Data): value = self.value / other.value unit = self.unit / other.unit n = other.n error = self.value / other.error return Data(str(value), str(error), n=n, sign=unit) else: raise TypeError( f"unsupported operand '/' for Const and {type(other)}") @instancemethod def __rtruediv__(self, other): """ Division with other object. :param other: Union[int, float] = other object to add with :return: Union[Data, Const] = Result type depends on the other object """ if isinstance(other, (int, float)): result = other / self.value unit = self.unit.flip() return Const(result, unit) else: raise ValueError( f"Unsupported operation '/' for Const and {type(other)}") @instancemethod def __pow__(self, other): """ Power a Const object :param other: Union[int, float] = object to power with :return: Const = result of the calculation """ typ_other = type(other) if typ_other == int or typ_other == float: result = self.value**other unit = self.unit**other return Const(result, unit) elif typ_other == Data: raise ArithmeticError("Try to use a Formula instead!") else: raise TypeError( f"Unsupported operation '/' for Const and {typ_other}") @unit_control def __lt__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value < other.value raise TypeError( f"unsupported operation '<' for Data and {type(other)}") @unit_control def __le__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value <= other.value raise TypeError( f"unsupported operation '<=' for Data and {type(other)}") @unit_control def __eq__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value == other.value raise TypeError( f"unsupported operation '==' for Data and {type(other)}") @unit_control def __ne__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value != other.value raise TypeError( f"unsupported operation '!=' for Data and {type(other)}") @unit_control def __ge__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value >= other.value raise TypeError( f"unsupported operation '>=' for Data and {type(other)}") @unit_control def __gt__(self, other): """ Compare Const with other objects. :param other: Const = object to compare with :return: bool = result of comparison """ if isinstance(other, Const): return self.value > other.value raise TypeError( f"unsupported operation '>' for Data and {type(other)}")
def test_str(self): self.assertEqual(str(Unit("m^2", "m")), "m") self.assertEqual(str(a), "m") self.assertEqual(str(c), "m^2")
def calc(self, formula, para_dict, column_name="", sign=True): """ Method for calculating new columns with dependencies to others given by a formula. The new column is added to the current data. :param formula: Formula-Object = Connection between parameters, and table columns :param para_dict: Dict[str, Optional[int, str, Any]] = dictionary specifying which objects to use in formula key (str): name of variable or parameter in formula-object value (int): index of column in table for the parameter at key value (str): fixed parameter for equation. Should be str(<float>) (equal for all columns) :param column_name: str = Name of the arising column :param sign: Optional[Unit, str] = sign/unit of the arising column :return: void """ type_check((formula, Formula)) if not self.datas: warnings.warn(f"No data found in table.") return # add new column name if column_name == "": column_name = f"Column {self.columns}" self.column_names.append(column_name) # update number of columns self.columns += 1 # add unit to column if sign is True: value_dict = {} for key in para_dict: if isinstance(para_dict[key], int): if self.units[para_dict[key]] != Unit(): value_dict[key] = Const(1, self.units[para_dict[key]]) else: value_dict[key] = 1 elif isinstance(para_dict[key], str): value_dict[key] = float(para_dict[key]) else: value_dict[key] = para_dict[key] unit = formula.calc_unit(value_dict) self.units.append(unit) elif sign is False: self.units.append(Unit("")) else: self.units.append(sign) # calculate data and insert them into the column datas = [] for row in self.datas: value_dict = { key: row[para_dict[key]] if isinstance(para_dict[key], int) else float(para_dict[key]) if isinstance(para_dict[key], str) else para_dict[key] for key in para_dict } value = formula.calc(value_dict, sign=False) if isinstance(value, (Data, Const)): value.unit = Unit() datas.append(list(row) + [value]) self.datas = datas
class Data: """ Main-Class of the project. Represents a value with uncertainty """ def __init__(self, value: str, error: str, sign: Union[str, Unit] = "", power: int = 0, n: int = 0): """ Initiate new value with uncertainty :param value: str = value :param error: str = uncertainty :param sign: Union[str, Unit] = unit of the value **sign-EBNF:** S := '"' units '"' | '"' units '/' units '"' units := unit | unit ';' units unit := string | string '^' integer :param power: int = power of the value (for dimensions like mV => power = -3 and unit = 'V') :param n: int = significant digits of the error (if 0 (Default): get digits from error: str) """ type_check((value, str), (error, str)) if n == 0: self.n = digits(error) else: self.n = n if isinstance(sign, str): sign = sign.split("/") if len(sign) > 1: sign = ["" if s == "1" else s for s in sign] self.unit = Unit(sign[0], sign[1]) else: self.unit = Unit(sign[0]) else: self.unit = sign self.power = power self.error = float(error) * 10**self.power self.value = float(value) * 10**self.power round_data(self) def __str__(self): """ Return a string representation of the Data. :return: str = string representation of the Data """ # insert values if self.n > 1: string = f"({self.value * 10 ** (-self.power):.{(self.n - 1)}f}±{self.error * (10 ** -self.power):.{(self.n - 1)}f})" elif self.n == 1: string = f"({self.value * 10 ** (-self.power):.0f}±{self.error * (10 ** -self.power):.0f})" else: raise ValueError("n could not be smaller than 1") # add power if self.power != 0: string += f"*10^{self.power}" # add unit unit = str(self.unit) if unit != "": string += f" {unit}" return string @instancemethod def __repr__(self): return self.__str__() def latex(self): # insert values if self.n > 1: string = f"({self.value * 10 ** (-self.power):.{(self.n - 1)}f}±{self.error * (10 ** -self.power):.{(self.n - 1)}f})" elif self.n == 1: string = f"({self.value * 10 ** (-self.power):.0f} \\pm {self.error * (10 ** -self.power):.0f})" else: raise ValueError("n could not be smaller than 1") # add power if self.power != 0: string += f"\\cdot 10^{{{self.power}}}" # add unit unit = str(self.unit) if unit != "": string += f" {unit}" return string # Calculations using simplified gauss def __number_mul(self, other): """ Helper function of multiplication of Data with float. :param other: Union[int, float] = value to multiplicative Data with :return: Data = result of the multiplication """ return Data(str(self.value * other), str(self.error * other), sign=self.unit, n=self.n) def __const_mul(self, other): """ Helper function of multiplication of Data with Const. :param other: Const = Const to multiplicative Data with :return: Data = result of the multiplication """ return Data(str(self.value * other.value), str(self.error * other.value), sign=self.unit * other.unit, n=self.n) def __data_mul(self, other): """ Helper function of multiplication of two Data. :param other: Data = The other Data to multiplicative Data with :return: Data = result of the multiplication """ result = self.value * other.value error = str(result * ((self.error / self.value)**2 + (other.error / other.value)**2)**0.5) significant_digits = min(self.n, other.n) unit = self.unit * other.unit result = str(result) return Data(result, error, sign=unit, n=significant_digits) @instancemethod def __mul__(self, other): """ Multiplication of Data object with other. Which will happen depend on the type of other. :param other: Union[Data, Const, int, float] = Object to multiply with :return: Data = result of the multiplication """ type_other = type(other) functions = { int: self.__number_mul, float: self.__number_mul, Const: self.__const_mul, Data: self.__data_mul } if type_other not in functions: raise ValueError( f"Unsupported operation '*' for Data and {type(other)}") return functions[type_other](other) @instancemethod def __rmul__(self, other): """ Multiplication of Data object with other. Which will happen depend on the type of other. :param other: Union[Data, Const, int, float] = Object to multiply with :return: Data = result of the multiplication """ return self.__mul__(other) @unit_control def __data_add(self, other): """ Helper function for addition of two Data. :param other: Data = other Data to add with Data :return: Data = result of the addition """ result = self.value + other.value significant_digits = min(self.n, other.n) error = str((self.error**2 + other.error**2)**0.5) unit = self.unit result = str(result) return Data(result, error, n=significant_digits, sign=unit) def __number_add(self, other): """ Helper function for addition of a Data and an Union[int, float]. :param other: Union[int, float] = value to add :return: Data = result of the addition """ if self.unit == Unit(""): data = Data(str(self.value + other), str(self.error), n=self.n) data.power = self.power return data @unit_control def __const_add(self, other): """ Helper function for addition of a Data and an Const. :param other: Const = value to add :return: Data = result of the addition """ result = self.value + other.value significant_digits = min(self.n, other.n) unit = self.unit result = str(result) return Data(result, self.error, n=self.n, sign=unit) @instancemethod def __add__(self, other): """ Addition of a Data and other. :param other: Union[Data, Const, int, float] = Object to add with :return: Data = result of the addition """ type_other = type(other) functions = { int: self.__number_add, float: self.__number_add, Const: self.__const_add, Data: self.__data_add } if type_other not in functions: raise ValueError( "Unsupported operation '+' for Data and {type(other)}") return functions[type_other](other) @instancemethod def __radd__(self, other): """ Addition of a Data and other. :param other: Union[Data, Const, int, float] = Object to add with :return: Data = result of the addition """ return self.__number_add(other) @unit_control def __data_sub(self, other): """ Helper function for subtraction of two Data. :param other: Data = other Data to subtract with Data :return: Data = result of the subtraction """ result = self.value - other.value significant_digits = min(self.n, other.n) error = str((self.error**2 + other.error**2)**0.5) unit = self.unit result = str(result) return Data(result, error, sign=unit, n=significant_digits) def __number_sub(self, other): """ Helper function for subtraction of a Data and an Union[int, float]. :param other: Union[int, float] = value to subtract :return: Data = result of the subtraction """ if self.unit == Unit(""): data = Data(str(self.value - other), str(self.error), n=self.n) data.power = self.power return data @unit_control def __const_sub(self, other): """ Helper function for subtraction of a Data and an Const. :param other: Const = value to subtract :return: Data = result of the subtraction """ return Data(str(self.value - other.value), str(self.error), n=self.n, sign=self.unit) @instancemethod def __sub__(self, other): """ Subtraction of two Data objects. :param other: Data = other Data to subtract with Data :return: Data = result of the subtraction """ type_other = type(other) functions = { int: self.__number_sub, float: self.__number_sub, Const: self.__const_sub, Data: self.__data_sub } if type_other not in functions: raise ValueError( "Unsupported operation '-' for Data and {type(other)}") return functions[type_other](other) @instancemethod def __rsub__(self, other): """ Subtraction of a Data and other. :param other: Union[Data, Const, int, float] = Object to subtract with :return: Data = result of the substraction """ return -1 * self.__number_sub(other) def __number_div(self, other): """ Helper function of division of Data with float. :param other: Union[int, float] = value to divide Data with :return: Data = result of division """ return Data(str(self.value / other), str(self.error / other), sign=self.unit, n=self.n) def __const_div(self, other): """ Helper function of division of Data with Const. :param other: Const = Const to divide Data with :return: Data = result of division """ result = str(self.value / other.value) unit = self.unit / other.unit error = str(self.error / other.value) significant_digits = self.n return Data(result, error, sign=unit, n=significant_digits) def __data_div(self, other): """ Helper function of division of Data with Data. :param other: Data = Data to divide Data with :return: Data = result of division """ result = self.value / other.value significant_digits = min(self.n, other.n) error = str(result * ((self.error / self.value)**2 + (other.error / other.value)**2)**0.5) result = str(result) unit = self.unit / other.unit return Data(result, error, sign=unit, n=significant_digits) @instancemethod def __truediv__(self, other): """ Division of a Data object with other. :param other: Union[Data, Const, int, float] = object to divide with :return: Data = result of the division """ type_other = type(other) functions = { int: self.__number_div, float: self.__number_div, Const: self.__const_div, Data: self.__data_div } if type_other not in functions: raise ValueError( f"Unsupported operation '/' for Data and {type_other}") return functions[type_other](other) @instancemethod def __rtruediv__(self, other): """ Division of a Data object with other. :param other: Union[int, float] = object to divide with :return: Data = result of the division """ typ_other = type(other) if typ_other == int or typ_other == float: result = other / self.value unit = self.unit.flip() return Data(str(result), str(result * (self.error / self.value)), sign=unit, n=self.n) else: raise ValueError( f"Unsupported operation '/' for Data and {typ_other}") def __pow__(self, other): """ Power of a Data object with other. :param other: Union[int, float] = object to power with :return: Data = result of the calculation """ typ_other = type(other) if typ_other == int or typ_other == float: result = self.value**other unit = self.unit**other return Data(str(result), str(result * (self.error / self.value)), sign=unit, n=self.n) elif typ_other == Data: raise ArithmeticError("Try to use a Formula instead!") else: raise TypeError( f"Unsupported operation '**' for Data and {typ_other}") # Data comparisons @unit_control def __data_lt(self, other): """ Helper function of lt comparison of Data with Data. :param other: Data = Data to compare with :return: bool = result of comparison """ return self.value + self.error + other.error < other.value @unit_control def __const_lt(self, other): """ Helper function of lt comparison of Data with Const. :param other: Const = Const to compare with :return: bool = result of comparison """ return self.value + self.error < other.value @instancemethod def __lt__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ type_other = type(other) functions = {Const: self.__const_lt, Data: self.__data_lt} if type_other not in functions: raise ValueError( f"Unsupported operation '<' for Data and {type_other}") return functions[type_other](other) @unit_control def __data_eq(self, other): """ Helper function of eq comparison of Data with Data. :param other: Data = Data to compare with :return: bool = result of comparison """ return self.value - self.error - other.error <= other.value <= self.value + self.error + other.error @unit_control def __const_eq(self, other): """ Helper function of eq comparison of Data with Const. :param other: Const = Const to compare with :return: bool = result of comparison """ return self.value - self.error <= other.value <= self.value + self.error @instancemethod def __eq__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ type_other = type(other) functions = {Const: self.__const_eq, Data: self.__data_eq} if type_other not in functions: raise ValueError( f"Unsupported operation '==' for Data and {type_other}") return functions[type_other](other) @unit_control def __data_gt(self, other): """ Helper function of gt comparison of Data with Data. :param other: Data = Data to compare with :return: bool = result of comparison """ return self.value - self.error - other.error > other.value @unit_control def __const_gt(self, other): """ Helper function of eq comparison of Data with Const. :param other: Const = Const to compare with :return: bool = result of comparison """ return self.value - self.error > other.value @instancemethod def __gt__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ type_other = type(other) functions = {Const: self.__const_gt, Data: self.__data_gt} if type_other not in functions: raise ValueError( f"Unsupported operation '==' for Data and {type_other}") return functions[type_other](other) @instancemethod def __ne__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ return not self.__eq__(other) @instancemethod def __ge__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ return self.__gt__(other) or self.__eq__(other) @instancemethod def __le__(self, other): """ Compare Data with other objects. :param other: Union[Data, Const] = object to compare with :return: bool = result of comparison """ return self.__lt__(other) or self.__eq__(other)