Jak podzielić / podzielić zbiór danych na treningowe i testowe zestawy danych, np. W celu weryfikacji krzyżowej?

99

Jaki jest dobry sposób na losowe podzielenie tablicy NumPy na zbiór danych treningowych i testowych / walidacyjnych? Coś podobnego do funkcji cvpartitionlub crossvalindw Matlabie.

erik
źródło

Odpowiedzi:

125

Jeśli chcesz podzielić zestaw danych raz na dwie części, możesz użyć numpy.random.shufflelub numpy.random.permutationjeśli chcesz śledzić indeksy:

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

lub

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

Istnieje wiele sposobów wielokrotnego dzielenia tego samego zestawu danych na potrzeby weryfikacji krzyżowej . Jedną ze strategii jest ponowne próbkowanie ze zbioru danych z powtórzeniem:

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

Wreszcie, sklearn zawiera kilka metod walidacji krzyżowej (k-fold, opuszczanie n-out, ...). Obejmuje również bardziej zaawansowane metody „próbkowania warstwowego” , które tworzą podział danych, który jest zrównoważony w odniesieniu do niektórych cech, na przykład w celu upewnienia się, że w zbiorze uczącym i testowym jest taki sam udział przykładów pozytywnych i negatywnych.

pberkes
źródło
13
dzięki za te rozwiązania. Ale czy ostatnia metoda, wykorzystująca randint, nie ma dużej szansy na podanie tych samych wskaźników zarówno dla zestawów testowych, jak i szkoleniowych?
ggauravr
3
Drugie rozwiązanie jest poprawną odpowiedzią, a pierwsze i trzecie nie. W przypadku pierwszego rozwiązania tasowanie zestawu danych nie zawsze jest opcją, istnieje wiele przypadków, w których trzeba zachować kolejność wprowadzania danych. A trzecia z nich mogłaby bardzo dobrze wytworzyć te same wskaźniki dla testów i treningu (jak wskazał @ggauravr).
pedram bashiri
Należy nie resample dla przekroju zestawu walidacji. Cały pomysł polega na tym, że zestaw CV nigdy wcześniej nie był widziany przez twojego algo. Zestawy treningowe i testowe służą do dopasowania danych, więc oczywiście uzyskasz dobre wyniki, jeśli włączysz je do zestawu CV. Chcę zagłosować za tą odpowiedzią, ponieważ potrzebowałem drugiego rozwiązania, ale ta odpowiedź ma problemy.
RubberDuck
55

Jest jeszcze jedna opcja, która po prostu pociąga za sobą użycie scikit-learn. Jak opisuje wiki scikit , możesz po prostu użyć następujących instrukcji:

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)

W ten sposób możesz zsynchronizować etykiety danych, które próbujesz podzielić na trening i test.

Paulo Malvar
źródło
1
Jest to bardzo praktyczna odpowiedź ze względu na realistyczną obsługę zarówno zestawu pociągów, jak i etykiet.
chinnychinchin
38

Tylko uwaga. Jeśli chcesz trenować, testować ORAZ zestawy walidacyjne, możesz to zrobić:

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)

Te parametry dadzą 70% na trening i po 15% na zestawy testowe i wartościowe. Mam nadzieję że to pomoże.

offwhitelotus
źródło
5
powinien prawdopodobnie dodać to do swojego kodu: from sklearn.cross_validation import train_test_splitaby było jasne, jakiego modułu używasz
Radix,
Czy to musi być przypadkowe?
liang
To znaczy, czy można podzielić zgodnie z podaną kolejnością X i Y?
liang
1
@liang nie, to nie musi być przypadkowe. możesz po prostu powiedzieć, że rozmiary zestawów pociągów, testów i walidacji będą procentami a, b i c całkowitego zbioru danych. powiedzmy a=0.7, b=0.15, c=0.15, i d = dataset, N=len(dataset), a następnie x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)]i x_val = dataset[int((a+b)*N):].
offwhitelotus
1
Wycofane: stackoverflow.com/a/34844352/4237080 , użyjfrom sklearn.model_selection import train_test_split
briennakh
14

Ponieważ sklearn.cross_validationmoduł został wycofany, możesz użyć:

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

Możesz również rozważyć rozwarstwiony podział na zbiór uczący i testowy. Rozpoczęty podział również generuje losowo zestaw treningowy i testowy, ale w taki sposób, aby zachować oryginalne proporcje klasowe. Dzięki temu zestawy uczące i testowe lepiej odzwierciedlają właściwości oryginalnego zestawu danych.

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]

Ten kod wyprowadza:

[1 2 3]
[1 2 3]
Apogentus
źródło
Dziękuję Ci! Nazewnictwo jest nieco mylące, value_indssą naprawdę indeksami, ale wynik nie jest indeksami, tylko maskami.
greenoldman
1

Napisałem funkcję do mojego własnego projektu, aby to zrobić (chociaż nie używa numpy):

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

Jeśli chcesz, aby fragmenty były losowe, po prostu potasuj listę przed przekazaniem jej.

Colin
źródło
0

Oto kod do podziału danych na n = 5 razy w sposób warstwowy

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

Dzięki pberkes za odpowiedź. Po prostu zmodyfikowałem to, aby uniknąć (1) zamiany podczas próbkowania (2) zduplikowane instancje występowały zarówno podczas treningu, jak i testowania:

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

Po przeczytaniu i wzięciu pod uwagę (wielu ...) różnych sposobów dzielenia danych w celu trenowania i testowania, postanowiłem trochę poczekać!

Użyłem 4 różnych metod (żadna z nich nie korzysta z biblioteki sklearn, która na pewno da najlepsze rezultaty, dając dobrze zaprojektowany i przetestowany kod):

  1. przetasuj całą macierz arr, a następnie podziel dane w celu trenowania i testowania
  2. przetasuj indeksy, a następnie przypisz im x i y, aby podzielić dane
  3. to samo, co metoda 2, ale w bardziej efektywny sposób
  4. przy użyciu pandy dataframe do podziału

metoda 3 wygrała zdecydowanie z najkrótszym czasem, po tym jak metoda 1, a metody 2 i 4 okazały się naprawdę nieefektywne.

Kod dla 4 różnych metod, które mierzyłem czasowo:

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)

A dla czasów minimalny czas na wykonanie z 3 powtórzeń 1000 pętli to:

  • Metoda 1: 0,35883826200006297 sekund
  • Metoda 2: 1,7157016959999964 sekund
  • Metoda 3: 1,7876616719995582 sekund
  • Metoda 4: 0,07562861499991413 sekund

Mam nadzieję, że to pomocne!

rotem
źródło
0

Prawdopodobnie będziesz musiał nie tylko podzielić się na trenowanie i testowanie, ale także weryfikację krzyżową, aby upewnić się, że model uogólnia. Tutaj zakładam 70% danych treningowych, 20% walidację i 10% wstrzymanie / dane testowe.

Sprawdź np.split :

Jeśli indices_or_sections to 1-D tablica posortowanych liczb całkowitych, wpisy wskazują, gdzie wzdłuż osi tablica jest podzielona. Na przykład [2, 3] dałoby dla osi = 0 wynik

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

Podzielony na test pociągu i ważny

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