डेटा को 3 सेटों में कैसे विभाजित करें (ट्रेन, सत्यापन और परीक्षण)?


146

मेरे पास एक पांडा डेटाफ़्रेम है और मैं इसे 3 अलग-अलग सेटों में विभाजित करना चाहता हूं। मुझे पता है कि का उपयोग कर train_test_split से sklearn.cross_validation, एक दो सेट (ट्रेन और परीक्षण) में डेटा विभाजित कर सकते हैं। हालाँकि, मुझे डेटा को तीन सेटों में विभाजित करने के बारे में कोई समाधान नहीं मिला। अधिमानतः, मैं मूल डेटा के सूचकांक चाहते हैं।

मुझे पता है कि एक वर्कअराउंड train_test_splitदो बार उपयोग करना होगा और किसी तरह सूचकांकों को समायोजित करना होगा। लेकिन क्या डेटा को 2 के बजाय 3 सेट में विभाजित करने का एक अधिक मानक / अंतर्निहित तरीका है?


5
यह आपके विशिष्ट प्रश्न का उत्तर नहीं देता है, लेकिन मुझे लगता है कि इसके लिए अधिक मानक दृष्टिकोण दो सेटों, ट्रेन और परीक्षण में विभाजित होगा, और प्रशिक्षण सेट पर क्रॉस-मान्यता चलाने से इस प्रकार अकेले "विकास" सेट की आवश्यकता समाप्त हो जाएगी। ।
डेविड

1
यह पहले आया था, और जहां तक ​​मुझे पता है कि अभी तक इसके लिए कोई अंतर्निहित पद्धति नहीं है।
अहान

5
मैं Hastie एट अल। के सुझाव है कि सांख्यिकीय लर्निंग द तत्वों क्यों दो (के बजाय तीन सेट का उपयोग करने पर चर्चा के लिए web.stanford.edu/~hastie/local.ftp/Springer/OLD/... मॉडल मूल्यांकन और चयन अध्याय)
अयान

2
@ डेविड कुछ मॉडलों में ओवरफिटिंग को रोकने के लिए, 2 के बजाय 3 सेट की आवश्यकता होती है। क्योंकि आपके डिजाइन विकल्पों में, आप टेस्ट सेट पर प्रदर्शन को बेहतर बनाने के लिए किसी भी तरह से ट्यूनिंग पैरामीटर हैं। इसे रोकने के लिए, एक विकास सेट की आवश्यकता होती है। तो, क्रॉस सत्यापन का उपयोग करना पर्याप्त नहीं होगा।
सेंटआयु

6
@ayhan, उस पुस्तक के लिए एक सही URL स्टेटवेब.स्टोनफोर्ड.ड्यू / ~tibs / ElemStatLearn / printings / , अध्याय 7 (पृष्ठ 219) है।
केमिली गौडेय्यून

जवाबों:


161

नम्र हल। हम पहले संपूर्ण डेटासेट को फेरबदल करेंगे (df.sample (1 = 1)) और फिर हमारे डेटा को निम्नलिखित भागों में विभाजित करें:

  • 60% - ट्रेन सेट,
  • 20% - सत्यापन सेट,
  • 20% - परीक्षण सेट

In [305]: train, validate, test = np.split(df.sample(frac=1), [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))]- numpy.split () केindices_or_sections लिए एक सरणी है ।

यहां np.split()उपयोग के लिए एक छोटा डेमो दिया गया है - चलो 20-तत्वों की सरणी को निम्नलिखित भागों में विभाजित करें: 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])]

@ क्या वास्तव में frac = 1 पैरामीटर क्या है?
स्पाइडरवैप 42

1
@ SpiderWasp42, फ़ंक्शन को सभी ( या अंश = ) पंक्तियों को वापस करने का frac=1निर्देश देता हैsample()100%1.0
MaxU

12
धन्यवाद @MaxU मैं चीजों को सरलीकृत रखने के लिए 2 चीजों का उल्लेख करना चाहता हूं। सबसे पहले, np.random.seed(any_number)हर रन के साथ समान परिणाम प्राप्त करने के लिए स्प्लिट लाइन से पहले का उपयोग करें । दूसरा, train:test:val::50:40:10उपयोग की तरह असमान अनुपात बनाने के लिए [int(.5*len(dfn)), int(.9*len(dfn))]। यहाँ पहला तत्व आकार को train(0.5%), दूसरा तत्व आकार को val(1-0.9 = 0.1%) के लिए और दोनों के बीच अंतर को दर्शाता है। आकार test(0.9-0.5 = 0.4%)। अगर मैं गलत हूं तो मुझे सही करें :)
dataLio

hrmm यह एक गलती है जब आप कहते हैं "यहाँ np.split () उपयोग के लिए एक छोटा सा डेमो है - चलो 20-तत्वों के सरणी को निम्नलिखित भागों में विभाजित करें: 90%, 10%, 10%:" मुझे पूरा यकीन है कि आप 80 का मतलब है %, 10%, 10%
केविन

अरे, @MaxU मैं एक मामला था, कुछ इसी तरह कुछ। मैं सोच रहा था कि क्या आप इसे मेरे लिए देख सकते हैं कि क्या यह देखने के लिए है और वहां मेरी मदद करें। यहाँ मेरा सवाल है stackoverflow.com/questions/54847668/…
दीपक एम

55

ध्यान दें:

रैंडमाइज्ड सेट क्रिएशन की सीडिंग को संभालने के लिए फंक्शन लिखा गया था। आपको सेट विभाजन पर भरोसा नहीं करना चाहिए जो सेट को यादृच्छिक नहीं करता है।

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

प्रदर्शन

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

यहाँ छवि विवरण दर्ज करें

train, validate, test = train_validate_test_split(df)

train

यहाँ छवि विवरण दर्ज करें

validate

यहाँ छवि विवरण दर्ज करें

test

यहाँ छवि विवरण दर्ज करें


1
मेरा मानना ​​है कि इस फ़ंक्शन के लिए 1 से n तक के सूचकांक मूल्यों के साथ df की आवश्यकता होती है। मेरे मामले में, मैंने df.loc का उपयोग करने के लिए फ़ंक्शन को संशोधित किया क्योंकि मेरे सूचकांक मूल्य इस सीमा में आवश्यक नहीं थे।
iOSBeginner

32

हालांकि, में डाटासेट विभाजित करने के लिए एक दृष्टिकोण train, test, cvसाथ 0.6, 0.2, 0.2उपयोग करने के लिए किया जाएगा train_test_splitविधि दो बार।

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)

बड़े डेटासेट के लिए उप
परिशिष्ट

@MaksymGanenko क्या आप विस्तृत कर सकते हैं?
blitu12345

आप दो अलग-अलग ऑपरेशन के साथ डेटा को विभाजित करने का सुझाव देते हैं। प्रत्येक डेटा विभाजन में डेटा कॉपी करना शामिल होता है। इसलिए जब आप कृत्रिम रूप से रैम और सीपीयू दोनों पर बोझ बनाने के बजाय दो अलग-अलग विभाजन ऑपरेशन का उपयोग करने का सुझाव देते हैं। तो आपका समाधान उप-योग है। डेटा विभाजन को एक ही ऑपरेशन के साथ किया जाना चाहिए जैसे np.split()। इसके अलावा, इस पर अतिरिक्त निर्भरता की आवश्यकता नहीं है sklearn
मैक्सिकम गेनेंको

@MaksymGanenko मेमोरी पर अतिरिक्त बोझ पर सहमत हुए, और उसी के लिए हम मेमोरी से मूल डेटा को हटा सकते हैं यानी (xtrain और लेबल)! और numpy का उपयोग करने के लिए आपके सुझाव के बारे में केवल पूर्णांक डेटा प्रकारों तक सीमित है जो अन्य डेटा प्रकारों के बारे में क्या है?
blitu12345

1
इस दृष्टिकोण का एक और लाभ यह है कि आप स्तरीकरण मापदंडों का उपयोग कर सकते हैं।
अमी तेवरी

7

यहाँ एक पायथन फ़ंक्शन है जो ट्रेन में एक पंडों डेटाफ्रेम को विभाजित करता है, स्तरीकृत नमूने के साथ ट्रेन सत्यापन, और परीक्षण डेटाफ़्रेम। यह train_test_split()दो बार scikit-learn के फ़ंक्शन को कॉल करके इस विभाजन को निष्पादित करता है ।

import pandas as pd
from sklearn.model_selection import train_test_split

def split_stratified_into_train_val_test(df_input, stratify_colname='y',
                                         frac_train=0.6, frac_val=0.15, frac_test=0.25,
                                         random_state=None):
    '''
    Splits a Pandas dataframe into three subsets (train, val, and test)
    following fractional ratios provided by the user, where each subset is
    stratified by the values in a specific column (that is, each subset has
    the same relative frequency of the values in the column). It performs this
    splitting by running train_test_split() twice.

    Parameters
    ----------
    df_input : Pandas dataframe
        Input dataframe to be split.
    stratify_colname : str
        The name of the column that will be used for stratification. Usually
        this column would be for the label.
    frac_train : float
    frac_val   : float
    frac_test  : float
        The ratios with which the dataframe will be split into train, val, and
        test data. The values should be expressed as float fractions and should
        sum to 1.0.
    random_state : int, None, or RandomStateInstance
        Value to be passed to train_test_split().

    Returns
    -------
    df_train, df_val, df_test :
        Dataframes containing the three splits.
    '''

    if frac_train + frac_val + frac_test != 1.0:
        raise ValueError('fractions %f, %f, %f do not add up to 1.0' % \
                         (frac_train, frac_val, frac_test))

    if stratify_colname not in df_input.columns:
        raise ValueError('%s is not a column in the dataframe' % (stratify_colname))

    X = df_input # Contains all columns.
    y = df_input[[stratify_colname]] # Dataframe of just the column on which to stratify.

    # Split original dataframe into train and temp dataframes.
    df_train, df_temp, y_train, y_temp = train_test_split(X,
                                                          y,
                                                          stratify=y,
                                                          test_size=(1.0 - frac_train),
                                                          random_state=random_state)

    # Split the temp dataframe into val and test dataframes.
    relative_frac_test = frac_test / (frac_val + frac_test)
    df_val, df_test, y_val, y_test = train_test_split(df_temp,
                                                      y_temp,
                                                      stratify=y_temp,
                                                      test_size=relative_frac_test,
                                                      random_state=random_state)

    assert len(df_input) == len(df_train) + len(df_val) + len(df_test)

    return df_train, df_val, df_test

नीचे काम करने का एक पूरा उदाहरण है।

एक डेटासेट पर विचार करें जिसमें एक लेबल है जिस पर आप स्तरीकरण करना चाहते हैं। मूल डेटासेट में इस लेबल का अपना वितरण है, 75% foo, 15% barऔर 10% कहते हैं baz। अब चलो डेटासेट को ट्रेन, सत्यापन में विभाजित करते हैं, और 60/20/20 अनुपात का उपयोग करके सबसेट में परीक्षण करते हैं, जहां प्रत्येक विभाजन लेबल के समान वितरण को बनाए रखता है। नीचे चित्रण देखें:

यहाँ छवि विवरण दर्ज करें

यहाँ उदाहरण के डाटासेट है:

df = pd.DataFrame( { 'A': list(range(0, 100)),
                     'B': list(range(100, 0, -1)),
                     'label': ['foo'] * 75 + ['bar'] * 15 + ['baz'] * 10 } )

df.head()
#    A    B label
# 0  0  100   foo
# 1  1   99   foo
# 2  2   98   foo
# 3  3   97   foo
# 4  4   96   foo

df.shape
# (100, 3)

df.label.value_counts()
# foo    75
# bar    15
# baz    10
# Name: label, dtype: int64

अब, split_stratified_into_train_val_test()60/20/20 अनुपात के बाद ट्रेन, सत्यापन और परीक्षण डेटाफ़्रेम प्राप्त करने के लिए ऊपर से फ़ंक्शन को कॉल करें ।

df_train, df_val, df_test = \
    split_stratified_into_train_val_test(df, stratify_colname='label', frac_train=0.60, frac_val=0.20, frac_test=0.20)

तीन डेटाफ़्रेम df_train, df_valऔर df_testसभी मूल पंक्तियाँ होती हैं, लेकिन उनके आकार उपरोक्त अनुपात का पालन करेंगे।

df_train.shape
#(60, 3)

df_val.shape
#(20, 3)

df_test.shape
#(20, 3)

इसके अलावा, प्रत्येक तीन विभाजन में लेबल का समान वितरण होगा, अर्थात 75% foo, 15% barऔर 10% baz

df_train.label.value_counts()
# foo    45
# bar     9
# baz     6
# Name: label, dtype: int64

df_val.label.value_counts()
# foo    15
# bar     3
# baz     2
# Name: label, dtype: int64

df_test.label.value_counts()
# foo    15
# bar     3
# baz     2
# Name: label, dtype: int64

NameError: नाम 'df' परिभाषित नहीं है। स्प्लिट_स्ट्रैटिड_इंटो_ट्रेन_वल_टैस्ट () में 'डीएफ' को 'डीएफ_इनपुट' से बदल दिया जाना चाहिए।
काल्पनिक पोलक

धन्यवाद। मैंने ठीक कर दिया। समस्या कोड के त्रुटि-हैंडलिंग पथ में थी।
stackoverflowuser2010

1

train_test_splitकई सेटों को विभाजित करने और कुछ अतिरिक्त कोड न लिखने के बाद रीइंडेक्सिंग का प्रदर्शन किए बिना उपयोग करना बहुत सुविधाजनक है । ऊपर दिए गए सर्वश्रेष्ठ उत्तर में यह उल्लेख नहीं है कि train_test_splitविभाजन के आकार में परिवर्तन नहीं करने का उपयोग करके दो बार अलग-अलग करने से शुरू में दिए गए विभाजन को नहीं दिया जाएगा:

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

तब x_remain परिवर्तन में सत्यापन और परीक्षण सेट का भाग गिना जा सकता है

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)

इस अवसर में सभी प्रारंभिक विभाजन सहेजे जाते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.