def __init__(self, xa: xr.DataArray, copy=True):
        """
        Create a TimeSeries from a (well-formed) DataArray.
        It is recommended to use the factory methods to create TimeSeries instead.

        See Also
        --------
        TimeSeries.from_dataframe : Create from a `DataFrame` (:class:`pandas.DataFrame`, :class:`polars.DataFrame`,
            and other backends).
        TimeSeries.from_group_dataframe : Create multiple TimeSeries by groups from a :class:`pandas.DataFrame`.
        TimeSeries.from_series : Create from a `Series` (:class:`pandas.Series`, :class:`polars.Series`, and other
            backends).
        TimeSeries.from_values : Create from a :class:`numpy.ndarray`.
        TimeSeries.from_times_and_values : Create from a time index and a :class:`numpy.ndarray`.
        TimeSeries.from_csv : Create from a CSV file.
        TimeSeries.from_json : Create from a JSON file.
        TimeSeries.from_xarray : Create from an :class:`xarray.DataArray`.
        """
        if not isinstance(xa, xr.DataArray):
            raise_log(
                ValueError(
                    "Data must be provided as an xarray DataArray instance. "
                    "If you need to create a TimeSeries from another type "
                    "(e.g. a DataFrame), look at TimeSeries factory methods "
                    "(e.g. TimeSeries.from_dataframe(), "
                    "TimeSeries.from_xarray(), TimeSeries.from_values()"
                    "TimeSeries.from_times_and_values(), etc...)."
                ),
                logger,
            )
        if len(xa.shape) != 3:
            raise_log(
                ValueError(
                    f"TimeSeries require DataArray of dimensionality 3 ({DIMS})."
                ),
                logger,
            )

        # Ideally values should be np.float, otherwise certain functionalities like diff()
        # relying on np.nan (which is a float) won't work very properly.
        if not np.issubdtype(xa.values.dtype, np.number):
            raise_log(
                ValueError("The time series must contain numeric values only."), logger
            )

        val_dtype = xa.values.dtype
        if not (
            np.issubdtype(val_dtype, np.float64) or np.issubdtype(val_dtype, np.float32)
        ):
            logger.warning(
                "TimeSeries is using a numeric type different from np.float32 or np.float64. "
                "Not all functionalities may work properly. It is recommended casting your data to floating "
                "point numbers before using TimeSeries."
            )

        if xa.dims[-2:] != DIMS[-2:]:
            # The first dimension represents the time and may be named differently.
            raise_log(
                ValueError(
                    f"The last two dimensions of the DataArray must be named {DIMS[-2:]}"
                ),
                logger,
            )

        # check that columns/component names are unique
        components = xa.get_index(DIMS[1])
        if not len(set(components)) == len(components):
            raise_log(
                ValueError(
                    f"The components (columns) names must be unique. Provided: {components}"
                ),
                logger,
            )

        # how the time dimension is named; we convert hashable to string
        self._time_dim = str(xa.dims[0])

        # The following sorting returns a copy, which we are relying on.
        # As of xarray 0.18.2, this sorting discards the freq of the index for some reason
        # https://github.com/pydata/xarray/issues/5466
        # We sort only if the time axis is not already sorted (monotonically increasing).
        self._xa = self._sort_index(xa, copy=copy)
        self._time_index = self._xa.get_index(self._time_dim)

        if not isinstance(self._time_index, VALID_INDEX_TYPES):
            raise_log(
                ValueError(
                    "The time dimension of the DataArray must be indexed either with a DatetimeIndex "
                    "or with an RangeIndex."
                ),
                logger,
            )

        self._has_datetime_index = isinstance(self._time_index, pd.DatetimeIndex)

        if self._has_datetime_index:
            # store original freq (see bug of sortby() above).
            freq_tmp = xa.get_index(self._time_dim).freq

            # if original frequency is known and positive (n > 0 -> increasing time index),
            # it is guaranteed that original array was sorted and new freq must be the same.
            # otherwise, infer the frequency from the sorted array
            if freq_tmp is not None and freq_tmp.n > 0:
                self._freq = freq_tmp
            else:
                self._freq = to_offset(self._xa.get_index(self._time_dim).inferred_freq)

            if self._freq is None:
                raise_log(
                    ValueError(
                        "The time index of the provided DataArray is missing the freq attribute, and the frequency "
                        "could not be directly inferred. This probably comes from inconsistent date frequencies with "
                        "missing dates. If you know the actual frequency, try setting `fill_missing_dates=True, "
                        "freq=actual_frequency`. If not, try setting `fill_missing_dates=True, freq=None` to see if a "
                        "frequency can be inferred."
                    ),
                    logger,
                )

            self._freq_str: str = self._freq.freqstr

            # reset freq inside the xarray index (see bug of sortby() above).
            self._xa.get_index(self._time_dim).freq = self._freq
        else:
            self._freq: int = self._time_index.step
            self._freq_str = None

        # check static covariates
        static_covariates = self._xa.attrs.get(STATIC_COV_TAG, None)
        if not (
            isinstance(static_covariates, (pd.Series, pd.DataFrame))
            or static_covariates is None
        ):
            raise_log(
                ValueError(
                    "`static_covariates` must be either a pandas Series, DataFrame or None"
                ),
                logger,
            )

        # check if valid static covariates for multivariate TimeSeries
        if isinstance(static_covariates, pd.DataFrame):
            n_components = len(static_covariates)
            if n_components > 1 and n_components != self.n_components:
                raise_log(
                    ValueError(
                        "When passing a multi-row pandas DataFrame, the number of rows must match the number of "
                        "components of the TimeSeries object (multi-component/multi-row static covariates "
                        "must map to each TimeSeries component)."
                    ),
                    logger,
                )
            if copy:
                static_covariates = static_covariates.copy()
        elif isinstance(static_covariates, pd.Series):
            static_covariates = static_covariates.to_frame().T
        else:  # None
            pass

        # prepare static covariates:
        if static_covariates is not None:
            static_covariates.index = (
                self.components
                if len(static_covariates) == self.n_components
                else [DEFAULT_GLOBAL_STATIC_COV_NAME]
            )
            static_covariates.columns.name = STATIC_COV_TAG
            # convert numerical columns to same dtype as series
            # we get all numerical columns, except those that have right dtype already
            cols_to_cast = static_covariates.select_dtypes(
                include=np.number, exclude=self.dtype
            ).columns

            # Calling astype is costly even when there's no change...
            if not cols_to_cast.empty:
                static_covariates = static_covariates.astype(
                    {col: self.dtype for col in cols_to_cast}, copy=False
                )

        # prepare metadata
        metadata = self._xa.attrs.get(METADATA_TAG, None)
        if metadata is not None and not isinstance(metadata, dict):
            raise_log(
                ValueError(
                    "`metadata` must be of type `dict` mapping metadata attributes to their values."
                ),
                logger,
            )

        # handle hierarchy
        hierarchy = self._xa.attrs.get(HIERARCHY_TAG, None)
        self._top_level_component = None
        self._bottom_level_components = None
        if hierarchy is not None:
            if not isinstance(hierarchy, dict):
                raise_log(
                    ValueError(
                        "The hierarchy must be a dict mapping (non-top) component names to their parent(s) "
                        "in the hierarchy."
                    ),
                    logger,
                )
            # pre-compute grouping information
            components_set = set(self.components)
            children = set(hierarchy.keys())

            # convert string ancestors to list of strings
            hierarchy = {
                k: ([v] if isinstance(v, str) else v) for k, v in hierarchy.items()
            }

            if not all(c in components_set for c in children):
                raise_log(
                    ValueError(
                        "The keys of the hierarchy must be time series components"
                    ),
                    logger,
                )
            ancestors = set().union(*hierarchy.values())
            if not all(a in components_set for a in ancestors):
                raise_log(
                    ValueError(
                        "The values of the hierarchy must only contain component names matching those "
                        "of the series."
                    ),
                    logger,
                )
            hierarchy_top = components_set - children
            if not len(hierarchy_top) == 1:
                raise_log(
                    ValueError(
                        "The hierarchy must be such that only one component does not appear as a key "
                        "(the top level component)."
                    ),
                    logger,
                )
            self._top_level_component = hierarchy_top.pop()
            if self._top_level_component not in ancestors:
                raise_log(
                    ValueError(
                        "Invalid hierarchy. Component {} appears as it should be top-level, but "
                        "does not appear as an ancestor in the hierarchy dict."
                    ),
                    logger,
                )
            bottom_level = components_set - ancestors

            # maintain the same order as the original components
            self._bottom_level_components = [
                c for c in self.components if c in bottom_level
            ]

        # store static covariates, hierarchy and metadata in attributes (potentially storing None)
        self._xa = _xarray_with_attrs(self._xa, static_covariates, hierarchy, metadata)

        Create a TimeSeries from a (well-formed) DataArray.
        It is recommended to use the factory methods to create TimeSeries instead.

        See Also
        --------
        TimeSeries.from_dataframe : Create from a `DataFrame` (:class:`pandas.DataFrame`, :class:`polars.DataFrame`,
            and other backends).
        TimeSeries.from_group_dataframe : Create multiple TimeSeries by groups from a :class:`pandas.DataFrame`.
        TimeSeries.from_series : Create from a `Series` (:class:`pandas.Series`, :class:`polars.Series`, and other
            backends).
        TimeSeries.from_values : Create from a :class:`numpy.ndarray`.
        TimeSeries.from_times_and_values : Create from a time index and a :class:`numpy.ndarray`.
        TimeSeries.from_csv : Create from a CSV file.
        TimeSeries.from_json : Create from a JSON file.
        TimeSeries.from_xarray : Create from an :class:`xarray.DataArray`.
        
darts/timeseries.py