Stratified Train / Test-split w scikit-learn

88

Muszę podzielić moje dane na zbiór uczący (75%) i zbiór testowy (25%). Obecnie robię to za pomocą poniższego kodu:

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

Chciałbym jednak podzielić zbiór danych treningowych na straty. Jak mogę to zrobić? Przyglądałem się tej StratifiedKFoldmetodzie, ale nie pozwala mi określić podziału 75% / 25% i tylko stratyfikować zbiór danych szkoleniowych.

pir
źródło

Odpowiedzi:

153

[aktualizacja do wersji 0.17]

Zobacz dokumenty 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)

[/ aktualizacja do wersji 0.17]

Jest wniosek wyciągnąć tutaj . Ale jeśli chcesz, możesz po prostu zrobić train, test = next(iter(StratifiedKFold(...))) i użyć indeksów pociągów i testów.

Andreas Mueller
źródło
1
@AndreasMueller Czy istnieje łatwy sposób na stratyfikację danych regresji?
Jordan,
3
@Jordan nic nie jest zaimplementowane w scikit-learn. Nie znam standardowego sposobu. Moglibyśmy użyć percentyli.
Andreas Mueller
@AndreasMueller Czy kiedykolwiek widziałeś zachowanie, w którym ta metoda jest znacznie wolniejsza niż StratifiedShuffleSplit? Używałem zbioru danych MNIST.
snymkpr
@activatedgeek, co wydaje się bardzo dziwne, ponieważ train_test_split (... stratify =) po prostu wywołuje StratifiedShuffleSplit i wykonuje pierwszy podział. Zapraszam do otwarcia problemu w module śledzącym za pomocą odtwarzalnego przykładu.
Andreas Mueller
@AndreasMueller Właściwie nie otworzyłem problemu, ponieważ mam silne przeczucie, że robię coś źle (mimo że są to tylko 2 wiersze). Ale jeśli nadal będę w stanie odtworzyć to dzisiaj wiele razy, zrobię to!
snymkpr
29

TL; DR: Użyj StratifiedShuffleSplit ztest_size=0.25

Scikit-learn zapewnia dwa moduły podziału warstwowego:

  1. StratifiedKFold : Ten moduł jest przydatny jako bezpośredni operator k-krotnej weryfikacji krzyżowej: tak jak w nim skonfiguruje n_foldszestawy szkoleniowe / testowe tak, aby klasy były równo zrównoważone w obu.

Oto kod (bezpośrednio z powyższej dokumentacji)

>>> 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 : ten moduł tworzy pojedynczy zestaw szkoleniowy / testowy mający równo zrównoważone (warstwowe) klasy. Zasadniczo tego chcesz w przypadku n_iter=1. Możesz tutaj wspomnieć o rozmiarze testu, tak samo jak wtrain_test_split

Kod:

>>> 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
pikantny
źródło
5
Zauważ, że od dnia 0.18.x, n_iterpowinno być n_splitsdla StratifiedShuffleSplit - i że jest dla niego nieco inne API: scikit-learn.org/stable/modules/generated/ ...
lollercoaster
2
Jeśli yjest to seria Pandas, użyjy.iloc[train_index], y.iloc[test_index]
Owlright
1
@Owlright Próbowałem użyć ramki danych pandy, a indeksy, które zwraca StratifiedShuffleSplit, nie są indeksami w ramce danych. dataframe index: 2,3,5 the first split in sss:[(array([2, 1]), array([0]))]:(
Meghna Natraj
2
@tangy, dlaczego jest to pętla for? czyż nie jest tak, że kiedy X_train, X_test = X[train_index], X[test_index]wywoływana jest linia , przesłania ona X_traini X_test? Dlaczego więc nie tylko jeden next(sss)?
Bartek Wójcik
13

Oto przykład danych ciągłych / regresji (do czasu rozwiązania tego problemu w serwisie GitHub ).

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
)
  • Gdzie startjest min i stopmax twojego ciągłego celu.
  • Jeśli tego nie ustawisz right=True, spowoduje to, że twoja maksymalna wartość będzie mniej więcej oddzielnym przedziałem, a twój podział zawsze się nie powiedzie, ponieważ za mało próbek będzie w tym dodatkowym przedziale.
Jordania
źródło
12

Możesz to zrobić po prostu train_test_split()metodą dostępną w Scikit, aby dowiedzieć się:

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

Przygotowałem również krótkie GitHub Gist, które pokazuje, jak to zrobić stratify działa opcja:

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

Shayan Amani
źródło
6

Oprócz zaakceptowanej odpowiedzi @Andreas Mueller, chcę tylko dodać, że jako @tangy wspomniany powyżej:

StratifiedShuffleSplit najbardziej przypomina train_test_split ( stratify = y) z dodatkowymi funkcjami:

  1. rozwarstwiać domyślnie
  2. określając n_splits , wielokrotnie dzieli dane
Maks
źródło
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
źródło
0

Aktualizacja @tangy answer z góry do aktualnej wersji scikit-learn: 0.23.2 ( dokumentacja StratifiedShuffleSplit ).

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
źródło