Làm thế nào để chia dữ liệu thành 3 bộ (đào tạo, xác nhận và kiểm tra)?


145

Tôi có một khung dữ liệu gấu trúc và tôi muốn chia nó thành 3 bộ riêng biệt. Tôi biết rằng bằng cách sử dụng train_test_split từ sklearn.cross_validation, người ta có thể chia dữ liệu thành hai bộ (tàu và kiểm tra). Tuy nhiên, tôi không thể tìm thấy bất kỳ giải pháp nào về việc chia dữ liệu thành ba bộ. Tốt hơn là, tôi muốn có các chỉ số của dữ liệu gốc.

Tôi biết rằng một cách giải quyết sẽ là sử dụng train_test_splithai lần và bằng cách nào đó điều chỉnh các chỉ số. Nhưng có cách nào chuẩn hơn / tích hợp để chia dữ liệu thành 3 bộ thay vì 2 không?


5
Điều này không trả lời câu hỏi cụ thể của bạn, nhưng tôi nghĩ rằng cách tiếp cận tiêu chuẩn hơn cho việc này sẽ được chia thành hai bộ, đào tạo và kiểm tra và chạy xác thực chéo trên tập huấn do đó loại bỏ nhu cầu về một bộ "phát triển" độc lập .
David

1
Điều này đã xuất hiện trước đây và theo như tôi biết thì chưa có phương pháp tích hợp nào cho việc đó.
ayhan

5
Tôi đề nghị các yếu tố của việc học thống kê của Hastie và cộng sự cho một cuộc thảo luận về lý do tại sao nên sử dụng ba bộ thay vì hai ( web.stanford.edu/~hastie/local.ftp/Springer/OLD/ chương đánh giá và lựa chọn mô hình)
ayhan

2
@David Trong một số mô hình để ngăn chặn quá mức, cần có 3 bộ thay vì 2. Vì trong lựa chọn thiết kế của bạn, bạn bằng cách nào đó điều chỉnh các tham số để cải thiện hiệu suất trên bộ kiểm tra. Để ngăn chặn điều đó, một bộ phát triển được yêu cầu. Vì vậy, sử dụng xác nhận chéo sẽ không đủ.
CentAu

6
@ayhan, một URL được sửa cho cuốn sách đó là statweb.stanford.edu/~tibs/ElemStatLearn/printings/ , chương 7 (trang 219).
Camille Goudeseune

Câu trả lời:


160

Dung dịch Numpy. Chúng tôi sẽ xáo trộn toàn bộ dữ liệu trước (df.sample (frac = 1)) và sau đó chia tập dữ liệu của chúng tôi thành các phần sau:

  • 60% - bộ xe lửa,
  • 20% - bộ xác nhận,
  • 20% - bộ kiểm tra

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))]- là một indices_or_sectionsmảng cho numpy.split () .

Đây là một bản demo nhỏ để np.split()sử dụng - hãy chia mảng 20 phần tử thành các phần sau: 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 chính xác thì tham số frac = 1 đang làm gì?
SpiderWasp42

1
@ SpiderWasp42, frac=1hướng dẫn sample()chức năng trả về tất cả ( 100%hoặc phân số = 1.0) hàng
MaxU

12
Cảm ơn @MaxU. Tôi muốn đề cập đến 2 điều để đơn giản hóa mọi thứ. Đầu tiên, sử dụng np.random.seed(any_number)trước dòng chia để có được kết quả tương tự với mỗi lần chạy. Thứ hai, để làm cho tỷ lệ không đồng đều như train:test:val::50:40:10sử dụng [int(.5*len(dfn)), int(.9*len(dfn))]. Ở đây, phần tử thứ nhất biểu thị kích thước cho train(0,5%), phần tử thứ hai biểu thị kích thước cho val(1-0,9 = 0,1%) và sự khác biệt giữa hai kích thước biểu thị cho test(0,9-0,5 = 0,4%). Sửa lỗi cho tôi nếu tôi sai :)
dataLeo

hrmm có phải là một sai lầm khi bạn nói "Đây là một bản demo nhỏ cho việc sử dụng np.split () - hãy chia mảng 20 phần tử thành các phần sau: 90%, 10%, 10%:" Tôi chắc chắn rằng bạn có nghĩa là 80 %, 10%, 10%
Kevin

Này, @MaxU Tôi có một trường hợp, một cái gì đó tương tự. Tôi đã tự hỏi nếu bạn có thể nhìn vào nó để xem nếu nó là và giúp tôi ở đó. Đây là câu hỏi stackoverflow.com/questions/54847668/
Deepak M

55

Ghi chú:

Hàm được viết để xử lý seeding của bộ tạo ngẫu nhiên. Bạn không nên dựa vào việc tách bộ mà không ngẫu nhiên các bộ.

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

Trình diễn

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

nhập mô tả hình ảnh ở đây

train, validate, test = train_validate_test_split(df)

train

nhập mô tả hình ảnh ở đây

validate

nhập mô tả hình ảnh ở đây

test

nhập mô tả hình ảnh ở đây


1
Tôi tin rằng hàm này yêu cầu một df với các giá trị chỉ số từ 1 đến n. Trong trường hợp của tôi, tôi đã sửa đổi hàm để sử dụng df.loc vì các giá trị chỉ mục của tôi không nhất thiết phải nằm trong phạm vi này.
iOSBeginner

32

Tuy nhiên, một cách tiếp cận để phân chia các tập dữ liệu vào train, test, cvvới 0.6, 0.2, 0.2sẽ được sử dụng các train_test_splitphương pháp hai lần.

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)

Không tối ưu cho các bộ dữ liệu lớn
Maksym Ganenko

@MaksymGanenko Bạn có thể giải thích rõ hơn không?
blitu12345

Bạn đề nghị tách dữ liệu với hai thao tác riêng biệt. Mỗi phân chia dữ liệu liên quan đến sao chép dữ liệu. Vì vậy, khi bạn đề xuất sử dụng hai thao tác phân tách riêng biệt thay vì một thao tác bạn tạo ra gánh nặng một cách giả tạo cho cả RAM và CPU. Vì vậy, giải pháp của bạn là tối ưu. Việc phân chia dữ liệu nên được thực hiện với một thao tác như thế nào np.split(). Ngoài ra, nó không yêu cầu phụ thuộc thêm vào sklearn.
Maksym Ganenko

@MaksymGanenko đồng ý về gánh nặng thêm cho bộ nhớ và tương tự, chúng ta có thể xóa dữ liệu gốc khỏi bộ nhớ tức là (xtrain & nhãn)! Và về đề xuất của bạn để sử dụng numpy có phần bị giới hạn chỉ với các loại dữ liệu số nguyên còn các loại dữ liệu khác thì sao?
blitu12345

1
lợi ích khác của phương pháp này là bạn có thể sử dụng các tham số phân tầng.
Ami Tavory

7

Dưới đây là một hàm Python phân chia một khung dữ liệu Pandas thành các chuỗi dữ liệu huấn luyện, xác nhận và kiểm tra với lấy mẫu phân tầng. Nó thực hiện việc phân tách này bằng cách gọi hàm scikit-learn train_test_split()hai lần.

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

Dưới đây là một ví dụ làm việc hoàn chỉnh.

Xem xét một tập dữ liệu có nhãn mà bạn muốn thực hiện phân tầng. Nhãn này có phân phối riêng trong bộ dữ liệu gốc, cho biết 75% foo, 15% barvà 10% baz. Bây giờ, hãy chia dữ liệu thành đào tạo, xác thực và kiểm tra thành các tập hợp con bằng cách sử dụng tỷ lệ 60/20/20, trong đó mỗi phân tách giữ lại phân phối nhãn giống nhau. Xem hình minh họa dưới đây:

nhập mô tả hình ảnh ở đây

Dưới đây là tập dữ liệu mẫu:

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

Bây giờ, hãy gọi split_stratified_into_train_val_test()hàm từ phía trên để lấy các tệp dữ liệu đào tạo, xác nhận và kiểm tra theo tỷ lệ 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)

Ba datafram df_train, df_valdf_testchứa tất cả các hàng ban đầu nhưng kích thước của chúng sẽ tuân theo tỷ lệ trên.

df_train.shape
#(60, 3)

df_val.shape
#(20, 3)

df_test.shape
#(20, 3)

Hơn nữa, mỗi trong số ba phần tách sẽ có cùng phân phối nhãn, cụ thể là 75% foo, 15% barvà 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: tên 'df' không được xác định. 'Df' trong split_stratified_into_train_val_test () nên được thay thế bằng 'df_input'.
Ảo ảnh Pollock

Cảm ơn. Tôi sửa nó rồi. Vấn đề là trong một đường dẫn xử lý lỗi của mã.
stackoverflowuser2010

1

Nó rất thuận tiện để sử dụng train_test_splitmà không cần thực hiện reindexing sau khi chia thành nhiều bộ và không viết một số mã bổ sung. Câu trả lời hay nhất ở trên không đề cập đến việc bằng cách tách hai lần bằng cách train_test_splitkhông thay đổi kích thước phân vùng sẽ không cung cấp phân vùng dự định ban đầu:

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

Sau đó, phần xác thực và bộ kiểm tra trong thay đổi x_remain và có thể được tính là

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)

Trong dịp này, tất cả các phân vùng ban đầu được lưu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.