Como dividir / particionar um conjunto de dados em conjuntos de dados de treinamento e teste para, por exemplo, validação cruzada?

99

Qual é uma boa maneira de dividir um array NumPy aleatoriamente em um conjunto de dados de treinamento e teste / validação? Algo semelhante ao cvpartitionou crossvalindfunciona no Matlab.

Erik
fonte

Respostas:

125

Se quiser dividir o conjunto de dados em duas metades, você pode usar numpy.random.shuffle, ou numpy.random.permutationse precisar acompanhar os índices:

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

ou

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,:]

Existem muitas maneiras de particionar repetidamente o mesmo conjunto de dados para validação cruzada . Uma estratégia é fazer uma nova amostra do conjunto de dados, com repetição:

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 contém vários métodos de validação cruzada (k-fold, leave-n-out, ...). Também inclui métodos de "amostragem estratificada" mais avançados que criam uma partição dos dados que é balanceada em relação a alguns recursos, por exemplo, para garantir que haja a mesma proporção de exemplos positivos e negativos no conjunto de treinamento e teste.

pberkes
fonte
13
obrigado por essas soluções. Mas, o último método, usando randint, não tem uma boa chance de dar os mesmos índices para os conjuntos de teste e treinamento?
ggauravr
3
A segunda solução é uma resposta válida, enquanto a 1ª e a 3ª não são. Para a primeira solução, embaralhar o conjunto de dados nem sempre é uma opção, há muitos casos em que você deve manter a ordem das entradas de dados. E o terceiro poderia muito bem produzir os mesmos índices para teste e treinamento (como apontado por @ggauravr).
pedram bashiri
Você não deve fazer uma nova amostra para seu conjunto de validação cruzada. A ideia é que o conjunto de CV nunca foi visto pelo seu algo antes. Os conjuntos de treinamento e teste são usados ​​para ajustar os dados, então é claro que você obterá bons resultados se incluí-los em seu conjunto de currículo. Quero votar positivamente nesta resposta porque a segunda solução é o que eu precisava, mas essa resposta tem problemas.
RubberDuck
55

Existe outra opção que envolve apenas o uso do scikit-learn. Conforme descreve o wiki do scikit , você pode simplesmente usar as seguintes instruções:

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)

Dessa forma, você pode manter em sincronia os rótulos dos dados que está tentando dividir em treinamento e teste.

Paulo Malvar
fonte
1
Esta é uma resposta muito prática, devido ao manuseio realista tanto do conjunto de trens quanto das etiquetas.
chinnychinchin
38

Apenas uma nota. Caso queira treinar, testar e conjuntos de validação, você pode fazer o seguinte:

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)

Esses parâmetros darão 70% para o treinamento e 15% para cada um dos conjuntos de teste e val. Espero que isto ajude.

offwhitelotus
fonte
5
provavelmente deve adicionar isso ao seu código: from sklearn.cross_validation import train_test_splitpara deixar claro qual módulo você está usando
Radix
Isso tem que ser aleatório?
liang
Ou seja, é possível dividir de acordo com a ordem dada por X e y?
liang
1
@liang não, não precisa ser aleatório. você poderia apenas dizer que os tamanhos dos conjuntos de treinamento, teste e validação serão a, b e c por cento do tamanho do conjunto de dados total. digamos a=0.7, b=0.15, c=0.15, e d = dataset, N=len(dataset)e, em seguida x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)], e x_val = dataset[int((a+b)*N):].
offwhitelotus
1
Obsoleto: stackoverflow.com/a/34844352/4237080 , usefrom sklearn.model_selection import train_test_split
briennakh
14

Como o sklearn.cross_validationmódulo foi descontinuado, você pode 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
fonte
5

Você também pode considerar a divisão estratificada em conjunto de treinamento e teste. A divisão Startified também gera conjuntos de treinamento e teste aleatoriamente, mas de forma que as proporções originais das classes sejam preservadas. Isso faz com que os conjuntos de treinamento e teste reflitam melhor as propriedades do conjunto de dados 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 resulta em:

[1 2 3]
[1 2 3]
Apogentus
fonte
Obrigado! A nomenclatura é um tanto enganosa, value_indssão realmente índices, mas a saída não são índices, apenas máscaras.
greenoldman
1

Eu escrevi uma função para meu próprio projeto para fazer isso (mas não 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

Se você quiser que os blocos sejam randomizados, apenas embaralhe a lista antes de passá-la.

Colin
fonte
0

Aqui está um código para dividir os dados em n = 5 dobras de maneira 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]
prasantha
fonte
0

Obrigado pberkes pela sua resposta. Acabei de modificá-lo para evitar (1) substituição durante a amostragem (2) ocorrências duplicadas no treinamento e no teste:

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
fonte
0

Depois de fazer algumas leituras e levar em consideração as (muitas ..) formas diferentes de dividir os dados para treinar e testar, decidi cronometrar!

Usei 4 métodos diferentes (nenhum deles está usando a biblioteca sklearn, que tenho certeza que dará os melhores resultados, já que é um código bem desenhado e testado):

  1. embaralhe toda a matriz e divida os dados para treinar e testar
  2. embaralhe os índices e atribua xey para dividir os dados
  3. igual ao método 2, mas de uma maneira mais eficiente de fazê-lo
  4. usando dataframe do pandas para dividir

o método 3 venceu de longe com o menor tempo, depois o método 1, e os métodos 2 e 4 descobriram ser realmente ineficientes.

O código para os 4 métodos diferentes que marquei:

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)

E para os tempos, o tempo mínimo para executar as 3 repetições de 1000 loops é:

  • 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 isso seja útil!

Rotem
fonte
0

Provavelmente, você não só precisará dividir em treinamento e teste, mas também validação cruzada para garantir que seu modelo seja generalizado. Aqui estou assumindo 70% de dados de treinamento, 20% de validação e 10% de validação / dados de teste.

Confira o np.split :

Se indices_or_sections for uma matriz 1-D de inteiros classificados, as entradas indicam onde ao longo do eixo a matriz é dividida. Por exemplo, [2, 3], para eixo = 0, resultaria em

ária [: 2] ária [2: 3] ária [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.
fonte
0

Dividir em teste de trem e 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
fonte