Veriler 3 sete nasıl bölünür (tren, doğrulama ve test)?


146

Bir panda veri çerçevem ​​var ve bunu 3 ayrı sete bölmek istiyorum. Ben kullanarak biliyorum train_test_split gelen sklearn.cross_validationbir iki set (tren ve test) verileri bölebilirsiniz. Ancak, verileri üç sete bölme konusunda herhangi bir çözüm bulamadım. Tercihen, orijinal verilerin endekslerine sahip olmak istiyorum.

Bir çözümün train_test_splitiki kez kullanmak ve bir şekilde endeksleri ayarlamak olacağını biliyorum . Ancak, verileri 2 yerine 3 sete bölmenin daha standart / yerleşik bir yolu var mı?


5
Bu sizin özel sorunuza cevap vermiyor, ancak bunun için daha standart bir yaklaşımın iki gruba ayrılması, eğitim ve test yapılması ve eğitim setinde çapraz doğrulamanın yapılması ve böylece tek başına bir "geliştirme" seti ihtiyacını ortadan kaldırmak olacağını düşünüyorum. .
David

1
Bu daha önce ortaya çıktı ve bildiğim kadarıyla bunun için yerleşik bir yöntem yok.
ayhan

5
Hastie ve arkadaşlarının neden iki yerine üç setin kullanıldığını tartışmak için İstatistiksel Öğrenme Unsurlarını öneriyorum ( web.stanford.edu/~hastie/local.ftp/Springer/OLD/… Model değerlendirme ve seçim bölümü)
ayhan

2
@David Bazı modellerde aşırı sığmayı önlemek için 2 yerine 3 sete ihtiyaç vardır. Çünkü tasarım seçeneklerinizde, test setindeki performansı artırmak için parametreleri bir şekilde ayarlıyorsunuz. Bunu önlemek için bir geliştirme seti gereklidir. Dolayısıyla, çapraz doğrulamanın kullanılması yeterli olmayacaktır.
CentAu

6
@ayhan, bu kitap için düzeltilmiş bir URL statweb.stanford.edu/~tibs/ElemStatLearn/printings/… , bölüm 7 (s. 219).
Camille Goudeseune

Yanıtlar:


161

Numpy çözümü. Önce tüm veri kümesini karıştırırız (df.sample (frac = 1)) ve sonra veri setimizi aşağıdaki parçalara böleriz:

  • % 60 - tren seti,
  • % 20 - doğrulama seti,
  • % 20 - test seti

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 için bir dizidir .

İşte kullanım için küçük bir demo np.split()- 20 elementlik diziyi aşağıdaki parçalara ayıralım:% 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])]

@root frac = 1 parametresi tam olarak ne yapıyor?
SpiderWasp42

1
SpiderWasp42 @, frac=1talimat sample()(bütün döndürmek için işlev 100%= veya fraksiyonu 1.0) satırları
MaxU

12
Teşekkürler @MaxU. İşleri basitleştirmek için 2 şeyden bahsetmek istiyorum. İlk np.random.seed(any_number)olarak, her çalışmada aynı sonucu elde etmek için bölünmüş çizgiden önce kullanın . İkincisi, train:test:val::50:40:10kullanım gibi eşit olmayan bir oran yapmak [int(.5*len(dfn)), int(.9*len(dfn))]. Burada ilk eleman boyutu train(% 0.5), ikinci eleman boyutu val(1-0.9 =% 0.1) ve iki eleman arasındaki farkı test(0.9-0.5 =% 0.4) gösterir. Yanılıyorsam düzelt beni :)
dataLeo

hrmm, "İşte np.split () kullanımı için küçük bir demo - 20 elemanlı diziyi şu bölümlere ayıralım:% 90,% 10,% 10:" 80 demek istediğinizden eminim %, 10%, 10%
Kevin

Hey, @MaxU Bir vakam vardı, biraz benzer bir şey. Bunun olup olmadığını görmek ve bana yardım etmek için ona bakıp bakamayacağınızı merak ediyordum. İşte sorum şu stackoverflow.com/questions/54847668/…
Deepak M

55

Not:

Rastgele küme oluşturma işleminin tohumlanması için fonksiyon yazılmıştır. Kümeleri rasgele ayarlamayan küme bölmelerine güvenmemelisiniz.

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

gösteri

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

resim açıklamasını buraya girin

train, validate, test = train_validate_test_split(df)

train

resim açıklamasını buraya girin

validate

resim açıklamasını buraya girin

test

resim açıklamasını buraya girin


1
Bu fonksiyon 1 ile n arasında değişen indeks değerleri olan bir df gerektirdiğine inanıyorum. Benim durumumda, dizin değerlerimin bu aralıkta olması gerekmediği için işlevi df.loc kullanacak şekilde değiştirdim.
iOSBeginner

32

Bununla birlikte, içine veri kümesi bölünmesi, bir yaklaşım train, test, cvile 0.6, 0.2, 0.2kullanmak olacaktır train_test_splitiki yöntem.

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)

Büyük veri kümeleri için
yetersiz

@MaksymGanenko Lütfen detaylandırır mısınız?
blitu12345

Verileri iki ayrı işlemle bölmenizi öneririz. Her veri bölümü veri kopyalamayı içerir. Yani bir yerine iki ayrı bölme işlemi kullanmanızı önerdiğinizde, hem RAM hem de CPU üzerinde yapay olarak yük oluşturursunuz. Yani çözümünüz yetersizdir. Veri bölünmesi gibi tek bir işlemle yapılmalıdır np.split(). Ayrıca, ek bağımlılık gerektirmez sklearn.
Maksym Ganenko

@MaksymGanenko hafıza üzerindeki ekstra yük üzerinde anlaştı ve aynı şekilde orijinal verileri hafızadan silebiliriz (xtrain ve etiketler)! Ve numpy kullanma önerileriniz sadece tamsayı veri türleriyle sınırlı olup diğer veri türlerine ne demeli?
blitu12345

1
bu yaklaşımın bir diğer yararı da tabakalaşma parametrelerini kullanabilmenizdir.
Ami Tavory

7

Burada, bir Pandas veri çerçevesini katmanlı örnekleme ile tren, doğrulama ve test veri çerçevelerine ayıran bir Python işlevi. Bu bölünmeyi scikit-learn işlevini train_test_split()iki kez çağırarak gerçekleştirir .

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

Aşağıda tam bir çalışma örneği verilmiştir.

Üzerinde katmanlaşmayı gerçekleştirmek istediğiniz etikete sahip bir veri kümesini düşünün. Bu etiketin orijinal veri kümesinde kendi dağılımı vardır, örneğin% 75,% foo15 barve% 10 baz. Şimdi veri kümesini trene, doğrulamaya ve teste 60/20/20 oranını kullanarak alt kümelere ayıralım, burada her bir bölünme etiketlerin aynı dağıtımını korur. Aşağıdaki çizime bakın:

resim açıklamasını buraya girin

Örnek veri kümesi aşağıdadır:

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

Şimdi, split_stratified_into_train_val_test()60/20/20 oranını izleyerek tren, doğrulama ve test veri çerçeveleri almak için yukarıdaki işlevi çağıralım.

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)

Üç dataframes df_train, df_valve df_testtüm orijinal satır içerebilir ama bunların boyutları yukarıda oranını takip edecek.

df_train.shape
#(60, 3)

df_val.shape
#(20, 3)

df_test.shape
#(20, 3)

Ayrıca, üç bölmenin her biri etiketin aynı dağılımına, yani% 75,% foo15 barve% 10'a sahip olacaktır 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' adı tanımlanmadı. Split_stratified_into_train_val_test () içindeki 'df', 'df_input' ile değiştirilmelidir.
Fantezi Pollock

Teşekkürler. Onardım. Sorun, kodun hata işleme yolundaydı.
stackoverflowuser2010

1

train_test_splitBirkaç sete bölündükten ve bazı ek kod yazmadan sonra yeniden endeksleme yapmadan kullanmak çok uygundur . Yukarıdaki en iyi yanıt, train_test_splitbölüm boyutlarını değiştirmeden iki kez ayırarak başlangıçta amaçlanan bölüm vermeyeceğinden bahsetmez :

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

Daha sonra doğrulama ve test setlerinin x_remain içindeki kısmı değişir ve şu şekilde sayılabilir:

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)

Bu durumda tüm ilk bölümler kaydedilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.