Comment créer des tests et former des échantillons à partir d'une trame de données avec des pandas?

324

J'ai un ensemble de données assez important sous la forme d'une trame de données et je me demandais comment je pourrais diviser la trame de données en deux échantillons aléatoires (80% et 20%) pour la formation et les tests.

Merci!

tooty44
la source

Réponses:

347

Je voudrais juste utiliser 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]

Et juste pour voir que cela a fonctionné:

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

In [16]: len(train)
Out[16]: 79
Andy Hayden
la source
3
Désolé, mon erreur. Tant que mskest de DTYPE bool, df[msk], df.iloc[msk]et df.loc[msk]revenir toujours le même résultat.
unutbu
2
Je pense que vous devriez utiliser randpour < 0.8donner un sens car il renvoie des nombres aléatoires uniformément répartis entre 0 et 1.
R. Max
4
Quelqu'un peut -il expliquer purement en termes de python ce qui se passe exactement dans les lignes in[12], in[13], in[14]? Je veux comprendre le code python lui-même ici
kuatroka
7
La réponse en utilisant sklearn de gobrewers14 est la meilleure. C'est moins complexe et plus facile à déboguer. Je recommande d'utiliser la réponse ci-dessous.
So S
2
@kuatroka np.random.rand(len(df))est un tableau de taille len(df)avec des valeurs flottantes réparties de manière aléatoire et uniforme dans la plage [0, 1]. Le < 0.8applique la comparaison par élément et stocke le résultat en place. Ainsi, les valeurs <0,8 deviennent Trueet la valeur> = 0,8 deviennentFalse
Kentzo
624

scikit learn'strain_test_split est une bonne chose.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
gobrewers14
la source
22
Cela renverra cependant des tableaux numpy et non des cadres de données Pandas
Bar
124
Au fait, il retourne un Pandas Dataframe maintenant (juste testé sur Sklearn 0.16.1)
Julien Marrec
5
Si vous recherchez KFold, c'est un peu plus complexe malheureusement. 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]voir l'exemple complet ici: quantstart.com/articles/…
ihadanny
12
Dans les nouvelles versions (0.18, peut-être plus tôt), importez from sklearn.model_selection import train_test_splitplutôt.
Mark
7
Dans la dernière version de SciKit, vous devez l'appeler maintenant comme:from sklearn.cross_validation import train_test_split
Horseshoe
290

L'échantillon aléatoire des pandas fonctionnera également

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
la source
Que signifie .index / où est la documentation de .index sur un DataFrame? Je ne le trouve pas.
dmonopoly
1
que fait random_statearg?
Rishabh Agrahari
1
@RishabhAgrahari mélange de manière aléatoire différentes données à chaque fois en fonction de l'argument frac. Si vous souhaitez contrôler le caractère aléatoire, vous pouvez indiquer votre propre graine, comme dans l'exemple.
MikeL
4
Cela semble bien fonctionner et une solution plus élégante que d'apporter Sklearn. Y a-t-il une raison pour laquelle cela ne devrait pas être une réponse mieux acceptée?
RajV
1
@peer cette limitation est facilement corrigée si un testensemble mélangé est souhaité, comme indiqué ici stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal
32

J'utiliserais le propre training_test_split de scikit-learn et le générerais à partir de l'index

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
la source
3
Le cross_validationmodule est désormais obsolète: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

Il existe de nombreuses façons de créer un train / test et même des échantillons de validation.

Cas 1: manière classique train_test_splitsans aucune option:

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

Cas 2: cas d'un très petit ensemble de données (<500 lignes): afin d'obtenir des résultats pour toutes vos lignes avec cette validation croisée. À la fin, vous aurez une prédiction pour chaque ligne de votre ensemble d'entraînement disponible.

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)

Cas 3a: ensembles de données non équilibrés à des fins de classification. Après le cas 1, voici la solution équivalente:

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)

Cas 3b: ensembles de données déséquilibrés à des fins de classification. Après le cas 2, voici la solution équivalente:

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)

Cas 4: vous devez créer un train / test / ensembles de validation sur le Big Data pour régler les hyperparamètres (60% train, 20% test et 20% 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
la source
13

Vous pouvez utiliser le code ci-dessous pour créer des échantillons de test et de formation:

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

La taille du test peut varier en fonction du pourcentage de données que vous souhaitez mettre dans votre jeu de données de test et de formation.

user1775015
la source
7

Il existe de nombreuses réponses valables. Ajouter un de plus au groupe. 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
la source
5

Vous pouvez également envisager une division stratifiée en ensemble de formation et de test. La division Startified génère également des formations et des tests définis de manière aléatoire mais de telle manière que les proportions de classe d'origine sont préservées. Les ensembles de formation et de test reflètent ainsi mieux les propriétés de l'ensemble de données d'origine.

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] et df [test_inds] vous donnent les ensembles de formation et de test de votre DataFrame df d'origine.

Apogentus
la source
Il s'agit de la stratégie préférable pour les tâches d'apprentissage supervisé.
vincentmajor
Lorsque j'essaie de l'utiliser, j'obtiens une erreur. ValueError: la destination de l'affectation est en lecture seule dans la ligne "np.random.shuffle (value_inds)"
Markus W
4

Si vous devez diviser vos données par rapport à la colonne des étiquettes dans votre ensemble de données, vous pouvez utiliser ceci:

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

et l'utiliser:

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

vous pouvez également passer random_state si vous souhaitez contrôler le caractère aléatoire divisé ou utiliser une graine aléatoire globale.

MikeL
la source
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
la source
2
Vous avez une petite erreur. Vous devez supprimer la colonne cible avant, vous la mettez dans train_test_split. data = data.drop (colonnes = ['nom_colonne'], axe = 1)
Anton Erjomin
3

Vous pouvez utiliser ~ (opérateur tilde) pour exclure les lignes échantillonnées à l'aide de df.sample (), laissant les pandas seuls gérer l'échantillonnage et le filtrage des index, pour obtenir deux ensembles.

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

C'est ce que j'ai écrit lorsque j'avais besoin de diviser un DataFrame. J'ai envisagé d'utiliser l'approche d'Andy ci-dessus, mais je n'aimais pas que je ne puisse pas contrôler exactement la taille des ensembles de données (c'est-à-dire que ce serait parfois 79, parfois 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
la source
2

Sélectionnez simplement la ligne de plage de df comme ceci

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
la source
3
Cela ne fonctionnerait que si les données de la trame de données étaient déjà commandées de manière aléatoire. Si l'ensemble de données est dérivé de sources multiples et a été ajouté à la même trame de données, il est tout à fait possible d'obtenir un ensemble de données très asymétrique pour la formation / les tests en utilisant ce qui précède.
Emil H
1
Vous pouvez mélanger le dataframe avant de le diviser stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
Absolutelty! Si vous ajoutez que dfdans votre extrait de code est (ou devrait être) mélangé, cela améliorera la réponse.
Emil H
2

Il y a beaucoup de bonnes réponses ci-dessus, donc je veux juste ajouter un autre exemple dans le cas où vous souhaitez spécifier le nombre exact d'échantillons pour le train et les ensembles de test en utilisant uniquement la numpybibliothèque.

# 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
la source
2

Pour diviser en plus de deux classes telles que le train, le test et la validation, on peut faire:

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]

Cela mettra environ 70% des données en formation, 15% en test et 15% en validation.

AHonarmand
la source
1
Vous voudrez peut-être modifier votre réponse pour ajouter "environ", si vous exécutez le code, vous verrez qu'il peut être assez différent du pourcentage exact. par exemple, je l'ai essayé sur 1000 articles et j'ai obtenu: 700, 141, 159 - donc 70%, 14% et 16%.
stason
2

vous devez convertir le cadre de données pandas en tableau numpy, puis reconvertir le tableau numpy en cadre de données

 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
la source
Les réponses de code uniquement ne sont pas acceptables sur Stack Overflow.
VFDan
1

Si vous souhaitez avoir une trame de données et deux trames de données (pas des tableaux numpy), cela devrait faire l'affaire:

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
la source
1

Vous pouvez utiliser la fonction df.as_matrix () et créer un tableau Numpy et le passer.

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
la source
1

Un peu plus élégant à mon goût est de créer une colonne aléatoire, puis de la diviser, de cette façon, nous pouvons obtenir une division qui conviendra à nos besoins et sera aléatoire.

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
la source
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
la source
2
Ce serait une meilleure réponse si vous expliquiez comment le code que vous avez fourni répond à la question.
pppery
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la manière et / ou la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
shaunakde
la première ligne renvoie une plage mélangée (par rapport à la taille de la trame de données) .La deuxième ligne représente la fraction souhaitée de l'ensemble de test.La troisième et la quatrième ligne incorporent la fraction dans la plage mélangée.Les lignes de repos doivent être explicites .Cordialement.
Elyte D General
1

Pas besoin de convertir en numpy. Il suffit d'utiliser un pandas df pour faire la division et il retournera un pandas df.

from sklearn.model_selection import train_test_split

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

Et si vous voulez séparer 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)
Curieux
la source
0

Je pense que vous devez également obtenir une copie et non une tranche de trame de données si vous souhaitez ajouter des colonnes plus tard.

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

Que dis-tu de ça? df est mon 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
la source