Train test split: примеры (PYTHON)

Функция train_test_split для разделения данных в машинном обучении
Раздел: Машинное обучение, Разделение данных
train_test_split(*arrays, test_size: float or int = None, train_size: float or int = None, ...): list

Функция train_test_split в библиотеке scikit-learn

Функция train_test_split из модуля sklearn.model_selection предназначена для разделения данных на обучающую и тестовую выборки. Использование этой функции является стандартным этапом подготовки данных для задач машинного обучения, когда необходимо оценить качество модели на независимых данных.

Функция позволяет разделять один или несколько массивов данных в заданной пропорции, обеспечивая случайное перемешивание элементов. Основное применение - создание обучающего и тестового наборов для последующего обучения и валидации моделей машинного обучения.

Аргументы функции

  • arrays - последовательность индексируемых объектов (массивы, списки, датафреймы). Минимум один объект для разделения.
  • test_size - определяет размер тестовой выборки. Может быть задан как float (доля от общего объема данных, от 0.0 до 1.0) или как int (абсолютное количество элементов). По умолчанию 0.25.
  • train_size - определяет размер обучающей выборки. Аналогично test_size может быть float или int. Если не задан, вычисляется как 1 - test_size.
  • random_state - управляет случайностью при перемешивании данных. Принимает целое число для воспроизводимости результатов.
  • shuffle - булево значение, указывающее, нужно ли перемешивать данные перед разделением. По умолчанию True.
  • stratify - массив меток для стратифицированного разделения. Сохраняет распределение классов в обучающей и тестовой выборках.

Возвращаемые значения

Функция возвращает список, содержащий разделенные версии входных массивов. Количество возвращаемых массивов равно удвоенному количеству входных массивов (каждый входной массив разделяется на обучающую и тестовую части).

Примеры использования train_test_split

Разделение одного массива данных:

from sklearn.model_selection import train_test_split
import numpy as np

X = np.arange(10).reshape(5, 2)
X_train, X_test = train_test_split(X, test_size=0.4, random_state=42)
print("Обучающая выборка:")
print(X_train)
print("Тестовая выборка:")
print(X_test)
Обучающая выборка:
[[4 5]
 [0 1]
 [6 7]]
Тестовая выборка:
[[2 3]
 [8 9]]

Разделение двух массивов (признаки и метки):

X = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
y = [0, 1, 0, 1, 0]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(f"X_train: {X_train}")
print(f"X_test: {X_test}")
print(f"y_train: {y_train}")
print(f"y_test: {y_test}")
X_train: [[9, 10], [3, 4], [1, 2]]
X_test: [[7, 8], [5, 6]]
y_train: [0, 1, 0]
y_test: [1, 0]

Стратифицированное разделение:

X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]])
y = np.array([0, 0, 0, 1, 1, 1])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, stratify=y, random_state=42)
print(f"Распределение в исходных данных: {np.bincount(y)}")
print(f"Распределение в обучающих данных: {np.bincount(y_train)}")
print(f"Распределение в тестовых данных: {np.bincount(y_test)}")
Распределение в исходных данных: [3 3]
Распределение в обучающих данных: [2 2]
Распределение в тестовых данных: [1 1]

Альтернативные функции в Python

Библиотека scikit-learn предлагает несколько альтернативных методов разделения данных:

StratifiedShuffleSplit - предоставляет более гибкий контроль над стратифицированным разделением, позволяя выполнять несколько итераций разделения с разными случайными состояниями.

TimeSeriesSplit - предназначен для временных рядов, где важно сохранять временной порядок. Разделение происходит последовательно без перемешивания.

KFold, StratifiedKFold - реализуют k-блочную кросс-валидацию. Разделяют данные на k блоков, каждый из которых по очереди используется в качестве тестового набора.

GroupShuffleSplit - обеспечивает разделение с учетом групп, гарантируя, что все элементы одной группы попадут либо в обучающую, либо в тестовую выборку.

Выбор функции зависит от специфики задачи. train_test_split оптимальна для простого случайного разделения, тогда как кросс-валидационные сплиттеры предпочтительнее при ограниченном объеме данных или необходимости более надежной оценки модели.

Аналоги функции в других языках программирования

R

В языке R функция createDataPartition из пакета caret выполняет схожую задачу:

library(caret)
data(iris)
set.seed(42)
trainIndex <- createDataPartition(iris$Species, p = 0.8, list = FALSE)
trainData <- iris[trainIndex, ]
testData <- iris[-trainIndex, ]

Java (Weka)

Библиотека Weka предоставляет класс RandomSplit для разделения данных:

import weka.core.Instances;
import java.io.FileReader;

Instances data = new Instances(new FileReader("data.arff"));
data.randomize(new Random(42));
int trainSize = (int) Math.round(data.numInstances() * 0.8);
int testSize = data.numInstances() - trainSize;
Instances train = new Instances(data, 0, trainSize);
Instances test = new Instances(data, trainSize, testSize);

JavaScript (TensorFlow.js)

TensorFlow.js предлагает метод tf.data.Dataset для работы с данными:

const features = tf.tensor2d([[1, 2], [3, 4], [5, 6], [7, 8]]);
const labels = tf.tensor1d([0, 1, 0, 1]);
const splitRatio = 0.75;
const trainSize = Math.floor(features.shape[0] * splitRatio);
const [trainFeatures, testFeatures] = tf.split(features, [trainSize, features.shape[0] - trainSize], 0);

PHP (PHP-ML)

Библиотека PHP-ML содержит функцию stratisfiedSplit:

use Phpml\ModelSelection\StratifiedRandomSplit;
use Phpml\Dataset\ArrayDataset;

$dataset = new ArrayDataset($features, $labels);
$split = new StratifiedRandomSplit($dataset, 0.3);
$train = $split->getTrain();
$test = $split->getTest();

SQL

В SQL данные можно разделить с помощью случайной выборки:

-- Создание обучающей выборки (70%)
CREATE TABLE train AS
SELECT * FROM dataset
WHERE random() < 0.7;

-- Создание тестовой выборки (30%)
CREATE TABLE test AS
SELECT * FROM dataset
WHERE NOT EXISTS (SELECT 1 FROM train WHERE train.id = dataset.id);

Типичные ошибки при использовании

Указание несуществующего размера выборки:

from sklearn.model_selection import train_test_split
import numpy as np

try:
    X = np.array([[1, 2], [3, 4], [5, 6]])
    X_train, X_test = train_test_split(X, test_size=2.0)  # Больше 1.0
    print("Разделение успешно")
except ValueError as e:
    print(f"Ошибка: {e}")
Ошибка: test_size=2.0 should be either an int and represent the absolute number of test samples, or a float and represent the proportion of the dataset to include in the test split

Некорректное использование параметра stratify:

try:
    X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    y = np.array([0, 1])  # Длина не совпадает с X
    X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
except ValueError as e:
    print(f"Ошибка: {e}")
Ошибка: The 'stratify' parameter should be of length equal to the number of samples in X, but got 2 samples and 4 entries in X.

Попытка стратифицированного разделения при непрерывной целевой переменной:

try:
    X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    y = np.array([1.5, 2.3, 3.1, 4.7])  # Непрерывные значения
    X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
except ValueError as e:
    print(f"Ошибка: {e}")
Ошибка: Supported target types are: ('binary', 'multiclass'). Got 'continuous' instead.

Забытый параметр shuffle при работе с временными рядами:

# Для временных рядов обычно отключают перемешивание
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([1, 2, 3, 4, 5])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)
print(f"X_train: {X_train.flatten()}")
print(f"X_test: {X_test.flatten()}")
X_train: [1 2 3 4]
X_test: [5]

Изменения в последних версиях

В версии scikit-learn 1.4 появилось предупреждение при использовании train_size и test_size одновременно, если их сумма не равна 1.0. Ранее такое использование могло приводить к неожиданным результатам.

Начиная с версии 0.24, параметр shuffle по умолчанию установлен в True для всех сплиттеров, включая train_test_split, что повышает согласованность API.

В версии 0.20 функция была перемещена из модуля sklearn.cross_validation в sklearn.model_selection. Старое расположение оставалось доступным для обратной совместимости, но в новых версиях рекомендуется использовать обновленный импорт.

В версии 1.1 улучшена обработка sparse матриц и pandas DataFrame, обеспечивающая более эффективное использование памяти при работе с большими наборами данных.

Расширенные примеры использования

Разделение нескольких массивов одновременно:

Пример python
from sklearn.model_selection import train_test_split
import numpy as np

X1 = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
X2 = np.array([[10, 20], [30, 40], [50, 60], [70, 80]])
y = np.array([0, 1, 0, 1])

X1_train, X1_test, X2_train, X2_test, y_train, y_test = train_test_split(
    X1, X2, y, test_size=0.25, random_state=42
)

print(f"X1_train:\n{X1_train}")
print(f"X2_train:\n{X2_train}")
print(f"y_train: {y_train}")
X1_train:
[[5 6]
 [1 2]
 [7 8]]
X2_train:
[[50 60]
 [10 20]
 [70 80]]
y_train: [0 0 1]

Использование с pandas DataFrame:

Пример python
import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.DataFrame({
    'feature1': [1, 2, 3, 4, 5, 6, 7, 8],
    'feature2': [10, 20, 30, 40, 50, 60, 70, 80],
    'target': [0, 1, 0, 1, 0, 1, 0, 1]
})

X = df[['feature1', 'feature2']]
y = df['target']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=42
)

print(f"Размер X_train: {X_train.shape}")
print(f"Размер X_test: {X_test.shape}")
print(f"Распределение в y_train:\n{y_train.value_counts()}")
Размер X_train: (5, 2)
Размер X_test: (3, 2)
Распределение в y_train:
1    3
0    2
Name: target, dtype: int64

Контроль баланса классов при малой выборке:

Пример python
import numpy as np
from sklearn.model_selection import train_test_split

# Создание несбалансированного набора данных
X = np.array([[i, i*2] for i in range(100)])
y = np.array([0]*90 + [1]*10)  # 90% класса 0, 10% класса 1

# Без стратификации
X_train1, X_test1, y_train1, y_test1 = train_test_split(X, y, test_size=0.2, random_state=42)

# Со стратификацией
X_train2, X_test2, y_train2, y_test2 = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)

print("Без стратификации:")
print(f"  Обучающая: {np.bincount(y_train1)}")
print(f"  Тестовая: {np.bincount(y_test1)}")
print("\nСо стратификацией:")
print(f"  Обучающая: {np.bincount(y_train2)}")
print(f"  Тестовая: {np.bincount(y_test2)}")
Без стратификации:
  Обучающая: [73  7]
  Тестовая: [17  3]

Со стратификацией:
  Обучающая: [72  8]
  Тестовая: [18  2]

Разделение с сохранением индексов для pandas:

Пример python
import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.DataFrame({
    'A': range(10),
    'B': range(10, 20)
}, index=[f'sample_{i}' for i in range(10)])

train_idx, test_idx = train_test_split(df.index, test_size=0.3, random_state=42)
train_df = df.loc[train_idx]
test_df = df.loc[test_idx]

print(f"Индексы обучающей выборки: {list(train_idx)}")
print(f"Индексы тестовой выборки: {list(test_idx)}")
Индексы обучающей выборки: ['sample_9', 'sample_1', 'sample_6', 'sample_7', 'sample_3', 'sample_0', 'sample_5']
Индексы тестовой выборки: ['sample_8', 'sample_4', 'sample_2']

питон train_test_split function comments

En
Train test split Split arrays into random train and test subsets