¿Cómo dividir los datos en 3 conjuntos (entrenamiento, validación y prueba)?

Resuelto CentAu asked hace 8 años • 13 respuestas

Tengo un marco de datos de pandas y deseo dividirlo en 3 conjuntos separados. Sé que al usar train_test_split de sklearn.cross_validation, se pueden dividir los datos en dos conjuntos (entrenar y probar). Sin embargo, no pude encontrar ninguna solución para dividir los datos en tres conjuntos. Preferiblemente, me gustaría tener los índices de los datos originales.

Sé que una solución sería utilizar train_test_splitdos tiempos y de alguna manera ajustar los índices. Pero, ¿existe una forma más estándar/integrada de dividir los datos en 3 conjuntos en lugar de 2?

CentAu avatar Jul 07 '16 23:07 CentAu
Aceptado

Solución numerosa. Primero mezclaremos todo el conjunto de datos ( df.sample(frac=1, random_state=42)) y luego dividiremos nuestro conjunto de datos en las siguientes partes:

  • 60% - juego de trenes,
  • 20% - conjunto de validación,
  • 20% - conjunto de prueba

In [305]: train, validate, test = \
              np.split(df.sample(frac=1, random_state=42), 
                       [int(.6*len(df)), int(.8*len(df))])

In [306]: train
Out[306]:
          A         B         C         D         E
0  0.046919  0.792216  0.206294  0.440346  0.038960
2  0.301010  0.625697  0.604724  0.936968  0.870064
1  0.642237  0.690403  0.813658  0.525379  0.396053
9  0.488484  0.389640  0.599637  0.122919  0.106505
8  0.842717  0.793315  0.554084  0.100361  0.367465
7  0.185214  0.603661  0.217677  0.281780  0.938540

In [307]: validate
Out[307]:
          A         B         C         D         E
5  0.806176  0.008896  0.362878  0.058903  0.026328
6  0.145777  0.485765  0.589272  0.806329  0.703479

In [308]: test
Out[308]:
          A         B         C         D         E
4  0.521640  0.332210  0.370177  0.859169  0.401087
3  0.333348  0.964011  0.083498  0.670386  0.169619

[int(.6*len(df)), int(.8*len(df))]- es una indices_or_sections matriz para numpy.split() .

Aquí hay una pequeña demostración de np.split()uso: dividamos una matriz de 20 elementos en las siguientes partes: 80%, 10%, 10%:

In [45]: a = np.arange(1, 21)

In [46]: a
Out[46]: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])

In [47]: np.split(a, [int(.8 * len(a)), int(.9 * len(a))])
Out[47]:
[array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16]),
 array([17, 18]),
 array([19, 20])]
MaxU - stand with Ukraine avatar Jul 07 '2016 16:07 MaxU - stand with Ukraine

Sin embargo, una forma de dividir el conjunto de datos en train, test, cvcon 0.6, 0.2, 0.2sería utilizar el train_test_splitmétodo dos veces.

from sklearn.model_selection import train_test_split

x, x_test, y, y_test = train_test_split(xtrain,labels,test_size=0.2,train_size=0.8)
x_train, x_cv, y_train, y_cv = train_test_split(x,y,test_size = 0.25,train_size =0.75)
blitu12345 avatar Mar 21 '2017 16:03 blitu12345

Nota:

La función se escribió para manejar la siembra de la creación de conjuntos aleatorios. No debes confiar en una división de conjuntos que no aleatorice los conjuntos.

import numpy as np
import pandas as pd

def train_validate_test_split(df, train_percent=.6, validate_percent=.2, seed=None):
    np.random.seed(seed)
    perm = np.random.permutation(df.index)
    m = len(df.index)
    train_end = int(train_percent * m)
    validate_end = int(validate_percent * m) + train_end
    train = df.iloc[perm[:train_end]]
    validate = df.iloc[perm[train_end:validate_end]]
    test = df.iloc[perm[validate_end:]]
    return train, validate, test

Demostración

np.random.seed([3,1415])
df = pd.DataFrame(np.random.rand(10, 5), columns=list('ABCDE'))
df

ingrese la descripción de la imagen aquí

train, validate, test = train_validate_test_split(df)

train

ingrese la descripción de la imagen aquí

validate

ingrese la descripción de la imagen aquí

test

ingrese la descripción de la imagen aquí

piRSquared avatar Jul 07 '2016 16:07 piRSquared

Aquí hay una función de Python que divide un marco de datos de Pandas en marcos de datos de entrenamiento, validación y prueba con muestreo estratificado. Realiza esta división llamando train_test_split()dos veces a la función de scikit-learn.

import pandas as pd
from sklearn.model_selection import train_test_split

def split_stratified_into_train_val_test(df_input, stratify_colname='y',
                                         frac_train=0.6, frac_val=0.15, frac_test=0.25,
                                         random_state=None):
    '''
    Splits a Pandas dataframe into three subsets (train, val, and test)
    following fractional ratios provided by the user, where each subset is
    stratified by the values in a specific column (that is, each subset has
    the same relative frequency of the values in the column). It performs this
    splitting by running train_test_split() twice.

    Parameters
    ----------
    df_input : Pandas dataframe
        Input dataframe to be split.
    stratify_colname : str
        The name of the column that will be used for stratification. Usually
        this column would be for the label.
    frac_train : float
    frac_val   : float
    frac_test  : float
        The ratios with which the dataframe will be split into train, val, and
        test data. The values should be expressed as float fractions and should
        sum to 1.0.
    random_state : int, None, or RandomStateInstance
        Value to be passed to train_test_split().

    Returns
    -------
    df_train, df_val, df_test :
        Dataframes containing the three splits.
    '''

    if frac_train + frac_val + frac_test != 1.0:
        raise ValueError('fractions %f, %f, %f do not add up to 1.0' % \
                         (frac_train, frac_val, frac_test))

    if stratify_colname not in df_input.columns:
        raise ValueError('%s is not a column in the dataframe' % (stratify_colname))

    X = df_input # Contains all columns.
    y = df_input[[stratify_colname]] # Dataframe of just the column on which to stratify.

    # Split original dataframe into train and temp dataframes.
    df_train, df_temp, y_train, y_temp = train_test_split(X,
                                                          y,
                                                          stratify=y,
                                                          test_size=(1.0 - frac_train),
                                                          random_state=random_state)

    # Split the temp dataframe into val and test dataframes.
    relative_frac_test = frac_test / (frac_val + frac_test)
    df_val, df_test, y_val, y_test = train_test_split(df_temp,
                                                      y_temp,
                                                      stratify=y_temp,
                                                      test_size=relative_frac_test,
                                                      random_state=random_state)

    assert len(df_input) == len(df_train) + len(df_val) + len(df_test)

    return df_train, df_val, df_test

A continuación se muestra un ejemplo funcional completo.

Considere un conjunto de datos que tiene una etiqueta sobre la cual desea realizar la estratificación. Esta etiqueta tiene su propia distribución en el conjunto de datos original, digamos 75% foo, 15% bary 10% baz. Ahora dividamos el conjunto de datos en entrenamiento, validación y prueba en subconjuntos usando una proporción de 60/20/20, donde cada división conserva la misma distribución de las etiquetas. Vea la siguiente ilustración:

ingrese la descripción de la imagen aquí

Aquí está el conjunto de datos de ejemplo:

df = pd.DataFrame( { 'A': list(range(0, 100)),
                     'B': list(range(100, 0, -1)),
                     'label': ['foo'] * 75 + ['bar'] * 15 + ['baz'] * 10 } )

df.head()
#    A    B label
# 0  0  100   foo
# 1  1   99   foo
# 2  2   98   foo
# 3  3   97   foo
# 4  4   96   foo

df.shape
# (100, 3)

df.label.value_counts()
# foo    75
# bar    15
# baz    10
# Name: label, dtype: int64

Ahora, llamemos a la split_stratified_into_train_val_test()función desde arriba para entrenar, validar y probar marcos de datos siguiendo una proporción de 60/20/20.

df_train, df_val, df_test = \
    split_stratified_into_train_val_test(df, stratify_colname='label', frac_train=0.60, frac_val=0.20, frac_test=0.20)

Los tres marcos de datos df_train, df_valy df_testcontienen todas las filas originales, pero sus tamaños seguirán la proporción anterior.

df_train.shape
#(60, 3)

df_val.shape
#(20, 3)

df_test.shape
#(20, 3)

Además, cada una de las tres divisiones tendrá la misma distribución de la etiqueta, es decir, 75% foo, 15% bary 10% baz.

df_train.label.value_counts()
# foo    45
# bar     9
# baz     6
# Name: label, dtype: int64

df_val.label.value_counts()
# foo    15
# bar     3
# baz     2
# Name: label, dtype: int64

df_test.label.value_counts()
# foo    15
# bar     3
# baz     2
# Name: label, dtype: int64
stackoverflowuser2010 avatar Mar 22 '2020 19:03 stackoverflowuser2010