Jak utworzyć próbki testowe i szkolić próbki z jednej ramki danych za pomocą pand?

322

Mam dość duży zestaw danych w postaci ramki danych i zastanawiałem się, jak mógłbym podzielić ramkę danych na dwie losowe próbki (80% i 20%) na potrzeby szkolenia i testowania.

Dzięki!

tooty44
źródło

Odpowiedzi:

346

Po prostu użyłbym numpy randn:

In [11]: df = pd.DataFrame(np.random.randn(100, 2))

In [12]: msk = np.random.rand(len(df)) < 0.8

In [13]: train = df[msk]

In [14]: test = df[~msk]

I tylko po to, aby zobaczyć, jak to działa:

In [15]: len(test)
Out[15]: 21

In [16]: len(train)
Out[16]: 79
Andy Hayden
źródło
3
Przepraszam, mój błąd. Dopóki mskjest dtype bool, df[msk], df.iloc[msk]i df.loc[msk]zawsze zwraca ten sam wynik.
unutbu
2
Myślę, że należy użyć randdo < 0.8sensu, ponieważ zwraca liczb losowych równomiernie rozłożone pomiędzy 0 a 1.
R. Max
4
Czy ktoś może wyjaśnić w kategoriach czysto Pythona, co dokładnie dzieje się w liniach in[12], in[13], in[14]? Chcę zrozumieć sam kod python tutaj
kuatroka
7
Odpowiedź przy użyciu sklearn od gobrewerów14 jest lepsza. Jest mniej skomplikowany i łatwiejszy do debugowania. Polecam skorzystać z odpowiedzi poniżej.
So S
2
@kuatroka np.random.rand(len(df))to tablica wielkości len(df)z losowo i równomiernie rozmieszczonymi wartościami zmiennoprzecinkowymi w zakresie [0, 1]. < 0.8Stosuje porównanie elementem mądry i zapisuje wynik w miejscu. Zatem wartości <0,8 stają się Truei wartość> = 0,8 stają sięFalse
Kentzo
623

scikit learn'strain_test_split jest dobry.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
gobrewery 14
źródło
22
Zwróci jednak tablice numpy, a nie ramki danych Pandas
Bar
124
Btw, teraz zwraca Pandas Dataframe (właśnie przetestowany na Sklearn 0.16.1)
Julien Marrec
5
Jeśli szukasz KFolda, jest on nieco bardziej złożony. kf = KFold(n, n_folds=folds) for train_index, test_index in kf: X_train, X_test = X.ix[train_index], X.ix[test_index]pełny przykład tutaj: quantstart.com/articles/…
ihadanny
12
W nowych wersjach (0.18, może wcześniej) importuj from sklearn.model_selection import train_test_splitzamiast tego.
Mark
7
W najnowszej wersji SciKit musisz nazwać to teraz jako:from sklearn.cross_validation import train_test_split
podkowa
289

Działa również losowa próbka pand

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
źródło
Co oznacza .index / gdzie jest dokumentacja .index w DataFrame? Nie mogę tego znaleźć
dmonopoly
1
co random_staterobi arg?
Rishabh Agrahari
1
@RishabhAgrahari losowo tasuje różne dane podzielone za każdym razem zgodnie z frac arg. Jeśli chcesz kontrolować losowość, możesz podać własne ziarno, jak w przykładzie.
MikeL
4
To wydaje się działać dobrze i jest bardziej eleganckim rozwiązaniem niż wprowadzanie sklearn. Czy istnieje powód, dla którego nie powinna to być lepiej akceptowana odpowiedź?
RajV
1
@peer to ograniczenie można łatwo usunąć, jeśli testpożądany jest zestaw losowy, jak wskazano tutaj stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal,
32

Korzystałbym z własnego Training_test_split scikit-learn i generowałem go z indeksu

from sklearn.model_selection import train_test_split


y = df.pop('output')
X = df

X_train,X_test,y_train,y_test = train_test_split(X.index,y,test_size=0.2)
X.iloc[X_train] # return dataframe train
Napitupulu Jon
źródło
3
cross_validationModuł jest teraz przestarzała:DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
Harry
19

Istnieje wiele sposobów tworzenia pociągu / testu, a nawet próbek walidacyjnych.

Przypadek 1: klasyczny sposób train_test_splitbez żadnych opcji:

from sklearn.model_selection import train_test_split
train, test = train_test_split(df, test_size=0.3)

Przypadek 2: przypadek bardzo małych zestawów danych (<500 wierszy): w celu uzyskania wyników dla wszystkich linii z tą weryfikacją krzyżową. Na koniec będziesz mieć jedną prognozę dla każdej linii dostępnego zestawu treningowego.

from sklearn.model_selection import KFold
kf = KFold(n_splits=10, random_state=0)
y_hat_all = []
for train_index, test_index in kf.split(X, y):
    reg = RandomForestRegressor(n_estimators=50, random_state=0)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    clf = reg.fit(X_train, y_train)
    y_hat = clf.predict(X_test)
    y_hat_all.append(y_hat)

Przypadek 3a: Niezrównoważone zestawy danych do celów klasyfikacji. Zgodnie z przypadkiem 1, oto równoważne rozwiązanie:

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.3)

Przypadek 3b: Niezrównoważone zestawy danych do celów klasyfikacji. Zgodnie z przypadkiem 2, oto równoważne rozwiązanie:

from sklearn.model_selection import StratifiedKFold
kf = StratifiedKFold(n_splits=10, random_state=0)
y_hat_all = []
for train_index, test_index in kf.split(X, y):
    reg = RandomForestRegressor(n_estimators=50, random_state=0)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    clf = reg.fit(X_train, y_train)
    y_hat = clf.predict(X_test)
    y_hat_all.append(y_hat)

Przypadek 4: musisz stworzyć zestawy pociągów / testów / walidacji na dużych danych, aby dostroić hiperparametry (60% pociągu, 20% testu i 20% wartości).

from sklearn.model_selection import train_test_split
X_train, X_test_val, y_train, y_test_val = train_test_split(X, y, test_size=0.6)
X_test, X_val, y_test, y_val = train_test_split(X_test_val, y_test_val, stratify=y, test_size=0.5)
yannick_leo
źródło
13

Możesz użyć poniższego kodu, aby utworzyć próbki testowe i szkolić:

from sklearn.model_selection import train_test_split
trainingSet, testSet = train_test_split(df, test_size=0.2)

Rozmiar testu może się różnić w zależności od odsetka danych, które chcesz umieścić w zestawie danych testu i pociągu.

użytkownik1775015
źródło
7

Istnieje wiele poprawnych odpowiedzi. Dodanie jeszcze jednego do grona. ze sklearn.cross_validation import train_test_split

#gets a random 80% of the entire set
X_train = X.sample(frac=0.8, random_state=1)
#gets the left out portion of the dataset
X_test = X.loc[~df_model.index.isin(X_train.index)]
Abhi
źródło
5

Możesz także rozważyć podział warstwowy na zestaw szkoleniowy i testowy. Podział startowy generuje również zestaw treningów i testów losowo, ale w taki sposób, aby zachować oryginalne proporcje klas. Dzięki temu zestawy szkoleniowe 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

df [train_inds] i df [test_inds] zapewniają zestawy szkoleniowe i testowe oryginalnej DataFrame df.

Apogentus
źródło
Jest to preferowana strategia dla nadzorowanych zadań edukacyjnych.
vincentmajor
Podczas próby użycia tego pojawia się błąd. ValueError: miejsce docelowe przypisania jest tylko do odczytu w wierszu „np.random.shuffle (value_inds)”
Markus W
4

Jeśli chcesz podzielić dane w odniesieniu do kolumny lables w zestawie danych, możesz użyć tego:

def split_to_train_test(df, label_column, train_frac=0.8):
    train_df, test_df = pd.DataFrame(), pd.DataFrame()
    labels = df[label_column].unique()
    for lbl in labels:
        lbl_df = df[df[label_column] == lbl]
        lbl_train_df = lbl_df.sample(frac=train_frac)
        lbl_test_df = lbl_df.drop(lbl_train_df.index)
        print '\n%s:\n---------\ntotal:%d\ntrain_df:%d\ntest_df:%d' % (lbl, len(lbl_df), len(lbl_train_df), len(lbl_test_df))
        train_df = train_df.append(lbl_train_df)
        test_df = test_df.append(lbl_test_df)

    return train_df, test_df

i użyj go:

train, test = split_to_train_test(data, 'class', 0.7)

możesz także przekazać parametr random_state, jeśli chcesz kontrolować losowość podzieloną lub użyć globalnego źródła losowego.

MikeL
źródło
3
import pandas as pd

from sklearn.model_selection import train_test_split

datafile_name = 'path_to_data_file'

data = pd.read_csv(datafile_name)

target_attribute = data['column_name']

X_train, X_test, y_train, y_test = train_test_split(data, target_attribute, test_size=0.8)
Pardhu Gopalam
źródło
2
Masz krótki błąd. Powinieneś upuścić kolumnę docelową wcześniej, umieszczasz ją w train_test_split. data = data.drop (kolumny = ['nazwa_kolumny'], oś = 1)
Anton Erjomin
3

Możesz użyć ~ (operator tyldy), aby wykluczyć wiersze próbkowane za pomocą df.sample (), pozwalając pandom samodzielnie obsługiwać próbkowanie i filtrowanie indeksów, aby uzyskać dwa zestawy.

train_df = df.sample(frac=0.8, random_state=100)
test_df = df[~df.index.isin(train_df.index)]
Pratik Deoolwadikar
źródło
2

To właśnie napisałem, gdy potrzebowałem podzielić ramkę danych. Zastanawiałem się nad zastosowaniem powyższego podejścia Andy'ego, ale nie podobało mi się, że nie mogłem dokładnie kontrolować wielkości zbiorów danych (tj. Czasami byłoby to 79, a czasem 81 itd.).

def make_sets(data_df, test_portion):
    import random as rnd

    tot_ix = range(len(data_df))
    test_ix = sort(rnd.sample(tot_ix, int(test_portion * len(data_df))))
    train_ix = list(set(tot_ix) ^ set(test_ix))

    test_df = data_df.ix[test_ix]
    train_df = data_df.ix[train_ix]

    return train_df, test_df


train_df, test_df = make_sets(data_df, 0.2)
test_df.head()
Anarcho-Chossid
źródło
2

Wystarczy wybrać wiersz zakresu z df w ten sposób

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
źródło
3
Działa to tylko wtedy, gdy dane w ramce danych są już losowo uporządkowane. Jeśli zestaw danych pochodzi z wielu źródeł i został dołączony do tej samej ramki danych, całkiem możliwe jest uzyskanie bardzo wypaczonego zestawu danych do szkolenia / testowania przy użyciu powyższego.
Emil H
1
Można przetasować dataframe przed podzielić go stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
Absolutnie! Jeśli dodasz, że dffragment kodu jest (lub powinien być) tasowany, poprawi to odpowiedź.
Emil H
2

Istnieje wiele świetnych odpowiedzi powyżej, więc chcę tylko dodać jeszcze jeden przykład w przypadku, gdy chcesz określić dokładną liczbę próbek dla pociągu i zestawów testowych, używając tylko numpybiblioteki.

# set the random seed for the reproducibility
np.random.seed(17)

# e.g. number of samples for the training set is 1000
n_train = 1000

# shuffle the indexes
shuffled_indexes = np.arange(len(data_df))
np.random.shuffle(shuffled_indexes)

# use 'n_train' samples for training and the rest for testing
train_ids = shuffled_indexes[:n_train]
test_ids = shuffled_indexes[n_train:]

train_data = data_df.iloc[train_ids]
train_labels = labels_df.iloc[train_ids]

test_data = data_df.iloc[test_ids]
test_labels = data_df.iloc[test_ids]
biendltb
źródło
2

Aby podzielić na więcej niż dwie klasy, takie jak szkolenie, test i walidacja, można:

probs = np.random.rand(len(df))
training_mask = probs < 0.7
test_mask = (probs>=0.7) & (probs < 0.85)
validatoin_mask = probs >= 0.85


df_training = df[training_mask]
df_test = df[test_mask]
df_validation = df[validatoin_mask]

W ten sposób około 70% danych będzie szkolonych, 15% w teście, a 15% w walidacji.

AHonarmand
źródło
1
Możesz zredagować swoją odpowiedź, aby dodać „w przybliżeniu”, jeśli uruchomisz kod, zobaczysz, że może on być zupełnie inny od dokładnego procentu. np. próbowałem na 1000 przedmiotach i dostałem: 700, 141, 159 - czyli 70%, 14% i 16%.
Stason
2

musisz przekonwertować ramkę danych pandy na tablicę numpy, a następnie przekonwertować tablicę numpy z powrotem na ramkę danych

 import pandas as pd
df=pd.read_csv('/content/drive/My Drive/snippet.csv', sep='\t')
from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
train1=pd.DataFrame(train)
test1=pd.DataFrame(test)
train1.to_csv('/content/drive/My Drive/train.csv',sep="\t",header=None, encoding='utf-8', index = False)
test1.to_csv('/content/drive/My Drive/test.csv',sep="\t",header=None, encoding='utf-8', index = False)
Shaina Raza
źródło
Tylko w kodzie odpowiedzi są niedopuszczalne w przypadku przepełnienia stosu.
VFDan
1

Jeśli chcesz mieć jedną ramkę danych i dwie ramki danych (nie tablic numpy), powinno to załatwić sprawę:

def split_data(df, train_perc = 0.8):

   df['train'] = np.random.rand(len(df)) < train_perc

   train = df[df.train == 1]

   test = df[df.train == 0]

   split_data ={'train': train, 'test': test}

   return split_data
Johnny V.
źródło
1

Możesz skorzystać z funkcji df.as_matrix () i utworzyć tablicę Numpy i przekazać ją.

Y = df.pop()
X = df.as_matrix()
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size = 0.2)
model.fit(x_train, y_train)
model.test(x_test)
kiran6
źródło
1

Nieco bardziej eleganckie według mnie jest utworzenie losowej kolumny, a następnie podzielenie przez nią, w ten sposób możemy uzyskać podział, który będzie odpowiadał naszym potrzebom i będzie losowy.

def split_df(df, p=[0.8, 0.2]):
import numpy as np
df["rand"]=np.random.choice(len(p), len(df), p=p)
r = [df[df["rand"]==val] for val in df["rand"].unique()]
return r
thebeancounter
źródło
0

Myślę, że musisz także otrzymać kopię, a nie kawałek ramki danych, jeśli chcesz później dodać kolumny.

msk = np.random.rand(len(df)) < 0.8
train, test = df[msk].copy(deep = True), df[~msk].copy(deep = True)
Hakim
źródło
0

Co powiesz na to? df jest moją ramką danych

total_size=len(df)

train_size=math.floor(0.66*total_size) (2/3 part of my dataset)

#training dataset
train=df.head(train_size)
#test dataset
test=df.tail(len(df) -train_size)
Akash Jain
źródło
0

Nie trzeba konwertować na numpy. Wystarczy użyć pandy df, aby dokonać podziału, a ona zwróci pand df.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
Wścibski
źródło
0
shuffle = np.random.permutation(len(df))
test_size = int(len(df) * 0.2)
test_aux = shuffle[:test_size]
train_aux = shuffle[test_size:]
TRAIN_DF =df.iloc[train_aux]
TEST_DF = df.iloc[test_aux]
Elyte D General
źródło
2
To byłaby lepsza odpowiedź, gdybyś wyjaśnił, w jaki sposób podany kod odpowiada na pytanie.
pppery
Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
shaunakde
pierwsza linia zwraca zakres tasowania (w odniesieniu do rozmiaru ramki danych). Druga linia reprezentuje pożądaną część zestawu testowego. Trzecia i czwarta linia zawiera ułamek w tasowanym zakresie. Pozostałe linie powinny być oczywiste .Pozdrowienia.
Elyte D General