Geschichteter Zug / Test-Split in Scikit-Learn

88

Ich muss meine Daten in einen Trainingssatz (75%) und einen Testsatz (25%) aufteilen. Ich mache das derzeit mit dem folgenden Code:

X, Xt, userInfo, userInfo_train = sklearn.cross_validation.train_test_split(X, userInfo)   

Ich möchte jedoch meinen Trainingsdatensatz schichten. Wie mache ich das? Ich habe mich mit der StratifiedKFoldMethode befasst, aber ich kann die 75% / 25% -Aufteilung nicht spezifizieren und nur den Trainingsdatensatz schichten.

pir
quelle

Antworten:

153

[Update für 0.17]

Siehe die Dokumente von sklearn.model_selection.train_test_split:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    stratify=y, 
                                                    test_size=0.25)

[/ Update für 0.17]

Es gibt eine Pull - Anforderung hier . Sie können aber einfach train, test = next(iter(StratifiedKFold(...))) die Zug- und Testindizes verwenden, wenn Sie möchten.

Andreas Müller
quelle
1
@AndreasMueller Gibt es eine einfache Möglichkeit, Regressionsdaten zu schichten?
Jordanien
3
@Jordan nichts ist in scikit-learn implementiert. Ich kenne keinen Standardweg. Wir könnten Perzentile verwenden.
Andreas Mueller
@AndreasMueller Haben Sie jemals das Verhalten gesehen, bei dem diese Methode erheblich langsamer ist als das StratifiedShuffleSplit? Ich habe den MNIST-Datensatz verwendet.
Snymkpr
@activatedgeek, das scheint sehr seltsam, da train_test_split (... stratify =) nur StratifiedShuffleSplit aufruft und den ersten Split übernimmt. Fühlen Sie sich frei, ein Problem auf dem Tracker mit einem reproduzierbaren Beispiel zu öffnen.
Andreas Mueller
@AndreasMueller Ich habe eigentlich kein Problem eröffnet, weil ich das starke Gefühl habe, etwas falsch zu machen (obwohl es nur 2 Zeilen sind). Aber wenn ich es heute noch mehrmals reproduzieren kann, mache ich das!
Snymkpr
29

TL; DR: Verwenden Sie StratifiedShuffleSplit mittest_size=0.25

Scikit-learn bietet zwei Module für Stratified Splitting:

  1. StratifiedKFold : Dieses Modul ist nützlich als direkter k-facher Kreuzvalidierungsoperator: Wie darin werden n_foldsTrainings- / Testsätze so eingerichtet, dass die Klassen in beiden gleichermaßen ausgeglichen sind.

Hier ist ein Code (direkt aus der obigen Dokumentation)

>>> skf = cross_validation.StratifiedKFold(y, n_folds=2) #2-fold cross validation
>>> len(skf)
2
>>> 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]
...    #fit and predict with X_train/test. Use accuracy metrics to check validation performance
  1. StratifiedShuffleSplit : Dieses Modul erstellt ein einzelnes Trainings- / Test-Set mit gleichermaßen ausgeglichenen (geschichteten) Klassen. Im Wesentlichen ist es das, was Sie mit dem wollen n_iter=1. Sie können die Testgröße hier wie in erwähnentrain_test_split

Code:

>>> sss = StratifiedShuffleSplit(y, n_iter=1, test_size=0.5, random_state=0)
>>> len(sss)
1
>>> for train_index, test_index in sss:
...    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]
>>> # fit and predict with your classifier using the above X/y train/test
scharf
quelle
5
Beachten Sie, dass ab 0.18.x, n_itersollte n_splitsfür StratifiedShuffleSplit - und dass es eine etwas andere API dafür gibt: scikit-learn.org/stable/modules/generated/…
lollercoaster
2
Wenn yes sich um eine Pandas-Serie handelt, verwenden Siey.iloc[train_index], y.iloc[test_index]
Owlright
1
@Owlright Ich habe versucht, einen Pandas-Datenrahmen zu verwenden, und die von StratifiedShuffleSplit zurückgegebenen Indizes sind nicht die Indizes im Datenrahmen. dataframe index: 2,3,5 the first split in sss:[(array([2, 1]), array([0]))]:(
Meghna Natraj
2
@tangy warum ist das eine for-Schleife? X_train, X_test = X[train_index], X[test_index]Ist es nicht so, dass eine aufgerufene Zeile überschreibt X_trainund X_test? Warum dann nicht nur eine einzige next(sss)?
Bartek Wójcik
13

Hier ist ein Beispiel für kontinuierliche / Regressionsdaten (bis dieses Problem auf GitHub behoben ist).

min = np.amin(y)
max = np.amax(y)

# 5 bins may be too few for larger datasets.
bins     = np.linspace(start=min, stop=max, num=5)
y_binned = np.digitize(y, bins, right=True)

X_train, X_test, y_train, y_test = train_test_split(
    X, 
    y, 
    stratify=y_binned
)
  • Wo startist min und stopist max von Ihrem kontinuierlichen Ziel.
  • Wenn Sie nicht festlegen right=True, wird Ihr Maximalwert mehr oder weniger zu einem separaten Bin, und Ihre Aufteilung schlägt immer fehl, da sich zu wenige Proben in diesem zusätzlichen Bin befinden.
Jordanien
quelle
12

Sie können dies einfach mit der train_test_split()in Scikit Learn verfügbaren Methode tun:

from sklearn.model_selection import train_test_split 
train, test = train_test_split(X, test_size=0.25, stratify=X['YOUR_COLUMN_LABEL']) 

Ich habe auch einen kurzen GitHub Gist vorbereitet, der zeigt, wie die stratifyOption funktioniert:

https://gist.github.com/SHi-ON/63839f3a3647051a180cb03af0f7d0d9

Shayan Amani
quelle
6

Zusätzlich zu der akzeptierten Antwort von @Andreas Mueller möchte ich diese als @tangy hinzufügen, die oben erwähnt wurde:

StratifiedShuffleSplit ähnelt am ehesten train_test_split ( stratify = y) mit zusätzlichen Funktionen von:

  1. standardmäßig schichten
  2. Durch Angabe von n_splits werden die Daten wiederholt aufgeteilt
Max
quelle
0
#train_size is 1 - tst_size - vld_size
tst_size=0.15
vld_size=0.15

X_train_test, X_valid, y_train_test, y_valid = train_test_split(df.drop(y, axis=1), df.y, test_size = vld_size, random_state=13903) 

X_train_test_V=pd.DataFrame(X_train_test)
X_valid=pd.DataFrame(X_valid)

X_train, X_test, y_train, y_test = train_test_split(X_train_test, y_train_test, test_size=tst_size, random_state=13903)
José Carlos Castro
quelle
0

Aktualisierung der @tangy-Antwort von oben auf die aktuelle Version von scikit-learn: 0.23.2 ( StratifiedShuffleSplit-Dokumentation ).

from sklearn.model_selection import StratifiedShuffleSplit

n_splits = 1  # We only want a single split in this case
sss = StratifiedShuffleSplit(n_splits=n_splits, test_size=0.25, random_state=0)

for train_index, test_index in sss.split(X, y):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
Roei Bahumi
quelle