Come suddividere / partizionare un set di dati in set di dati di addestramento e test per, ad esempio, la convalida incrociata?


99

Qual è un buon modo per dividere un array NumPy in modo casuale in un set di dati di addestramento e test / convalida? Qualcosa di simile alle funzioni cvpartitiono crossvalindin Matlab.

Risposte:


125

Se vuoi dividere il set di dati una volta in due metà, puoi usare numpy.random.shuffle, o numpy.random.permutationse devi tenere traccia degli indici:

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

Esistono molti modi per partizionare ripetutamente lo stesso set di dati per la convalida incrociata . Una strategia consiste nel ricampionare dal set di dati, con la ripetizione:

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

Infine, sklearn contiene diversi metodi di convalida incrociata (k-fold, leave-n-out, ...). Include anche metodi più avanzati di "campionamento stratificato" che creano una partizione dei dati bilanciata rispetto ad alcune caratteristiche, ad esempio per assicurarsi che ci sia la stessa proporzione di esempi positivi e negativi nel set di addestramento e test.


13
grazie per queste soluzioni. Ma l'ultimo metodo, utilizzando randint, non ha buone possibilità di fornire gli stessi indici sia per i set di test che per quelli di allenamento?
ggauravr

3
La seconda soluzione è una risposta valida mentre la 1a e la 3a non lo sono. Per la prima soluzione, mescolare il set di dati non è sempre un'opzione, ci sono molti casi in cui è necessario mantenere l'ordine degli input dei dati. E il terzo potrebbe benissimo produrre gli stessi indici per test e allenamento (come sottolineato da @ggauravr).
pedram bashiri

Si dovrebbe non ricampionare per il set di validazione incrociata. L'idea è che il set di CV non è mai stato visto prima dal tuo algoritmo. I set di formazione e test vengono utilizzati per adattare i dati, quindi ovviamente otterrai buoni risultati se li includi nel tuo set di CV. Voglio dare un voto positivo a questa risposta perché la seconda soluzione è ciò di cui avevo bisogno, ma questa risposta ha problemi.
RubberDuck

55

C'è un'altra opzione che implica solo l'uso di scikit-learn. Come descrive il wiki di scikit , puoi semplicemente usare le seguenti istruzioni:

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)

In questo modo puoi mantenere sincronizzate le etichette per i dati che stai cercando di suddividere in addestramento e test.


1
Questa è una risposta molto pratica, grazie alla gestione realistica sia del treno che delle etichette.
chinnychinchin

38

Solo una nota. Nel caso in cui desideri addestrare, testare E set di convalida, puoi farlo:

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)

Questi parametri daranno il 70% all'addestramento e il 15% ciascuno ai set di test e val. Spero che questo ti aiuti.


5
probabilmente dovresti aggiungere questo al tuo codice: from sklearn.cross_validation import train_test_splitper chiarire quale modulo stai usando
Radix

Deve essere casuale?
liang

Cioè, è possibile dividere secondo l'ordine dato da X e y?
liang

1
@liang no, non deve essere casuale. si potrebbe semplicemente dire che le dimensioni del set di addestramento, test e convalida saranno a, bec percento della dimensione del set di dati totale. Diciamo a=0.7, b=0.15, c=0.15, e d = dataset, N=len(dataset)quindi 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
Deprecato: stackoverflow.com/a/34844352/4237080 , utilizzofrom sklearn.model_selection import train_test_split
briennakh

14

Poiché il sklearn.cross_validationmodulo è stato deprecato, puoi utilizzare:

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)

5

Puoi anche considerare la divisione stratificata in set di addestramento e test. La divisione startificata genera anche training e test set casualmente, ma in modo tale da preservare le proporzioni originali della classe. In questo modo i set di addestramento e test riflettono meglio le proprietà del set di dati originale.

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]

Questo codice restituisce:

[1 2 3]
[1 2 3]

Grazie! La denominazione è alquanto fuorviante, value_indssono veramente indici, ma l'output non sono indici, solo maschere.
greenoldman

1

Ho scritto una funzione per il mio progetto per farlo (non usa numpy, però):

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 vuoi che i blocchi siano randomizzati, rimescola l'elenco prima di passarlo.


0

Ecco un codice per suddividere i dati in n = 5 pieghe in modo stratificato

% 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]

0

Grazie Pberkes per la tua risposta. L'ho appena modificato per evitare (1) la sostituzione durante il campionamento (2) di istanze duplicate sia nell'addestramento che nel test:

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)

0

Dopo aver letto un po 'e tenendo conto dei (molti ..) diversi modi di suddividere i dati da allenare e testare, ho deciso di timeit!

Ho usato 4 metodi diversi (nessuno dei quali utilizza la libreria sklearn, che sono sicuro darà i migliori risultati, dato che è un codice ben progettato e testato):

  1. mescola l'intera matrice arr e poi dividi i dati per addestrarli e testarli
  2. mescola gli indici e poi assegnagli xey per dividere i dati
  3. uguale al metodo 2, ma in un modo più efficiente per farlo
  4. utilizzando pandas dataframe per dividere

il metodo 3 ha vinto di gran lunga con il tempo più breve, dopo quel metodo 1, e il metodo 2 e 4 si sono rivelati davvero inefficienti.

Il codice per i 4 diversi metodi che ho cronometrato:

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 per i tempi, il tempo minimo per eseguire su 3 ripetizioni di 1000 loop è:

  • Metodo 1: 0,35883826200006297 secondi
  • Metodo 2: 1.7157016959999964 secondi
  • Metodo 3: 1.7876616719995582 secondi
  • Metodo 4: 0,07562861499991413 secondi

Spero sia utile!


0

Probabilmente non sarà solo necessario suddividere in training e test, ma anche una convalida incrociata per assicurarsi che il modello sia generalizzato. Qui presumo il 70% di dati di addestramento, il 20% di convalida e il 10% di dati di controllo / test.

Dai un'occhiata a np.split :

Se indices_or_sections è un array 1-D di numeri interi ordinati, le voci indicano il punto in cui l'array è suddiviso lungo l'asse. Ad esempio, [2, 3], per asse = 0, restituirebbe

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))]) 

0

Diviso in test del treno e valido

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.