7.3. Предобработка данных#

The sklearn.preprocessing пакет предоставляет несколько общих вспомогательных функций и классов-трансформеров для преобразования исходных векторов признаков в представление, более подходящее для последующих оценщиков.

В целом, многие алгоритмы обучения, такие как линейные модели, выигрывают от стандартизации набора данных (см. Важность масштабирования признаков). Если в наборе присутствуют выбросы, робастные скейлеры или другие трансформеры могут быть более подходящими. Поведение различных скейлеров, трансформеров и нормализаторов на наборе данных, содержащем маргинальные выбросы, выделено в Сравнение влияния различных масштабировщиков на данные с выбросами.

7.3.1. Стандартизация, или удаление среднего и масштабирование дисперсии#

Стандартизация наборов данных является общее требование для многих оценщиков машинного обучения реализованы в scikit-learn; они могут работать плохо, если отдельные признаки не выглядят более или менее как стандартные нормально распределенные данные: Гауссовы с нулевое среднее и единичная дисперсия.

На практике мы часто игнорируем форму распределения и просто преобразуем данные, центрируя их путём удаления среднего значения каждого признака, затем масштабируем, деля непостоянные признаки на их стандартное отклонение.

Например, многие элементы, используемые в целевой функции алгоритма обучения (такие как RBF-ядро метода опорных векторов или регуляризаторы l1 и l2 линейных моделей), могут предполагать, что все признаки центрированы около нуля или имеют дисперсию того же порядка. Если признак имеет дисперсию, на порядки большую, чем у других, он может доминировать в целевой функции и помешать оценщику правильно обучаться на других признаках, как ожидается.

The preprocessing модуль предоставляет StandardScaler вспомогательный класс, который является быстрым и простым способом выполнения следующей операции на массиво-подобном наборе данных:

>>> from sklearn import preprocessing
>>> import numpy as np
>>> X_train = np.array([[ 1., -1.,  2.],
...                     [ 2.,  0.,  0.],
...                     [ 0.,  1., -1.]])
>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> scaler
StandardScaler()

>>> scaler.mean_
array([1., 0., 0.33])

>>> scaler.scale_
array([0.81, 0.81, 1.24])

>>> X_scaled = scaler.transform(X_train)
>>> X_scaled
array([[ 0.  , -1.22,  1.33 ],
       [ 1.22,  0.  , -0.267],
       [-1.22,  1.22, -1.06 ]])

Масштабированные данные имеют нулевое среднее значение и единичную дисперсию:

>>> X_scaled.mean(axis=0)
array([0., 0., 0.])

>>> X_scaled.std(axis=0)
array([1., 1., 1.])

Этот класс реализует Transformer API для вычисления среднего значения и стандартного отклонения на обучающем наборе, чтобы позже применить то же преобразование к тестовому набору. Этот класс, следовательно, подходит для использования на ранних этапах Pipeline:

>>> from sklearn.datasets import make_classification
>>> from sklearn.linear_model import LogisticRegression
>>> from sklearn.model_selection import train_test_split
>>> from sklearn.pipeline import make_pipeline
>>> from sklearn.preprocessing import StandardScaler

>>> X, y = make_classification(random_state=42)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
>>> pipe = make_pipeline(StandardScaler(), LogisticRegression())
>>> pipe.fit(X_train, y_train)  # apply scaling on training data
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('logisticregression', LogisticRegression())])

>>> pipe.score(X_test, y_test)  # apply scaling on testing data, without leaking training data.
0.96

Можно отключить либо центрирование, либо масштабирование, передав with_mean=False или with_std=False в конструктор класса StandardScaler.

7.3.1.1. Масштабирование признаков до диапазона#

Альтернативная стандартизация — масштабирование признаков до заданного минимального и максимального значений, часто между нулем и единицей, или так, чтобы максимальное абсолютное значение каждого признака масштабировалось до единичного размера. Это может быть достигнуто с помощью MinMaxScaler или MaxAbsScaler, соответственно.

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

Вот пример масштабирования игрушечной матрицы данных до [0, 1] диапазон:

>>> X_train = np.array([[ 1., -1.,  2.],
...                     [ 2.,  0.,  0.],
...                     [ 0.,  1., -1.]])
...
>>> min_max_scaler = preprocessing.MinMaxScaler()
>>> X_train_minmax = min_max_scaler.fit_transform(X_train)
>>> X_train_minmax
array([[0.5       , 0.        , 1.        ],
       [1.        , 0.5       , 0.33333333],
       [0.        , 1.        , 0.        ]])

Затем тот же экземпляр преобразователя может быть применен к некоторым новым тестовым данным, невидимым во время вызова fit: те же операции масштабирования и сдвига будут применены для согласованности с преобразованием, выполненным на обучающих данных:

>>> X_test = np.array([[-3., -1.,  4.]])
>>> X_test_minmax = min_max_scaler.transform(X_test)
>>> X_test_minmax
array([[-1.5       ,  0.        ,  1.66666667]])

Можно исследовать атрибуты масштабатора, чтобы узнать точную природу преобразования, изученного на обучающих данных:

>>> min_max_scaler.scale_
array([0.5       , 0.5       , 0.33])

>>> min_max_scaler.min_
array([0.        , 0.5       , 0.33])

Если MinMaxScaler получает явный feature_range=(min, max) полная формула:

X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))

X_scaled = X_std * (max - min) + min

MaxAbsScaler работает очень похожим образом, но масштабирует так, что обучающие данные лежат в диапазоне [-1, 1] путём деления на наибольшее максимальное значение в каждом признаке. Предназначена для данных, уже центрированных около нуля или разреженных данных.

Вот как использовать игрушечные данные из предыдущего примера с этим масштабатором:

>>> X_train = np.array([[ 1., -1.,  2.],
...                     [ 2.,  0.,  0.],
...                     [ 0.,  1., -1.]])
...
>>> max_abs_scaler = preprocessing.MaxAbsScaler()
>>> X_train_maxabs = max_abs_scaler.fit_transform(X_train)
>>> X_train_maxabs
array([[ 0.5, -1. ,  1. ],
       [ 1. ,  0. ,  0. ],
       [ 0. ,  1. , -0.5]])
>>> X_test = np.array([[ -3., -1.,  4.]])
>>> X_test_maxabs = max_abs_scaler.transform(X_test)
>>> X_test_maxabs
array([[-1.5, -1. ,  2. ]])
>>> max_abs_scaler.scale_
array([2.,  1.,  2.])

7.3.1.2. Масштабирование разреженных данных#

Центрирование разреженных данных разрушило бы структуру разреженности в данных, и поэтому редко является разумным действием. Однако масштабирование разреженных входных данных может иметь смысл, особенно если признаки имеют разные масштабы.

MaxAbsScaler был специально разработан для масштабирования разреженных данных и является рекомендуемым способом для этого. Однако, StandardScaler может принимать scipy.sparse матрицы в качестве входных данных, при условии, что with_mean=False явно передается в конструктор. В противном случае ValueError будет вызвано, так как тихое центрирование нарушило бы разреженность и часто приводило бы к сбою выполнения из-за непреднамеренного выделения чрезмерного объема памяти. RobustScaler не может быть обучен на разреженных входах, но вы можете использовать transform метод на разреженных входных данных.

Обратите внимание, что масштабаторы принимают как сжатый разреженный формат строк, так и сжатый разреженный формат столбцов (см. scipy.sparse.csr_matrix и scipy.sparse.csc_matrix). Любые другие разреженные входные данные будут преобразуется в представление Compressed Sparse Rows. Чтобы избежать ненужных копий памяти, рекомендуется выбирать представление CSR или CSC заранее.

Наконец, если ожидается, что центрированные данные будут достаточно малы, явное преобразование входных данных в массив с использованием toarray метод разреженных матриц является другим вариантом.

7.3.1.3. Масштабирование данных с выбросами#

Если ваши данные содержат много выбросов, масштабирование с использованием среднего и дисперсии данных, вероятно, будет работать не очень хорошо. В таких случаях можно использовать RobustScaler в качестве прямой замены. Она использует более робастные оценки центра и размаха ваших данных.

Ссылки#

Дополнительное обсуждение важности центрирования и масштабирования данных доступно в этом FAQ: Следует ли нормализовать/стандартизировать/масштабировать данные?

Масштабирование против отбеливания#

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

Чтобы решить эту проблему, вы можете использовать PCA с whiten=True для дальнейшего устранения линейной корреляции между признаками.

7.3.1.4. Центрирование ядерных матриц#

Если у вас есть матрица ядра ядра \(K\) который вычисляет скалярное произведение в пространстве признаков (возможно, неявно) определённом функцией \(\phi(\cdot)\), a KernelCenterer может преобразовать матрицу ядра так, чтобы она содержала скалярные произведения в пространстве признаков, определенном \(\phi\) с последующим удалением среднего в этом пространстве. Другими словами, KernelCenterer вычисляет центрированную матрицу Грама, связанную с положительно полуопределенным ядром \(K\).

Математическая формулировка#

Теперь мы можем рассмотреть математическую формулировку, имея интуитивное представление. Пусть \(K\) быть матрицей ядра формы (n_samples, n_samples) вычислено из \(X\), матрица данных формы (n_samples, n_features), во время fit шаг. \(K\) определяется как

\[K(X, X) = \phi(X) . \phi(X)^{T}\]

\(\phi(X)\) является функциональным отображением \(X\) в гильбертово пространство. Центрированное ядро \(\tilde{K}\) определяется как:

\[\tilde{K}(X, X) = \tilde{\phi}(X) . \tilde{\phi}(X)^{T}\]

где \(\tilde{\phi}(X)\) результаты центрирования \(\phi(X)\) в гильбертовом пространстве.

Таким образом, можно вычислить \(\tilde{K}\) путем отображения \(X\) используя функцию \(\phi(\cdot)\) и центрируем данные в этом новом пространстве. Однако, ядра часто используются, потому что они позволяют некоторые алгебраические вычисления, которые избегают явного вычисления этого отображения с использованием \(\phi(\cdot)\). Действительно, можно неявно центрировать, как показано в Приложении B в [Scholkopf1998]:

\[\tilde{K} = K - 1_{\text{n}_{samples}} K - K 1_{\text{n}_{samples}} + 1_{\text{n}_{samples}} K 1_{\text{n}_{samples}}\]

\(1_{\text{n}_{samples}}\) является матрицей (n_samples, n_samples) где все элементы равны \(\frac{1}{\text{n}_{samples}}\). В transform шаг, ядро становится \(K_{test}(X, Y)\) определяется как:

\[K_{test}(X, Y) = \phi(Y) . \phi(X)^{T}\]

\(Y\) имеет ли тестовый набор данных форму (n_samples_test, n_features) и, следовательно, \(K_{test}\) имеет форму (n_samples_test, n_samples). В этом случае, центрирование \(K_{test}\) выполняется следующим образом:

\[\tilde{K}_{test}(X, Y) = K_{test} - 1'_{\text{n}_{samples}} K - K_{test} 1_{\text{n}_{samples}} + 1'_{\text{n}_{samples}} K 1_{\text{n}_{samples}}\]

\(1'_{\text{n}_{samples}}\) является матрицей формы (n_samples_test, n_samples) где все элементы равны \(\frac{1}{\text{n}_{samples}}\).

Ссылки

7.3.2. Нелинейное преобразование#

Доступны два типа преобразований: квантильные преобразования и степенные преобразования. И квантильные, и степенные преобразования основаны на монотонных преобразованиях признаков и, таким образом, сохраняют ранги значений вдоль каждого признака.

Квантильные преобразования приводят все признаки к одному желаемому распределению на основе формулы \(G^{-1}(F(X))\) где \(F\) является кумулятивной функцией распределения признака и \(G^{-1}\) the функция квантиля желаемого распределения вывода \(G\). Эта формула использует два следующих факта: (i) если \(X\) является случайной величиной с непрерывной кумулятивной функцией распределения \(F\) затем \(F(X)\) равномерно распределена на \([0,1]\); (ii) если \(U\) является случайной величиной с равномерным распределением на \([0,1]\) затем \(G^{-1}(U)\) имеет распределение \(G\). Выполняя ранговое преобразование, квантильное преобразование сглаживает необычные распределения и менее подвержено влиянию выбросов, чем методы масштабирования. Однако оно искажает корреляции и расстояния внутри и между признаками.

Степенное преобразование — это семейство параметрических преобразований, которые стремятся отобразить данные из любого распределения как можно ближе к нормальному распределению.

7.3.2.1. Отображение на равномерное распределение#

QuantileTransformer предоставляет непараметрическое преобразование для отображения данных в равномерное распределение со значениями от 0 до 1:

>>> from sklearn.datasets import load_iris
>>> from sklearn.model_selection import train_test_split
>>> X, y = load_iris(return_X_y=True)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
>>> quantile_transformer = preprocessing.QuantileTransformer(random_state=0)
>>> X_train_trans = quantile_transformer.fit_transform(X_train)
>>> X_test_trans = quantile_transformer.transform(X_test)
>>> np.percentile(X_train[:, 0], [0, 25, 50, 75, 100])
array([ 4.3,  5.1,  5.8,  6.5,  7.9])

Этот признак соответствует длине чашелистика в см. После применения квантильного преобразования эти ориентиры близко приближаются к ранее определенным процентилям:

>>> np.percentile(X_train_trans[:, 0], [0, 25, 50, 75, 100])
...
array([ 0.00 ,  0.24,  0.49,  0.73,  0.99 ])

Это можно подтвердить на независимом тестовом наборе с аналогичными замечаниями:

>>> np.percentile(X_test[:, 0], [0, 25, 50, 75, 100])
...
array([ 4.4  ,  5.125,  5.75 ,  6.175,  7.3  ])
>>> np.percentile(X_test_trans[:, 0], [0, 25, 50, 75, 100])
...
array([ 0.01,  0.25,  0.46,  0.60 ,  0.94])

7.3.2.2. Преобразование к гауссовскому распределению#

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

PowerTransformer в настоящее время предоставляет два таких степенных преобразования: преобразование Йео-Джонсона и преобразование Бокса-Кокса.

Преобразование Йео-Джонсона#
\[\begin{split}x_i^{(\lambda)} = \begin{cases} [(x_i + 1)^\lambda - 1] / \lambda & \text{if } \lambda \neq 0, x_i \geq 0, \\[8pt] \ln{(x_i + 1)} & \text{if } \lambda = 0, x_i \geq 0 \\[8pt] -[(-x_i + 1)^{2 - \lambda} - 1] / (2 - \lambda) & \text{if } \lambda \neq 2, x_i < 0, \\[8pt] - \ln (- x_i + 1) & \text{if } \lambda = 2, x_i < 0 \end{cases}\end{split}\]
Преобразование Бокса-Кокса#
\[\begin{split}x_i^{(\lambda)} = \begin{cases} \dfrac{x_i^\lambda - 1}{\lambda} & \text{if } \lambda \neq 0, \\[8pt] \ln{(x_i)} & \text{if } \lambda = 0, \end{cases}\end{split}\]

Box-Cox можно применять только к строго положительным данным. В обоих методах преобразование параметризуется \(\lambda\), которое определяется методом максимального правдоподобия. Вот пример использования преобразования Бокса-Кокса для отображения выборок, взятых из логнормального распределения, в нормальное распределение:

>>> pt = preprocessing.PowerTransformer(method='box-cox', standardize=False)
>>> X_lognormal = np.random.RandomState(616).lognormal(size=(3, 3))
>>> X_lognormal
array([[1.28, 1.18 , 0.84 ],
       [0.94, 1.60 , 0.388],
       [1.35, 0.217, 1.09 ]])
>>> pt.fit_transform(X_lognormal)
array([[ 0.49 ,  0.179, -0.156],
       [-0.051,  0.589, -0.576],
       [ 0.69 , -0.849,  0.101]])

Хотя приведённый выше пример устанавливает standardize опция для False, PowerTransformer будет применять нормализацию с нулевым средним и единичной дисперсией к преобразованному выводу по умолчанию.

Ниже приведены примеры применения преобразований Бокса-Кокса и Йео-Джонсона к различным вероятностным распределениям. Обратите внимание, что при применении к некоторым распределениям степенные преобразования дают результаты, очень близкие к гауссовским, но с другими они неэффективны. Это подчеркивает важность визуализации данных до и после преобразования.

../_images/sphx_glr_plot_map_data_to_normal_001.png

Также возможно преобразовать данные к нормальному распределению с помощью QuantileTransformer установкой output_distribution='normal'. Используя предыдущий пример с набором данных iris:

>>> quantile_transformer = preprocessing.QuantileTransformer(
...     output_distribution='normal', random_state=0)
>>> X_trans = quantile_transformer.fit_transform(X)
>>> quantile_transformer.quantiles_
array([[4.3, 2. , 1. , 0.1],
       [4.4, 2.2, 1.1, 0.1],
       [4.4, 2.2, 1.2, 0.1],
       ...,
       [7.7, 4.1, 6.7, 2.5],
       [7.7, 4.2, 6.7, 2.5],
       [7.9, 4.4, 6.9, 2.5]])

Таким образом, медиана входных данных становится средним выходных данных, центрированным на 0. Нормальный выход обрезается так, чтобы минимум и максимум входных данных — соответствующие квантилям 1e-7 и 1 - 1e-7 соответственно — не становились бесконечными при преобразовании.

7.3.3. Нормализация#

Нормализация является процессом масштабирование отдельных выборок для придания единичной нормы. Этот процесс может быть полезен, если вы планируете использовать квадратичную форму, такую как скалярное произведение или любое другое ядро, для количественной оценки сходства любой пары образцов.

Это предположение является основой Векторная пространственная модель часто используется в контекстах классификации текста и кластеризации.

Функция normalize предоставляет быстрый и простой способ выполнить эту операцию на одном массиво-подобном наборе данных, используя либо l1, l2, или max нормы:

>>> X = [[ 1., -1.,  2.],
...      [ 2.,  0.,  0.],
...      [ 0.,  1., -1.]]
>>> X_normalized = preprocessing.normalize(X, norm='l2')

>>> X_normalized
array([[ 0.408, -0.408,  0.812],
       [ 1.   ,  0.   ,  0.   ],
       [ 0.   ,  0.707, -0.707]])

The preprocessing модуль дополнительно предоставляет служебный класс Normalizer который реализует ту же операцию с использованием Transformer API (даже несмотря на то, что fit метод бесполезен в этом случае: класс не имеет состояния, так как эта операция обрабатывает выборки независимо).

Этот класс, следовательно, подходит для использования на ранних этапах Pipeline:

>>> normalizer = preprocessing.Normalizer().fit(X)  # fit does nothing
>>> normalizer
Normalizer()

Экземпляр нормализатора затем может использоваться на векторах образцов как любой преобразователь:

>>> normalizer.transform(X)
array([[ 0.408, -0.408,  0.812],
       [ 1.   ,  0.   ,  0.   ],
       [ 0.   ,  0.707, -0.707]])

>>> normalizer.transform([[-1.,  1., 0.]])
array([[-0.707,  0.707,  0.]])

Примечание: L2-нормализация также известна как предобработка пространственного знака.

Разреженный вход#

normalize и Normalizer принимать как плотные массиво-подобные объекты, так и разреженные матрицы из scipy.sparse в качестве входных данных.

Для разреженных входных данных преобразован в представление сжатых разреженных строк (см. scipy.sparse.csr_matrix) перед передачей в эффективные Cython-подпрограммы. Чтобы избежать ненужного копирования памяти, рекомендуется выбирать представление CSR на более раннем этапе.

7.3.4. Кодирование категориальных признаков#

Часто признаки представлены не как непрерывные значения, а как категориальные. Например, у человека могут быть признаки ["male", "female"], ["from Europe", "from US", "from Asia"], ["uses Firefox", "uses Chrome", "uses Safari", "uses Internet Explorer"]. Такие признаки могут быть эффективно закодированы как целые числа, например ["male", "from US", "uses Internet Explorer"] может быть выражено как [0, 1, 3] в то время как ["female", "from Asia", "uses Chrome"] будет [1, 2, 1].

Чтобы преобразовать категориальные признаки в такие целочисленные коды, мы можем использовать OrdinalEncoder. Этот оценщик преобразует каждый категориальный признак в один новый признак целых чисел (от 0 до n_categories - 1):

>>> enc = preprocessing.OrdinalEncoder()
>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OrdinalEncoder()
>>> enc.transform([['female', 'from US', 'uses Safari']])
array([[0., 1., 1.]])

Однако такое целочисленное представление не может быть использовано напрямую со всеми оценщиками scikit-learn, так как они ожидают непрерывный ввод и интерпретировали бы категории как упорядоченные, что часто нежелательно (например, набор браузеров был упорядочен произвольно).

По умолчанию, OrdinalEncoder также пропускает пропущенные значения, которые указаны с помощью np.nan.

>>> enc = preprocessing.OrdinalEncoder()
>>> X = [['male'], ['female'], [np.nan], ['female']]
>>> enc.fit_transform(X)
array([[ 1.],
       [ 0.],
       [nan],
       [ 0.]])

OrdinalEncoder предоставляет параметр encoded_missing_value для кодирования пропущенных значений без необходимости создания конвейера и использования SimpleImputer.

>>> enc = preprocessing.OrdinalEncoder(encoded_missing_value=-1)
>>> X = [['male'], ['female'], [np.nan], ['female']]
>>> enc.fit_transform(X)
array([[ 1.],
       [ 0.],
       [-1.],
       [ 0.]])

Вышеуказанная обработка эквивалентна следующему конвейеру:

>>> from sklearn.pipeline import Pipeline
>>> from sklearn.impute import SimpleImputer
>>> enc = Pipeline(steps=[
...     ("encoder", preprocessing.OrdinalEncoder()),
...     ("imputer", SimpleImputer(strategy="constant", fill_value=-1)),
... ])
>>> enc.fit_transform(X)
array([[ 1.],
       [ 0.],
       [-1.],
       [ 0.]])

Другой способ преобразования категориальных признаков в признаки, которые можно использовать с оценщиками scikit-learn, — это использование one-of-K, также известного как one-hot или фиктивное кодирование. Этот тип кодирования можно получить с помощью OneHotEncoder, который преобразует каждый категориальный признак с n_categories возможные значения в n_categories бинарные признаки, с одним из них равным 1, а всеми остальными 0.

Продолжая приведенный выше пример:

>>> enc = preprocessing.OneHotEncoder()
>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OneHotEncoder()
>>> enc.transform([['female', 'from US', 'uses Safari'],
...                ['male', 'from Europe', 'uses Safari']]).toarray()
array([[1., 0., 0., 1., 0., 1.],
       [0., 1., 1., 0., 0., 1.]])

По умолчанию значения, которые может принимать каждый признак, автоматически выводятся из набора данных и могут быть найдены в categories_ attribute:

>>> enc.categories_
[array(['female', 'male'], dtype=object), array(['from Europe', 'from US'], dtype=object), array(['uses Firefox', 'uses Safari'], dtype=object)]

Это можно явно указать с помощью параметра categories. There are two genders, four possible continents and four web browsers in our dataset:

>>> genders = ['female', 'male']
>>> locations = ['from Africa', 'from Asia', 'from Europe', 'from US']
>>> browsers = ['uses Chrome', 'uses Firefox', 'uses IE', 'uses Safari']
>>> enc = preprocessing.OneHotEncoder(categories=[genders, locations, browsers])
>>> # Note that for there are missing categorical values for the 2nd and 3rd
>>> # feature
>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OneHotEncoder(categories=[['female', 'male'],
                          ['from Africa', 'from Asia', 'from Europe',
                           'from US'],
                          ['uses Chrome', 'uses Firefox', 'uses IE',
                           'uses Safari']])
>>> enc.transform([['female', 'from Asia', 'uses Chrome']]).toarray()
array([[1., 0., 0., 1., 0., 0., 1., 0., 0., 0.]])

Если существует вероятность, что обучающие данные могут иметь пропущенные категориальные признаки, часто лучше указать handle_unknown='infrequent_if_exist' вместо установки categories вручную, как выше. Когда handle_unknown='infrequent_if_exist' указан и во время преобразования встречаются неизвестные категории, ошибка не будет вызвана, но результирующие one-hot кодированные столбцы для этого признака будут все нулями или считаться редкой категорией, если это включено. (handle_unknown='infrequent_if_exist' поддерживается только для one-hot кодирования):

>>> enc = preprocessing.OneHotEncoder(handle_unknown='infrequent_if_exist')
>>> X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
>>> enc.fit(X)
OneHotEncoder(handle_unknown='infrequent_if_exist')
>>> enc.transform([['female', 'from Asia', 'uses Chrome']]).toarray()
array([[1., 0., 0., 0., 0., 0.]])

Также возможно закодировать каждый столбец в n_categories - 1 столбцы вместо n_categories столбцы с помощью drop параметр. Этот параметр позволяет пользователю указать категорию для каждого признака, который нужно удалить. Это полезно для избежания коллинеарности во входной матрице в некоторых классификаторах. Такая функциональность полезна, например, при использовании нерегуляризованной регрессии (LinearRegression), поскольку коллинеарность приведет к необратимости ковариационной матрицы:

>>> X = [['male', 'from US', 'uses Safari'],
...      ['female', 'from Europe', 'uses Firefox']]
>>> drop_enc = preprocessing.OneHotEncoder(drop='first').fit(X)
>>> drop_enc.categories_
[array(['female', 'male'], dtype=object), array(['from Europe', 'from US'], dtype=object),
 array(['uses Firefox', 'uses Safari'], dtype=object)]
>>> drop_enc.transform(X).toarray()
array([[1., 1., 1.],
       [0., 0., 0.]])

Может потребоваться удалить один из двух столбцов только для признаков с 2 категориями. В этом случае можно установить параметр drop='if_binary'.

>>> X = [['male', 'US', 'Safari'],
...      ['female', 'Europe', 'Firefox'],
...      ['female', 'Asia', 'Chrome']]
>>> drop_enc = preprocessing.OneHotEncoder(drop='if_binary').fit(X)
>>> drop_enc.categories_
[array(['female', 'male'], dtype=object), array(['Asia', 'Europe', 'US'], dtype=object),
 array(['Chrome', 'Firefox', 'Safari'], dtype=object)]
>>> drop_enc.transform(X).toarray()
array([[1., 0., 0., 1., 0., 0., 1.],
       [0., 0., 1., 0., 0., 1., 0.],
       [0., 1., 0., 0., 1., 0., 0.]])

В преобразованном X, первый столбец — это кодировка признака с категориями "мужской"/"женский", а оставшиеся 6 столбцов — кодировка 2 признаков с соответственно 3 категориями каждый.

Когда handle_unknown='ignore' и drop не является None, неизвестные категории будут закодированы как все нули:

>>> drop_enc = preprocessing.OneHotEncoder(drop='first',
...                                        handle_unknown='ignore').fit(X)
>>> X_test = [['unknown', 'America', 'IE']]
>>> drop_enc.transform(X_test).toarray()
array([[0., 0., 0., 0., 0.]])

Все категории в X_test неизвестны во время преобразования и будут отображены в нули. Это означает, что неизвестные категории будут иметь то же отображение, что и отброшенная категория. OneHotEncoder.inverse_transform будет отображать все нули в отброшенную категорию, если категория отброшена и None если категория не удалена:

>>> drop_enc = preprocessing.OneHotEncoder(drop='if_binary', sparse_output=False,
...                                        handle_unknown='ignore').fit(X)
>>> X_test = [['unknown', 'America', 'IE']]
>>> X_trans = drop_enc.transform(X_test)
>>> X_trans
array([[0., 0., 0., 0., 0., 0., 0.]])
>>> drop_enc.inverse_transform(X_trans)
array([['female', None, None]], dtype=object)
Поддержка категориальных признаков с пропущенными значениями#

OneHotEncoder поддерживает категориальные признаки с пропущенными значениями, рассматривая пропущенные значения как дополнительную категорию:

>>> X = [['male', 'Safari'],
...      ['female', None],
...      [np.nan, 'Firefox']]
>>> enc = preprocessing.OneHotEncoder(handle_unknown='error').fit(X)
>>> enc.categories_
[array(['female', 'male', nan], dtype=object),
array(['Firefox', 'Safari', None], dtype=object)]
>>> enc.transform(X).toarray()
array([[0., 1., 0., 0., 1., 0.],
      [1., 0., 0., 0., 0., 1.],
      [0., 0., 1., 1., 0., 0.]])

страница бенчмарков scikit-learn np.nan и None, они будут рассматриваться как отдельные категории:

>>> X = [['Safari'], [None], [np.nan], ['Firefox']]
>>> enc = preprocessing.OneHotEncoder(handle_unknown='error').fit(X)
>>> enc.categories_
[array(['Firefox', 'Safari', None, nan], dtype=object)]
>>> enc.transform(X).toarray()
array([[0., 1., 0., 0.],
      [0., 0., 1., 0.],
      [0., 0., 0., 1.],
      [1., 0., 0., 0.]])

См. Загрузка признаков из словарей для категориальных признаков, которые представлены как словарь, а не как скаляры.

7.3.4.1. Редкие категории#

OneHotEncoder и OrdinalEncoder поддерживать агрегирование редких категорий в один выход для каждого признака. Параметры для включения сбора редких категорий: min_frequency и max_categories.

  1. min_frequency является либо целым числом больше или равным 1, либо числом с плавающей точкой в интервале (0.0, 1.0). Если min_frequency является целым числом, категории с мощностью меньше min_frequency будет считаться редким. Если min_frequency является числом с плавающей точкой, категории с мощностью меньше этой доли от общего количества образцов будут считаться редкими. Значение по умолчанию равно 1, что означает, что каждая категория кодируется отдельно.

  2. max_categories является либо None или любое целое число больше 1. Этот параметр устанавливает верхний предел количества выходных признаков для каждого входного признака. max_categories включает функцию, которая объединяет редкие категории.

В следующем примере с OrdinalEncoder, категории 'dog' и 'snake' считаются редкими:

>>> X = np.array([['dog'] * 5 + ['cat'] * 20 + ['rabbit'] * 10 +
...               ['snake'] * 3], dtype=object).T
>>> enc = preprocessing.OrdinalEncoder(min_frequency=6).fit(X)
>>> enc.infrequent_categories_
[array(['dog', 'snake'], dtype=object)]
>>> enc.transform(np.array([['dog'], ['cat'], ['rabbit'], ['snake']]))
array([[2.],
       [0.],
       [1.],
       [2.]])

OrdinalEncoder’s max_categories делать не учитывать пропущенные или неизвестные категории. Установка unknown_value или encoded_missing_value в целое число будет увеличивать количество уникальных целочисленных кодов на один каждый раз. Это может привести к max_categories + 2 целочисленные коды. В следующем примере «a» и «d» считаются редкими и группируются вместе в одну категорию, «b» и «c» являются собственными категориями, неизвестные значения кодируются как 3, а пропущенные значения кодируются как 4.

>>> X_train = np.array(
...     [["a"] * 5 + ["b"] * 20 + ["c"] * 10 + ["d"] * 3 + [np.nan]],
...     dtype=object).T
>>> enc = preprocessing.OrdinalEncoder(
...     handle_unknown="use_encoded_value", unknown_value=3,
...     max_categories=3, encoded_missing_value=4)
>>> _ = enc.fit(X_train)
>>> X_test = np.array([["a"], ["b"], ["c"], ["d"], ["e"], [np.nan]], dtype=object)
>>> enc.transform(X_test)
array([[2.],
       [0.],
       [1.],
       [2.],
       [3.],
       [4.]])

Аналогично, OneHotEncoder можно настроить для группировки редких категорий:

>>> enc = preprocessing.OneHotEncoder(min_frequency=6, sparse_output=False).fit(X)
>>> enc.infrequent_categories_
[array(['dog', 'snake'], dtype=object)]
>>> enc.transform(np.array([['dog'], ['cat'], ['rabbit'], ['snake']]))
array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

Установив handle_unknown в 'infrequent_if_exist', неизвестные категории будут считаться редкими:

>>> enc = preprocessing.OneHotEncoder(
...    handle_unknown='infrequent_if_exist', sparse_output=False, min_frequency=6)
>>> enc = enc.fit(X)
>>> enc.transform(np.array([['dragon']]))
array([[0., 0., 1.]])

OneHotEncoder.get_feature_names_out использует 'infrequent' как имя редкого признака:

>>> enc.get_feature_names_out()
array(['x0_cat', 'x0_rabbit', 'x0_infrequent_sklearn'], dtype=object)

Когда 'handle_unknown' установлено в 'infrequent_if_exist' и неизвестная категория встречается при преобразовании:

  1. Если поддержка редких категорий не была настроена или не было редких категорий во время обучения, результирующие столбцы с one-hot кодированием для этого признака будут состоять из всех нулей. При обратном преобразовании неизвестная категория будет обозначена как None.

  2. Если во время обучения есть редкая категория, неизвестная категория будет считаться редкой. При обратном преобразовании 'infrequent_sklearn' будет использоваться для представления редкой категории.

Редкие категории также можно настроить с помощью max_categoriesВ следующем примере мы устанавливаем max_categories=2 для ограничения количества признаков в выходных данных. Это приведёт к тому, что все, кроме 'cat' категория, которая будет считаться редкой, приводя к двум признакам: один для 'cat' и один для редких категорий - которые являются всеми остальными:

>>> enc = preprocessing.OneHotEncoder(max_categories=2, sparse_output=False)
>>> enc = enc.fit(X)
>>> enc.transform([['dog'], ['cat'], ['rabbit'], ['snake']])
array([[0., 1.],
       [1., 0.],
       [0., 1.],
       [0., 1.]])

Если оба max_categories и min_frequency являются нестандартными значениями, то категории выбираются на основе min_frequency сначала и max_categories категории сохраняются. В следующем примере, min_frequency=4 рассматривает только snake быть редкими, но max_categories=3, принудительно dog также быть нечастыми:

>>> enc = preprocessing.OneHotEncoder(min_frequency=4, max_categories=3, sparse_output=False)
>>> enc = enc.fit(X)
>>> enc.transform([['dog'], ['cat'], ['rabbit'], ['snake']])
array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

Если есть редкие категории с одинаковой мощностью на границе отсечения max_categories, затем первый max_categories принимаются на основе лексикографического упорядочения. В следующем примере «b», «c» и «d» имеют одинаковую мощность и с max_categories=2, "b" и "c" встречаются редко, потому что они имеют более высокий лексический порядок.

>>> X = np.asarray([["a"] * 20 + ["b"] * 10 + ["c"] * 10 + ["d"] * 10], dtype=object).T
>>> enc = preprocessing.OneHotEncoder(max_categories=3).fit(X)
>>> enc.infrequent_categories_
[array(['b', 'c'], dtype=object)]

7.3.4.2. Целевой кодировщик#

The TargetEncoder использует среднее целевой переменной, условное по категориальному признаку, для кодирования неупорядоченных категорий, т.е. номинальных категорий [PAR] [MIC]. Эта схема кодирования полезна для категориальных признаков с высокой кардинальностью, где one-hot кодирование раздуло бы пространство признаков, делая его более затратным для обработки последующей моделью. Классический пример категорий с высокой кардинальностью — основанные на местоположении, такие как почтовый индекс или регион.

Целевые переменные бинарной классификации#

Для бинарной классификации целевая кодировка задаётся как:

\[S_i = \lambda_i\frac{n_{iY}}{n_i} + (1 - \lambda_i)\frac{n_Y}{n}\]

где \(S_i\) это кодировка для категории \(i\), \(n_{iY}\) это количество наблюдений с \(Y=1\) и категория \(i\), \(n_i\) это количество наблюдений с категорией \(i\), \(n_Y\) это количество наблюдений с \(Y=1\), \(n\) это количество наблюдений, и \(\lambda_i\) является коэффициентом сжатия для категории \(i\). Коэффициент сжатия задается как:

\[\lambda_i = \frac{n_i}{m + n_i}\]

где \(m\) является коэффициентом сглаживания, который контролируется с помощью smooth параметр в TargetEncoder. Большие коэффициенты сглаживания будут придавать больше веса глобальному среднему. Когда smooth="auto", коэффициент сглаживания вычисляется как эмпирическая байесовская оценка: \(m=\sigma_i^2/\tau^2\), где \(\sigma_i^2\) является дисперсией y с категорией \(i\) и \(\tau^2\) является глобальной дисперсией y.

Целевые переменные многоклассовой классификации#

Для многоклассовых целевых переменных классификации формулировка аналогична бинарной классификации:

\[S_{ij} = \lambda_i\frac{n_{iY_j}}{n_i} + (1 - \lambda_i)\frac{n_{Y_j}}{n}\]

где \(S_{ij}\) это кодировка для категории \(i\) и класс \(j\), \(n_{iY_j}\) является числом наблюдений с \(Y=j\) и категория \(i\), \(n_i\) — это количество наблюдений с категорией \(i\), \(n_{Y_j}\) является числом наблюдений с \(Y=j\), \(n\) это количество наблюдений, и \(\lambda_i\) является коэффициентом сжатия для категории \(i\).

Непрерывные цели#

Для непрерывных целевых переменных формулировка аналогична бинарной классификации:

\[S_i = \lambda_i\frac{\sum_{k\in L_i}Y_k}{n_i} + (1 - \lambda_i)\frac{\sum_{k=1}^{n}Y_k}{n}\]

где \(L_i\) это набор наблюдений с категорией \(i\) и \(n_i\) — это количество наблюдений с категорией \(i\).

Примечание

В TargetEncoder, fit(X, y).transform(X) . Наличие образца fit_transform(X, y).

fit_transform внутренне полагается на кросс-фиттинг схема для предотвращения утечки информации о цели в представление во время обучения, особенно для неинформативных категориальных переменных с высокой кардинальностью (признаки со многими уникальными категориями, где каждая категория появляется только несколько раз), и помогает предотвратить переобучение нижестоящей модели на ложных корреляциях. В fit_transform, обучающие данные разделяются на k фолдов (определяется cv параметр) и каждый фолд кодируется с использованием кодировок, изученных с помощью другие k-1 фолдов. По этой причине обучающие данные всегда должны обучаться и преобразовываться с помощью fit_transform(X_train, y_train).

Эта диаграмма показывает кросс-фиттинг схема в fit_transform с значением по умолчанию cv=5:

../_images/target_encoder_cross_validation.svg

The fit метод делает не использовать любой кросс-фиттинг схемы и обучает одно кодирование на всем тренировочном наборе. Не рекомендуется использовать этот метод, так как он может привести к утечке данных, как упоминалось выше. Используйте fit_transform вместо этого.

Во время fit_transform, кодировщик изучает кодировки категорий из полных обучающих данных и сохраняет их в encodings_ атрибут. Промежуточные кодировки, изученные для каждого фолда во время кросс-фиттинг процесса являются временными и не сохраняются. Сохраненные кодировки затем могут быть использованы для преобразования тестовых данных с помощью encoder.transform(X_test).

Примечание

TargetEncoder учитывает пропущенные значения, такие как np.nan или None, как другую категорию и кодирует их как любую другую категорию. Категории, которые не были замечены во время fit кодируются средним значением целевой переменной, т.е. target_mean_.

Примеры

Ссылки

7.3.5. Дискретизация#

Дискретизация (также известное как квантование или бининг) предоставляет способ разделения непрерывных признаков на дискретные значения. Некоторые наборы данных с непрерывными признаками могут выиграть от дискретизации, потому что дискретизация может преобразовать набор данных непрерывных атрибутов в набор с только номинальными атрибутами.

Однократно закодированные дискретизированные признаки могут сделать модель более выразительной, сохраняя интерпретируемость. Например, предварительная обработка с дискретизатором может ввести нелинейность в линейные модели. Для более продвинутых возможностей, в частности гладких, см. Генерация полиномиальных признаков ниже.

7.3.5.1. K-биновая дискретизация#

KBinsDiscretizer дискретизирует признаки в k бины:

>>> X = np.array([[ -3., 5., 15 ],
...               [  0., 6., 14 ],
...               [  6., 3., 11 ]])
>>> est = preprocessing.KBinsDiscretizer(n_bins=[3, 2, 2], encode='ordinal').fit(X)

По умолчанию выходные данные кодируются в разреженную матрицу с помощью one-hot кодирования (см. Кодирование категориальных признаков) и это можно настроить с помощью encode параметр. Для каждого признака границы бинов вычисляются во время fit и вместе с количеством бинов они определяют интервалы. Таким образом, для текущего примера эти интервалы определены как:

  • признак 1: \({[-\infty, -1), [-1, 2), [2, \infty)}\)

  • признак 2: \({[-\infty, 5), [5, \infty)}\)

  • признак 3: \({[-\infty, 14), [14, \infty)}\)

На основе этих интервалов бинов, X преобразуется следующим образом:

>>> est.transform(X)
array([[ 0., 1., 1.],
       [ 1., 1., 1.],
       [ 2., 0., 0.]])

Полученный набор данных содержит порядковые атрибуты, которые могут быть дополнительно использованы в Pipeline.

Дискретизация похожа на построение гистограмм для непрерывных данных. Однако гистограммы фокусируются на подсчёте признаков, попадающих в определённые бины, тогда как дискретизация фокусируется на присвоении значений признаков этим бинам.

KBinsDiscretizer реализует различные стратегии бинирования, которые можно выбрать с помощью strategy параметр. Стратегия 'uniform' использует бины постоянной ширины. Стратегия 'quantile' использует значения квантилей для создания одинаково заполненных бинов в каждом признаке. Стратегия 'kmeans' определяет бины на основе процедуры кластеризации k-средних, выполняемой для каждого признака независимо.

Учтите, что можно указать пользовательские бины, передав вызываемый объект, определяющий стратегию дискретизации в FunctionTransformer. Например, мы можем использовать функцию Pandas pandas.cut:

>>> import pandas as pd
>>> import numpy as np
>>> from sklearn import preprocessing
>>>
>>> bins = [0, 1, 13, 20, 60, np.inf]
>>> labels = ['infant', 'kid', 'teen', 'adult', 'senior citizen']
>>> transformer = preprocessing.FunctionTransformer(
...     pd.cut, kw_args={'bins': bins, 'labels': labels, 'retbins': False}
... )
>>> X = np.array([0.2, 2, 15, 25, 97])
>>> transformer.fit_transform(X)
['infant', 'kid', 'teen', 'adult', 'senior citizen']
Categories (5, object): ['infant' < 'kid' < 'teen' < 'adult' < 'senior citizen']

Примеры

7.3.5.2. Бинаризация признаков#

Бинаризация признаков является процессом пороговое преобразование числовых признаков для получения логических значений. Это может быть полезно для последующих вероятностных оценщиков, которые предполагают, что входные данные распределены согласно многомерному Распределение Бернулли. Например, это имеет место для BernoulliRBM.

Также распространено в сообществе обработки текстов использовать двоичные значения признаков (вероятно, для упрощения вероятностных рассуждений), даже если нормализованные частоты (также известные как частоты терминов) или признаки со значениями TF-IDF часто работают немного лучше на практике.

Что касается Normalizer, служебный класс Binarizer предназначен для использования на ранних этапах Pipeline. fit метод ничего не делает, так как каждый образец обрабатывается независимо от других:

>>> X = [[ 1., -1.,  2.],
...      [ 2.,  0.,  0.],
...      [ 0.,  1., -1.]]

>>> binarizer = preprocessing.Binarizer().fit(X)  # fit does nothing
>>> binarizer
Binarizer()

>>> binarizer.transform(X)
array([[1., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.]])

Можно настроить порог бинаризатора:

>>> binarizer = preprocessing.Binarizer(threshold=1.1)
>>> binarizer.transform(X)
array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 0., 0.]])

Что касается Normalizer класс, модуль preprocessing предоставляет вспомогательную функцию binarize для использования, когда API трансформатора не требуется.

Обратите внимание, что Binarizer похож на KBinsDiscretizer когда k = 2, и когда граница бина находится на значении threshold.

7.3.6. Заполнение пропущенных значений#

Инструменты для заполнения пропущенных значений обсуждаются в Заполнение пропущенных значений.

7.3.7. Генерация полиномиальных признаков#

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

7.3.7.1. Полиномиальные признаки#

Простой и распространенный метод — использование полиномиальных признаков, которые могут получить признаки высокого порядка и члены взаимодействия. Реализовано в PolynomialFeatures:

>>> import numpy as np
>>> from sklearn.preprocessing import PolynomialFeatures
>>> X = np.arange(6).reshape(3, 2)
>>> X
array([[0, 1],
       [2, 3],
       [4, 5]])
>>> poly = PolynomialFeatures(2)
>>> poly.fit_transform(X)
array([[ 1.,  0.,  1.,  0.,  0.,  1.],
       [ 1.,  2.,  3.,  4.,  6.,  9.],
       [ 1.,  4.,  5., 16., 20., 25.]])

Признаки X были преобразованы из \((X_1, X_2)\) to \((1, X_1, X_2, X_1^2, X_1X_2, X_2^2)\).

В некоторых случаях требуются только члены взаимодействия между признаками, и это может быть получено с настройкой interaction_only=True:

>>> X = np.arange(9).reshape(3, 3)
>>> X
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> poly = PolynomialFeatures(degree=3, interaction_only=True)
>>> poly.fit_transform(X)
array([[  1.,   0.,   1.,   2.,   0.,   0.,   2.,   0.],
       [  1.,   3.,   4.,   5.,  12.,  15.,  20.,  60.],
       [  1.,   6.,   7.,   8.,  42.,  48.,  56., 336.]])

Признаки X были преобразованы из \((X_1, X_2, X_3)\) to \((1, X_1, X_2, X_3, X_1X_2, X_1X_3, X_2X_3, X_1X_2X_3)\).

Обратите внимание, что полиномиальные признаки используются неявно в методы ядра (например, SVC, KernelPCA) при использовании полиномиального Функции ядра.

См. Полиномиальная и сплайновая интерполяция для гребневой регрессии с использованием созданных полиномиальных признаков.

7.3.7.2. Преобразователь сплайнов#

Другой способ добавления нелинейных членов вместо чистых полиномов признаков — создание сплайн-базисных функций для каждого признака с помощью SplineTransformer. Сплайны — это кусочно-полиномиальные функции, параметризованные их полиномиальной степенью и позициями узлов. Сплайны SplineTransformer реализует B-сплайн базис, см. ссылки ниже.

Примечание

The SplineTransformer обрабатывает каждый признак отдельно, т.е. не даст вам членов взаимодействия.

Некоторые преимущества сплайнов перед полиномами:

  • B-сплайны очень гибкие и устойчивые, если вы сохраняете фиксированную низкую степень, обычно 3, и экономно адаптируете количество узлов. Полиномам потребовалась бы более высокая степень, что приводит к следующему пункту.

  • B-сплайны не имеют колебательного поведения на границах, как полиномы (чем выше степень, тем хуже). Это известно как Феномен Рунге.

  • B-сплайны предоставляют хорошие варианты для экстраполяции за границы, т.е. за пределы диапазона подогнанных значений. Посмотрите на опцию extrapolation.

  • B-сплайны генерируют матрицу признаков с ленточной структурой. Для одного признака каждая строка содержит только degree + 1 ненулевые элементы, которые встречаются последовательно и являются четными положительными числами. Это приводит к матрице с хорошими числовыми свойствами, например, с низким числом обусловленности, в отличие от матрицы полиномов, которая известна под названием Матрица ВандермондаНизкое число обусловленности важно для устойчивых алгоритмов линейных моделей.

Следующий фрагмент кода показывает сплайны в действии:

>>> import numpy as np
>>> from sklearn.preprocessing import SplineTransformer
>>> X = np.arange(5).reshape(5, 1)
>>> X
array([[0],
       [1],
       [2],
       [3],
       [4]])
>>> spline = SplineTransformer(degree=2, n_knots=3)
>>> spline.fit_transform(X)
array([[0.5  , 0.5  , 0.   , 0.   ],
       [0.125, 0.75 , 0.125, 0.   ],
       [0.   , 0.5  , 0.5  , 0.   ],
       [0.   , 0.125, 0.75 , 0.125],
       [0.   , 0.   , 0.5  , 0.5  ]])

Поскольку X отсортирован, можно легко увидеть вывод ленточной матрицы. Только три средние диагонали ненулевые для degree=2. Чем выше степень, тем больше перекрытие сплайнов.

Интересно, что SplineTransformer of degree=0 то же самое, что KBinsDiscretizer с encode='onehot-dense' и n_bins = n_knots - 1 if knots = strategy.

Примеры

Ссылки#

7.3.8. Пользовательские преобразователи#

Часто требуется преобразовать существующую функцию Python в трансформер для помощи в очистке или обработке данных. Вы можете реализовать трансформер из произвольной функции с помощью FunctionTransformer. Например, чтобы построить преобразователь, который применяет логарифмическое преобразование в конвейере, сделайте:

>>> import numpy as np
>>> from sklearn.preprocessing import FunctionTransformer
>>> transformer = FunctionTransformer(np.log1p, validate=True)
>>> X = np.array([[0, 1], [2, 3]])
>>> # Since FunctionTransformer is no-op during fit, we can call transform directly
>>> transformer.transform(X)
array([[0.        , 0.69314718],
       [1.09861229, 1.38629436]])

Вы можете убедиться, что func и inverse_func являются обратными друг другу, установив check_inverse=True и вызов fit до transform. Обратите внимание, что выводится предупреждение, которое можно превратить в ошибку с помощью filterwarnings:

>>> import warnings
>>> warnings.filterwarnings("error", message=".*check_inverse*.",
...                         category=UserWarning, append=False)

Для полного примера кода, демонстрирующего использование FunctionTransformer для извлечения признаков из текстовых данных см. Трансформер столбцов с разнородными источниками данных и Инженерия временных признаков.