Bagaimana cara membuat tes dan melatih sampel dari satu kerangka data dengan panda?

324

Saya memiliki dataset yang cukup besar dalam bentuk kerangka data dan saya bertanya-tanya bagaimana saya dapat membagi kerangka data menjadi dua sampel acak (80% dan 20%) untuk pelatihan dan pengujian.

Terima kasih!

tooty44
sumber

Jawaban:

347

Saya hanya akan menggunakan 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]

Dan hanya untuk melihat ini berhasil:

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

In [16]: len(train)
Out[16]: 79
Andy Hayden
sumber
3
Maaf, kesalahan saya. Selama mskini dari dtype bool, df[msk], df.iloc[msk]dan df.loc[msk]selalu mengembalikan hasil yang sama.
unutbu
2
Saya pikir Anda harus menggunakan randuntuk < 0.8masuk akal karena mengembalikan merata nomor acak antara 0 dan 1.
R. Max
4
Dapatkah seseorang menjelaskan murni dalam hal python apa sebenarnya yang terjadi di garis in[12], in[13], in[14]? Saya ingin mengerti kode python itu sendiri di sini
kuatroka
7
Jawaban menggunakan sklearn dari gobrewers14 adalah yang lebih baik. Ini kurang rumit dan lebih mudah untuk di-debug. Saya sarankan menggunakan jawaban di bawah ini.
Jadi S
2
@kuatroka np.random.rand(len(df))adalah array ukuran len(df)dengan nilai float yang terdistribusi secara acak dan seragam dalam kisaran [0, 1]. The < 0.8berlaku perbandingan unsur-bijaksana dan menyimpan hasilnya di tempat. Dengan demikian nilai <0,8 menjadi Truedan nilai> = 0,8 menjadiFalse
Kentzo
624

scikit belajar itutrain_test_split bagus.

from sklearn.model_selection import train_test_split

train, test = train_test_split(df, test_size=0.2)
gobrewers14
sumber
22
Namun ini akan mengembalikan array numpy dan bukan Pandas Dataframe
Bar
124
Btw, itu mengembalikan Bingkai Data Pandas sekarang (hanya diuji pada Sklearn 0.16.1)
Julien Marrec
5
Jika Anda mencari KFold, ini sedikit lebih rumit. 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]lihat contoh lengkap di sini: quantstart.com/articles/…
ihadanny
12
Di versi baru (0.18, mungkin lebih awal), impor sebagai from sklearn.model_selection import train_test_splitgantinya.
Tandai
7
Dalam versi SciKit terbaru, Anda perlu menyebutnya sekarang sebagai:from sklearn.cross_validation import train_test_split
sepatu kuda
290

Sampel acak panda juga akan berfungsi

train=df.sample(frac=0.8,random_state=200) #random state is a seed value
test=df.drop(train.index)
PagMax
sumber
Apa arti .index / di mana dokumentasi untuk .index pada DataFrame? Saya tidak dapat menemukannya.
dmonopoly
1
apa yang sedang random_statedilakukan arg?
Rishabh Agrahari
1
@RishabhAgrahari secara acak mengacak data yang berbeda setiap kali sesuai dengan frac arg. Jika Anda ingin mengontrol keacakan, Anda dapat menyatakan benih Anda sendiri, seperti dalam contoh.
MikeL
4
Ini tampaknya bekerja dengan baik dan solusi yang lebih elegan daripada membawa sklearn. Apakah ada alasan mengapa ini bukan jawaban yang diterima dengan lebih baik?
RajV
1
@ peer batasan itu mudah diperbaiki jika testset acak diinginkan seperti yang ditunjukkan di sini stackoverflow.com/questions/29576430/shuffle-dataframe-rows . test=df.drop(train.index).sample(frac=1.0)
Alok Lal
32

Saya akan menggunakan training_test_split scikit-learn sendiri, dan menghasilkannya dari indeks

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
sumber
3
The cross_validationModul sekarang tidak berlaku: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

Ada banyak cara untuk membuat kereta / tes dan bahkan sampel validasi.

Kasus 1: cara klasik train_test_splittanpa opsi apa pun:

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

Kasus 2: kasus dataset sangat kecil (<500 baris): untuk mendapatkan hasil untuk semua baris Anda dengan validasi silang ini. Pada akhirnya, Anda akan memiliki satu prediksi untuk setiap baris dari set pelatihan yang tersedia.

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)

Kasus 3a: Kumpulan data tidak seimbang untuk tujuan klasifikasi. Mengikuti kasus 1, berikut ini adalah solusi yang setara:

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)

Kasus 3b: Kumpulan data tidak seimbang untuk tujuan klasifikasi. Mengikuti kasus 2, berikut adalah solusi yang setara:

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)

Kasus 4: Anda perlu membuat set train / test / validasi pada data besar untuk menyetel hyperparameter (60% train, 20% test, dan 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
sumber
13

Anda dapat menggunakan kode di bawah ini untuk membuat sampel uji dan latih:

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

Ukuran tes dapat bervariasi tergantung pada persentase data yang ingin Anda masukkan dalam dataset tes dan kereta Anda.

pengguna1775015
sumber
7

Ada banyak jawaban yang valid. Menambahkan satu lagi ke banyak. dari 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
sumber
5

Anda juga dapat mempertimbangkan pembagian bertingkat ke dalam set pelatihan dan pengujian. Divisi Startified juga menghasilkan pelatihan dan pengujian yang ditetapkan secara acak tetapi sedemikian rupa sehingga proporsi kelas asli dipertahankan. Ini membuat set pelatihan dan pengujian lebih mencerminkan properti dari dataset asli.

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] dan df [test_inds] memberi Anda set pelatihan dan pengujian df DataFrame asli Anda.

Apogentus
sumber
Ini adalah strategi yang lebih disukai untuk tugas belajar yang diawasi.
vincentmajor
Ketika mencoba menggunakan ini saya mendapatkan kesalahan. ValueError: tujuan penugasan hanya dapat dibaca di baris "np.random.shuffle (value_inds)"
Markus W
4

Jika Anda perlu untuk membagi data Anda sehubungan dengan kolom lables di set data Anda, Anda dapat menggunakan ini:

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

dan gunakan:

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

Anda juga dapat melewatkan random_state jika Anda ingin mengontrol pembagian acak atau menggunakan beberapa seed acak global.

MikeL
sumber
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
sumber
2
Anda memiliki kesalahan pendek. Anda harus meletakkan kolom target sebelumnya, Anda memasukkannya ke train_test_split. data = data.drop (kolom = ['column_name'], sumbu = 1)
Anton Erjomin
3

Anda dapat menggunakan ~ (operator tilde) untuk mengecualikan baris yang diambil sampelnya menggunakan df.sample (), membiarkan panda menangani sampel dan penyaringan indeks, untuk mendapatkan dua set.

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

Ini adalah apa yang saya tulis ketika saya perlu membagi DataFrame. Saya mempertimbangkan untuk menggunakan pendekatan Andy di atas, tetapi tidak suka bahwa saya tidak dapat mengontrol ukuran set data dengan tepat (yaitu, kadang-kadang 79, kadang 81, dll.).

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

Cukup pilih rentang baris dari df seperti ini

row_count = df.shape[0]
split_point = int(row_count*1/5)
test_data, train_data = df[:split_point], df[split_point:]
Makio
sumber
3
Ini hanya akan berfungsi jika data dalam kerangka data sudah dipesan secara acak. Jika dataset berasal dari sumber ultiple dan telah ditambahkan ke kerangka data yang sama maka sangat mungkin untuk mendapatkan dataset yang sangat miring untuk pelatihan / pengujian menggunakan di atas.
Emil H
1
Anda dapat mengocok dataframe sebelum membaginya stackoverflow.com/questions/29576430/shuffle-dataframe-rows
Makio
1
Absolutelty! Jika Anda menambahkan bahwa dfdalam cuplikan kode Anda (atau seharusnya) dikocok, itu akan meningkatkan jawabannya.
Emil H
2

Ada banyak jawaban bagus di atas jadi saya hanya ingin menambahkan satu contoh lagi dalam hal Anda ingin menentukan jumlah sampel yang tepat untuk kereta dan set tes dengan hanya menggunakan numpyperpustakaan.

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

Untuk membaginya menjadi lebih dari dua kelas seperti kereta, tes, dan validasi, orang dapat melakukan:

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]

Ini akan menempatkan sekitar 70% data dalam pelatihan, 15% dalam tes, dan 15% dalam validasi.

AHonarmand
sumber
1
Anda mungkin ingin mengedit jawaban Anda untuk menambahkan "kira-kira", jika Anda menjalankan kode Anda akan melihat bahwa itu bisa sangat tidak sesuai dengan persentase yang tepat. misalnya saya mencobanya pada 1000 item dan mendapat: 700, 141, 159 - jadi 70%, 14% dan 16%.
stason
2

Anda perlu mengubah panda dataframe menjadi array numpy dan kemudian mengubah array numpy kembali ke dataframe

 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
sumber
Jawaban khusus kode tidak dapat diterima di Stack Overflow.
VFDan
1

Jika Anda ingin memiliki satu kerangka data dan dua kerangka data keluar (bukan array numpy), ini harus melakukan trik:

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

Anda dapat menggunakan fungsi df.as_matrix () dan membuat Numpy-array dan meneruskannya.

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

Sedikit lebih elegan untuk seleraku adalah membuat kolom acak dan kemudian membaginya, dengan cara ini kita bisa mendapatkan split yang sesuai dengan kebutuhan kita dan akan acak.

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
penghitung uang
sumber
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
sumber
2
Ini akan menjadi jawaban yang lebih baik jika Anda menjelaskan bagaimana kode yang Anda berikan menjawab pertanyaan itu.
pppery
Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa memecahkan masalah akan meningkatkan nilai jangka panjang jawaban.
shaunakde
baris pertama mengembalikan rentang yang dikocok (berkenaan dengan ukuran kerangka data). Baris kedua mewakili fraksi yang diinginkan dari set tes. Baris ketiga dan seterusnya menggabungkan fraksi ke dalam kisaran yang dikocok. Baris lainnya harus jelas. .Salam.
Elyte D General
1

Tidak perlu mengkonversi ke numpy. Cukup gunakan panda df untuk melakukan split dan itu akan mengembalikan panda df.

from sklearn.model_selection import train_test_split

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

Dan jika Anda ingin memisahkan x dari y

X_train, X_test, y_train, y_test = train_test_split(df[list_of_x_cols], df[y_col],test_size=0.2)
Yg ingin tahu
sumber
0

Saya pikir Anda juga perlu mendapatkan salinan, bukan sepotong dataframe jika Anda ingin menambahkan kolom nanti.

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

Bagaimana dengan ini? df adalah kerangka data saya

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
sumber