¿Cómo creo muestras de prueba y entrenamiento a partir de un marco de datos con pandas?

324

Tengo un conjunto de datos bastante grande en forma de un marco de datos y me preguntaba cómo podría dividir el marco de datos en dos muestras aleatorias (80% y 20%) para capacitación y pruebas.

¡Gracias!

tooty44
fuente

Respuestas:

347

Solo usaría 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]

Y solo para ver esto ha funcionado:

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

In [16]: len(train)
Out[16]: 79
Andy Hayden
fuente
3
Perdón mi error. Mientras mskes de dtype bool, df[msk], df.iloc[msk]y df.loc[msk]siempre devuelven el mismo resultado.
unutbu
2
Creo que se debe utilizar randpara < 0.8dar sentido porque devuelve uniformemente distribuida números aleatorios entre 0 y 1.
R. Max
44
¿Puede alguien explicar en términos puramente pitón exactamente lo que sucede en las líneas in[12], in[13], in[14]? Quiero entender el código de Python en sí mismo
kuatroka
77
La respuesta usando sklearn de gobrewers14 es la mejor. Es menos complejo y más fácil de depurar. Recomiendo usar la respuesta a continuación.
Entonces S
2
@kuatroka np.random.rand(len(df))es una matriz de tamaño len(df)con valores flotantes distribuidos aleatoria y uniformemente en el rango [0, 1]. El < 0.8aplica la comparación elemento a elemento y almacena el resultado en su lugar. Por lo tanto, los valores <0.8 se convierten Truey el valor> = 0.8 se conviertenFalse
Kentzo
624

scikit learn'strain_test_split es bueno.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
cerveceros14
fuente
22
Sin embargo
Bar
124
Por cierto, ahora devuelve un Marco de datos de Pandas (recién probado en Sklearn 0.16.1)
Julien Marrec
55
Si está buscando KFold, lamentablemente es un poco más complejo. 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]vea el ejemplo completo aquí: quantstart.com/articles/…
ihadanny
12
En las nuevas versiones (0.18, tal vez antes), importe como en su from sklearn.model_selection import train_test_splitlugar.
Mark
77
En la versión más reciente de SciKit, debe llamarlo ahora como:from sklearn.cross_validation import train_test_split
herradura
290

La muestra aleatoria de pandas también funcionará

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
fuente
¿Qué significa .index / dónde está la documentación para .index en un DataFrame? No puedo encontrarlo
dmonopolio
1
¿Qué está random_statehaciendo arg?
Rishabh Agrahari
1
@RishabhAgrahari baraja aleatoriamente diferentes datos divididos cada vez de acuerdo con el argumento frac. Si desea controlar la aleatoriedad, puede indicar su propia semilla, como en el ejemplo.
MikeL
44
Esto parece funcionar bien y es una solución más elegante que traer sklearn. ¿Hay alguna razón por la cual esta no debería ser una respuesta mejor aceptada?
RajV
1
@peer esa limitación se soluciona fácilmente si testse desea un conjunto aleatorio como se indica aquí stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal
32

Usaría el propio training_test_split de scikit-learn y lo generaría a partir del í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
fuente
3
El cross_validationmódulo ahora está en desuso: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

Hay muchas formas de crear un tren / prueba e incluso muestras de validación.

Caso 1: forma clásica train_test_splitsin ninguna opción:

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

Caso 2: caso de un conjunto de datos muy pequeño (<500 filas): para obtener resultados para todas sus líneas con esta validación cruzada. Al final, tendrá una predicción para cada línea de su conjunto de entrenamiento 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)

Caso 3a: conjuntos de datos no balanceados para fines de clasificación. Siguiendo el caso 1, aquí está la solución 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 datos no balanceados para fines de clasificación. Siguiendo el caso 2, aquí está la solución 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: necesita crear un conjunto de tren / prueba / validación en big data para sintonizar hiperparámetros (60% tren, 20% prueba y 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
fuente
13

Puede usar el siguiente código para crear muestras de prueba y entrenamiento:

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

El tamaño de la prueba puede variar según el porcentaje de datos que desea incluir en su conjunto de datos de prueba y entrenamiento.

usuario1775015
fuente
7

Hay muchas respuestas válidas. Agregando uno más al 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
fuente
5

También puede considerar la división estratificada en conjunto de entrenamiento y prueba. La división Startified también genera un conjunto de entrenamiento y pruebas al azar, pero de tal manera que se preservan las proporciones originales de clase Esto hace que los conjuntos de entrenamiento y prueba reflejen mejor las propiedades del conjunto de datos 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] y df [test_inds] le brindan los conjuntos de entrenamiento y prueba de su DataFrame df original.

Apogentus
fuente
Esta es la estrategia preferible para las tareas de aprendizaje supervisado.
vincentmajor
Cuando intento usar esto, recibo un error. ValueError: el destino de la asignación es de solo lectura en la línea "np.random.shuffle (value_inds)"
Markus W
4

Si necesita dividir sus datos con respecto a la columna de etiquetas en su conjunto de datos, puede usar esto:

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

y úsalo:

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

También puede pasar random_state si desea controlar la aleatoriedad dividida o utilizar alguna semilla aleatoria global.

MikeL
fuente
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
fuente
2
Tienes un pequeño error. Deberías soltar la columna de destino antes, ponerla en train_test_split. data = data.drop (columnas = ['nombre_columna'], eje = 1)
Anton Erjomin
3

Puede usar ~ (operador de tilde) para excluir las filas muestreadas usando df.sample (), dejando que solo los pandas manejen el muestreo y el filtrado de índices, para obtener dos conjuntos.

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

Esto es lo que escribí cuando necesitaba dividir un DataFrame. Pensé en usar el enfoque de Andy anterior, pero no me gustó que no pudiera controlar el tamaño de los conjuntos de datos exactamente (es decir, a veces sería 79, a veces 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()
Anarco-Chossid
fuente
2

Simplemente seleccione la fila de rango de df como este

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
fuente
3
Esto solo funcionaría si los datos en el marco de datos ya están ordenados aleatoriamente. Si el conjunto de datos se deriva de las fuentes principales y se ha agregado al mismo marco de datos, entonces es muy posible obtener un conjunto de datos muy sesgado para entrenamiento / prueba usando lo anterior.
Emil H
1
Puede barajar el marco de datos antes de dividirlo stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
¡Absolutamente! Si agrega que dfen su fragmento de código se baraja (o debería), mejorará la respuesta.
Emil H
2

Hay muchas respuestas excelentes arriba, así que solo quiero agregar un ejemplo más en el caso de que desee especificar el número exacto de muestras para el tren y los conjuntos de prueba utilizando solo la 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
fuente
2

Para dividirse en más de dos clases, tales como entrenamiento, prueba y validación, uno puede hacer:

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]

Esto colocará aproximadamente el 70% de los datos en capacitación, el 15% en pruebas y el 15% en validación.

AHonarmand
fuente
1
Es posible que desee editar su respuesta para agregar "aproximadamente", si ejecuta el código verá que puede estar bastante alejado del porcentaje exacto. Por ejemplo, lo probé en 1000 artículos y obtuve: 700, 141, 159, es decir, 70%, 14% y 16%.
Stason
2

necesita convertir el marco de datos de pandas en una matriz numpy y luego volver a convertir la matriz numpy en un marco de datos

 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
fuente
Las respuestas de solo código no son aceptables en Stack Overflow.
VFDan
1

Si su deseo es tener un marco de datos entrante y dos marcos de datos fuera (no matrices numpy), esto debería ser el truco:

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

Puede hacer uso de la función df.as_matrix () y crear Numpy-array y pasarlo.

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

Un poco más elegante para mi gusto es crear una columna aleatoria y luego dividirla, de esta manera podemos obtener una división que se adapte a nuestras necesidades y será aleatoria.

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
fuente
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
fuente
2
Esta sería una mejor respuesta si explicara cómo el código que proporcionó responde a la pregunta.
pppery
Si bien este código puede responder la pregunta, proporcionar un contexto adicional con respecto a cómo y / o por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Shaunakde
la primera línea devuelve un rango aleatorio (con respecto al tamaño del marco de datos). La segunda línea representa la fracción deseada del conjunto de prueba. La tercera y cuarta línea incorpora la fracción en el rango aleatorio. Las líneas restantes deben explicarse por sí mismas. .Saludos.
Elyte D General
1

No es necesario convertir a numpy. Simplemente use un pandas df para hacer la división y devolverá un pandas df.

from sklearn.model_selection import train_test_split

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

Y si quieres 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
fuente
0

Creo que también necesita obtener una copia, no una porción de marco de datos, si desea agregar columnas más adelante.

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

¿Qué tal esto? df es mi marco de datos

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
fuente