Como crio amostras de teste e treinamento de um quadro de dados com pandas?

324

Eu tenho um conjunto de dados bastante grande na forma de um quadro de dados e queria saber como seria capaz de dividir o quadro de dados em duas amostras aleatórias (80% e 20%) para treinamento e teste.

Obrigado!

tooty44
fonte

Respostas:

347

Eu usaria apenas numpy's 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]

E só para ver isso funcionou:

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

In [16]: len(train)
Out[16]: 79
Andy Hayden
fonte
3
Desculpe meu erro. Enquanto mské de dtipo bool, df[msk], df.iloc[msk]e df.loc[msk]sempre retornam o mesmo resultado.
unutbu
2
Eu acho que você deve usar randpara < 0.8fazer sentido porque ele retorna uniformemente distribuído números aleatórios entre 0 e 1.
R. Max
4
Alguém pode explicar em termos puramente python que exatamente acontece em linhas in[12], in[13], in[14]? Eu quero entender o próprio código python aqui
kuatroka
7
A resposta usando sklearn de gobrewers14 é a melhor. É menos complexo e mais fácil de depurar. Eu recomendo usar a resposta abaixo.
Então S
2
@kuatroka np.random.rand(len(df))é uma matriz de tamanho len(df)com valores flutuantes distribuídos aleatoriamente e uniformemente no intervalo [0, 1]. O < 0.8aplica a comparação elemento a elemento e armazena o resultado no local. Assim valores <0,8 tornou Trueeo valor> = 0,8 tornarFalse
Kentzo
624

o scikit learn'strain_test_split é bom.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
gobrewers14
fonte
22
Isso irá retornar as matrizes numpy e não Pandas Dataframes no entanto
Bar
124
Btw, ele retorna um Dataframe do Pandas agora (apenas testado no Sklearn 0.16.1)
Julien Marrec
5
Se você está procurando pelo KFold, é um pouco mais complexo, infelizmente. 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]veja exemplo completo aqui: quantstart.com/articles/…
ihadanny
12
Nas novas versões (0,18, talvez anterior), importe como from sklearn.model_selection import train_test_splitalternativa.
Mark
7
Na versão mais recente do SciKit, você precisa chamá-lo agora como:from sklearn.cross_validation import train_test_split
ferradura
290

A amostra aleatória do Pandas também funcionará

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
fonte
O que significa .index / onde está a documentação para .index em um DataFrame? Não consigo encontrar.
21817 dmonopoly
1
o que random_statearg está fazendo?
Rishabh Agrahari
1
O @RishabhAgrahari embaralha aleatoriamente diferentes dados divididos a cada momento, de acordo com o argumento. Se você deseja controlar a aleatoriedade, pode declarar sua própria semente, como no exemplo.
quer
4
Isso parece funcionar bem e é uma solução mais elegante do que trazer o sklearn. Existe uma razão para que essa não seja uma resposta melhor aceita?
RajV 7/08/19
1
@peer Essa limitação é facilmente remediada se um testconjunto aleatório for desejado, como indicado aqui stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal
32

Eu usaria o training_test_split do scikit-learn e o geraria a partir do índice

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
fonte
3
O cross_validationmódulo agora está obsoleto: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
20

Existem várias maneiras de criar um trem / teste e até amostras de validação.

Caso 1: maneira clássica, train_test_splitsem opções:

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

Caso 2: caso de conjuntos de dados muito pequenos (<500 linhas): para obter resultados para todas as suas linhas com essa validação cruzada. No final, você terá uma previsão para cada linha do seu conjunto de treinamento disponível.

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)

Caso 3a: Conjuntos de dados desequilibrados para fins de classificação. Seguindo o caso 1, aqui está a solução equivalente:

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)

Caso 3b: Conjuntos de dados desequilibrados para fins de classificação. Seguindo o caso 2, aqui está a solução equivalente:

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)

Caso 4: você precisa criar um conjunto de treinamento / teste / validação no big data para ajustar os hiperparâmetros (60% de treinamento, 20% de teste e 20% de val).

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
fonte
13

Você pode usar o código abaixo para criar amostras de teste e treinamento:

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

O tamanho do teste pode variar dependendo da porcentagem de dados que você deseja colocar no seu teste e treinar o conjunto de dados.

user1775015
fonte
7

Existem muitas respostas válidas. Adicionando mais um ao grupo. de 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
fonte
5

Você também pode considerar a divisão estratificada em conjunto de treinamento e teste. A divisão iniciada também gera um conjunto de treinamento e teste aleatoriamente, mas de maneira que as proporções originais da classe sejam preservadas. Isso faz com que os conjuntos de treinamento e teste reflitam melhor as propriedades do conjunto de dados original.

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] e df [test_inds] fornecem os conjuntos de treinamento e teste do seu DataFrame df original.

Apogentus
fonte
Essa é a estratégia preferível para tarefas de aprendizado supervisionado.
Vincentmajor #
Ao tentar usar isso, estou recebendo um erro. ValueError: destino atribuição é somente leitura na linha "np.random.shuffle (value_inds)"
Markus W
4

Se você precisar dividir seus dados com relação à coluna lables no seu conjunto de dados, poderá usar este:

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

e use-o:

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

você também pode passar random_state se desejar controlar a aleatoriedade de divisão ou usar alguma semente aleatória global.

MikeL
fonte
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
fonte
2
Você tem um pequeno erro. Você deve soltar a coluna de destino antes e colocá-la em train_test_split. data = data.drop (colunas = ['nome_da_coluna'], eixo = 1)
Anton Erjomin
3

Você pode usar ~ (operador til) para excluir as linhas amostradas usando df.sample (), permitindo que os pandas gerenciem sozinho a amostragem e a filtragem de índices para obter dois conjuntos.

train_df = df.sample(frac=0.8, random_state=100)
test_df = df[~df.index.isin(train_df.index)]
Pratik Deoolwadikar
fonte
2

Foi o que escrevi quando precisei dividir um DataFrame. Eu considerei usar a abordagem de Andy acima, mas não gostei de não poder controlar exatamente o tamanho dos conjuntos de dados (ou seja, às vezes seria 79, às vezes 81, etc.).

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
fonte
2

Basta selecionar a linha do intervalo de df assim

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
fonte
3
Isso só funcionaria se os dados no quadro de dados já estivessem ordenados aleatoriamente. Se o conjunto de dados é derivado de várias fontes e foi anexado ao mesmo quadro de dados, é bem possível obter um conjunto de dados muito distorcido para treinamento / teste usando o descrito acima.
Emil H
1
Você pode embaralhar trama de dados antes de dividi-lo stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
Absolutamente! Se você adicionar que dfno seu snippet de código é (ou deveria ser) embaralhado, a resposta será melhorada.
Emil H #
2

Há muitas ótimas respostas acima, então só quero adicionar mais um exemplo, caso você queira especificar o número exato de amostras para os conjuntos de trem e teste usando apenas a numpybiblioteca.

# 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
fonte
2

Para dividir em mais de duas classes, como treinamento, teste e validação, é possível:

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]

Isso colocará aproximadamente 70% dos dados em treinamento, 15% em teste e 15% em validação.

AHonarmand
fonte
1
Você pode editar sua resposta para adicionar "aproximadamente", se você executar o código, verá que ele pode estar bastante diferente da porcentagem exata. por exemplo, tentei em 1000 itens e obtive: 700, 141, 159 - então 70%, 14% e 16%.
stason
2

você precisa converter o quadro de dados do pandas em matriz numpy e, em seguida, converter a matriz numpy novamente em quadro de dados

 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
fonte
Respostas somente de código não são aceitáveis ​​no estouro de pilha.
VFDan 30/03
1

Se seu desejo é ter um quadro de dados dentro e dois quadros de dados fora (não matrizes numpy), isso deve fazer o truque:

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
fonte
1

Você pode usar a função df.as_matrix () e criar Numpy-array e passá-la.

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
fonte
1

Um pouco mais elegante para o meu gosto é criar uma coluna aleatória e depois dividi-la, dessa forma, podemos obter uma divisão que atenda às nossas necessidades e que seja aleatória.

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
fonte
1
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
fonte
2
Essa seria uma resposta melhor se você explicasse como o código que você forneceu responde à pergunta.
pppery
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e / ou por que resolve o problema melhoraria o valor a longo prazo da resposta.
shaunakde
a primeira linha retorna um intervalo aleatório (em relação ao tamanho do quadro de dados). A segunda linha representa a fração desejada do conjunto de testes. A terceira e a quarta linha incorporam a fração no intervalo aleatório. .Saudações.
Elyte D General
1

Não há necessidade de converter para numpy. Basta usar um pandas df para fazer a divisão e ele retornará um pandas df.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)

E se você quiser dividir x de y

X_train, X_test, y_train, y_test = train_test_split(df[list_of_x_cols], df[y_col],test_size=0.2)
Curioso
fonte
0

Eu acho que você também precisa obter uma cópia e não uma fatia do dataframe, se quiser adicionar colunas mais tarde.

msk = np.random.rand(len(df)) < 0.8
train, test = df[msk].copy(deep = True), df[~msk].copy(deep = True)
Hakim
fonte
0

Que tal agora? df é meu dataframe

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
fonte