コード例 #1
0
 def _interp_time_orientation(self, time: Time) -> xr.DataArray:
     """Interpolate the orientation in time."""
     if "time" not in self.orientation.dims:  # don't interpolate static
         return self.orientation
     if time.max() <= self.time.min():  # only use edge timestamp
         return self.orientation.isel(time=0).data
     if time.min() >= self.time.max():  # only use edge timestamp
         return self.orientation.isel(time=-1).data
     # full interpolation with overlapping times
     return ut.xr_interp_orientation_in_time(self.orientation, time)
コード例 #2
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
 def test_quantity(arg, unit, expected):
     """Test conversion to pint.Quantity with different scales."""
     t = Time(arg)
     q = Time(arg).as_quantity(unit)
     expected = Q_(expected, unit)
     assert np.allclose(q, expected)
     if t.is_absolute:
         assert t.reference_time == q.time_ref
     if unit == "s":
         assert np.all(q == t.quantity)
コード例 #3
0
ファイル: test_asdf_time.py プロジェクト: CagtayFabry/weldx
def test_time_classes(inputs, time_ref):
    data = write_read_buffer({"root": inputs})
    assert np.all(data["root"] == inputs)

    if isinstance(inputs, pd.Index) and not inputs.is_monotonic:
        # this is not valid for the time class, hence cancel here
        return

    t1 = Time(inputs, time_ref)
    t2 = write_read_buffer({"root": t1})["root"]
    assert t1.equals(t2)
コード例 #4
0
    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 result.units == U_(unit_exp)

        time = Time(time)
        if len(time) == 1:
            assert result.time is None
        else:
            assert np.all(Time(result.time, result._reference_time) == time)
コード例 #5
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_resample(number_or_interval, exp_values):
        """Test resample method."""
        t_ref = "2000-01-01"
        t_delta = Time(Q_([3, 5, 8, 9], "s"))
        t_abs = t_delta + t_ref

        exp_delta = Time(Q_(exp_values, "s"))
        exp_abs = exp_delta + t_ref

        result_delta = t_delta.resample(number_or_interval)
        result_abs = t_abs.resample(number_or_interval)

        assert result_delta.all_close(exp_delta)
        assert result_abs.all_close(exp_abs)
コード例 #6
0
ファイル: xarray.py プロジェクト: CagtayFabry/weldx
def xr_interp_coordinates_in_time(da: xr.DataArray,
                                  times: types_time_like) -> xr.DataArray:
    """Interpolate an xarray DataArray that represents 3d coordinates in time.

    Parameters
    ----------
    da : xarray.DataArray
        xarray DataArray
    times : pandas.TimedeltaIndex or pandas.DatetimeIndex
        Time data

    Returns
    -------
    xarray.DataArray
        Interpolated data

    """
    if "time" not in da.dims:  # not time dependent
        return da

    times = Time(times).as_pandas_index()
    da = da.weldx.time_ref_unset()
    da = xr_interp_like(da, {"time": times},
                        assume_sorted=True,
                        broadcast_missing=False,
                        fillna=True)
    da = da.weldx.time_ref_restore()

    if len(da.time) == 1:  # remove "time dimension" for static cases
        return da.isel({"time": 0})

    return da
コード例 #7
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_union(test_instance, list_of_objects, time_exp):
        """Test input types for Time.union function.

        Parameters
        ----------
        list_of_objects:
            List with input objects
        time_exp:
            Expected result time

        """
        if test_instance:
            instance = Time(list_of_objects[0])
            assert np.all(instance.union(list_of_objects[1:]) == time_exp)
        else:
            assert np.all(Time.union(list_of_objects) == time_exp)
コード例 #8
0
 def _interp_time_coordinates(self, time: Time) -> xr.DataArray:
     """Interpolate the coordinates in time."""
     if isinstance(self.coordinates, TimeSeries):
         time_interp = Time(time, self.reference_time)
         coordinates = self._coords_from_discrete_time_series(
             self.coordinates.interp_time(time_interp)
         )
         if self.has_reference_time:
             coordinates.weldx.time_ref = self.reference_time
         return coordinates
     if "time" not in self.coordinates.dims:  # don't interpolate static
         return self.coordinates
     if time.max() <= self.time.min():  # only use edge timestamp
         return self.coordinates.isel(time=0).data
     if time.min() >= self.time.max():  # only use edge timestamp
         return self.coordinates.isel(time=-1).data
     # full interpolation with overlapping times
     return ut.xr_interp_coordinates_in_time(self.coordinates, time)
コード例 #9
0
ファイル: xarray.py プロジェクト: CagtayFabry/weldx
def xr_3d_vector(
    data: wxt.ArrayLike,
    time: types_time_like = None,
    add_dims: list[str] = None,
    add_coords: dict[str, Any] = None,
) -> xr.DataArray:
    """Create an xarray 3d vector with correctly named dimensions and coordinates.

    Parameters
    ----------
    data
        Full data array.
    time
        Optional values that will fill the 'time' dimension.
    add_dims
        Addition dimensions to add between ["time", "c"].
        If either "c" or "time" are present in add_dims they are used to locate the
        dimension position in the passed array.
    add_coords
        Additional coordinates to assign to the xarray.
        ("c" and "time" coordinates will be assigned automatically)

    Returns
    -------
    xarray.DataArray

    """
    if add_dims is None:
        add_dims = []
    if add_coords is None:
        add_coords = {}

    dims = ["c"]
    coords = dict(c=["x", "y", "z"])

    # if data is static but time passed we discard time information
    if time is not None and Q_(data).ndim == 1:
        time = None

    # remove duplicates and keep order
    dims = list(dict.fromkeys(add_dims + dims))

    if time is not None:
        if "time" not in dims:  # prepend to beginning if not already set
            dims = ["time"] + dims
        coords["time"] = time  # type: ignore[assignment]

    if "time" in coords:
        coords["time"] = Time(coords["time"]).index

    coords = dict(add_coords, **coords)

    da = xr.DataArray(data=data, dims=dims, coords=coords).transpose(..., "c")

    return da.astype(float).weldx.time_ref_restore()
コード例 #10
0
    def time(self) -> Union[Time, None]:
        """Get the time union of the local coordinate system (None if system is static).

        Returns
        -------
        xarray.DataArray
            Time-like data array representing the time union of the LCS

        """
        if "time" in self._dataset.coords:
            return Time(self._dataset.time, self.reference_time)
        return None
コード例 #11
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
def _initialize_delta_type(cls_type, values, unit):
    """Initialize the passed time type."""
    if cls_type is np.timedelta64:
        if isinstance(values, List):
            return np.array(values, dtype=f"timedelta64[{unit}]")
        return np.timedelta64(values, unit)
    if cls_type is Time:
        return Time(Q_(values, unit))
    if cls_type is str:
        if not isinstance(values, List):
            return f"{values}{unit}"
        return [f"{v}{unit}" for v in values]
    return cls_type(values, unit)
コード例 #12
0
    def _build_time(
        coordinates: Union[types_coordinates, TimeSeries],
        time: types_time_like,
        time_ref: types_timestamp_like,
    ) -> Union[Time, None]:
        if time is None:
            if isinstance(coordinates, TimeSeries) and coordinates.is_discrete:
                time = coordinates.time
            # this branch is relevant if coordinates and orientations are xarray types
            elif time_ref is not None:
                time = time_ref

        return Time(time, time_ref) if time is not None else None
コード例 #13
0
ファイル: test_utility.py プロジェクト: CagtayFabry/weldx
 def _default_dicts():
     """Return two equivalent deeply nested structures to be modified by tests."""
     a = {
         "foo": np.arange(3),
         "x": {
             0: [1, 2, 3]
         },
         "bar": True,
         "s": {1, 2, 3},
         "t": Time(["1s", "2s", "3s"]),
     }
     b = copy.deepcopy(a)
     return a, b
コード例 #14
0
    def interp_time(
        self,
        time: types_time_like,
        time_ref: types_timestamp_like = None,
    ) -> LocalCoordinateSystem:
        """Interpolates the data in time.

        Note that the returned system won't be time dependent anymore if only a single
        time value was passed. The resulting system is constant and the passed time
        value will be stripped from the result.
        Additionally, if the passed time range does not overlap with the time range of
        the coordinate system, the resulting system won't be time dependent neither
        because the values outside of the coordinate systems time range are considered
        as being constant.

        Parameters
        ----------
        time :
            Target time values. If `None` is passed, 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

        time = Time(time, time_ref)

        if self.has_reference_time != time.is_absolute:
            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."
            )

        orientation = self._interp_time_orientation(time)
        coordinates = self._interp_time_coordinates(time)

        # remove time if orientations and coordinates are single values (static)
        if orientation.ndim == 2 and coordinates.ndim == 1:
            time = None

        return LocalCoordinateSystem(orientation, coordinates, time)
コード例 #15
0
    def _unify_time_axis(
        orientation: xr.DataArray, coordinates: Union[xr.DataArray, TimeSeries]
    ) -> tuple:
        """Unify time axis of orientation and coordinates if both are DataArrays."""
        if (
            not isinstance(coordinates, TimeSeries)
            and ("time" in orientation.dims)
            and ("time" in coordinates.dims)
            and (not np.all(orientation.time.data == coordinates.time.data))
        ):
            time_union = Time.union([orientation.time, coordinates.time])
            orientation = ut.xr_interp_orientation_in_time(orientation, time_union)
            coordinates = ut.xr_interp_coordinates_in_time(coordinates, time_union)

        return (orientation, coordinates)
コード例 #16
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_init(
        self,
        input_vals: Union[type, tuple[type, str]],
        set_time_ref: bool,
        scl: bool,
        arr: bool,
    ):
        """Test the `__init__` method of the time class.

        Parameters
        ----------
        input_vals :
            Either a compatible time type or a tuple of two values. The tuple is needed
            in case the tested time type can either represent relative time values as
            well as absolute ones. In this case, the first value is the type. The
            second value is a string specifying if the type represents absolute
            ("datetime") or relative ("timedelta") values.
        set_time_ref :
            If `True`, a reference time will be passed to the `__init__` method
        scl :
            If `True`, the data of the passed type consists of a single value.
        arr :
            If `True`, the data of the passed type is an array

        """
        input_type, is_timedelta = self._parse_time_type_test_input(input_vals)

        # skip matrix cases that do not work --------------------
        if arr and input_type in [Timedelta, Timestamp]:
            return
        if not arr and input_type in [DTI, TDI]:
            return

        # create input values -----------------------------------
        delta_val = [1, 2, 3]
        abs_val = [f"2000-01-01 16:00:0{v}" for v in delta_val]

        time = _initialize_time_type(input_type, delta_val, abs_val,
                                     is_timedelta, arr, scl)
        time_ref = "2000-01-01 15:00:00" if set_time_ref else None

        # create `Time` instance --------------------------------
        time_class_instance = Time(time, time_ref)

        # check results -----------------------------------------
        exp = self._get_init_exp_values(is_timedelta, time_ref, scl, delta_val,
                                        abs_val)

        assert time_class_instance.is_absolute == exp["is_absolute"]
        assert time_class_instance.reference_time == exp["time_ref"]
        assert np.all(time_class_instance.as_timedelta() == exp["timedelta"])
        if exp["is_absolute"]:
            assert np.all(time_class_instance.as_datetime() == exp["datetime"])
        else:
            with pytest.raises(TypeError):
                time_class_instance.as_datetime()
コード例 #17
0
ファイル: xarray.py プロジェクト: CagtayFabry/weldx
def xr_interp_orientation_in_time(da: xr.DataArray,
                                  time: types_time_like) -> xr.DataArray:
    """Interpolate an xarray DataArray that represents orientation data in time.

    Parameters
    ----------
    da :
        xarray DataArray containing the orientation as matrix
    time :
        Time data

    Returns
    -------
    xarray.DataArray
        Interpolated data

    """
    if "time" not in da.dims:
        return da
    if len(da.time) == 1:  # remove "time dimension" for static case
        return da.isel({"time": 0})

    time = Time(time).as_pandas_index()
    time_da = Time(da).as_pandas_index()
    time_ref = da.weldx.time_ref

    if not len(time_da) > 1:
        raise ValueError("Invalid time format for interpolation.")

    # extract intersecting times and add time range boundaries of the data set
    times_ds_limits = pd.Index([time_da.min(), time_da.max()])
    times_union = time.union(times_ds_limits)
    times_intersect = times_union[(times_union >= times_ds_limits[0])
                                  & (times_union <= times_ds_limits[1])]

    # interpolate rotations in the intersecting time range
    rotations_key = Rot.from_matrix(da.transpose(..., "time", "c", "v").data)
    times_key = time_da.view(np.int64)
    rotations_interp = Slerp(times_key,
                             rotations_key)(times_intersect.view(np.int64))
    da = xr_3d_matrix(rotations_interp.as_matrix(), times_intersect)

    # use interp_like to select original time values and correctly fill time dimension
    da = xr_interp_like(da, {"time": time}, fillna=True)

    # resync and reset to correct format
    if time_ref:
        da.weldx.time_ref = time_ref
    da = da.weldx.time_ref_restore().transpose(..., "time", "c", "v")

    if len(da.time) == 1:  # remove "time dimension" for static case
        return da.isel({"time": 0})

    return da
コード例 #18
0
ファイル: xarray.py プロジェクト: CagtayFabry/weldx
    def time_ref(self, value: types_timestamp_like):
        """Convert INPLACE to new reference time.

        If no reference time exists, the new value will be assigned.
        """
        if value is None:
            raise TypeError("'None' is not allowed as value.")
        if "time" in self._obj.coords:
            value = Time(value).as_timestamp()
            if self._obj.weldx.time_ref and is_timedelta64_dtype(
                    self._obj.time):
                if value == self._obj.weldx.time_ref:
                    return
                _attrs = self._obj.time.attrs
                time_delta = value - self._obj.weldx.time_ref
                self._obj["time"] = self._obj.time.data - time_delta
                self._obj.time.attrs = _attrs  # restore old attributes !
                self._obj.time.attrs[
                    "time_ref"] = value  # set new time_ref value
            else:
                self._obj.time.attrs["time_ref"] = value
コード例 #19
0
ファイル: _util.py プロジェクト: CagtayFabry/weldx
def check_coordinate_system(
    lcs: tf.LocalCoordinateSystem,
    orientation_expected: Union[np.ndarray, list[list[Any]], DataArray],
    coordinates_expected: Union[np.ndarray, list[Any], DataArray],
    positive_orientation_expected: bool = True,
    time=None,
    time_ref=None,
):
    """Check the values of a coordinate system.

    Parameters
    ----------
    lcs :
        Coordinate system that should be checked
    orientation_expected :
        Expected orientation
    coordinates_expected :
        Expected coordinates
    positive_orientation_expected :
        Expected orientation
    time :
        A pandas.DatetimeIndex object, if the coordinate system is expected to
        be time dependent. None otherwise.
    time_ref:
        The expected reference time

    """
    orientation_expected = np.array(orientation_expected)
    coordinates_expected = np.array(coordinates_expected)

    if time is not None:
        assert orientation_expected.ndim == 3 or coordinates_expected.ndim == 2
        assert np.all(lcs.time == Time(time, time_ref))
        assert lcs.reference_time == time_ref

    check_coordinate_system_orientation(
        lcs.orientation, orientation_expected, positive_orientation_expected
    )

    assert np.allclose(lcs.coordinates.values, coordinates_expected, atol=1e-9)
コード例 #20
0
ファイル: xarray.py プロジェクト: CagtayFabry/weldx
def xr_3d_matrix(data: wxt.ArrayLike, time: Time = None) -> xr.DataArray:
    """Create an xarray 3d matrix with correctly named dimensions and coordinates.

    Parameters
    ----------
    data :
        Data
    time :
        Optional time data (Default value = None)

    Returns
    -------
    xarray.DataArray

    """
    if time is not None and np.array(data).ndim == 3:
        if isinstance(time, Time):
            time = time.as_pandas_index()
        da = xr.DataArray(
            data=data,
            dims=["time", "c", "v"],
            coords={
                "time": time,
                "c": ["x", "y", "z"],
                "v": [0, 1, 2]
            },
        )
    else:
        da = xr.DataArray(
            data=data,
            dims=["c", "v"],
            coords={
                "c": ["x", "y", "z"],
                "v": [0, 1, 2]
            },
        )
    return da.astype(float).weldx.time_ref_restore()
コード例 #21
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_convert_util():
        """Test basic conversion functions from/to xarray/pint."""
        t = pd.date_range("2020", periods=10, freq="1s")
        ts = t[0]

        arr = xr.DataArray(
            np.arange(10),
            dims=["time"],
            coords={"time": t - ts},
        )
        arr.time.weldx.time_ref = ts
        time = Time(arr)

        assert len(time) == len(t)
        assert time.equals(Time(t))

        time_q = time.as_quantity()
        assert np.all(time_q == Q_(range(10), "s"))
        assert time_q.time_ref == ts

        arr2 = time.as_data_array()
        assert arr.time.identical(arr2.time)
コード例 #22
0
ファイル: time.py プロジェクト: CagtayFabry/weldx
 def from_yaml_tree(self, node: dict, tag: str, ctx):
     """Construct Time from tree."""
     return Time(node["values"], node.get("reference_time"))
コード例 #23
0
ファイル: time.py プロジェクト: CagtayFabry/weldx
 def to_yaml_tree(self, obj: Time, tag: str, ctx) -> dict:
     """Convert to python dict."""
     tree = dict()
     tree["values"] = obj.as_pandas()
     tree["reference_time"] = obj._time_ref
     return tree
コード例 #24
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_add_timedelta(
        self,
        other_type,
        other_on_rhs: bool,
        unit: str,
        time_class_is_array: bool,
        other_is_array: bool,
    ):
        """Test the `__add__` method if the `Time` class represents a time delta.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        unit :
            The time unit to use
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        other_type, is_timedelta = self._parse_time_type_test_input(other_type)

        # skip array cases where the type does not support arrays
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __radd__ cases where we got conflicts with the other types' __add__
        if not other_on_rhs and other_type in (
                Q_,
                np.ndarray,
                np.timedelta64,
                np.datetime64,
                DTI,
                TDI,
        ):
            return

        # setup rhs
        delta_val = [4, 6, 8]
        if unit == "s":
            abs_val = [f"2000-01-01 10:00:0{v}" for v in delta_val]
        else:
            abs_val = [f"2000-01-01 1{v}:00:00" for v in delta_val]
        other = _initialize_time_type(
            other_type,
            delta_val,
            abs_val,
            is_timedelta,
            other_is_array,
            not other_is_array,
            unit,
        )

        # setup lhs
        time_class_values = [1, 2, 3] if time_class_is_array else [1]
        time_class = Time(Q_(time_class_values, unit))

        # setup expected values
        add = delta_val if other_is_array else delta_val[0]
        exp_val = np.array(time_class_values) + add
        exp_val += 0 if is_timedelta else time_class_values[0] - exp_val[0]

        exp_time_ref = None if is_timedelta else abs_val[0]
        exp = Time(Q_(exp_val, unit), exp_time_ref)

        # calculate and evaluate result
        res = time_class + other if other_on_rhs else other + time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)
コード例 #25
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
 def test_resample_exceptions(values, number_or_interval, raises):
     """Test possible exceptions of the resample method."""
     with pytest.raises(raises):
         Time(values).resample(number_or_interval)
コード例 #26
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_add_datetime(
        other_type,
        other_on_rhs: bool,
        time_class_is_array: bool,
        other_is_array: bool,
    ):
        """Test the `__add__` method if the `Time` class represents a datetime.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        # skip array cases where the type does not support arrays
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __radd__ cases where we got conflicts with the other types' __add__
        if not other_on_rhs and other_type in (Q_, np.ndarray, np.timedelta64,
                                               TDI):
            return

        # setup rhs
        delta_val = [4, 6, 8]

        other = _initialize_time_type(
            other_type,
            delta_val,
            None,
            True,
            other_is_array,
            not other_is_array,
            "s",
        )

        # setup lhs
        time_class_values = [1, 2, 3] if time_class_is_array else [1]
        time_class = Time(Q_(time_class_values, "s"), "2000-01-01 10:00:00")

        # setup expected values
        add = delta_val if other_is_array else delta_val[0]
        exp_val = np.array(time_class_values) + add

        exp_time_ref = time_class.reference_time
        exp = Time(Q_(exp_val, "s"), exp_time_ref)

        # calculate and evaluate result
        res = time_class + other if other_on_rhs else other + time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)
コード例 #27
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
 def test_duration(values, exp_duration):
     """Test the duration property."""
     t = Time(values)
     assert t.duration.all_close(exp_duration)
コード例 #28
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
 def _date_diff(date_1: str, date_2: str, unit: str) -> int:
     """Calculate the diff between two dates in the specified unit."""
     return int(
         Time(Timestamp(date_1) -
              Timestamp(date_2)).as_quantity().m_as(unit))
コード例 #29
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
 def test_pandas_index(arg, expected):
     """Test conversion to appropriate pd.TimedeltaIndex or pd.DatetimeIndex."""
     t = Time(arg)
     assert np.all(t.as_pandas_index() == expected)
     assert np.all(t.as_pandas_index() == t.index)
     assert np.all(t.as_timedelta_index() == t.timedelta)
コード例 #30
0
ファイル: test_time.py プロジェクト: CagtayFabry/weldx
    def test_sub(
        self,
        other_type,
        other_on_rhs: bool,
        unit: str,
        time_class_is_array: bool,
        time_class_is_timedelta: bool,
        other_is_array: bool,
    ):
        """Test the `__sub__` method of the `Time` class.

        Parameters
        ----------
        other_type :
            The type of the other object
        other_on_rhs :
            If `True`, the other type is on the rhs of the + sign and on the lhs
            otherwise
        unit :
            The time unit to use
        time_class_is_array :
            If `True`, the `Time` instance contains 3 time values and 1 otherwise
        time_class_is_timedelta :
            If `True`, the `Time` instance represents a time delta and a datetime
            otherwise
        other_is_array :
            If `True`, the other time object contains 3 time values and 1 otherwise

        """
        other_type, other_is_timedelta = self._parse_time_type_test_input(
            other_type)
        if other_on_rhs:
            lhs_is_array = time_class_is_array
            lhs_is_timedelta = time_class_is_timedelta
            rhs_is_array = other_is_array
            rhs_is_timedelta = other_is_timedelta
        else:
            lhs_is_array = other_is_array
            lhs_is_timedelta = other_is_timedelta
            rhs_is_array = time_class_is_array
            rhs_is_timedelta = time_class_is_timedelta

        # skip array cases where the type does not support arrays or scalars
        if other_type in [Timedelta, Timestamp] and other_is_array:
            return
        if not other_is_array and other_type in [DTI, TDI]:
            return

        # skip __rsub__ cases where we got conflicts with the other types' __sub__
        if not other_on_rhs and other_type in (
                Q_,
                np.ndarray,
                np.timedelta64,
                np.datetime64,
                DTI,
                TDI,
        ):
            return

        # skip cases where an absolute time is on the rhs, since pandas does
        # not support this case (and it does not make sense)
        if lhs_is_timedelta and not rhs_is_timedelta:
            return

        # skip cases where the lhs is a scalar and the rhs is an array because it will
        # always involve non monotonically increasing array values, which is forbidden.
        if rhs_is_array and not lhs_is_array:
            return

        # test values
        vals_lhs = [3, 5, 9] if lhs_is_array else [3]
        vals_rhs = [1, 2, 3] if rhs_is_array else [1]

        # setup rhs
        other_val = vals_rhs if other_on_rhs else vals_lhs
        if unit == "s":
            abs_val = [f"2000-01-01 10:00:0{v}" for v in other_val]
        else:
            abs_val = [f"2000-01-01 1{v}:00:00" for v in other_val]
        other = _initialize_time_type(
            other_type,
            other_val,
            abs_val,
            other_is_timedelta,
            other_is_array,
            not other_is_array,
            unit,
        )

        # setup lhs
        time_class_values = vals_lhs if other_on_rhs else vals_rhs
        time_class_time_ref = None if time_class_is_timedelta else "2000-01-01 11:00:00"
        time_class = Time(Q_(time_class_values, unit), time_class_time_ref)

        # setup expected values
        sub = vals_rhs if other_is_array else vals_rhs[0]
        exp_val = np.array(vals_lhs) - sub
        if not other_is_timedelta:
            if time_class_is_timedelta:
                exp_val -= time_class_values[0] + exp_val[0]
            else:
                d = self._date_diff(time_class_time_ref, abs_val[0],
                                    unit) + vals_rhs[0]
                exp_val += d if other_on_rhs else (d + exp_val[0]) * -1

        exp_time_ref = None
        if not other_is_timedelta and time_class_is_timedelta:
            exp_time_ref = abs_val[0]
        elif other_is_timedelta and not time_class_is_timedelta:
            exp_time_ref = time_class_time_ref
        exp = Time(Q_(exp_val, unit), exp_time_ref)

        # calculate and evaluate result
        res = time_class - other if other_on_rhs else other - time_class

        assert res.reference_time == exp.reference_time
        assert np.all(res.as_timedelta() == exp.as_timedelta())
        assert np.all(res == exp)