Scikit-learn: Grupo estratificadoKFold

Creado en 11 abr. 2019  ·  48Comentarios  ·  Fuente: scikit-learn/scikit-learn

Descripción

Actualmente, sklearn no tiene una función de kfold de grupo estratificado. O podemos usar la estratificación o podemos usar el grupo kfold. Sin embargo, sería bueno tener ambos.

Me gustaría implementarlo, si decidimos tenerlo.

Comentario más útil

Sería bueno si las personas interesadas pudieran describir su caso de uso y lo que realmente quieren de esto.

Caso de uso muy común en medicina y biología cuando tiene medidas repetidas.
Un ejemplo: suponga que desea clasificar una enfermedad, por ejemplo, la enfermedad de Alzheimer (EA) frente a controles sanos a partir de imágenes de RM. Para el mismo sujeto, es posible que tenga varios escaneos (de sesiones de seguimiento o datos longitudinales). Supongamos que tiene un total de 1000 sujetos, 200 de ellos diagnosticados con AD (clases desequilibradas). La mayoría de los sujetos tienen un escaneo, pero para algunos de ellos hay 2 o 3 imágenes disponibles. Al entrenar/probar el clasificador, debe asegurarse de que las imágenes del mismo tema estén siempre en el mismo pliegue para evitar la fuga de datos.
Es mejor usar StratifiedGroupKFold para esto: estratificar para tener en cuenta el desequilibrio de clases pero con la restricción de grupo de que un sujeto no debe aparecer en diferentes pliegues.
NB: Sería bueno hacerlo repetible.

A continuación, una implementación de ejemplo, inspirada en 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

Comparando RepeatedStratifiedKFold (una muestra del mismo grupo puede aparecer en ambos pliegues) con 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

Todos 48 comentarios

@TomDLT @NicolasHug ¿Qué opinas?

Puede ser interesante en teoría, pero no estoy seguro de cuán útil sería en la práctica. Sin duda, podemos mantener el problema abierto y ver cuántas personas solicitan esta función.

¿Supones que cada grupo está en una sola clase?

Ver también #9413

@jnothman Sí, tenía algo similar en mente. Sin embargo, veo que la solicitud de extracción aún está abierta. Quise decir que un grupo no se repetirá en los pliegues. Si tenemos ID como grupos, no se producirá una misma ID en varios pliegues

Entiendo que esto es relevante para el uso de RFECV.
Actualmente, el valor predeterminado es usar un cv StratifiedKFold. Su ajuste () también toma grupos =
Sin embargo: parece que los grupos no se respetan al ejecutar fit(). Sin advertencia (podría considerarse un error).

La agrupación Y la estratificación son útiles para conjuntos de datos bastante desequilibrados con dependencia entre registros
(en mi caso, el mismo individuo tiene múltiples registros, pero todavía hay una gran cantidad de grupos = personas en relación con la cantidad de divisiones; imagino que habría problemas prácticos ya que la cantidad de grupos únicos en la clase minoritaria se acerca a el número de divisiones).

Entonces: +1!

Esto definitivamente sería útil. Por ejemplo, trabajar con datos médicos de series de tiempo altamente desequilibrados, manteniendo a los pacientes separados pero (aproximadamente) equilibrando la clase desequilibrada en cada pliegue.

También descubrí que StratifiedKFold toma grupos como parámetro pero no se agrupa de acuerdo con ellos, probablemente debería estar marcado.

Otro buen uso de esta función serían los datos financieros, que suelen estar muy desequilibrados. En mi caso, tengo un conjunto de datos muy desequilibrado con varios registros para la misma entidad (solo diferentes puntos en el tiempo). Queremos hacer un GroupKFold para evitar fugas, pero también estratificar ya que debido al alto desequilibrio, podríamos terminar con grupos con muy pocos o ninguno positivo.

también vea # 14524, creo?

Otro caso de uso para Stratified GroupShuffleSplit y GroupKFold son los diseños biológicos de "medidas repetidas", donde tiene múltiples muestras por sujeto u otra unidad biológica principal. También en muchos conjuntos de datos del mundo real en biología hay un desequilibrio de clases. Cada grupo de muestras tiene la misma clase. Por eso es importante estratificar y mantener los grupos juntos.

Descripción

Actualmente, sklearn no tiene una función de kfold de grupo estratificado. O podemos usar la estratificación o podemos usar el grupo kfold. Sin embargo, sería bueno tener ambos.

Me gustaría implementarlo, si decidimos tenerlo.

Hola, creo que seria bastante util para medicina ML. ¿Ya está implementado?

@amueller ¿Crees que deberíamos implementar esto, dado que la gente está interesada en esto?

También estoy muy interesado... sería realmente útil en espectroscopia cuando tiene varias medidas repetidas para cada una de sus muestras, realmente necesitan permanecer en el mismo pliegue durante la validación cruzada. Y si tiene varias clases desequilibradas que está tratando de clasificar, realmente querrá usar la función de estratificación también. ¡Por lo tanto yo también lo voto! Lo siento, no soy lo suficientemente bueno para participar en el desarrollo, pero para aquellos que participarán, pueden estar seguros de que se utilizará :-)
pulgares arriba para todo el equipo. ¡Gracias!

Mire los problemas a los que se hace referencia y las relaciones públicas en este hilo, ya que al menos se ha intentado trabajar en StratifiedGroupKFold . Ya hice un StratifiedGroupShuffleSplit #15239 que solo necesita pruebas, pero ya lo he usado bastante para mi propio trabajo.

Creo que deberíamos implementarlo, pero creo que todavía no sé lo que realmente queremos. @hermidalc tiene la restricción de que los miembros de un mismo grupo deben ser de la misma clase. Ese no es el caso general, ¿verdad?

Sería bueno si las personas interesadas pudieran describir su caso de uso y lo que realmente quieren de esto.

Hay # 15239 # 14524 y # 9413 que recuerdo que tienen semánticas diferentes.

@amueller totalmente de acuerdo contigo, pasé algunas horas hoy buscando algo entre las diferentes versiones disponibles (#15239 #14524 y #9413) pero realmente no pude entender si alguna de estas encajaría con mi necesidad. Así que aquí está mi caso de uso si puede ayudar:
Tengo 1000 muestras. cada muestra se ha medido 3 veces con un espectrómetro NIR, por lo que cada muestra tiene 3 réplicas que quiero que permanezcan juntas todo el tiempo...
Estas 1000 muestras pertenecen a 6 clases diferentes con un número muy diferente de muestras en cada una:
clase 1: 400 muestras
clase 2: 300 muestras
clase 3: 100 muestras
clase 4: 100 muestras
clase 5: 70 muestras
clase 6: 30 muestras
Quiero construir un clasificador para cada clase. Entonces, la clase 1 frente a todas las demás clases, luego la clase 2 frente a todas las demás clases, etc.
Para maximizar la precisión de cada uno de mis clasificadores, es importante que tenga muestras de las 6 clases representadas en cada uno de los pliegues, porque mis clases no son tan diferentes, por lo tanto, realmente ayuda crear un borde preciso para tener siempre las 6 clases representadas. en cada pliegue.

Es por eso que creo que un grupo estratificado (siempre mis 6 clases representadas en cada pliegue) (mantenga siempre juntas las 3 medidas repetidas de cada una de mis muestras) kfold parece ser mucho lo que estoy buscando aquí.
¿Alguna opinión?

Mi caso de uso y por qué escribí StratifiedGroupShuffleSplit es para admitir diseños de medidas repetidas https://en.wikipedia.org/wiki/Repeated_measures_design. En mis casos de uso, los miembros del mismo grupo deben ser de la misma clase.

@fcoppey Para ti, las muestras dentro de un grupo siempre tienen la misma clase, ¿verdad?

@hermidalc No estoy muy familiarizado con la terminología, pero de wikipedia parece que "diseño de medidas repetidas" no significa que el mismo grupo deba estar dentro de la misma clase, ya que dice "Una prueba cruzada tiene un diseño de medidas repetidas en el que a cada paciente se le asigna una secuencia de dos o más tratamientos, de los cuales uno puede ser un tratamiento estándar o un placebo".
Al relacionar esto con una configuración de ML, podría intentar predecir a partir de las mediciones si un individuo acaba de recibir tratamiento o placebo, o podría intentar predecir un resultado dado el tratamiento.
Para cualquiera de esos, la clase para el mismo individuo podría cambiar, ¿verdad?

Independientemente del nombre, me parece que ambos tienen el mismo caso de uso, mientras estaba pensando en un caso similar al que se describe en el estudio cruzado. O tal vez un poco más simple: podría hacer que un paciente se enferme con el tiempo (o mejore), por lo que el resultado para un paciente podría cambiar.

En realidad, el artículo de wikipedia al que se vincula dice explícitamente "Análisis longitudinal: los diseños de medidas repetidas permiten a los investigadores monitorear cómo cambian los participantes con el tiempo, tanto en situaciones a corto como a largo plazo", por lo que creo que eso significa que se incluye el cambio de clase.
Si hay otra palabra que significa que la medición se realiza en las mismas condiciones, ¿podríamos usar esa palabra?

@amueller sí, tiene razón, me di cuenta de que escribí mal arriba donde quise decir en mis casos de uso de este diseño, no en este caso de uso en general.

Puede haber muchos tipos bastante elaborados de diseños de medidas repetidas, aunque en los dos tipos que he necesitado StratifiedGroupShuffleSplit se mantiene la restricción de la misma clase dentro del grupo (muestreo longitudinal antes y después del tratamiento al predecir la respuesta al tratamiento, pretratamiento múltiple muestras por sujeto en diferentes lugares del cuerpo al predecir la respuesta al tratamiento).

Necesitaba algo de inmediato que funcionara, así que quería ponerlo ahí para que otros lo usaran y comenzar algo en sklearn, además, si no me equivoco, es más complicado diseñar la lógica de estratificación cuando las etiquetas de clase dentro del grupo pueden ser diferentes.

@amueller sí siempre. Son réplicas de una misma medida para incluir la intravariabilidad del dispositivo en la predicción.

@hermidalc sí, este caso es mucho más fácil. Si es una necesidad común, me complace que la agreguemos. Solo debemos asegurarnos de que el nombre quede algo claro para lo que hace, y debemos pensar si estas dos versiones deben vivir en la misma clase.

Debería ser bastante fácil hacer que StratifiedKFold haga esto. Hay dos opciones: asegurarse de que cada pliegue contenga un número similar de muestras, o asegurarse de que cada pliegue contenga un número similar de grupos.
El segundo es trivial de hacer (simplemente pretender que cada grupo es un solo punto y pasar a StratifiedKFold ). Eso es lo que haces en tu PR, parece.

GroupKFold Creo que intercambia heurísticamente los dos al agregar primero el pliegue más pequeño. No estoy seguro de cómo se traduciría eso al caso estratificado, por lo que estoy contento con el uso de su enfoque.

¿Deberíamos agregar también GroupStratifiedKFold en el mismo PR? ¿O dejar eso para más tarde?
Los otros RP tienen objetivos ligeramente diferentes. Sería bueno si alguien pudiera escribir cuáles son los diferentes casos de uso (probablemente no tengo tiempo ahora).

+1 para manejar por separado la restricción de grupo donde todas las muestras tienen la misma clase.

@hermidalc sí, este caso es mucho más fácil. Si es una necesidad común, me complace que la agreguemos. Solo debemos asegurarnos de que el nombre quede algo claro para lo que hace, y debemos pensar si estas dos versiones deben vivir en la misma clase.

No entiendo totalmente esto, StratifiedGroupShuffleSplit y StratifiedGroupKFold donde puede hacer que los miembros de cada grupo sean de diferentes clases deberían tener exactamente el mismo comportamiento de división cuando el usuario especifica que todos los miembros del grupo sean de la misma clase. ¿Cuándo se pueden mejorar las partes internas más adelante y el comportamiento existente será el mismo?

El segundo es trivial de hacer (simplemente pretender que cada grupo es un solo punto y pasar a StratifiedKFold ). Eso es lo que haces en tu PR, parece.

GroupKFold Creo que intercambia heurísticamente los dos al agregar primero el pliegue más pequeño. No estoy seguro de cómo se traduciría eso al caso estratificado, por lo que estoy contento con el uso de su enfoque.

¿Deberíamos agregar también GroupStratifiedKFold en el mismo PR? ¿O dejar eso para más tarde?
Los otros RP tienen objetivos ligeramente diferentes. Sería bueno si alguien pudiera escribir cuáles son los diferentes casos de uso (probablemente no tengo tiempo ahora).

Agregaré StatifiedGroupKFold usando el enfoque de "cada muestra individual de grupo" que usé.

Sería bueno si las personas interesadas pudieran describir su caso de uso y lo que realmente quieren de esto.

Caso de uso muy común en medicina y biología cuando tiene medidas repetidas.
Un ejemplo: suponga que desea clasificar una enfermedad, por ejemplo, la enfermedad de Alzheimer (EA) frente a controles sanos a partir de imágenes de RM. Para el mismo sujeto, es posible que tenga varios escaneos (de sesiones de seguimiento o datos longitudinales). Supongamos que tiene un total de 1000 sujetos, 200 de ellos diagnosticados con AD (clases desequilibradas). La mayoría de los sujetos tienen un escaneo, pero para algunos de ellos hay 2 o 3 imágenes disponibles. Al entrenar/probar el clasificador, debe asegurarse de que las imágenes del mismo tema estén siempre en el mismo pliegue para evitar la fuga de datos.
Es mejor usar StratifiedGroupKFold para esto: estratificar para tener en cuenta el desequilibrio de clases pero con la restricción de grupo de que un sujeto no debe aparecer en diferentes pliegues.
NB: Sería bueno hacerlo repetible.

A continuación, una implementación de ejemplo, inspirada en 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

Comparando RepeatedStratifiedKFold (una muestra del mismo grupo puede aparecer en ambos pliegues) con 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 para estratificadoGroupKfold. Estoy tratando de detectar caídas de personas mayores, tomando sensores del reloj inteligente. Como no tenemos muchos datos de caídas, hacemos simulaciones con diferentes relojes que obtienen diferentes clases. También realizo aumentos en los datos antes de entrenarlos. de cada punto de datos creo 9 puntos, y esto es un grupo. es importante que un grupo no esté tanto en el tren como en la prueba como se explica

También me gustaría poder usar StratifiedGroupKFold. Estoy viendo un conjunto de datos para predecir crisis financieras, donde los años anteriores, posteriores y durante cada crisis son su propio grupo. Durante el entrenamiento y la validación cruzada, los miembros de cada grupo no deben filtrarse entre los pliegues.

¿Hay alguna forma de generalizar eso para el escenario multilabel (Multilabel_
estratificadoGroupKfold)?

+1 por esto. Estamos analizando las cuentas de usuario en busca de spam, por lo que queremos agrupar por usuario, pero también estratificar porque el spam tiene una incidencia relativamente baja. Para nuestro caso de uso, cualquier usuario que envíe spam una vez se marca como spammer en todos los datos, por lo que un miembro del grupo siempre tendrá la misma etiqueta.

Gracias por proporcionar un caso de uso clásico para enmarcar la documentación,
@felipe-iv!

Agregué una implementación de StratifiedGroupKFold a mi mismo PR #15239 como StratifiedGroupShuffleSplit .

Aunque, como puede ver en PR, la lógica para ambos es mucho más simple que https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602 porque el mío solo intenta preservar el porcentaje de grupos para cada clase (no porcentaje de muestras) para que pueda aprovechar el código existente StratifiedKFold y StratifiedShuffleSplit pasándole información de grupo única. Pero ambas implementaciones producen pliegues donde las muestras de cada grupo permanecen juntas en el mismo pliegue.

Aunque votaría por métodos más sofisticados basados ​​en https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602

Aquí hay versiones completas de StratifiedGroupKFold y RepeatedStratifiedGroupKFold usando el código proporcionado por @mrunibe , que simplifiqué aún más y cambié un par de cosas. Estas clases también siguen el diseño de cómo se hacen otras clases de sklearn CV del mismo tipo.

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 Estoy bastante confundido con lo que hemos resuelto al mirar hacia atrás de vez en cuando. (¡Desafortunadamente mi tiempo ya no es lo que solía ser!) ¿Puede darme una idea de lo que recomendaría incluir en scikit-learn?

@hermidalc Estoy bastante confundido con lo que hemos resuelto al mirar hacia atrás de vez en cuando. (¡Desafortunadamente mi tiempo ya no es lo que solía ser!) ¿Puede darme una idea de lo que recomendaría incluir en scikit-learn?

Quería hacer una mejor implementación que la que hice en #15239. La implementación en ese PR funciona pero se estratifica en los grupos para que la lógica sea sencilla, aunque esto no es lo ideal.

Entonces, lo que hice arriba (gracias a @mrunibe y kaggle de jakubwasikowski) es una mejor implementación de StratifiedGroupKFold que se estratifica en las muestras. Quiero portar la misma lógica para hacer un mejor StratifiedGroupShuffleSplit y luego estará listo. Pondré el nuevo código en #15239 para reemplazar la implementación anterior.

Me disculpo con respecto a mis relaciones públicas que están sin terminar, ¡estoy obteniendo mi doctorado, así que nunca tengo tiempo!

Gracias @hermidalc y @mrunibe por brindar la implementación. También he estado buscando un método StratifiedGroupKFold para manejar datos médicos que tengan un fuerte desequilibrio de clases y una gran variedad de muestras por sujeto. GroupKFold por sí mismo crea subconjuntos de datos de entrenamiento que contienen solo una clase.

Quiero portar la misma lógica para hacer un mejor StratifiedGroupShuffleSplit y luego estará listo.

Sin duda, podríamos considerar fusionar StratifiedGroupKFold antes de que StratifiedGroupShuffleSplit esté listo.

Me disculpo con respecto a mis relaciones públicas que están sin terminar, ¡estoy obteniendo mi doctorado, así que nunca tengo tiempo!

¡Háganos saber si desea ayuda para completarlo!

Y buena suerte con tu trabajo de doctorado.

Aquí hay versiones completas de StratifiedGroupKFold y RepeatedStratifiedGroupKFold usando el código proporcionado por @mrunibe , que simplifiqué aún más y cambié un par de cosas. Estas clases también siguen el diseño de cómo se hacen otras clases de sklearn CV del mismo tipo.

¿Es posible probar esto? Traté de cortar y pegar con algunas de las diversas dependencias, pero nunca terminaba. Me encantaría probar esta clase en mi proyecto. Solo trato de ver si hay una manera disponible ahora para hacer eso.

@hermidalc ¡ Espero que su trabajo de doctorado haya tenido éxito!
Estoy ansioso por ver este implemento hecho también, ya que mi trabajo de doctorado en Geociencias necesita esta función de estratificación con control de grupo. Pasé algunas horas implementando esta idea de dividir manualmente en mi proyecto. Pero dejé de terminarlo por la misma razón... el progreso del doctorado. Entonces, puedo entender perfectamente cómo el trabajo de doctorado puede torturar el tiempo de una persona. LOL Sin presión. Por ahora, uso GroupShuffleSplit como alternativa.

Salud

@bfeeny @dispink es muy fácil usar las dos clases que escribí arriba. Cree un archivo, por ejemplo split.py con lo siguiente. Luego, en su código de usuario, si el script está en el mismo directorio que split.py , simplemente importe 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 ¡ Gracias por la respuesta positiva!
Rápidamente lo adopto como lo describiste. Sin embargo, solo puedo obtener las divisiones que solo tienen datos en el conjunto de entrenamiento o prueba. Hasta donde entiendo la descripción del código, no hay ningún parámetro para especificar la proporción entre los conjuntos de entrenamiento y prueba, ¿verdad?
Sé que es un conflicto entre la estratificación, el control de grupo y la proporción de conjuntos de datos... Por eso dejé de continuar... Pero tal vez todavía podamos encontrar compromisos para trabajar.
image

Atentamente

@hermidalc ¡ Gracias por la respuesta positiva!
Rápidamente lo adopto como lo describiste. Sin embargo, solo puedo obtener las divisiones que solo tienen datos en el conjunto de entrenamiento o prueba. Hasta donde entiendo la descripción del código, no hay ningún parámetro para especificar la proporción entre los conjuntos de entrenamiento y prueba, ¿verdad?
Sé que es un conflicto entre la estratificación, el control de grupo y la proporción de conjuntos de datos... Por eso dejé de continuar... Pero tal vez todavía podamos encontrar compromisos para trabajar.

Para probar, hice split.py y ejecuté este ejemplo en ipython y funciona. He estado usando estos iteradores de CV personalizados en mi trabajo durante mucho tiempo y no tienen ningún problema de mi parte. Por cierto, estoy usando scikit-learn 0.22.2 no 0.23.x, así que no estoy seguro de si esa es la causa del problema. ¿Podría intentar ejecutar este ejemplo a continuación y ver si puede reproducirlo? Si puede, entonces podría ser algo con y y groups en su trabajo.

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]

Parece haber un interés regular en esta característica, @hermidalc , y nosotros
probablemente podría encontrar a alguien para terminarlo si no le importa.

@hermidalc 'Debe asegurarse de que cada muestra en el mismo grupo tenga la misma etiqueta de clase'. Obviamente ese es el problema. Mis muestras en el mismo grupo no comparten la misma clase. Mmm... parece ser otra rama del desarrollo.
Muchas gracias de todos modos.

@hermidalc 'Debe asegurarse de que cada muestra en el mismo grupo tenga la misma etiqueta de clase'. Obviamente ese es el problema. Mis muestras en el mismo grupo no comparten la misma clase. Mmm... parece ser otra rama del desarrollo.
Muchas gracias de todos modos.

Sí, esto se ha discutido en varios hilos aquí. Es otro caso de uso más complejo que es útil, pero muchos como yo no necesitamos ese caso de uso actualmente, pero necesitamos algo que mantenga a los grupos juntos pero estratifique en las muestras. El requisito del código anterior es que todas las muestras de cada grupo pertenezcan a la misma clase.

En realidad @dispink me equivoqué, este algoritmo no requiere que todos los miembros de un grupo pertenezcan a la misma clase. Por ejemplo:

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]

Por lo tanto, no estoy muy seguro de qué está pasando con sus datos, ya que incluso con sus capturas de pantalla no puede ver realmente cuál es el diseño de sus datos y qué podría estar sucediendo. Le sugiero que primero reproduzca los ejemplos que mostré aquí para asegurarse de que no sea un problema de la versión de scikit-learn (ya que estoy usando 0.22.2) y si puede reproducirlo, le sugiero que comience con pequeñas partes de su datos y probarlos. El uso de ~104k muestras es difícil de solucionar.

@hermidalc Gracias por la respuesta!
De hecho, puedo reproducir el resultado anterior, por lo que ahora estoy solucionando problemas con datos más pequeños.

+1

¿A alguien le importa si retomo este problema?
Parece que #15239 junto con https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -600894432 ya tienen una implementación y solo quedan por hacer las pruebas unitarias.

¿Fue útil esta página
0 / 5 - 0 calificaciones