202 votes

Comment diviser les données en 3 ensembles (formation, validation et test) ?

J'ai un dataframe pandas et je souhaite le diviser en 3 ensembles distincts. Je sais qu'en utilisant train_test_split de sklearn.cross_validation on peut diviser les données en deux ensembles (formation et test). Cependant, je n'ai pas trouvé de solution pour diviser les données en trois ensembles. De préférence, j'aimerais avoir les indices des données originales.

Je sais qu'une solution de contournement serait d'utiliser train_test_split deux fois et ajuster les indices d'une manière ou d'une autre. Mais existe-t-il un moyen plus standard / intégré de diviser les données en 3 ensembles au lieu de 2 ?

6 votes

Cela ne répond pas à votre question spécifique, mais je pense que l'approche la plus standard pour cela serait de diviser en deux ensembles, formation et test, et d'exécuter une validation croisée sur l'ensemble de formation, éliminant ainsi le besoin d'un ensemble de "développement" autonome.

1 votes

Cette question a déjà été soulevée et, pour autant que je sache, il n'existe pas encore de méthode intégrée pour cela.

5 votes

Je suggère l'article de Hastie et al. Les éléments de l'apprentissage statistique pour une discussion sur les raisons d'utiliser trois ensembles au lieu de deux ( web.stanford.edu/~hastie/local.ftp/Springer/OLD/ Chapitre sur l'évaluation et la sélection des modèles)

234voto

MaxU Points 5284

Solution Numpy. Nous allons d'abord mélanger l'ensemble des données ( df.sample(frac=1, random_state=42) ), puis nous avons divisé notre ensemble de données en plusieurs parties :

  • 60 % - train set,
  • 20% - ensemble de validation,
  • 20% - jeu d'essai

In [305]: train, validate, test = \
              np.split(df.sample(frac=1, random_state=42), 
                       [int(.6*len(df)), int(.8*len(df))])

In [306]: train
Out[306]:
          A         B         C         D         E
0  0.046919  0.792216  0.206294  0.440346  0.038960
2  0.301010  0.625697  0.604724  0.936968  0.870064
1  0.642237  0.690403  0.813658  0.525379  0.396053
9  0.488484  0.389640  0.599637  0.122919  0.106505
8  0.842717  0.793315  0.554084  0.100361  0.367465
7  0.185214  0.603661  0.217677  0.281780  0.938540

In [307]: validate
Out[307]:
          A         B         C         D         E
5  0.806176  0.008896  0.362878  0.058903  0.026328
6  0.145777  0.485765  0.589272  0.806329  0.703479

In [308]: test
Out[308]:
          A         B         C         D         E
4  0.521640  0.332210  0.370177  0.859169  0.401087
3  0.333348  0.964011  0.083498  0.670386  0.169619

[int(.6*len(df)), int(.8*len(df))] - est un indices_or_sections pour numpy.split() .

Voici une petite démo pour np.split() Utilisation - divisons un tableau de 20 éléments en les parties suivantes : 80%, 10%, 10% :

In [45]: a = np.arange(1, 21)

In [46]: a
Out[46]: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])

In [47]: np.split(a, [int(.8 * len(a)), int(.9 * len(a))])
Out[47]:
[array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16]),
 array([17, 18]),
 array([19, 20])]

0 votes

@Root que fait exactement le paramètre frac=1 ?

2 votes

@SpiderWasp42, frac=1 donne des instructions à sample() pour retourner tous les ( 100% ou fraction = 1.0 ) rangées

19 votes

Merci @MaxU. J'aimerais mentionner deux choses pour simplifier les choses. Premièrement, utilisez np.random.seed(any_number) avant la ligne de partage pour obtenir le même résultat à chaque exécution. Deuxièmement, pour faire des rapports inégaux comme train:test:val::50:40:10 utiliser [int(.5*len(dfn)), int(.9*len(dfn))] . Ici, le premier élément indique la taille pour train (0,5%), le deuxième élément indique la taille pour val (1-0,9 = 0,1 %) et la différence entre les deux indique la taille de l'entreprise. test (0.9-0.5 = 0.4%). Corrigez-moi si je me trompe :)

66voto

piRSquared Points 159

Nota:

La fonction a été écrite pour gérer l'ensemencement de la création d'un ensemble aléatoire. Vous ne devriez pas compter sur une division d'ensemble qui ne rend pas les ensembles aléatoires.

import numpy as np
import pandas as pd

def train_validate_test_split(df, train_percent=.6, validate_percent=.2, seed=None):
    np.random.seed(seed)
    perm = np.random.permutation(df.index)
    m = len(df.index)
    train_end = int(train_percent * m)
    validate_end = int(validate_percent * m) + train_end
    train = df.iloc[perm[:train_end]]
    validate = df.iloc[perm[train_end:validate_end]]
    test = df.iloc[perm[validate_end:]]
    return train, validate, test

Démonstration

np.random.seed([3,1415])
df = pd.DataFrame(np.random.rand(10, 5), columns=list('ABCDE'))
df

enter image description here

train, validate, test = train_validate_test_split(df)

train

enter image description here

validate

enter image description here

test

enter image description here

2 votes

Je crois que cette fonction nécessite un df avec des valeurs d'index allant de 1 à n. Dans mon cas, j'ai modifié la fonction pour utiliser df.loc car mes valeurs d'index n'étaient pas nécessairement dans cette plage.

62voto

blitu12345 Points 1562

Cependant, une approche de la division de l'ensemble de données en train , test , cv con 0.6 , 0.2 , 0.2 serait d'utiliser le train_test_split deux fois.

from sklearn.model_selection import train_test_split

x, x_test, y, y_test = train_test_split(xtrain,labels,test_size=0.2,train_size=0.8)
x_train, x_cv, y_train, y_cv = train_test_split(x,y,test_size = 0.25,train_size =0.75)

0 votes

Sous-optimal pour les grands ensembles de données

1 votes

@MaksymGanenko Pouvez-vous préciser ?

0 votes

Vous proposez de diviser les données avec deux opérations distinctes. Chaque fractionnement de données implique la copie de données. Ainsi, lorsque vous proposez d'utiliser deux opérations de fractionnement distinctes au lieu d'une seule, vous créez artificiellement une charge sur la RAM et le CPU. Votre solution est donc sous-optimale. La division des données doit être effectuée en une seule opération, par exemple np.split() . De plus, il ne nécessite pas de dépendance supplémentaire sur les éléments suivants sklearn .

8voto

rohan chikorde Points 67

Une approche consiste à utiliser deux fois la fonction train_test_split.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test 
= train_test_split(X, y, test_size=0.2, random_state=1)

X_train, X_val, y_train, y_val 
= train_test_split(X_train, y_train, test_size=0.25, random_state=1)

2voto

A.Ametov Points 23

Il est très pratique à utiliser train_test_split sans effectuer de réindexation après avoir divisé en plusieurs ensembles et sans écrire de code supplémentaire. La meilleure réponse ci-dessus ne mentionne pas qu'en séparant deux fois à l'aide de train_test_split ne pas changer la taille des partitions ne donnera pas la partition initialement prévue :

x_train, x_remain = train_test_split(x, test_size=(val_size + test_size))

Puis la part des ensembles de validation et de test dans le changement x_remain et pourrait être compté comme

new_test_size = np.around(test_size / (val_size + test_size), 2)
# To preserve (new_test_size + new_val_size) = 1.0 
new_val_size = 1.0 - new_test_size

x_val, x_test = train_test_split(x_remain, test_size=new_test_size)

A cette occasion, toutes les partitions initiales sont sauvegardées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X