def build_time_index( time: types_timeindex = None, time_ref: pd.Timestamp = None, ) -> pd.TimedeltaIndex: """Build time index used for xarray objects. Parameters ---------- time: Datetime- or Timedelta-like time index. time_ref: Reference timestamp for Timedelta inputs. Returns ------- pandas.TimedeltaIndex """ if time is None: return time, time_ref time = util.to_pandas_time_index(time) if isinstance(time, pd.DatetimeIndex): if time_ref is None: time_ref = time[0] time = time - time_ref return time, time_ref
def test_interp_time(ts, time, magnitude_exp, unit_exp): """Test the interp_time function.""" result = ts.interp_time(time) assert np.all(np.isclose(result.data.magnitude, magnitude_exp)) assert Q_(1, str(result.data.units)) == Q_(1, unit_exp) if isinstance(time, pint.Quantity): assert np.all(result.time == ut.to_pandas_time_index(time)) else: assert np.all(result.time == time)
def time(self) -> Union[None, pd.TimedeltaIndex]: """Return the data's timestamps. Returns ------- pandas.TimedeltaIndex: Timestamps of the data """ if isinstance(self._data, xr.DataArray) and len(self._data.time) > 1: return ut.to_pandas_time_index(self._data.time.data) return None
def interp_time( self, time: types_time_and_lcs, time_ref: Union[pd.Timestamp, None] = None, ) -> "LocalCoordinateSystem": """Interpolates the data in time. Parameters ---------- time : Series of times. If passing "None" no interpolation will be performed. time_ref: The reference timestamp Returns ------- LocalCoordinateSystem Coordinate system with interpolated data """ if (not self.is_time_dependent) or (time is None): return self # use LCS reference time if none provided if isinstance(time, LocalCoordinateSystem) and time_ref is None: time_ref = time.reference_time time = ut.to_pandas_time_index(time) if self.has_reference_time != (time_ref is not None or isinstance(time, pd.DatetimeIndex)): raise TypeError( "Only 1 reference time provided for time dependent coordinate " "system. Either the reference time of the coordinate system or the " "one passed to the function is 'None'. Only cases where the " "reference times are both 'None' or both contain a timestamp are " "allowed. Also check that the reference time has the correct type." ) if self.has_reference_time and (not isinstance(time, pd.DatetimeIndex)): time = time + time_ref orientation = ut.xr_interp_orientation_in_time(self.orientation, time) coordinates = ut.xr_interp_coordinates_in_time(self.coordinates, time) return LocalCoordinateSystem(orientation, coordinates, time_ref=time_ref)
def interp_time(self, time: Union[pd.TimedeltaIndex, pint.Quantity], time_unit: str = "s") -> xr.DataArray: """Interpolate the TimeSeries in time. If the internal data consists of discrete values, an interpolation with the prescribed interpolation method is performed. In case of mathematical expression, the expression is evaluated for the given timestamps. Parameters ---------- time: A set of timestamps. time_unit: Only important if the time series is described by an expression and a 'pandas.TimedeltaIndex' is passed to this function. In this case, time is converted to a quantity with the provided unit. Even though pint handles unit prefixes automatically, the accuracy of the results can be heavily influenced if the provided unit results in extreme large or small values when compared to the parameters of the expression. Returns ------- xarray.DataArray: A data array containing the interpolated data. """ if isinstance(self._data, xr.DataArray): if isinstance(time, pint.Quantity): time = ut.to_pandas_time_index(time) if not isinstance(time, pd.TimedeltaIndex): raise ValueError( '"time" must be a time quantity or a "pandas.TimedeltaIndex".' ) # constant values are also treated by this branch if self._data.attrs["interpolation"] == "linear" or self.shape[ 0] == 1: return ut.xr_interp_like( self._data, {"time": time}, assume_sorted=False, broadcast_missing=False, ) dax = self._data.reindex({"time": time}, method="ffill") return dax.fillna(self._data[0]) # Transform time to both formats if isinstance(time, pint.Quantity) and time.check( UREG.get_dimensionality("s")): time_q = time time_pd = ut.to_pandas_time_index(time) elif isinstance(time, pd.TimedeltaIndex): time_q = ut.pandas_time_delta_to_quantity(time, time_unit) time_pd = time else: raise ValueError( '"time" must be a time quantity or a "pandas.TimedeltaIndex".') if len(self.shape) > 1 and np.iterable(time_q): while len(time_q.shape) < len(self.shape): time_q = time_q[:, np.newaxis] # evaluate expression data = self._data.evaluate(**{self._time_var_name: time_q}) data = data.astype( float).to_reduced_units() # float conversion before reduce! # create data array if not np.iterable(data): # make sure quantity is not scalar value data = np.expand_dims(data, 0) dax = xr.DataArray(data=data) # don't know exact dimensions so far return dax.rename({"dim_0": "time"}).assign_coords({"time": time_pd})
def __init__( self, data: Union[pint.Quantity, MathematicalExpression], time: Union[None, pd.TimedeltaIndex, pint.Quantity] = None, interpolation: str = "linear", ): """Construct a TimSeries. Parameters ---------- data: Either a pint.Quantity or a weldx.MathematicalExpression. If a mathematical expression is chosen, it is only allowed to have a single free variable, which represents time. time: An instance of pandas.TimedeltaIndex if a quantity is passed and 'None' otherwise. interpolation: A string defining the desired interpolation method. This is only relevant if a quantity is passed as data. Currently supported interpolation methods are: 'step', 'linear'. """ self._data = None self._time_var_name = None self._shape = None self._units = None if isinstance(data, pint.Quantity): if not np.iterable(data): # expand dim for scalar input data = np.expand_dims(data, 0) if time is None: # constant value case time = pd.TimedeltaIndex([0]) interpolation = None elif interpolation not in self._valid_interpolations: raise ValueError( "A valid interpolation method must be specified if discrete " f'values are used. "{interpolation}" is not supported') if isinstance(time, pint.Quantity): time = ut.to_pandas_time_index(time) if not isinstance(time, pd.TimedeltaIndex): raise ValueError( '"time" must be a time quantity or a "pandas.TimedeltaIndex".' ) dax = xr.DataArray( data=data, attrs={"interpolation": interpolation}, ) self._data = dax.rename({ "dim_0": "time" }).assign_coords({"time": time}) elif isinstance(data, MathematicalExpression): if data.num_variables != 1: raise Exception( "The mathematical expression must have exactly 1 free " "variable that represents time.") time_var_name = data.get_variable_names()[0] try: eval_data = data.evaluate(**{time_var_name: Q_(1, "second")}) self._units = eval_data.units if np.iterable(eval_data): self._shape = eval_data.shape else: self._shape = (1, ) except pint.errors.DimensionalityError: raise Exception( "Expression can not be evaluated with " '"weldx.Quantity(1, "seconds")"' ". Ensure that every parameter posses the correct unit.") self._data = data self._time_var_name = time_var_name try: self.interp_time(Q_([1, 2], "second")) self.interp_time(Q_([1, 2, 3], "second")) except Exception as e: raise Exception( "The expression can not be evaluated with arrays of time deltas. " "Ensure that all parameters that are multiplied with the time " "variable have an outer dimension of size 1. This dimension is " "broadcasted during multiplication. The original error message was:" f' "{str(e)}"') else: raise TypeError(f'The data type "{type(data)}" is not supported.')
def test_to_pandas_time_index_exceptions(arg, exception): """Test correct exceptions on invalid inputs.""" with pytest.raises(exception): ut.to_pandas_time_index(arg)
def test_to_pandas_time_index(arg, expected): """Test conversion to appropriate pd.TimedeltaIndex or pd.DatetimeIndex.""" assert np.all(ut.to_pandas_time_index(arg) == expected)