Comment diviser / partitionner un ensemble de données en ensembles de données d'entraînement et de test pour, par exemple, la validation croisée?

99

Quel est un bon moyen de diviser un tableau NumPy de manière aléatoire en un ensemble de données d'entraînement et de test / validation? Quelque chose de similaire aux fonctions cvpartitionou crossvalinddans Matlab.

erik
la source

Réponses:

125

Si vous souhaitez diviser l'ensemble de données une fois en deux moitiés, vous pouvez utiliser numpy.random.shuffle, ou numpy.random.permutationsi vous avez besoin de garder une trace des indices:

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

Il existe de nombreuses façons de partitionner à plusieurs reprises le même ensemble de données pour la validation croisée . Une stratégie consiste à rééchantillonner à partir de l'ensemble de données, avec répétition:

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

Enfin, sklearn contient plusieurs méthodes de validation croisée (k-fold , Leave -n-out, ...). Il comprend également des méthodes plus avancées «d'échantillonnage stratifié» qui créent une partition des données équilibrée par rapport à certaines caractéristiques, par exemple pour s'assurer qu'il y a la même proportion d'exemples positifs et négatifs dans l'ensemble d'apprentissage et de test.

pberkes
la source
13
merci pour ces solutions. Mais la dernière méthode, utilisant randint, n'a-t-elle pas de bonnes chances de donner les mêmes indices pour les ensembles de test et d'entraînement?
ggauravr
3
La deuxième solution est une réponse valide alors que les 1ère et 3ème ne le sont pas. Pour la 1ère solution, mélanger l'ensemble de données n'est pas toujours une option, il existe de nombreux cas où vous devez conserver l'ordre des entrées de données. Et le 3ème pourrait très bien produire les mêmes indices pour le test et l'entraînement (comme le souligne @ggauravr).
pedram bashiri le
Vous ne devez pas rééchantillonner pour votre ensemble de validation croisée. L'idée générale est que l'ensemble de CV n'a jamais été vu par votre algo auparavant. Les ensembles de formation et de test sont utilisés pour ajuster les données, vous obtiendrez bien sûr de bons résultats si vous les incluez dans votre ensemble de CV. Je veux voter pour cette réponse parce que la 2ème solution est ce dont j'avais besoin, mais cette réponse a des problèmes.
RubberDuck
55

Il existe une autre option qui implique simplement d'utiliser scikit-learn. Comme le décrit le wiki de scikit , vous pouvez simplement utiliser les instructions suivantes:

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 cette façon, vous pouvez synchroniser les étiquettes des données que vous essayez de diviser en entraînement et en test.

Paulo Malvar
la source
1
C'est une réponse très pratique, en raison de la manipulation réaliste de la rame et des étiquettes.
chinnychinchin
38

Juste une note. Si vous souhaitez entraîner, tester et valider des ensembles, vous pouvez le faire:

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)

Ces paramètres donneront 70% à la formation et 15% chacun pour tester et valider les ensembles. J'espère que cela t'aides.

offwhitelotus
la source
5
devrait probablement ajouter ceci à votre code: from sklearn.cross_validation import train_test_splitpour préciser le module que vous utilisez
Radix
Cela doit-il être aléatoire?
liang
Autrement dit, est-il possible de diviser selon l'ordre donné par X et y?
liang
1
@liang non, cela n'a pas à être aléatoire. vous pouvez simplement dire que les tailles de l'ensemble de train, de test et de validation seront a, b et c pour cent de la taille de l'ensemble de données total. disons a=0.7, b=0.15, c=0.15et d = dataset, N=len(dataset)puis x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)]et x_val = dataset[int((a+b)*N):].
offwhitelotus le
1
Obsolète: stackoverflow.com/a/34844352/4237080 , usefrom sklearn.model_selection import train_test_split
briennakh
14

Le sklearn.cross_validationmodule étant obsolète, vous pouvez utiliser:

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
la source
5

Vous pouvez également envisager une division stratifiée en ensemble de formation et de test. La division Startified génère également un ensemble de formation et de tests de manière aléatoire, mais de manière à conserver les proportions de classe d'origine. Cela permet aux ensembles de formation et de test de mieux refléter les propriétés de l'ensemble de données d'origine.

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]

Ce code génère:

[1 2 3]
[1 2 3]
Apogentus
la source
Je vous remercie! La dénomination est quelque peu trompeuse, ce value_indssont vraiment des indices, mais la sortie ne sont pas des indices, seulement des masques.
greenoldman
1

J'ai écrit une fonction pour mon propre projet pour faire cela (il n'utilise pas numpy, cependant):

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 vous voulez que les morceaux soient randomisés, mélangez simplement la liste avant de la transmettre.

Colin
la source
0

Voici un code pour diviser les données en n = 5 fois de manière stratifiée

% 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
la source
0

Merci pberkes pour votre réponse. Je viens de le modifier pour éviter (1) le remplacement lors de l'échantillonnage (2) des instances dupliquées se sont produites à la fois lors de la formation et des tests:

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
la source
0

Après avoir fait quelques lectures et pris en compte les (nombreuses ..) différentes façons de fractionner les données à entraîner et à tester, j'ai décidé de timeit!

J'ai utilisé 4 méthodes différentes (aucune d'entre elles n'utilise la bibliothèque sklearn, qui, j'en suis sûr, donnera les meilleurs résultats, étant donné que c'est un code bien conçu et testé):

  1. mélangez toute la matrice arr, puis divisez les données pour entraîner et tester
  2. mélangez les indices puis attribuez-les x et y pour diviser les données
  3. identique à la méthode 2, mais de manière plus efficace pour le faire
  4. utilisation de pandas dataframe pour diviser

la méthode 3 a gagné de loin avec le temps le plus court, après cette méthode 1, et les méthodes 2 et 4 se sont révélées vraiment inefficaces.

Le code pour les 4 méthodes différentes que j'ai chronométrées:

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)

Et pour les temps, le temps minimum pour exécuter sur 3 répétitions de 1000 boucles est:

  • Méthode 1: 0,35883826200006297 secondes
  • Méthode 2: 1,7157016959999964 secondes
  • Méthode 3: 1,7876616719995582 secondes
  • Méthode 4: 0,07562861499991413 secondes

J'espère que c'est utile!

Rotem
la source
0

Vous devrez probablement non seulement vous diviser en train et tester, mais également effectuer une validation croisée pour vous assurer que votre modèle se généralise. Ici, je suppose que 70% de données d'entraînement, 20% de validation et 10% de données de maintien / test.

Découvrez le np.split :

Si indices_or_sections est un tableau 1-D d'entiers triés, les entrées indiquent où le long de l'axe le tableau est divisé. Par exemple, [2, 3] donnerait, pour axis = 0,

ary [: 2] ary [2: 3] ary [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.
la source
0

Split en train test et valide

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
la source