¿Cómo dividir / particionar un conjunto de datos en conjuntos de datos de entrenamiento y prueba para, por ejemplo, validación cruzada?

99

¿Cuál es una buena manera de dividir una matriz NumPy aleatoriamente en un conjunto de datos de entrenamiento y prueba / validación? Algo similar a las funciones cvpartitiono crossvalinden Matlab.

erik
fuente

Respuestas:

125

Si desea dividir el conjunto de datos una vez en dos mitades, puede usar numpy.random.shuffle, o numpy.random.permutationsi necesita realizar un seguimiento de los índices:

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
numpy.random.shuffle(x)
training, test = x[:80,:], x[80:,:]

o

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
indices = numpy.random.permutation(x.shape[0])
training_idx, test_idx = indices[:80], indices[80:]
training, test = x[training_idx,:], x[test_idx,:]

Hay muchas formas de particionar repetidamente el mismo conjunto de datos para la validación cruzada . Una estrategia es volver a muestrear a partir del conjunto de datos, con repetición:

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
training_idx = numpy.random.randint(x.shape[0], size=80)
test_idx = numpy.random.randint(x.shape[0], size=20)
training, test = x[training_idx,:], x[test_idx,:]

Finalmente, sklearn contiene varios métodos de validación cruzada (k-fold, leave-n-out, ...). También incluye métodos más avanzados de "muestreo estratificado" que crean una partición de los datos que está equilibrada con respecto a algunas características, por ejemplo, para asegurarse de que haya la misma proporción de ejemplos positivos y negativos en el conjunto de entrenamiento y prueba.

pberkes
fuente
13
gracias por estas soluciones. Pero, ¿no tiene el último método, el uso de randint, una buena posibilidad de dar los mismos índices para los conjuntos de prueba y entrenamiento?
ggauravr
3
La segunda solución es una respuesta válida, mientras que la primera y la tercera no lo son. Para la primera solución, barajar el conjunto de datos no siempre es una opción, hay muchos casos en los que debe mantener el orden de las entradas de datos. Y el tercero podría muy bien producir los mismos índices para prueba y entrenamiento (como lo señaló @ggauravr).
pedram bashiri
Usted debe no volver a muestrear para el conjunto de validación cruzada. La idea es que su algoritmo nunca haya visto el conjunto de CV. Los conjuntos de entrenamiento y prueba se utilizan para ajustar los datos, por lo que, por supuesto, obtendrá buenos resultados si los incluye en su conjunto de CV. Quiero votar a favor de esta respuesta porque la segunda solución es lo que necesitaba, pero esta respuesta tiene problemas.
RubberDuck
55

Hay otra opción que solo implica usar scikit-learn. Como describe la wiki de scikit , puede usar las siguientes instrucciones:

from sklearn.model_selection import train_test_split

data, labels = np.arange(10).reshape((5, 2)), range(5)

data_train, data_test, labels_train, labels_test = train_test_split(data, labels, test_size=0.20, random_state=42)

De esta manera, puede mantener sincronizadas las etiquetas de los datos que está intentando dividir en entrenamiento y prueba.

Paulo Malvar
fuente
1
Esta es una respuesta muy práctica, debido al manejo realista tanto del juego de trenes como de las etiquetas.
chinnychinchin
38

Solo una nota. En caso de que desee entrenar, probar y conjuntos de validación, puede hacer esto:

from sklearn.cross_validation import train_test_split

X = get_my_X()
y = get_my_y()
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
x_test, x_val, y_test, y_val = train_test_split(x_test, y_test, test_size=0.5)

Estos parámetros darán un 70% al entrenamiento y un 15% a cada uno de los conjuntos de prueba y val. Espero que esto ayude.

offwhitelotus
fuente
5
probablemente debería agregar esto a su código: from sklearn.cross_validation import train_test_splitpara dejar en claro qué módulo está utilizando
Radix
¿Tiene que ser aleatorio?
liang
Es decir, ¿es posible dividir según el orden dado por X e y?
liang
1
@liang no, no tiene por qué ser aleatorio. simplemente podría decir que los tamaños de los conjuntos de tren, prueba y validación serán a, byc por ciento del tamaño del conjunto de datos total. Digamos a=0.7, b=0.15, c=0.15, y d = dataset, N=len(dataset)y, a continuación x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)]y x_val = dataset[int((a+b)*N):].
offwhitelotus
1
En desuso: stackoverflow.com/a/34844352/4237080 , usefrom sklearn.model_selection import train_test_split
briennakh
14

Como el sklearn.cross_validationmódulo quedó obsoleto, puede usar:

import numpy as np
from sklearn.model_selection import train_test_split
X, y = np.arange(10).reshape((5, 2)), range(5)

X_trn, X_tst, y_trn, y_tst = train_test_split(X, y, test_size=0.2, random_state=42)
M. Mashaye
fuente
5

También puede considerar la división estratificada en conjunto de entrenamiento y prueba. La división iniciada también genera conjuntos de entrenamiento y prueba aleatoriamente, pero de tal manera que se conservan las proporciones originales de la clase. Esto hace que los conjuntos de entrenamiento y prueba reflejen mejor las propiedades del conjunto de datos original.

import numpy as np  

def get_train_test_inds(y,train_proportion=0.7):
    '''Generates indices, making random stratified split into training set and testing sets
    with proportions train_proportion and (1-train_proportion) of initial sample.
    y is any iterable indicating classes of each observation in the sample.
    Initial proportions of classes inside training and 
    testing sets are preserved (stratified sampling).
    '''

    y=np.array(y)
    train_inds = np.zeros(len(y),dtype=bool)
    test_inds = np.zeros(len(y),dtype=bool)
    values = np.unique(y)
    for value in values:
        value_inds = np.nonzero(y==value)[0]
        np.random.shuffle(value_inds)
        n = int(train_proportion*len(value_inds))

        train_inds[value_inds[:n]]=True
        test_inds[value_inds[n:]]=True

    return train_inds,test_inds

y = np.array([1,1,2,2,3,3])
train_inds,test_inds = get_train_test_inds(y,train_proportion=0.5)
print y[train_inds]
print y[test_inds]

Este código genera:

[1 2 3]
[1 2 3]
Apogentus
fuente
¡Gracias! La denominación es algo engañosa, value_indsson verdaderamente índices, pero la salida no son índices, solo máscaras.
greenoldman
1

Escribí una función para mi propio proyecto para hacer esto (aunque no usa numpy):

def partition(seq, chunks):
    """Splits the sequence into equal sized chunks and them as a list"""
    result = []
    for i in range(chunks):
        chunk = []
        for element in seq[i:len(seq):chunks]:
            chunk.append(element)
        result.append(chunk)
    return result

Si desea que los fragmentos sean aleatorios, simplemente mezcle la lista antes de pasarla.

Colin
fuente
0

Aquí hay un código para dividir los datos en n = 5 pliegues de manera estratificada

% X = data array
% y = Class_label
from sklearn.cross_validation import StratifiedKFold
skf = StratifiedKFold(y, n_folds=5)
for train_index, test_index in skf:
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
prashanth
fuente
0

Gracias pberkes por tu respuesta. Lo modifiqué para evitar (1) el reemplazo mientras se tomaban muestras (2) ocurrieron instancias duplicadas tanto en el entrenamiento como en las pruebas:

training_idx = np.random.choice(X.shape[0], int(np.round(X.shape[0] * 0.8)),replace=False)
training_idx = np.random.permutation(np.arange(X.shape[0]))[:np.round(X.shape[0] * 0.8)]
    test_idx = np.setdiff1d( np.arange(0,X.shape[0]), training_idx)
Zahran
fuente
0

Después de leer un poco y tener en cuenta las (muchas ...) formas diferentes de dividir los datos para entrenar y probar, ¡decidí cronometrarlo!

Utilicé 4 métodos diferentes (ninguno de ellos está usando la biblioteca sklearn, que estoy seguro dará los mejores resultados, dado que está bien diseñado y probado código):

  1. mezclar todo el arreglo de matriz y luego dividir los datos para entrenar y probar
  2. mezclar los índices y luego asignarle xey para dividir los datos
  3. igual que el método 2, pero de una manera más eficiente para hacerlo
  4. usando pandas dataframe para dividir

el método 3 ganó con mucho con el tiempo más corto, después del método 1, y se descubrió que el método 2 y 4 eran realmente ineficientes.

El código para los 4 métodos diferentes que cronometré:

import numpy as np
arr = np.random.rand(100, 3)
X = arr[:,:2]
Y = arr[:,2]
spl = 0.7
N = len(arr)
sample = int(spl*N)

#%% Method 1:  shuffle the whole matrix arr and then split
np.random.shuffle(arr)
x_train, x_test, y_train, y_test = X[:sample,:], X[sample:, :], Y[:sample, ], Y[sample:,]

#%% Method 2: shuffle the indecies and then shuffle and apply to X and Y
train_idx = np.random.choice(N, sample)
Xtrain = X[train_idx]
Ytrain = Y[train_idx]

test_idx = [idx for idx in range(N) if idx not in train_idx]
Xtest = X[test_idx]
Ytest = Y[test_idx]

#%% Method 3: shuffle indicies without a for loop
idx = np.random.permutation(arr.shape[0])  # can also use random.shuffle
train_idx, test_idx = idx[:sample], idx[sample:]
x_train, x_test, y_train, y_test = X[train_idx,:], X[test_idx,:], Y[train_idx,], Y[test_idx,]

#%% Method 4: using pandas dataframe to split
import pandas as pd
df = pd.read_csv(file_path, header=None) # Some csv file (I used some file with 3 columns)

train = df.sample(frac=0.7, random_state=200)
test = df.drop(train.index)

Y para los tiempos, el tiempo mínimo para ejecutar de 3 repeticiones de 1000 bucles es:

  • Método 1: 0.35883826200006297 segundos
  • Método 2: 1,7157016959999964 segundos
  • Método 3: 1.7876616719995582 segundos
  • Método 4: 0.07562861499991413 segundos

¡Espero que sea de ayuda!

rotem
fuente
0

Es probable que no solo deba dividirse en entrenamiento y prueba, sino también una validación cruzada para asegurarse de que su modelo se generalice. Aquí estoy asumiendo 70% de datos de entrenamiento, 20% de validación y 10% de datos de prueba / retención.

Consulte el np.split :

Si índices_o_secciones es una matriz 1-D de enteros ordenados, las entradas indican en qué parte del eje se divide la matriz. Por ejemplo, [2, 3], para el eje = 0, resultaría en

ario [: 2] ario [2: 3] ario [3:]

t, v, h = np.split(df.sample(frac=1, random_state=1), [int(0.7*len(df)), int(0.9*len(df))]) 
B.Mr.W.
fuente
0

Dividido en prueba de tren y válido

x =np.expand_dims(np.arange(100), -1)


print(x)

indices = np.random.permutation(x.shape[0])

training_idx, test_idx, val_idx = indices[:int(x.shape[0]*.9)], indices[int(x.shape[0]*.9):int(x.shape[0]*.95)],  indices[int(x.shape[0]*.9):int(x.shape[0]*.95)]


training, test, val = x[training_idx,:], x[test_idx,:], x[val_idx,:]

print(training, test, val)
Rajat Subhra Bhowmick
fuente