Hier ist eine Python-Funktion, die einen Pandas-Datenrahmen in Zug-, Validierungs- und Testdatenrahmen mit geschichteten Stichproben aufteilt. Diese Aufteilung wird durchgeführt, indem die Funktion von scikit-learn train_test_split()
zweimal aufgerufen wird .
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
Unten finden Sie ein vollständiges Arbeitsbeispiel.
Stellen Sie sich einen Datensatz mit einer Bezeichnung vor, auf der Sie die Schichtung durchführen möchten. Dieses Etikett hat eine eigene Verteilung im Originaldatensatz, z. B. 75% foo
, 15% bar
und 10% baz
. Lassen Sie uns nun den Datensatz in Zug, Validierung und Test in Teilmengen unter Verwendung eines Verhältnisses von 60/20/20 aufteilen, wobei jede Aufteilung die gleiche Verteilung der Beschriftungen beibehält. Siehe die Abbildung unten:
Hier ist der Beispieldatensatz:
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
Rufen wir nun die split_stratified_into_train_val_test()
Funktion von oben auf, um Zug-, Validierungs- und Testdatenrahmen nach einem Verhältnis von 60/20/20 abzurufen.
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)
Die drei Datenrahmen df_train
, df_val
und df_test
enthalten alle ursprünglichen Reihen , aber ihre Größe wird das obige Verhältnis folgen.
df_train.shape
#(60, 3)
df_val.shape
#(20, 3)
df_test.shape
#(20, 3)
Ferner hat jede der drei Teilungen die gleiche Verteilung des Etiketts, nämlich 75% foo
, 15% bar
und 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