Before feeding the data to a model, it is usually necessary to pre-process it. This is necessary due to the mathematical formulation of these models, where some of them might require data to follow specific patterns. Here, the following pre-processing strategies are going to be presented: normalization, encoding and feature selection.
Normalization
Normalization is a class of transformations that make features present more uniform statistics. The first approach for normalizing data is the absolute maximum normalization. This technique takes the a set of features \(\boldsymbol X \in \mathbb R^{N\times n}\), where \(N\) is the number of observations and \(n\) is the number of features, and applies the following transformation: \[
\boldsymbol{\tilde X} = \frac{\boldsymbol{X}}{\max\mid\boldsymbol{X}\mid},
\tag{1}\] where \(\boldsymbol{\tilde X}\) is the normalized set of features. This normalization scales the data between \(-1\) and \(1\), while preserving sparsity. Note that the maximum is taken feature-wise, and so is the division.
Another type of normalization is the \(\min-\max\) normalization, which scales the data between \(0\) and \(1\), where the minimum value of the original data is mapped to \(0\), and the maximum to \(1\). It is given by: \[
\boldsymbol{\tilde X} = \frac{\boldsymbol{X} - \min(\boldsymbol{X})}{\max(\boldsymbol{X}) - \min(\boldsymbol{X})}.
\tag{2}\] Note that unlike absolute maximum normalization, \(\min-\max\) normalization does not preserve sparsity.
Finally, standard normalization scales the data to so that each feature has a standard deviation of \(1\) and a mean of \(0\). The transformation is given by: \[
\boldsymbol{\tilde X} = \frac{\boldsymbol{X} - \boldsymbol{\mu}_{\boldsymbol{X}}}{\boldsymbol{\sigma}_{\boldsymbol{X}}},
\tag{3}\] where \(\boldsymbol{\mu}_{\boldsymbol{X}}\) are the mean values of the features and \(\boldsymbol{\sigma}_{\boldsymbol{X}}\) are the standard deviations.
Code: Normalization
Here, synthetic data is going to be normalized, in order to compare the different transformations explained. Importing packages.
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.preprocessing import MaxAbsScaler, MinMaxScaler, StandardScaler
Another important part of pre-processing is encoding features that are not numerical. This envolves finding good representations for features that might be stored as strings, for example. When there is some sense of order in the feature, the ordinal encoder is used. It converts the \(m\) original classes into integers, keeping the order. On the other hand, if there is no order, the data is passed through a one-hot encoder, which converts the original \(m\) classes into \(m\) new features, where the columns are sparse, only containing \(1\) when the observations belongs to the specific class.
Code: Encoding
Here, examples of ordinal and one-hot encoding are going to be presented. Importing the libraries.
import polars as plimport numpy as npfrom sklearn.preprocessing import OneHotEncoder, OrdinalEncoder
shape: (5, 4)
┌─────┬───────┬────────┬───────┐
│ ID ┆ Color ┆ Size ┆ Price │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ i64 │
╞═════╪═══════╪════════╪═══════╡
│ 1 ┆ Red ┆ Small ┆ 10 │
│ 2 ┆ Blue ┆ Large ┆ 20 │
│ 3 ┆ Green ┆ Medium ┆ 15 │
│ 4 ┆ Blue ┆ Small ┆ 12 │
│ 5 ┆ Red ┆ Medium ┆ 18 │
└─────┴───────┴────────┴───────┘
As the Color feature has no order in its values, one-hot encoding is going to be used. As for the Size feature, values assume order, so ordinal encoding is needed.
In some cases, the dataset has numerous features, resulting in complex and computationally expensive models. Therefore, it is interesting to select a small subset of features to decrease the computational cost of the trained model. This selection is important to avoid the curse of dimensionality, in which the increase of features in the model worsens its performance.
The first strategy is to eliminate features by variance. When a feature does not vary throughout observations, it does not add new information to the model, therefore, it can be excluded. Similarly, one can define a variance threshold for eliminating features.
A more advanced approach is to use the Variance Inflation Factor (VIF) algorithm. It removes features based on their multicollinearity with other features. That is, if one feature is a linear combination of other ones, it is removed. For each feature \(x_j \in \boldsymbol{X}\), the VIF trains a linear regression with \(x_j\) as a target. The coefficient of determination \(R^2\) of the final model is used to calculate the VIF coefficient: \[
\text{VIF}(x_j) = \frac{1}{1-R_j^2}.
\tag{4}\] If this coefficient is equal to \(1\), there is no multicollinearity, while higher values indicate correlation. With that, given a threshold – generally \(5\) or \(10\) –, features can be removed.
Code: Feature Selection
In this code, VIF will be used to remove features that can be represented via linear combination of other features. Importing libraries.
import numpy as npimport polars as plimport statsmodels.api as smfrom statsmodels.stats.outliers_influence import variance_inflation_factor
Generating data, and creating a linear combination of features with extra noise.
df_with_const = df.with_columns(pl.lit(1).alias("const"))X_matrix = df_with_const.to_numpy()for i, col inenumerate(df_with_const.columns):if col !="const": vif = variance_inflation_factor(X_matrix, i)print(f"{col}: {vif:.2f}")
As the results show, feature x5 has the highest VIF value, it would be the first feature to be dropped. Note that the other features also have high values, because the generation of the linearly dependent feature also make other features dependent on it.