def concat(self, new_name, *quants, longname=""): """ concatenates quantities Args: new_name: name of new quantity quants: quantities to be concatenated """ values = [] errors = [] dim = None for q_str in quants: q = quantities.parse_expr(q_str, self.data) # check dimension if dim is None: dim = q.dim else: if not dim == q.dim: raise RuntimeError("dimension mismatch\n%s != %s" % (dim, q.dim)) # check if values or errors are None if not values is None: if q.value is None: values = None else: v = q.value if not isinstance(q.value, np.ndarray): v = v.reshape((1)) values.append(v) if not errors is None: if q.error is None: errors = None else: u = q.error if not isinstance(q.error, np.ndarray): u = u.reshape((1)) errors.append(u) # concatenate new_value = None new_error = None if not values is None: new_value = np.concatenate(values) if not errors is None: new_error = np.concatenate(errors) if new_value is None and new_error is None: raise RuntimeError( "Could not concatenate. At least one value and one error are None." ) new_q = quantities.Quantity(new_name, longname) new_q.value = new_value new_q.error = new_error new_q.dim = dim self.data[new_name] = new_q
def slice(self, new_name, quantity, start=0, end=None, longname=""): """ creates new quantity from data set that only contains values from start to end Args: new_name: name of new quantity quantity: name of quantity to be sliced start: number of value in data set where new quantity is supposed to start first value is 0 end: number of value to be the first one not taken into the new quantity None to get all values until the end longname: long name of new quantity """ q = quantities.parse_expr(quantity, self.data) new_value = None new_uncert = None # check if values or uncerts are None if not q.value is None: if not isinstance(q.value, np.ndarray): raise RuntimeError( "Could not slice '%s'. It's not a data set." % quantity) if end is None: new_value = q.value[start:] else: new_value = q.value[start:end] if not q.uncert is None: if not isinstance(q.value, np.ndarray): raise RuntimeError( "Could not slice '%s'. Uncertainty is not an array." % quantity) if end is None: new_uncert = q.uncert[start:] else: new_uncert = q.uncert[start:end] new_q = quantities.Quantity(new_name, longname) new_q.value = new_value new_q.uncert = new_uncert new_q.dim = q.dim self.data[new_name] = new_q
def mean_value(self, quantity_to_assign, *quants, weighted=None, longname=None): """ Calculates mean value of quantities and assigns it to new quantity Args: quantity_to_assign: name or quantity object of new mean value quantities: one or more quantities names or objects of which mean value shall be calculated weighted: if True, will weight mean value by errors (returns error if not possible) if False, will not weight mean value by errors if None, will try to weight mean value, but if at least one error is not given, will not weight it longname: description for mean value quantity """ # get quantities quantities_obj = [] for q in quants: q_obj = quantities.parse_expr(q, self.data) assert isinstance(q_obj, quantities.Quantity) quantities_obj.append(q_obj) if isinstance(quantity_to_assign, str): name = quantity_to_assign elif isinstance(quantity_to_assign, quantities.Quantity): name = quantity_to_assign.name quantity_to_assign = quantities.Quantity(name, longname) self.data[name] = quantity_to_assign # standard behaviour for "weighted" if weighted is True: force_weighted = True else: force_weighted = False if weighted is None: weighted = True mean_value.mean_value(quantity_to_assign, quantities_obj, weighted=weighted, force_weighted=force_weighted)
def assign(self, name, value=None, error=None, unit=None, longname=None, value_unit=None, error_unit=None, replace=False, ignore_dim=False): """ Assigns value and/or error to quantity Args: name: quantity name longname: description of quantity value: value to assign, can be expression, string, list or number error: error to assign, can be expression, string, list or number, but mustn't depend on other quantities unit: unit of both value and error, replaces 'value_unit' and 'error_unit' if given value_unit: value unit expression or string error_unit: error unit expression or string replace: if True, will replace quantity instead of trying to keep data ignore_dim: if True, will ignore calculated dimension and use given unit instead """ if not unit is None: value_unit = unit error_unit = unit if value is None and error is None: raise ValueError( "At least either value or error must be specified.") value_len = None value_dim = None value_formula = None error_len = None error_dim = None error_formula = None # if value is given if not value is None: # parse unit if given if not value_unit is None: factor, value_dim, value_unit = units.parse_unit(value_unit) # parse value if isinstance(value, list) or isinstance(value, tuple): # if it's a list, parse each element parsed_list = [] for v in value: parsed_list.append(quantities.parse_expr(v, self.data)) elif isinstance(value, str) or isinstance(value, Expr): # if it's not a list, parse once value = quantities.parse_expr(value, self.data) # if it's a calculation if isinstance(value, Expr) and not value.is_number: # calculate value from dependency value_formula = value value = quantities.get_value(value_formula) # calculate dimension from dependency if not ignore_dim: calculated_dim = quantities.get_dimension(value_formula) if not value_dim is None and not calculated_dim == value_dim: raise RuntimeError( "dimension mismatch for '%s'\n%s != %s" % (name, value_dim, calculated_dim)) elif value_dim is None: value_dim = calculated_dim else: # if ignore_dim is True and there's no unit given -> dimensionless if value_dim is None: factor = 1 value_dim = Dimension() value_unit = S.One # calculated value must be converted to given unit (ignore_dim=True) value = np.float_(factor) * value # if it's a number else: # if no unit given, set dimensionless if value_unit is None: factor = 1 value_dim = Dimension() value_unit = S.One value = np.float_(factor) * np.float_(value) # calculate value length if isinstance(value, np.ndarray): value_len = len(value) else: value_len = 1 # if error is given if not error is None: # parse unit if given if not error_unit is None: factor, error_dim, error_unit = units.parse_unit(error_unit) # parse value if isinstance(error, list) or isinstance(error, tuple): # if it's a list, parse each element parsed_list = [] for u in error: parsed_list.append(quantities.parse_expr(u, self.data)) elif isinstance(error, str) or isinstance(error, Expr): # if it's not a list, parse once error = quantities.parse_expr(error, self.data) # make sure error is a number if isinstance(error, Expr) and not error.is_number: raise RuntimeError("error '%s' is not a number" % error) # if no unit given, set dimensionless if error_unit is None: factor = 1 error_dim = Dimension() error_unit = S.One error = np.float_(factor) * np.float_(error) # calculate error length, ignore len(error)==1 because it can be duplicated to fit any value length if isinstance(error, np.ndarray): error_len = len(error) # if error can be calculated elif not value_formula is None: error, error_formula = quantities.get_error(value_formula) # merge dimensions dim = value_dim if not dim is None and not error_dim is None and not dim == error_dim: raise RuntimeError( "value dimension and error dimension are not the same\n%s != %s" % (dim, error_dim)) if not error_dim is None: dim = error_dim # merge lengths new_len = value_len if not new_len is None and not error_len is None and not new_len == error_len: raise RuntimeError( "value length doesn't fit error length for '%s':\n%s != %s" % (name, new_len, error_len)) if not error_len is None: new_len = error_len # if quantity didn't exist if not name in self.data or replace: self.data[name] = quantities.Quantity(name) # if it did exist else: # get old length, len(error)=1 is not a length, because it can be duplicated to fit any value length old_len = None if not self.data[name].value is None: if isinstance(self.data[name].value, np.ndarray): old_len = len(self.data[name].value) else: old_len = 1 if not self.data[name].error is None and isinstance( self.data[name].error, np.ndarray): old_len = len(self.data[name].error) # if new dimension or new length, create new quantity if (not self.data[name].dim == dim or (not old_len is None and not new_len is None and not old_len == new_len)): self.data[name] = quantities.Quantity(name) # save stuff if not longname is None: self.data[name].longname = longname if not value is None: self.data[name].value = value self.data[name].value_formula = value_formula if not value_unit is None: self.data[name].prefer_unit = value_unit elif not error_unit is None: self.data[name].prefer_unit = error_unit if not error is None: self.data[name].error = error self.data[name].error_formula = error_formula self.data[name].dim = dim # check if error must be duplicated to adjust to value length if isinstance(self.data[name].value, np.ndarray) and isinstance( self.data[name].error, np.float_): error_arr = np.full(len(self.data[name].value), self.data[name].error) self.data[name].error = error_arr
def fit(self, fit_function, xydata, parameters, weighted=None, plot=False, ignore_dim=False): """ fits function to data Args: fit_function: function to fit, e.g. "n*t**2 + m*t + b" xydata: pair of x-quantity and y-quantity of data to fit to, e.g. ["t","U"] parameters: list of parameters in fit function, e.g. ["n","m","b"] weighted: if True, will weight fit by errors (returns error if not possible) if False, will not weight fit by errors if None, will try to weight fit, but if at least one error is not given, will not weight it plot: Bool, if data and fit function should be plotted ignore_dim: if True, will ignore dimensions and just calculate in base units instead """ if self.config["fit_module"] == "scipy": import errorpro.fit_scipy as fit_module else: raise ValueError("no fit module called '%s'." % self.config["fit_module"]) # get parameter quantities parameters_obj = [] for p in parameters: if isinstance(p, str): if not p in self.data: self.data[p] = quantities.Quantity(p) self.data[p].dim = Dimension() parameters_obj.append(self.data[p]) elif isinstance(p, quantities.Quantity): parameters_obj.append(p) else: raise TypeError( "parameters can only be strings or Quantity objects") # parse fit function fit_function = quantities.parse_expr(fit_function, self.data) # get data quantities x_data = quantities.parse_expr(xydata[0], self.data) # if x-data is an expression if not isinstance(x_data, quantities.Quantity): dummy = quantities.Quantity() fit_function = fit_function.subs(x_data, dummy) dummy.value = quantities.get_value(x_data) dummy.error = quantities.get_error(x_data)[0] dummy.dim = quantities.get_dimension(x_data) x_data = dummy y_data = quantities.parse_expr(xydata[1], self.data) # if y-data is an expression if not isinstance(y_data, quantities.Quantity): dummy = quantities.Quantity() dummy.value = quantities.get_value(y_data) dummy.error = quantities.get_error(y_data)[0] dummy.dim = quantities.get_dimension(y_data) y_data = dummy # check if dimension fits if not ignore_dim: try: dim_func = quantities.get_dimension(fit_function) except ValueError: dim_func = None if not dim_func == y_data.dim: # try to solve for dimensionless parameters known_dimensions = {x_data.name: x_data.dim} known_dimensions = dim_solve(fit_function, y_data.dim, known_dimensions) for q_name in known_dimensions: if q_name in self.data: if not self.data[q_name].dim == known_dimensions[ q_name]: self.data[q_name].dim = known_dimensions[q_name] self.data[q_name].prefer_unit = None dim_func = quantities.get_dimension(fit_function) # if it still doesn't work, raise error if not dim_func == y_data.dim: raise RuntimeError("Finding dimensions of fit parameters was not sucessful.\n"\ "Check fit function or specify parameter units manually.\n"\ "This error will occur until dimensions are right.") # fit values, errors = fit_module.fit(x_data, y_data, fit_function, parameters_obj, weighted) # save results i = 0 for p in parameters_obj: p.value = values[i] p.value_formula = "fit" p.error = errors[i] p.error_formula = "fit" i += 1 # plot if plot: return plotting.plot([(x_data, y_data), (x_data, fit_function)], self.config, ignore_dim=ignore_dim) else: return self.table(*parameters_obj)