def map(
        self,
        fn: Union[
            Callable[[np.number], np.number],
            Callable[[Union[pd.Timestamp, int], np.number], np.number],
        ],
    ) -> Self:  # noqa: E501
        """
        Applies the function `fn` to the underlying NumPy array containing this series' values.

        Return a new TimeSeries instance. If `fn` takes 1 argument it is simply applied on the backing array
        of shape (time, n_components, n_samples).
        If it takes 2 arguments, it is applied repeatedly on the (ts, value[ts]) tuples, where
        "ts" denotes a timestamp value, and "value[ts]" denote the array of values at this timestamp, of shape
        (n_components, n_samples).

        Parameters
        ----------
        fn
            Either a function which takes a NumPy array and returns a NumPy array of same shape;
            e.g., `lambda x: x ** 2`, `lambda x: x / x.shape[0]` or `np.log`.
            It can also be a function which takes a timestamp and array, and returns a new array of same shape;
            e.g., `lambda ts, x: x / ts.days_in_month`.
            The type of `ts` is either `pd.Timestamp` (if the series is indexed with a DatetimeIndex),
            or an integer otherwise (if the series is indexed with an RangeIndex).

        Returns
        -------
        TimeSeries
            A new TimeSeries instance
        """
        if not isinstance(fn, Callable):
            raise_log(TypeError("fn should be callable"), logger)

        if isinstance(fn, np.ufunc):
            if fn.nin == 1 and fn.nout == 1:
                num_args = 1
            elif fn.nin == 2 and fn.nout == 1:
                num_args = 2
            else:
                raise_log(
                    ValueError(
                        "fn must have either one or two arguments and return a single value"
                    ),
                    logger,
                )
        else:
            try:
                num_args = len(signature(fn).parameters)
            except ValueError:
                raise_log(
                    ValueError(
                        "inspect.signature(fn) failed. Try wrapping fn in a lambda, e.g. lambda x: fn(x)"
                    ),
                    logger,
                )

        new_xa = self._xa.copy()
        if num_args == 1:  # apply fn on values directly
            new_xa.values = fn(self._xa.values)

        elif num_args == 2:  # map function uses timestamp f(timestamp, x)
            # go over shortest amount of iterations, either over time steps or components and samples
            if self.n_timesteps <= self.n_components * self.n_samples:
                new_vals = np.vstack([
                    np.expand_dims(fn(self.time_index[i], self._xa[i, :, :]), axis=0)
                    for i in range(self.n_timesteps)
                ])
            else:
                new_vals = np.stack(
                    [
                        np.column_stack([
                            fn(self.time_index, self._xa[:, i, j])
                            for j in range(self.n_samples)
                        ])
                        for i in range(self.n_components)
                    ],
                    axis=1,
                )
            new_xa.values = new_vals

        else:
            raise_log(ValueError("fn must have either one or two arguments"), logger)

        return self.__class__(new_xa)

        Applies the function `fn` to the underlying NumPy array containing this series' values.

        Return a new TimeSeries instance. If `fn` takes 1 argument it is simply applied on the backing array
        of shape (time, n_components, n_samples).
        If it takes 2 arguments, it is applied repeatedly on the (ts, value[ts]) tuples, where
        "ts" denotes a timestamp value, and "value[ts]" denote the array of values at this timestamp, of shape
        (n_components, n_samples).

        Parameters
        ----------
        fn
            Either a function which takes a NumPy array and returns a NumPy array of same shape;
            e.g., `lambda x: x ** 2`, `lambda x: x / x.shape[0]` or `np.log`.
            It can also be a function which takes a timestamp and array, and returns a new array of same shape;
            e.g., `lambda ts, x: x / ts.days_in_month`.
            The type of `ts` is either `pd.Timestamp` (if the series is indexed with a DatetimeIndex),
            or an integer otherwise (if the series is indexed with an RangeIndex).

        Returns
        -------
        TimeSeries
            A new TimeSeries instance
        
darts/timeseries.py