Как создать тестовые и обучающие образцы из одного кадра данных с пандами?

324

У меня есть довольно большой набор данных в виде фрейма данных, и мне было интересно, как я смогу разделить фрейм данных на две случайные выборки (80% и 20%) для обучения и тестирования.

Спасибо!

tooty44
источник

Ответы:

347

Я бы просто использовал 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]

И просто чтобы увидеть это сработало:

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

In [16]: len(train)
Out[16]: 79
Энди Хейден
источник
3
Извините моя ошибка. Пока mskимеет DTYPE bool, df[msk], df.iloc[msk]и df.loc[msk]всегда возвращают один и тот же результат.
unutbu
2
Я думаю, что вы должны использовать, randчтобы < 0.8иметь смысл, потому что он возвращает равномерно распределенные случайные числа между 0 и 1.
Р. Макс
4
Может кто - то объяснить исключительно в терминах питона , что именно происходит в линиях in[12], in[13], in[14]? Я хочу понять сам код Python здесь
kuatroka
7
Ответ с использованием sklearn от gobrewers14 - лучший. Это менее сложно и легче для отладки. Я рекомендую использовать ответ ниже.
Итак,
2
@kuatroka np.random.rand(len(df))- это массив размеров len(df)со случайно и равномерно распределенными значениями с плавающей точкой в ​​диапазоне [0, 1]. < 0.8Применяется сравнение поэлементно и сохраняет результат на месте. Таким образом, значения <0,8 становятся, Trueа значения> = 0,8 становятсяFalse
Кензо
624

Scikit Learn'strain_test_split это хороший.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
gobrewers14
источник
22
Тем не менее, это вернет массивные массивы, а не Pandas Dataframes
Bar
124
Кстати, теперь он возвращает данные Pandas Dataframe (только что протестирован на Sklearn 0.16.1)
Julien Marrec
5
Если вы ищете KFold, это немного сложнее, к сожалению. 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]см полный пример здесь: quantstart.com/articles/...
ihadanny
12
В новых версиях (0.18, может быть и раньше) импортируйте как from sklearn.model_selection import train_test_splitвместо.
Марк
7
В новейшей версии SciKit вы должны называть это сейчас:from sklearn.cross_validation import train_test_split
подкова
290

Панда случайная выборка также будет работать

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
источник
Что означает .index / где находится документация для .index в DataFrame? Я не могу найти это.
dmonopoly
1
что random_stateделает арг?
Ришабх Аграри
1
@RishabhAgrahari случайным образом перетасовывает различные данные, каждый раз разделенные в соответствии с аргументом фракцией. Если вы хотите контролировать случайность, вы можете указать свое собственное семя, как в примере.
MikeL
4
Это, кажется, работает хорошо и более элегантное решение, чем использование sklearn. Есть ли причина, по которой этот ответ не должен быть лучше принят?
RajV
1
@ Считайте, что это ограничение легко устранить, если требуется перемешанный testнабор, как указано здесь stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal
32

Я бы использовал собственный обучающий тест scikit-learn и сгенерировал бы его из индекса

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
Напитупулу Джон
источник
3
cross_validationМодуль теперь осуждается: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.
Гарри
20

Есть много способов создать поезд / тест и даже проверочные образцы.

Случай 1: классический способ train_test_splitбез каких-либо опций:

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

Случай 2: случай очень маленьких наборов данных (<500 строк): чтобы получить результаты для всех ваших строк с помощью этой перекрестной проверки. В конце у вас будет один прогноз для каждой строки вашего доступного учебного набора.

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)

Случай 3a: Несбалансированные наборы данных для целей классификации. Следуя случаю 1, вот эквивалентное решение:

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)

Случай 3b: Несбалансированные наборы данных для целей классификации. Следуя случаю 2, вот эквивалентное решение:

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)

Случай 4: вам нужно создать наборы поездов / тестов / проверок больших данных для настройки гиперпараметров (60% поезд, 20% тест и 20% вал).

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
источник
13

Вы можете использовать приведенный ниже код для создания тестовых и обучающих образцов:

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

Размер теста может варьироваться в зависимости от процента данных, которые вы хотите поместить в свой набор данных теста и обучения.

user1775015
источник
7

Есть много действительных ответов. Добавляем еще один в кучу. из 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)]
Аби
источник
5

Вы также можете рассмотреть разделение на учебно-испытательный комплекс. Подразделение Startered также генерирует наборы для обучения и тестирования случайным образом, но таким образом, что сохраняются исходные пропорции классов. Это позволяет обучающим и тестовым наборам лучше отражать свойства исходного набора данных.

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] и df [test_inds] дают вам наборы для обучения и тестирования вашего оригинального df.

Apogentus
источник
Это предпочтительная стратегия для контролируемых учебных задач.
Vincentmajor
При попытке использовать это я получаю сообщение об ошибке. ValueError: назначение назначения доступно только для чтения в строке "np.random.shuffle (value_inds)"
Markus W
4

Если вам нужно разделить данные по столбцу меток в вашем наборе данных, вы можете использовать это:

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

и использовать это:

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

вы также можете передать random_state, если вы хотите контролировать случайную разбивку или использовать глобальное случайное начальное число.

Микель
источник
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)
Пардху Гопалам
источник
2
У тебя короткая ошибка. Вы должны удалить целевой столбец до того, как поместить его в train_test_split. data = data.drop (columns = ['column_name'], axis = 1)
Антон Ержомин
3

Вы можете использовать ~ (оператор тильды), чтобы исключить строки, выбранные с помощью df.sample (), позволяя только пандам обрабатывать выборку и фильтрацию индексов, чтобы получить два набора.

train_df = df.sample(frac=0.8, random_state=100)
test_df = df[~df.index.isin(train_df.index)]
Пратик Деолвадикар
источник
2

Это то, что я написал, когда мне нужно было разделить DataFrame. Я рассмотрел использование подхода Энди выше, но мне не понравилось, что я не мог точно контролировать размер наборов данных (т. Е. Иногда это будет 79, иногда 81 и т. Д.).

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()
Анархо-Chossid
источник
2

Просто выберите строку диапазона из DF, как это

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
источник
3
Это будет работать, только если данные в кадре данных уже упорядочены случайным образом. Если набор данных получен из ультрасовременных источников и добавлен к одному и тому же фрейму данных, то вполне возможно получить очень искаженный набор данных для обучения / тестирования с использованием описанного выше.
Эмиль Х
1
Вы можете перетасовать dataframe , прежде чем разделить его stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
Absolutelty! Если вы добавите, что dfфрагмент кода (или должен быть) перемешан, это улучшит ответ.
Эмиль Х,
2

Выше приведено много отличных ответов, поэтому я просто хочу добавить еще один пример на тот случай, если вы хотите указать точное количество образцов для набора поездов и тестов, используя только numpyбиблиотеку.

# 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
источник
2

Чтобы разделить более чем на два класса, таких как тренировка, тестирование и валидация, можно сделать:

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]

Это даст примерно 70% данных для обучения, 15% для тестирования и 15% для проверки.

AHonarmand
источник
1
Возможно, вы захотите отредактировать свой ответ, добавив «приблизительно», если вы запустите код, вы увидите, что он может быть совершенно не похож на точный процент. Например, я попробовал это на 1000 предметов и получил: 700, 141, 159 - так 70%, 14% и 16%.
Стон
2

вам нужно преобразовать pandas dataframe в массив numpy, а затем преобразовать массив numpy обратно в массив данных

 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
источник
Ответы только на код недопустимы при переполнении стека.
VFDan
1

Если вы хотите иметь один и два фрейма данных (а не пустые массивы), это должно сработать:

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
Джонни V
источник
1

Вы можете использовать функцию df.as_matrix (), создать Numpy-массив и передать его.

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
источник
1

Немного более элегантно на мой вкус - создать случайный столбец, а затем разделить его, таким образом, мы можем получить разделение, которое будет соответствовать нашим потребностям и будет случайным.

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
источник
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
источник
2
Это было бы лучшим ответом, если бы вы объяснили, как предоставленный код отвечает на вопрос.
pppery
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
shaunakde
первая строка возвращает перетасованный диапазон (по отношению к размеру информационного кадра). Вторая строка представляет желаемую долю тестового набора. Третья и четвертая строка включают дробь в перетасованный диапазон. Остальные строки должны быть самоочевидными. .С уважением.
Elyte D General
1

Не нужно конвертировать в NumPy. Просто используйте pandas df для разделения, и он вернет pandas df.

from sklearn.model_selection import train_test_split

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

И если вы хотите разделить х от у

X_train, X_test, y_train, y_test = train_test_split(df[list_of_x_cols], df[y_col],test_size=0.2)
пронырливый
источник
0

Я думаю, вам также нужно получить копию, а не фрагмент данных, если вы хотите добавить столбцы позже.

msk = np.random.rand(len(df)) < 0.8
train, test = df[msk].copy(deep = True), df[~msk].copy(deep = True)
крупный чиновник
источник
0

Как насчет этого? DF - мой фрейм данных

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)
Акаш джайн
источник