Wie kann ein Datensatz in Trainings- und Testdatensätze aufgeteilt / partitioniert werden, um beispielsweise eine Kreuzvalidierung durchzuführen?


99

Was ist ein guter Weg, um ein NumPy-Array zufällig in Trainings- und Test- / Validierungsdatensätze aufzuteilen? Ähnliches wie cvpartitionoder crossvalindfunktioniert in Matlab.

Antworten:


125

Wenn Sie den Datensatz einmal in zwei Hälften teilen möchten, können Sie Folgendes verwenden numpy.random.shuffleoder numpy.random.permutationdie Indizes im Auge behalten:

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

oder

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

Es gibt viele Möglichkeiten, denselben Datensatz wiederholt für die Kreuzvalidierung zu partitionieren . Eine Strategie besteht darin, mit Wiederholung ein Resample aus dem Datensatz zu erstellen:

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

Schließlich enthält sklearn mehrere Kreuzvalidierungsmethoden (k-fach , weglassen , ...). Es enthält auch fortgeschrittenere "geschichtete Stichproben" -Methoden, mit denen eine Partition der Daten erstellt wird, die in Bezug auf einige Merkmale ausgewogen ist, um beispielsweise sicherzustellen, dass der Trainings- und Testsatz den gleichen Anteil an positiven und negativen Beispielen enthält.


13
Vielen Dank für diese Lösungen. Aber hat die letzte Methode mit Randint nicht gute Chancen, sowohl für Test- als auch für Trainingssätze dieselben Indizes anzugeben?
Ggauravr

3
Die zweite Lösung ist eine gültige Antwort, die erste und dritte nicht. Bei der ersten Lösung ist das Mischen des Datensatzes nicht immer eine Option. In vielen Fällen müssen Sie die Reihenfolge der Dateneingaben beibehalten. Und der dritte könnte sehr gut die gleichen Indizes für Test und Training erzeugen (wie von @ggauravr hervorgehoben).
Pedram Bashiri

Sie sollten Ihr Kreuzvalidierungsset nicht erneut abtasten. Die ganze Idee ist, dass der Lebenslauf noch nie von Ihrem Algo gesehen wurde. Die Trainings- und Testsätze werden verwendet, um die Daten anzupassen. Sie erhalten also natürlich gute Ergebnisse, wenn Sie diese in Ihren Lebenslauf aufnehmen. Ich möchte diese Antwort positiv bewerten, da die zweite Lösung das ist, was ich brauchte, aber diese Antwort hat Probleme.
RubberDuck

55

Es gibt noch eine andere Option, bei der nur Scikit-Learn verwendet wird. Wie im Wiki von scikit beschrieben , können Sie einfach die folgenden Anweisungen verwenden:

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)

Auf diese Weise können Sie die Beschriftungen für die Daten, die Sie in Training und Test aufteilen möchten, synchron halten.


1
Dies ist eine sehr praktische Antwort, da sowohl das Zugset als auch die Etiketten realistisch gehandhabt werden.
Chinnychinchin

38

Nur eine Notiz. Wenn Sie Trainings-, Test- UND Validierungssätze wünschen, können Sie dies tun:

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)

Diese Parameter geben 70% für das Training und jeweils 15% für Test- und Val-Sets. Hoffe das hilft.


5
sollte dies wahrscheinlich zu Ihrem Code hinzufügen: from sklearn.cross_validation import train_test_splitum klar zu machen, welches Modul Sie verwenden
Radix

Muss das zufällig sein?
Liang

Ist es also möglich, nach der angegebenen Reihenfolge von X und y zu teilen?
Liang

1
@liang nein es muss nicht zufällig sein. Sie können einfach sagen, dass die Zug-, Test- und Validierungssatzgrößen a, b und c Prozent der Größe des gesamten Datensatzes betragen. lassen Sie uns sagen a=0.7, b=0.15, c=0.15, und d = dataset, N=len(dataset)dann x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)]und x_val = dataset[int((a+b)*N):].
Offwhitelotus

1
Veraltet: stackoverflow.com/a/34844352/4237080 , usefrom sklearn.model_selection import train_test_split
briennakh

14

Da das sklearn.cross_validationModul veraltet war, können Sie Folgendes verwenden:

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

Sie können auch eine geschichtete Unterteilung in Trainings- und Testgruppen in Betracht ziehen. Die gestartete Aufteilung generiert auch zufällig Trainings- und Testsätze, jedoch so, dass die ursprünglichen Klassenanteile erhalten bleiben. Dadurch spiegeln Trainings- und Testsätze die Eigenschaften des Originaldatensatzes besser wider.

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]

Dieser Code gibt aus:

[1 2 3]
[1 2 3]

Danke dir! Die Benennung ist etwas irreführend, value_indssind wirklich Indizes, aber die Ausgabe sind keine Indizes, nur Masken.
Greenoldman

1

Ich habe eine Funktion für mein eigenes Projekt geschrieben, um dies zu tun (es wird jedoch kein Numpy verwendet):

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

Wenn Sie möchten, dass die Chunks zufällig ausgewählt werden, mischen Sie einfach die Liste, bevor Sie sie übergeben.


0

Hier ist ein Code zum Aufteilen der Daten in n = 5 Falten in geschichteter Weise

% 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

Danke pberkes für deine Antwort. Ich habe es nur geändert, um zu vermeiden, dass (1) ersetzt wird, während Stichproben (2) doppelte Instanzen sowohl beim Training als auch beim Testen auftraten:

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

Nachdem ich etwas gelesen und die (vielen ..) verschiedenen Arten der Aufteilung der Daten zum Trainieren und Testen berücksichtigt hatte, entschied ich mich für timeit!

Ich habe 4 verschiedene Methoden verwendet (keine von ihnen verwendet die Bibliothek sklearn, die mit Sicherheit die besten Ergebnisse liefert, da es sich um gut gestalteten und getesteten Code handelt):

  1. mische die gesamte Matrix arr und teile dann die Daten auf, um zu trainieren und zu testen
  2. Mische die Indizes und weise sie dann x und y zu, um die Daten zu teilen
  3. Wie Methode 2, jedoch effizienter
  4. Verwenden von Pandas Dataframe zum Teilen

Methode 3 gewann bei weitem mit der kürzesten Zeit nach dieser Methode 1, und Methode 2 und 4 erwiesen sich als wirklich ineffizient.

Der Code für die 4 verschiedenen Methoden, die ich zeitlich festgelegt habe:

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)

Und für die Zeit beträgt die Mindestzeit für die Ausführung von 3 Wiederholungen von 1000 Schleifen:

  • Methode 1: 0,35883826200006297 Sekunden
  • Methode 2: 1,7157016959999964 Sekunden
  • Methode 3: 1,7876616719995582 Sekunden
  • Methode 4: 0,07562861499991413 Sekunden

Ich hoffe das ist hilfreich!


0

Wahrscheinlich müssen Sie sich nicht nur in Zug und Test aufteilen, sondern auch gegenseitig validieren, um sicherzustellen, dass Ihr Modell verallgemeinert wird. Hier gehe ich von 70% Trainingsdaten, 20% Validierung und 10% Holdout- / Testdaten aus.

Schauen Sie sich die np.split an :

Wenn indices_or_sections ein 1-D-Array sortierter Ganzzahlen ist, geben die Einträge an, wo entlang der Achse das Array aufgeteilt ist. Zum Beispiel würde [2, 3] für Achse = 0 ergeben

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

In Zugversuche aufgeteilt und gültig

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.