Scikit-learn: Groupe stratifiéKFold

Créé le 11 avr. 2019  ·  48Commentaires  ·  Source: scikit-learn/scikit-learn

La description

Actuellement, sklearn n'a pas de fonction de groupe stratifié kfold. Soit nous pouvons utiliser la stratification, soit nous pouvons utiliser le groupe kfold. Cependant, il serait bon d'avoir les deux.

Je voudrais le mettre en œuvre, si nous décidons de l'avoir.

Commentaire le plus utile

Ce serait bien si les personnes intéressées pouvaient décrire leur cas d'utilisation et ce qu'elles en attendent vraiment.

Cas d'utilisation très courant en médecine et en biologie lorsque vous avez des mesures répétées.
Un exemple : supposons que vous souhaitiez classer une maladie, par exemple la maladie d'Alzheimer (MA) par rapport à des témoins sains à partir d'images IRM. Pour le même sujet, vous pouvez avoir plusieurs scans (provenant de séances de suivi ou de données longitudinales). Supposons que vous ayez un total de 1000 sujets, dont 200 sont diagnostiqués avec AD (classes déséquilibrées). La plupart des sujets ont un seul scan, mais pour certains d'entre eux 2 ou 3 images sont disponibles. Lors de la formation/du test du classificateur, vous voulez vous assurer que les images du même sujet sont toujours dans le même pli pour éviter les fuites de données.
Il est préférable d'utiliser StratifiedGroupKFold pour cela : stratifier pour tenir compte du déséquilibre des classes, mais avec la contrainte de groupe selon laquelle un sujet ne doit pas apparaître dans différents plis.
NB: Ce serait bien de le rendre répétable.

Ci-dessous un exemple d'implémentation, inspiré de kaggle-kernel .

import numpy as np
from collections import Counter, defaultdict
from sklearn.utils import check_random_state

class RepeatedStratifiedGroupKFold():

    def __init__(self, n_splits=5, n_repeats=1, random_state=None):
        self.n_splits = n_splits
        self.n_repeats = n_repeats
        self.random_state = random_state

    # Implementation based on this kaggle kernel:
    #    https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def split(self, X, y=None, groups=None):
        k = self.n_splits
        def eval_y_counts_per_fold(y_counts, fold):
            y_counts_per_fold[fold] += y_counts
            std_per_label = []
            for label in range(labels_num):
                label_std = np.std(
                    [y_counts_per_fold[i][label] / y_distr[label] for i in range(k)]
                )
                std_per_label.append(label_std)
            y_counts_per_fold[fold] -= y_counts
            return np.mean(std_per_label)

        rnd = check_random_state(self.random_state)
        for repeat in range(self.n_repeats):
            labels_num = np.max(y) + 1
            y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
            y_distr = Counter()
            for label, g in zip(y, groups):
                y_counts_per_group[g][label] += 1
                y_distr[label] += 1

            y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
            groups_per_fold = defaultdict(set)

            groups_and_y_counts = list(y_counts_per_group.items())
            rnd.shuffle(groups_and_y_counts)

            for g, y_counts in sorted(groups_and_y_counts, key=lambda x: -np.std(x[1])):
                best_fold = None
                min_eval = None
                for i in range(k):
                    fold_eval = eval_y_counts_per_fold(y_counts, i)
                    if min_eval is None or fold_eval < min_eval:
                        min_eval = fold_eval
                        best_fold = i
                y_counts_per_fold[best_fold] += y_counts
                groups_per_fold[best_fold].add(g)

            all_groups = set(groups)
            for i in range(k):
                train_groups = all_groups - groups_per_fold[i]
                test_groups = groups_per_fold[i]

                train_indices = [i for i, g in enumerate(groups) if g in train_groups]
                test_indices = [i for i, g in enumerate(groups) if g in test_groups]

                yield train_indices, test_indices

Comparaison RepeatedStratifiedKFold (un échantillon du même groupe peut apparaître dans les deux volets) avec RepeatedStratifiedGroupKFold :

import matplotlib.pyplot as plt
from sklearn import model_selection

def plot_cv_indices(cv, X, y, group, ax, n_splits, lw=10):
    for ii, (tr, tt) in enumerate(cv.split(X=X, y=y, groups=group)):
        indices = np.array([np.nan] * len(X))
        indices[tt] = 1
        indices[tr] = 0

        ax.scatter(range(len(indices)), [ii + .5] * len(indices),
                   c=indices, marker='_', lw=lw, cmap=plt.cm.coolwarm,
                   vmin=-.2, vmax=1.2)

    ax.scatter(range(len(X)), [ii + 1.5] * len(X), c=y, marker='_',
               lw=lw, cmap=plt.cm.Paired)
    ax.scatter(range(len(X)), [ii + 2.5] * len(X), c=group, marker='_',
               lw=lw, cmap=plt.cm.tab20c)

    yticklabels = list(range(n_splits)) + ['class', 'group']
    ax.set(yticks=np.arange(n_splits+2) + .5, yticklabels=yticklabels,
           xlabel='Sample index', ylabel="CV iteration",
           ylim=[n_splits+2.2, -.2], xlim=[0, 100])
    ax.set_title('{}'.format(type(cv).__name__), fontsize=15)


# demonstration
np.random.seed(1338)
n_splits = 4
n_repeats=5


# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)

percentiles_classes = [.4, .6]
y = np.hstack([[ii] * int(100 * perc) for ii, perc in enumerate(percentiles_classes)])

# Evenly spaced groups
g = np.hstack([[ii] * 5 for ii in range(20)])


fig, ax = plt.subplots(1,2, figsize=(14,4))

cv_nogrp = model_selection.RepeatedStratifiedKFold(n_splits=n_splits,
                                                   n_repeats=n_repeats,
                                                   random_state=1338)
cv_grp = RepeatedStratifiedGroupKFold(n_splits=n_splits,
                                      n_repeats=n_repeats,
                                      random_state=1338)

plot_cv_indices(cv_nogrp, X, y, g, ax[0], n_splits * n_repeats)
plot_cv_indices(cv_grp, X, y, g, ax[1], n_splits * n_repeats)

plt.show()

RepeatedStratifiedGroupKFold_demo

Tous les 48 commentaires

@TomDLT @NicolasHug Qu'en pensez-vous ?

Peut-être intéressant en théorie, mais je ne suis pas sûr de son utilité dans la pratique. Nous pouvons certainement garder le problème ouvert et voir combien de personnes demandent cette fonctionnalité

Supposez-vous que chaque groupe est dans une seule classe ?

Voir aussi #9413

@jnothman Oui, j'avais une chose similaire en tête. Cependant, je vois que la pull request est toujours ouverte. Je voulais dire qu'un groupe ne sera pas répété à travers les plis. Si nous avons des ID en tant que groupes, un même ID ne se produira pas dans plusieurs plis

Je comprends que cela est pertinent pour l'utilisation de RFECV.
Actuellement, cela utilise par défaut un cv StratifiedKFold. Son fit () prend également des groupes =
Cependant : il apparaît que les groupes ne sont pas respectés lors de l'exécution de fit(). Aucun avertissement (peut être considéré comme un bogue).

Le regroupement ET la stratification sont utiles pour les ensembles de données assez déséquilibrés avec une dépendance inter-enregistrements
(dans mon cas, le même individu a plusieurs enregistrements, mais il y a toujours un grand nombre de groupes = personnes par rapport au nombre de scissions ; j'imagine qu'il y aurait des problèmes pratiques car le nombre de groupes uniques dans la classe minoritaire se rapproche le nombre de divisions).

Alors : +1 !

Ce serait certainement utile. Par exemple, travailler avec des données médicales de séries chronologiques très déséquilibrées, garder les patients séparés mais équilibrer (approximativement) la classe déséquilibrée dans chaque pli.

J'ai également constaté que StratifiedKFold prend des groupes comme paramètre mais ne les regroupe pas en fonction d'eux, devrait probablement être signalé.

Une autre bonne utilisation de cette fonctionnalité serait les données financières, qui sont généralement très déséquilibrées. Dans mon cas, j'ai un ensemble de données très déséquilibré avec plusieurs enregistrements pour la même entité (juste des moments différents). Nous voulons faire un GroupKFold pour éviter les fuites, mais aussi stratifier car en raison du déséquilibre élevé, nous pourrions nous retrouver avec des groupes avec très peu ou pas de positifs.

voir aussi #14524 je pense ?

Un autre cas d'utilisation pour Stratified GroupShuffleSplit et GroupKFold est les conceptions biologiques à "mesures répétées", où vous avez plusieurs échantillons par sujet ou autre unité biologique parente. De plus, dans de nombreux ensembles de données du monde réel en biologie, il existe un déséquilibre de classe. Chaque groupe d'échantillons a la même classe. Il est donc important de stratifier et de garder les groupes ensemble.

La description

Actuellement, sklearn n'a pas de fonction de groupe stratifié kfold. Soit nous pouvons utiliser la stratification, soit nous pouvons utiliser le groupe kfold. Cependant, il serait bon d'avoir les deux.

Je voudrais le mettre en œuvre, si nous décidons de l'avoir.

Salut, je pense que ce serait très utile pour la médecine ML. Est-il déjà implémenté ?

@amueller Pensez-vous que nous devrions mettre cela en œuvre, étant donné que les gens sont intéressés par cela ?

Je suis très intéressé aussi... ce serait vraiment utile en spectroscopie lorsque vous avez plusieurs mesures répétées pour chacun de vos échantillons, elles doivent vraiment rester dans le même pli pendant la validation croisée. Et si vous essayez de classer plusieurs classes déséquilibrées, vous souhaitez également utiliser la fonction de stratification. donc moi aussi je vote pour ! Désolé, je ne suis pas assez bon pour participer au développement, mais pour ceux qui y participeront, vous pouvez être sûrs qu'il sera utilisé :-)
bravo à toute l'équipe. Merci!

Veuillez consulter les problèmes référencés et les relations publiques dans ce fil de discussion, car des travaux ont au moins été tentés sur StratifiedGroupKFold . J'ai déjà fait un StratifiedGroupShuffleSplit #15239 qui a juste besoin de tests mais que j'ai déjà utilisé pas mal pour mon propre travail.

Je pense que nous devrions le mettre en œuvre, mais je pense que je ne sais toujours pas ce que nous voulons réellement. @hermidalc a une restriction selon laquelle les membres du même groupe doivent appartenir à la même classe. Ce n'est pas le cas général, non ?

Ce serait bien si les personnes intéressées pouvaient décrire leur cas d'utilisation et ce qu'elles en attendent vraiment.

Il y a #15239 #14524 et #9413 dont je me souviens avoir tous une sémantique différente.

@amueller totalement d'accord avec vous, j'ai passé quelques heures aujourd'hui à chercher quelque chose entre les différentes versions disponibles (#15239 #14524 et #9413) mais je n'arrivais pas vraiment à comprendre si l'une d'entre elles correspondait à mon besoin. Voici donc mon cas d'utilisation si cela peut aider:
J'ai 1000 échantillons. chaque échantillon a été mesuré 3 fois avec un spectromètre NIR, donc chaque échantillon a 3 répliques que je veux garder ensemble tout du long...
Ces 1000 échantillons appartiennent à 6 classes différentes avec un nombre très différent d'échantillons dans chacune :
classe 1 : 400 échantillons
classe 2 : 300 échantillons
classe 3 : 100 échantillons
classe 4 : 100 échantillons
classe 5 : 70 échantillons
classe 6 : 30 échantillons
Je veux construire un classificateur pour chaque classe. Donc la classe 1 contre toutes les autres classes, puis la classe 2 contre toutes les autres classes, etc.
Pour maximiser la précision de chacun de mes classificateurs, il est important que j'aie des échantillons des 6 classes représentées dans chacun des plis, car mes classes ne sont pas si différentes donc cela aide vraiment à créer une bordure précise pour avoir toujours les 6 classes représentées dans chaque pli.

C'est pourquoi je crois qu'un groupe stratifié (toujours mes 6 classes représentées dans chaque pli) (gardez toujours les 3 mesures répétées de chacun de mes échantillons ensemble) kfold semble être tout à fait ce que je recherche ici.
Un avis ?

Mon cas d'utilisation et pourquoi j'ai écrit StratifiedGroupShuffleSplit est de prendre en charge les conceptions de mesures répétées https://en.wikipedia.org/wiki/Repeated_measures_design. Dans mes cas d'utilisation, les membres du même groupe doivent appartenir à la même classe.

@fcoppey Pour vous, les samples au sein d'un groupe ont toujours la même classe, non ?

@hermidalc Je ne connais pas très bien la terminologie, mais d'après wikipedia, cela ressemble à une "conception de mesures répétées" ne signifie pas que le même groupe doit être dans la même classe car il est dit "Un essai croisé a une conception de mesures répétées dans laquelle chaque patient est assigné à une séquence de deux traitements ou plus, dont l'un peut être un traitement standard ou un placebo."
En reliant cela à un paramètre ML, vous pouvez soit essayer de prédire à partir de mesures si un individu vient de recevoir un traitement ou un placebo, soit vous pouvez essayer de prédire un résultat compte tenu du traitement.
Pour l'un ou l'autre, la classe d'un même individu pourrait changer, n'est-ce pas ?

Quel que soit le nom, il me semble que vous avez tous les deux le même cas d'utilisation, alors que je pensais à un cas similaire à celui décrit dans l'étude croisée. Ou peut-être un peu plus simple : vous pourriez avoir un patient qui tombe malade au fil du temps (ou qui s'améliore), de sorte que le résultat pour un patient peut changer.

En fait, l'article de wikipedia auquel vous vous connectez dit explicitement "L'analyse longitudinale - Les conceptions de mesures répétées permettent aux chercheurs de surveiller l'évolution des participants dans le temps, à la fois dans les situations à long et à court terme.", donc je pense que cela signifie que le changement de classe est inclus.
S'il y a un autre mot qui signifie que la mesure est faite dans les mêmes conditions, alors on pourrait utiliser ce mot ?

@amueller oui tu as raison, j'ai réalisé que j'avais mal écrit ci-dessus où je voulais dire dans mes cas d'utilisation de cette conception pas dans ce cas d'utilisation en général.

Il peut y avoir de nombreux types assez élaborés de modèles de mesures répétées, bien que dans les deux types dont j'ai eu besoin StratifiedGroupShuffleSplit , la restriction de même classe au sein du groupe soit maintenue (échantillonnage longitudinal avant et après le traitement lors de la prédiction de la réponse au traitement, prétraitement multiple échantillons par sujet à différents endroits du corps lors de la prédiction de la réponse au traitement).

J'avais besoin de quelque chose qui fonctionne tout de suite, donc je voulais le mettre à la disposition des autres et pour commencer quelque chose sur sklearn, et si je ne me trompe pas, il est plus compliqué de concevoir la logique de stratification lorsque les étiquettes de classe de groupe peuvent être différentes.

@amueller oui toujours. Ce sont des répliques d'une même mesure afin d'inclure l'intravariabilité de l'appareil dans la prédiction.

@hermidalc oui, ce cas est beaucoup plus simple. S'il s'agit d'un besoin commun, je suis heureux que nous l'ajoutions. Nous devrions simplement nous assurer que le nom indique clairement ce qu'il fait, et nous devrions réfléchir à la question de savoir si ces deux versions devraient vivre dans la même classe.

Il devrait être assez facile de faire en sorte que StratifiedKFold fasse cela. Il existe deux options : assurez-vous que chaque pli contient un nombre similaire d'échantillons ou assurez-vous que chaque pli contient un nombre similaire de groupes.
Le second est trivialement à faire (en prétendant simplement que chaque groupe est un seul point et en passant à StratifiedKFold ). C'est ce que vous faites dans vos relations publiques, semble-t-il.

Je pense que GroupKFold échange heuristiquement les deux en ajoutant le plus petit pli en premier. Je ne sais pas comment cela se traduirait dans le cas stratifié, donc je suis heureux d'utiliser votre approche.

Devrions-nous également ajouter GroupStratifiedKFold dans le même PR ? Ou laisser ça pour plus tard ?
Les autres PR ont des objectifs légèrement différents. Ce serait bien si quelqu'un pouvait écrire quels sont les différents cas d'utilisation (je n'ai probablement pas le temps en ce moment).

+1 pour gérer séparément la contrainte de groupe où tous les échantillons ont la même classe.

@hermidalc oui, ce cas est beaucoup plus simple. S'il s'agit d'un besoin commun, je suis heureux que nous l'ajoutions. Nous devrions simplement nous assurer que le nom indique clairement ce qu'il fait, et nous devrions réfléchir à la question de savoir si ces deux versions devraient vivre dans la même classe.

Je ne comprends pas tout à fait cela, un StratifiedGroupShuffleSplit et StratifiedGroupKFold où vous pouvez avoir des membres de chaque groupe appartenant à des classes différentes devraient avoir exactement le même comportement de division lorsque l'utilisateur spécifie que tous les membres du groupe doivent être de la même classe. Quand peut-on simplement améliorer les composants internes plus tard et le comportement existant sera-t-il le même ?

Le second est trivialement à faire (en prétendant simplement que chaque groupe est un seul point et en passant à StratifiedKFold ). C'est ce que vous faites dans vos relations publiques, semble-t-il.

Je pense que GroupKFold échange heuristiquement les deux en ajoutant le plus petit pli en premier. Je ne sais pas comment cela se traduirait dans le cas stratifié, donc je suis heureux d'utiliser votre approche.

Devrions-nous également ajouter GroupStratifiedKFold dans le même PR ? Ou laisser ça pour plus tard ?
Les autres PR ont des objectifs légèrement différents. Ce serait bien si quelqu'un pouvait écrire quels sont les différents cas d'utilisation (je n'ai probablement pas le temps en ce moment).

J'ajouterai StatifiedGroupKFold en utilisant l'approche "chaque échantillon unique de groupe" que j'ai utilisée.

Ce serait bien si les personnes intéressées pouvaient décrire leur cas d'utilisation et ce qu'elles en attendent vraiment.

Cas d'utilisation très courant en médecine et en biologie lorsque vous avez des mesures répétées.
Un exemple : supposons que vous souhaitiez classer une maladie, par exemple la maladie d'Alzheimer (MA) par rapport à des témoins sains à partir d'images IRM. Pour le même sujet, vous pouvez avoir plusieurs scans (provenant de séances de suivi ou de données longitudinales). Supposons que vous ayez un total de 1000 sujets, dont 200 sont diagnostiqués avec AD (classes déséquilibrées). La plupart des sujets ont un seul scan, mais pour certains d'entre eux 2 ou 3 images sont disponibles. Lors de la formation/du test du classificateur, vous voulez vous assurer que les images du même sujet sont toujours dans le même pli pour éviter les fuites de données.
Il est préférable d'utiliser StratifiedGroupKFold pour cela : stratifier pour tenir compte du déséquilibre des classes, mais avec la contrainte de groupe selon laquelle un sujet ne doit pas apparaître dans différents plis.
NB: Ce serait bien de le rendre répétable.

Ci-dessous un exemple d'implémentation, inspiré de kaggle-kernel .

import numpy as np
from collections import Counter, defaultdict
from sklearn.utils import check_random_state

class RepeatedStratifiedGroupKFold():

    def __init__(self, n_splits=5, n_repeats=1, random_state=None):
        self.n_splits = n_splits
        self.n_repeats = n_repeats
        self.random_state = random_state

    # Implementation based on this kaggle kernel:
    #    https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def split(self, X, y=None, groups=None):
        k = self.n_splits
        def eval_y_counts_per_fold(y_counts, fold):
            y_counts_per_fold[fold] += y_counts
            std_per_label = []
            for label in range(labels_num):
                label_std = np.std(
                    [y_counts_per_fold[i][label] / y_distr[label] for i in range(k)]
                )
                std_per_label.append(label_std)
            y_counts_per_fold[fold] -= y_counts
            return np.mean(std_per_label)

        rnd = check_random_state(self.random_state)
        for repeat in range(self.n_repeats):
            labels_num = np.max(y) + 1
            y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
            y_distr = Counter()
            for label, g in zip(y, groups):
                y_counts_per_group[g][label] += 1
                y_distr[label] += 1

            y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
            groups_per_fold = defaultdict(set)

            groups_and_y_counts = list(y_counts_per_group.items())
            rnd.shuffle(groups_and_y_counts)

            for g, y_counts in sorted(groups_and_y_counts, key=lambda x: -np.std(x[1])):
                best_fold = None
                min_eval = None
                for i in range(k):
                    fold_eval = eval_y_counts_per_fold(y_counts, i)
                    if min_eval is None or fold_eval < min_eval:
                        min_eval = fold_eval
                        best_fold = i
                y_counts_per_fold[best_fold] += y_counts
                groups_per_fold[best_fold].add(g)

            all_groups = set(groups)
            for i in range(k):
                train_groups = all_groups - groups_per_fold[i]
                test_groups = groups_per_fold[i]

                train_indices = [i for i, g in enumerate(groups) if g in train_groups]
                test_indices = [i for i, g in enumerate(groups) if g in test_groups]

                yield train_indices, test_indices

Comparaison RepeatedStratifiedKFold (un échantillon du même groupe peut apparaître dans les deux volets) avec RepeatedStratifiedGroupKFold :

import matplotlib.pyplot as plt
from sklearn import model_selection

def plot_cv_indices(cv, X, y, group, ax, n_splits, lw=10):
    for ii, (tr, tt) in enumerate(cv.split(X=X, y=y, groups=group)):
        indices = np.array([np.nan] * len(X))
        indices[tt] = 1
        indices[tr] = 0

        ax.scatter(range(len(indices)), [ii + .5] * len(indices),
                   c=indices, marker='_', lw=lw, cmap=plt.cm.coolwarm,
                   vmin=-.2, vmax=1.2)

    ax.scatter(range(len(X)), [ii + 1.5] * len(X), c=y, marker='_',
               lw=lw, cmap=plt.cm.Paired)
    ax.scatter(range(len(X)), [ii + 2.5] * len(X), c=group, marker='_',
               lw=lw, cmap=plt.cm.tab20c)

    yticklabels = list(range(n_splits)) + ['class', 'group']
    ax.set(yticks=np.arange(n_splits+2) + .5, yticklabels=yticklabels,
           xlabel='Sample index', ylabel="CV iteration",
           ylim=[n_splits+2.2, -.2], xlim=[0, 100])
    ax.set_title('{}'.format(type(cv).__name__), fontsize=15)


# demonstration
np.random.seed(1338)
n_splits = 4
n_repeats=5


# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)

percentiles_classes = [.4, .6]
y = np.hstack([[ii] * int(100 * perc) for ii, perc in enumerate(percentiles_classes)])

# Evenly spaced groups
g = np.hstack([[ii] * 5 for ii in range(20)])


fig, ax = plt.subplots(1,2, figsize=(14,4))

cv_nogrp = model_selection.RepeatedStratifiedKFold(n_splits=n_splits,
                                                   n_repeats=n_repeats,
                                                   random_state=1338)
cv_grp = RepeatedStratifiedGroupKFold(n_splits=n_splits,
                                      n_repeats=n_repeats,
                                      random_state=1338)

plot_cv_indices(cv_nogrp, X, y, g, ax[0], n_splits * n_repeats)
plot_cv_indices(cv_grp, X, y, g, ax[1], n_splits * n_repeats)

plt.show()

RepeatedStratifiedGroupKFold_demo

+1 pour stratifiéGroupKfold. J'essaie de détecter les chutes de personnes âgées, en prenant des capteurs de la montre intelligente. puisque nous n'avons pas beaucoup de données sur les chutes - nous faisons des simulations avec différentes montres qui obtiennent différentes classes. Je fais aussi des augmentations sur les données avant de les former. à partir de chaque point de données, je crée 9 points - et c'est un groupe. il est important qu'un groupe ne soit pas à la fois en train et en test comme expliqué

J'aimerais également pouvoir utiliser StratifiedGroupKFold. Je regarde un ensemble de données pour prédire les crises financières, où les années avant, après et pendant chaque crise constituent son propre groupe. Pendant la formation et la validation croisée, les membres de chaque groupe ne doivent pas fuir entre les plis.

Est-il possible de généraliser cela pour le scénario multilabel (Multilabel_
stratifiéGroupKfold) ?

+1 pour ça. Nous analysons les comptes d'utilisateurs pour détecter le spam, nous souhaitons donc regrouper par utilisateur, mais également stratifier car le spam est relativement peu fréquent. Pour notre cas d'utilisation, tout utilisateur qui spamme une fois est signalé comme spammeur dans toutes les données, de sorte qu'un membre du groupe aura toujours la même étiquette.

Merci d'avoir fourni un cas d'utilisation classique pour encadrer la documentation,
@philip-iv !

J'ai ajouté une implémentation StratifiedGroupKFold à mon même PR #15239 en tant que StratifiedGroupShuffleSplit .

Bien que, comme vous pouvez le voir dans le PR, la logique pour les deux est beaucoup plus simple que https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602 car le mien ne tente que de préserver le pourcentage de groupes pour chaque classe (pas le pourcentage d'échantillons) afin que je puisse tirer parti du code StratifiedKFold et StratifiedShuffleSplit existant en lui transmettant des informations de groupe uniques. Mais les deux implémentations produisent des plis où les échantillons de chaque groupe restent ensemble dans le même pli.

Bien que je voterais pour des méthodes plus sophistiquées basées sur https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602

Voici des versions complètes de StratifiedGroupKFold et RepeatedStratifiedGroupKFold utilisant le code fourni par @mrunibe que j'ai encore simplifié et modifié quelques éléments. Ces cours suivent également la conception des autres cours de CV sklearn du même type.

class StratifiedGroupKFold(_BaseKFold):
    """Stratified K-Folds iterator variant with non-overlapping groups.

    This cross-validation object is a variation of StratifiedKFold that returns
    stratified folds with non-overlapping groups. The folds are made by
    preserving the percentage of samples for each class.

    The same group will not appear in two different folds (the number of
    distinct groups has to be at least equal to the number of folds).

    The difference between GroupKFold and StratifiedGroupKFold is that
    the former attempts to create balanced folds such that the number of
    distinct groups is approximately the same in each fold, whereas
    StratifiedGroupKFold attempts to create folds which preserve the
    percentage of samples for each class.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    shuffle : bool, default=False
        Whether to shuffle each class's samples before splitting into batches.
        Note that the samples within each split will not be shuffled.

    random_state : int or RandomState instance, default=None
        When `shuffle` is True, `random_state` affects the ordering of the
        indices, which controls the randomness of each fold for each class.
        Otherwise, leave `random_state` as `None`.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import StratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = StratifiedGroupKFold(n_splits=3)
    >>> for train_idxs, test_idxs in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 6 6 7]
           [1 1 1 0 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 8 8]
           [0 0 1 1 1 0 0]
    TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
           [0 0 1 1 1 1 0 0 0 0 0 0]
     TEST: [2 2 6 6 7]
           [1 1 0 0 0]
    TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
           [0 0 1 1 1 1 1 0 0 0 0 0]
     TEST: [4 5 5 5 5]
           [1 0 0 0 0]

    See also
    --------
    StratifiedKFold: Takes class information into account to build folds which
        retain class distributions (for binary or multiclass classification
        tasks).

    GroupKFold: K-fold iterator variant with non-overlapping groups.
    """

    def __init__(self, n_splits=5, shuffle=False, random_state=None):
        super().__init__(n_splits=n_splits, shuffle=shuffle,
                         random_state=random_state)

    # Implementation based on this kaggle kernel:
    # https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def _iter_test_indices(self, X, y, groups):
        labels_num = np.max(y) + 1
        y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
        y_distr = Counter()
        for label, group in zip(y, groups):
            y_counts_per_group[group][label] += 1
            y_distr[label] += 1

        y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
        groups_per_fold = defaultdict(set)

        groups_and_y_counts = list(y_counts_per_group.items())
        rng = check_random_state(self.random_state)
        if self.shuffle:
            rng.shuffle(groups_and_y_counts)

        for group, y_counts in sorted(groups_and_y_counts,
                                      key=lambda x: -np.std(x[1])):
            best_fold = None
            min_eval = None
            for i in range(self.n_splits):
                y_counts_per_fold[i] += y_counts
                std_per_label = []
                for label in range(labels_num):
                    std_per_label.append(np.std(
                        [y_counts_per_fold[j][label] / y_distr[label]
                         for j in range(self.n_splits)]))
                y_counts_per_fold[i] -= y_counts
                fold_eval = np.mean(std_per_label)
                if min_eval is None or fold_eval < min_eval:
                    min_eval = fold_eval
                    best_fold = i
            y_counts_per_fold[best_fold] += y_counts
            groups_per_fold[best_fold].add(group)

        for i in range(self.n_splits):
            test_indices = [idx for idx, group in enumerate(groups)
                            if group in groups_per_fold[i]]
            yield test_indices


class RepeatedStratifiedGroupKFold(_RepeatedSplits):
    """Repeated Stratified K-Fold cross validator.

    Repeats Stratified K-Fold with non-overlapping groups n times with
    different randomization in each repetition.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    n_repeats : int, default=10
        Number of times cross-validator needs to be repeated.

    random_state : int or RandomState instance, default=None
        Controls the generation of the random states for each repetition.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import RepeatedStratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = RepeatedStratifiedGroupKFold(n_splits=2, n_repeats=2,
    ...                                   random_state=36851234)
    >>> for train_index, test_index in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
    TRAIN: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
     TEST: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
    TRAIN: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]
     TEST: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
    TRAIN: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
     TEST: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]

    Notes
    -----
    Randomized CV splitters may return different results for each call of
    split. You can make the results identical by setting `random_state`
    to an integer.

    See also
    --------
    RepeatedStratifiedKFold: Repeats Stratified K-Fold n times.
    """

    def __init__(self, n_splits=5, n_repeats=10, random_state=None):
        super().__init__(StratifiedGroupKFold, n_splits=n_splits,
                         n_repeats=n_repeats, random_state=random_state)

@hermidalc Je suis assez confus quant à ce que nous avons résolu en y repensant de temps en temps. (Malheureusement, mon temps n'est plus ce qu'il était !) Pouvez-vous me donner une idée de ce que vous recommanderiez d'inclure dans scikit-learn ?

@hermidalc Je suis assez confus quant à ce que nous avons résolu en y repensant de temps en temps. (Malheureusement, mon temps n'est plus ce qu'il était !) Pouvez-vous me donner une idée de ce que vous recommanderiez d'inclure dans scikit-learn ?

J'ai voulu faire une meilleure implémentation que ce que j'ai fait dans #15239. La mise en œuvre dans ce PR fonctionne mais se stratifie sur les groupes pour rendre la logique simple, bien que ce ne soit pas idéal.

Donc ce que j'ai fait ci-dessus (merci à @mrunibe et kaggle de jakubwasikowski) est une meilleure implémentation de StratifiedGroupKFold qui se stratifie sur les échantillons. Je veux porter la même logique pour faire un meilleur StratifiedGroupShuffleSplit et ensuite ce sera prêt. Je vais mettre le nouveau code dans #15239 pour remplacer l'ancienne implémentation.

Je m'excuse pour mes PR qui ne sont pas terminés, je fais mon doctorat donc je n'ai jamais le temps !

Merci @hermidalc et @mrunibe pour avoir fourni la mise en œuvre. J'ai également recherché une méthode StratifiedGroupKFold pour traiter les données médicales qui présentent un fort déséquilibre de classe et un nombre très variable d'échantillons par sujet. GroupKFold crée par lui-même des sous-ensembles de données d'apprentissage contenant une seule classe.

Je veux porter la même logique pour faire un meilleur StratifiedGroupShuffleSplit et ensuite ce sera prêt.

Nous pourrions certainement envisager de fusionner StratifiedGroupKFold avant que StratifiedGroupShuffleSplit ne soit prêt.

Je m'excuse pour mes PR qui ne sont pas terminés, je fais mon doctorat donc je n'ai jamais le temps !

Faites-nous savoir si vous voulez de l'aide pour le terminer!

Et bonne chance pour votre travail de doctorat

Voici des versions complètes de StratifiedGroupKFold et RepeatedStratifiedGroupKFold utilisant le code fourni par @mrunibe que j'ai encore simplifié et modifié quelques éléments. Ces cours suivent également la conception des autres cours de CV sklearn du même type.

Est-il possible d'essayer cela? J'ai essayé de copier et coller avec certaines des différentes dépendances, mais cela ne finissait jamais. J'aimerais essayer cette classe dans mon projet. J'essaie juste de voir s'il existe maintenant un moyen de le faire.

@hermidalc J'espère que votre travail de doctorat a été réussi !
J'ai hâte de voir cette mise en œuvre également puisque mon travail de doctorat en géosciences a besoin de cette fonctionnalité de stratification avec contrôle de groupe. J'ai passé quelques heures à mettre en œuvre cette idée de fractionnement manuel sur mon projet. Mais j'ai renoncé à le terminer pour la même raison... les progrès du doctorat. Donc, je peux tout à fait comprendre comment le travail de doctorat peut torturer le temps d'une personne. LOL Pas de pression. Pour l'instant, j'utilise GroupShuffleSplit comme alternative.

Acclamations

@bfeeny @dispink il est très facile d'utiliser les deux classes que j'ai écrites ci-dessus. Créez un fichier par exemple split.py avec ce qui suit. Ensuite dans votre code utilisateur si le script est dans le même répertoire que split.py vous importez simplement from split import StratifiedGroupKFold, RepeatedStratifiedGroupKFold

from collections import Counter, defaultdict

import numpy as np

from sklearn.model_selection._split import _BaseKFold, _RepeatedSplits
from sklearn.utils.validation import check_random_state


class StratifiedGroupKFold(_BaseKFold):
    """Stratified K-Folds iterator variant with non-overlapping groups.

    This cross-validation object is a variation of StratifiedKFold that returns
    stratified folds with non-overlapping groups. The folds are made by
    preserving the percentage of samples for each class.

    The same group will not appear in two different folds (the number of
    distinct groups has to be at least equal to the number of folds).

    The difference between GroupKFold and StratifiedGroupKFold is that
    the former attempts to create balanced folds such that the number of
    distinct groups is approximately the same in each fold, whereas
    StratifiedGroupKFold attempts to create folds which preserve the
    percentage of samples for each class.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    shuffle : bool, default=False
        Whether to shuffle each class's samples before splitting into batches.
        Note that the samples within each split will not be shuffled.

    random_state : int or RandomState instance, default=None
        When `shuffle` is True, `random_state` affects the ordering of the
        indices, which controls the randomness of each fold for each class.
        Otherwise, leave `random_state` as `None`.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import StratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = StratifiedGroupKFold(n_splits=3)
    >>> for train_idxs, test_idxs in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 6 6 7]
           [1 1 1 0 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 8 8]
           [0 0 1 1 1 0 0]
    TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
           [0 0 1 1 1 1 0 0 0 0 0 0]
     TEST: [2 2 6 6 7]
           [1 1 0 0 0]
    TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
           [0 0 1 1 1 1 1 0 0 0 0 0]
     TEST: [4 5 5 5 5]
           [1 0 0 0 0]

    See also
    --------
    StratifiedKFold: Takes class information into account to build folds which
        retain class distributions (for binary or multiclass classification
        tasks).

    GroupKFold: K-fold iterator variant with non-overlapping groups.
    """

    def __init__(self, n_splits=5, shuffle=False, random_state=None):
        super().__init__(n_splits=n_splits, shuffle=shuffle,
                         random_state=random_state)

    # Implementation based on this kaggle kernel:
    # https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def _iter_test_indices(self, X, y, groups):
        labels_num = np.max(y) + 1
        y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
        y_distr = Counter()
        for label, group in zip(y, groups):
            y_counts_per_group[group][label] += 1
            y_distr[label] += 1

        y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
        groups_per_fold = defaultdict(set)

        groups_and_y_counts = list(y_counts_per_group.items())
        rng = check_random_state(self.random_state)
        if self.shuffle:
            rng.shuffle(groups_and_y_counts)

        for group, y_counts in sorted(groups_and_y_counts,
                                      key=lambda x: -np.std(x[1])):
            best_fold = None
            min_eval = None
            for i in range(self.n_splits):
                y_counts_per_fold[i] += y_counts
                std_per_label = []
                for label in range(labels_num):
                    std_per_label.append(np.std(
                        [y_counts_per_fold[j][label] / y_distr[label]
                         for j in range(self.n_splits)]))
                y_counts_per_fold[i] -= y_counts
                fold_eval = np.mean(std_per_label)
                if min_eval is None or fold_eval < min_eval:
                    min_eval = fold_eval
                    best_fold = i
            y_counts_per_fold[best_fold] += y_counts
            groups_per_fold[best_fold].add(group)

        for i in range(self.n_splits):
            test_indices = [idx for idx, group in enumerate(groups)
                            if group in groups_per_fold[i]]
            yield test_indices


class RepeatedStratifiedGroupKFold(_RepeatedSplits):
    """Repeated Stratified K-Fold cross validator.

    Repeats Stratified K-Fold with non-overlapping groups n times with
    different randomization in each repetition.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    n_repeats : int, default=10
        Number of times cross-validator needs to be repeated.

    random_state : int or RandomState instance, default=None
        Controls the generation of the random states for each repetition.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import RepeatedStratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = RepeatedStratifiedGroupKFold(n_splits=2, n_repeats=2,
    ...                                   random_state=36851234)
    >>> for train_index, test_index in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
    TRAIN: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
     TEST: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
    TRAIN: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]
     TEST: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
    TRAIN: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
     TEST: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]

    Notes
    -----
    Randomized CV splitters may return different results for each call of
    split. You can make the results identical by setting `random_state`
    to an integer.

    See also
    --------
    RepeatedStratifiedKFold: Repeats Stratified K-Fold n times.
    """

    def __init__(self, n_splits=5, n_repeats=10, random_state=None):
        super().__init__(StratifiedGroupKFold, n_splits=n_splits,
                         n_repeats=n_repeats, random_state=random_state)

@hermidalc Merci pour la réponse positive !
Je l'adopte rapidement comme tu l'as décrit. Cependant, je ne peux obtenir que les fractionnements qui ne contiennent que des données dans l'ensemble d'entraînement ou de test. Pour autant que je comprenne la description du code, il n'y a pas de paramètre pour spécifier la proportion entre les ensembles d'entraînement et de test, n'est-ce pas ?
Je sais que c'est un conflit entre la stratification, le contrôle de groupe et la proportion des ensembles de données... C'est pourquoi j'ai renoncé à continuer... Mais peut-être que nous pouvons encore trouver des compromis pour contourner.
image

Sincèrement

@hermidalc Merci pour la réponse positive !
Je l'adopte rapidement comme tu l'as décrit. Cependant, je ne peux obtenir que les fractionnements qui ne contiennent que des données dans l'ensemble d'entraînement ou de test. Pour autant que je comprenne la description du code, il n'y a pas de paramètre pour spécifier la proportion entre les ensembles d'entraînement et de test, n'est-ce pas ?
Je sais que c'est un conflit entre la stratification, le contrôle de groupe et la proportion des ensembles de données... C'est pourquoi j'ai renoncé à continuer... Mais peut-être que nous pouvons encore trouver des compromis pour contourner.

Pour tester, j'ai créé le split.py et j'ai exécuté cet exemple dans ipython et cela fonctionne. J'utilise depuis longtemps ces itérateurs de CV personnalisés dans mon travail et ils n'ont aucun problème de mon côté. BTW J'utilise scikit-learn 0.22.2 et non 0.23.x, donc je ne sais pas si c'est la cause du problème. Pourriez-vous s'il vous plaît essayer d'exécuter cet exemple ci-dessous et voir si vous pouvez le reproduire? Si vous le pouvez, cela pourrait être quelque chose avec les y et groups dans votre travail.

In [6]: import numpy as np 
   ...: from split import StratifiedGroupKFold 
   ...:  
   ...: X = np.ones((17, 2)) 
   ...: y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 
   ...: groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8]) 
   ...: cv = StratifiedGroupKFold(n_splits=3, shuffle=True, random_state=777) 
   ...: for train_idxs, test_idxs in cv.split(X, y, groups): 
   ...:     print("TRAIN:", groups[train_idxs]) 
   ...:     print("      ", y[train_idxs]) 
   ...:     print(" TEST:", groups[test_idxs]) 
   ...:     print("      ", y[test_idxs]) 
   ...:                                                                                                                                                                                                    
TRAIN: [2 2 4 5 5 5 5 6 6 7]
       [1 1 1 0 0 0 0 0 0 0]
 TEST: [1 1 3 3 3 8 8]
       [0 0 1 1 1 0 0]
TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
       [0 0 1 1 1 1 0 0 0 0 0 0]
 TEST: [2 2 6 6 7]
       [1 1 0 0 0]
TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
       [0 0 1 1 1 1 1 0 0 0 0 0]
 TEST: [4 5 5 5 5]
       [1 0 0 0 0]

Il semble y avoir un intérêt régulier pour cette fonctionnalité, @hermidalc , et nous
pourrait probablement trouver quelqu'un pour le terminer si cela ne vous dérangeait pas.

@hermidalc 'Vous devez vous assurer que chaque échantillon du même groupe a la même étiquette de classe.' De toute évidence, c'est le problème. Mes échantillons dans le même groupe ne partagent pas la même classe. Mmm... ça semble être une autre branche du développement.
Merci beaucoup en tout cas.

@hermidalc 'Vous devez vous assurer que chaque échantillon du même groupe a la même étiquette de classe.' De toute évidence, c'est le problème. Mes échantillons dans le même groupe ne partagent pas la même classe. Mmm... ça semble être une autre branche du développement.
Merci beaucoup en tout cas.

Oui, cela a été discuté dans divers sujets ici. C'est un autre cas d'utilisation plus complexe qui est utile, mais beaucoup comme moi n'ont pas besoin de ce cas d'utilisation actuellement, mais avaient besoin de quelque chose qui garde les groupes ensemble tout en stratifiant les échantillons. L'exigence du code ci-dessus est que tous les échantillons de chaque groupe appartiennent à la même classe.

En fait @dispink j'avais tort, cet algorithme n'exige pas que tous les membres d'un groupe appartiennent à la même classe. Par example:

In [2]: X = np.ones((17, 2)) 
   ...: y =      np.array([0, 2, 1, 1, 2, 0, 0, 1, 2, 1, 1, 1, 0, 2, 0, 1, 0]) 
   ...: groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8]) 
   ...: cv = StratifiedGroupKFold(n_splits=3) 
   ...: for train_idxs, test_idxs in cv.split(X, y, groups): 
   ...:     print("TRAIN:", groups[train_idxs]) 
   ...:     print("      ", y[train_idxs]) 
   ...:     print(" TEST:", groups[test_idxs]) 
   ...:     print("      ", y[test_idxs]) 
   ...:                                                                                                                                                                                                    
TRAIN: [1 1 2 2 3 3 3 4 8 8]
       [0 2 1 1 2 0 0 1 1 0]
 TEST: [5 5 5 5 6 6 7]
       [2 1 1 1 0 2 0]
TRAIN: [1 1 4 5 5 5 5 6 6 7 8 8]
       [0 2 1 2 1 1 1 0 2 0 1 0]
 TEST: [2 2 3 3 3]
       [1 1 2 0 0]
TRAIN: [2 2 3 3 3 5 5 5 5 6 6 7]
       [1 1 2 0 0 2 1 1 1 0 2 0]
 TEST: [1 1 4 8 8]
       [0 2 1 1 0]

Je ne sais donc pas exactement ce qui se passe avec vos données, car même avec vos captures d'écran, vous ne pouvez pas vraiment voir quelle est la disposition de vos données et ce qui pourrait se passer. Je suggérerais que vous reproduisiez d'abord les exemples que j'ai montrés ici pour vous assurer qu'il ne s'agit pas d'un problème de version scikit-learn (puisque j'utilise 0.22.2) et si vous pouvez le reproduire, je vous suggérerais de commencer à partir de petites parties de votre données et testez-les. L'utilisation d'environ 104 000 échantillons est difficile à résoudre.

@hermidalc Merci pour la réponse !
Je peux en fait reproduire le résultat ci-dessus, donc je suis en train de dépanner avec des données plus petites maintenant.

+1

Est-ce que ça dérange quelqu'un si je récupère ce problème ?
Il semble que # 15239 avec le https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -600894432 ont déjà une implémentation et seuls les tests unitaires restent à faire.

Cette page vous a été utile?
0 / 5 - 0 notes