import re


def _apply_extractor(extractor, X, ch_names, return_as_df):
    """Utility function to apply features extractor to ndarray X.

    Parameters
    ----------
    extractor : Instance of :class:`~sklearn.pipeline.FeatureUnion` or
    :class:`~sklearn.pipeline.Pipeline`.

    X : ndarray, shape (n_channels, n_times)

    ch_names : list of str or None

    return_as_df : bool

    Returns
    -------
    X : ndarray, shape (n_features,)

    feature_names : list of str | None
        Not None, only if ``return_as_df`` is True.
    """
    X = extractor.fit_transform(X)
    feature_names = None
    if return_as_df:
        feature_names = extractor.get_feature_names()
        if ch_names is not None:  # rename channels
            mapping = {'ch%s' % i: ch_name
                       for i, ch_name in enumerate(ch_names)}
            for pattern, translation in mapping.items():
                r = re.compile(rf'{pattern}(?=_)|{pattern}\b')
                feature_names = [
                    r.sub(string=feature_name, repl=translation)
                    for feature_name in feature_names]

    return X, feature_names