def classify_subjects_parallel(sub, subs, feature_spaces, model, cv):
    """ Helper function to parallelize analysis across subjects. """
    scores, coefs = [], dict()
    for i, fs in enumerate(feature_spaces):

        if not isinstance(fs, (tuple, list)):
            fs = (fs, )

        fs_name = '+'.join(fs)

        dl = DataLoader(sub=sub, log_level=30)
        dl.load_y(strategy_doubles='hard')
        dl.load_X(feature_set=fs, n_comp=100)
        X_val, y_val = dl.return_Xy()

        other_X, other_y = [], []
        other_subs = [s for s in subs if s != sub]
        for other_sub in other_subs:
            dl = DataLoader(sub=other_sub, log_level=30)
            dl.load_y(strategy_doubles='hard')
            dl.load_X(feature_set=fs, n_comp=100)
            this_X, this_y = dl.return_Xy()
            other_X.append(this_X)
            other_y.append(this_y)

        X = pd.concat(other_X, axis=0)
        y = pd.concat(other_y, axis=0)

        scores_, coefs_, model_ = cross_val_predict_and_score(
            estimator=model,
            X=X,
            y=y,
            cv=cv,
            scoring=roc_auc_score_per_class,
            X_val=X_val,
            y_val=y_val,
            per_class=True,
            return_model=True)
        joblib.dump(model_,
                    f'models/sub-{sub}_type-between_fs-{fs_name}_model.jl')

        dl.log.warning(
            f"sub-{sub} scores: {np.round(scores_, 2)} (fs = {fs_name})")
        scores_df = pd.DataFrame(scores_, columns=['score'])
        scores_df['feature_set'] = fs_name
        scores_df['emotion'] = dl.le.classes_
        scores_df['sub'] = sub
        scores.append(scores_df)

        coefs_df = pd.DataFrame(data=coefs_, columns=X.columns)
        coefs_df['feature_set'] = fs_name
        coefs_df['emotion'] = dl.le.classes_
        coefs_df['sub'] = sub
        coefs[fs_name] = coefs_df

    scores_df = pd.concat(scores, axis=0)
    return scores_df, coefs
def classify_subjects_parallel(sub, feature_spaces, model, cv):
    """ Helper function to parallelize analysis across subjects. """
    scores, preds, coefs = [], [], dict()
    for fs in feature_spaces:
        
        if not isinstance(fs, (tuple, list)):
            fs = (fs,)
        
        fs_name = '+'.join(fs)

        dl = DataLoader(sub=sub, log_level=30)
        dl.load_y(strategy_doubles='hard')
        dl.load_X(feature_set=fs, n_comp=100)
        X, y = dl.return_Xy()

        preds_, scores_, coefs_, model_ = cross_val_predict_and_score(
            estimator=model,
            X=X, y=y,
            cv=cv,
            scoring=tjur_score,
            between_sub=False,
            soft=True
        )
        joblib.dump(model_, f'models/sub-{sub}_analysis-within_split-train_fs-{fs_name}_model.jl')

        dl.log.warning(f"sub-{sub} scores: {np.round(scores_, 2)} (fs = {fs_name})")
        scores_df = pd.DataFrame(scores_, columns=['score'])
        scores_df['feature_set'] = fs_name
        scores_df['emotion'] = dl.le.classes_
        scores_df['sub'] = sub
        scores.append(scores_df)

        for i in range(len(preds_)):
            preds_[i]['feature_set'] = fs_name
            preds_[i]['sub'] = sub
            preds_[i]['rep'] = i
        
        preds.append(pd.concat(preds_, axis=0))

        coefs_df = pd.DataFrame(data=coefs_, columns=X.columns)
        coefs_df['feature_set'] = fs_name
        coefs_df['emotion'] = dl.le.classes_
        coefs_df['sub'] = sub
        coefs[fs_name] = coefs_df

    scores = pd.concat(scores, axis=0)
    preds = pd.concat(preds, axis=0)
    return preds, scores, coefs
def classify_fs_parallel(subs, fs, model, cv):
    """ Helper function to parallelize analysis across FSs. """

    if not isinstance(fs, (tuple, list)):
        fs = (fs, )

    fs_name = '+'.join(fs)

    X, y = [], []
    for sub in subs:
        dl = DataLoader(sub=sub, log_level=30)
        dl.load_y(strategy_doubles='hard')
        dl.load_X(feature_set=fs, n_comp=100)
        this_X, this_y = dl.return_Xy()
        X.append(this_X)
        y.append(this_y)

    X = pd.concat(X, axis=0)
    y = pd.concat(y, axis=0)

    preds_, scores_, coefs_, model_ = cross_val_predict_and_score(
        estimator=model, X=X, y=y, cv=cv, scoring=tjur_score, soft=True)

    joblib.dump(
        model_,
        f'models/sub-{sub}_analysis-between_split-train_fs-{fs_name}_model.jl')

    dl.log.warning(
        f"sub-{sub} scores: {np.round(scores_, 2)} (fs = {fs_name})")
    scores = pd.DataFrame(scores_, columns=['score'])
    scores['feature_set'] = fs_name
    scores['emotion'] = dl.le.classes_
    scores['sub'] = sub

    for i in range(len(preds_)):
        preds_[i]['feature_set'] = fs_name
        preds_[i]['rep'] = i

    preds = pd.concat(preds_, axis=0)

    coefs = pd.DataFrame(data=coefs_, columns=X.columns)
    coefs['feature_set'] = fs_name
    coefs['emotion'] = dl.le.classes_

    return preds, scores, coefs