I recently encountered the Mutual Information concept, and started reading on it. As I saw that it can get non-linear relations, it seemed to me that it might be a more powerful method to choose which features to keep in a ML model compared to correlation coefficient.
However, I've just run a simple experiment, where I've created 10 different f(x) where five of them don't have any random noise (y-values can be 100% extracted from x-values) and the other five have random noise:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.feature_selection import mutual_info_regression
# Create x-values and noise
x = np.random.uniform(-10, 10, size=500)
noise = np.random.uniform(-10, 10, size=500)
# Create several y-values
y1 = x
y2 = x + noise
y3 = 3*x
y4 = 3*x + noise
y5 = x**2
y6 = x**2 + noise
y7 = 2**x
y8 = 2**x + 10 * noise
y9 = -3 * x**3 + 9 * x**2 + 6 * x + 53
y10 = -3 * x**3 + 9 * x**2 + 6 * x + 53 + 100 * noise
# Create functions to iterate and plot
ids = [1, 6, 2, 7, 3, 8, 4, 9, 5, 10]
y_funcs = [y1, y2, y3, y4, y5, y6, y7, y8, y9, y10]
titles = ["Y1 = X", "Y2 = X + noise", "Y3 = 3X", "Y4 = 3X + noise", "Y5 = X^2", "Y6 = X^2 + noise",
"Y7 = 2^X", "Y8 = 2^X + 10*noise", "Y9 = -3X^3 + ... + 53", "Y10 = -3X^3 + ... + 100*noise"]
# Create a grid plot
plt.figure(figsize=(15,6))
plt.tight_layout()
for i, y_vals, title in zip(ids, y_funcs, titles):
plt.subplot(2, 5, i)
plt.scatter(x, y_vals, alpha=0.25)
plt.title(title)
plt.xticks([])
plt.yticks([])
plt.show()
Then, I've calculated the correlation coefficients and MI scores between x-feature and each of these y-features. To facilitate comparison between these metrics, I've divided MI scores by 5, so we understand the relative power of this metric compared to correlation coefficient:
# Generate correlation coefficients
### Generate the dataframe
data = np.transpose(np.array([x, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10]))
df = pd.DataFrame(data, columns = ["x", "y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8", "y9", "y10"])
### Define correlation matrix (method = Pearson: standard correlation coefficient)
corr_matrix = df.corr(method="pearson")["x"]
# Generate MI scores
## Get the necessary lists
y_funcs = [y1, y2, y3, y4, y5, y6, y7, y8, y9, y10]
cols = ["y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8", "y9", "y10"]
### Get Mutual Information score for each variable
mi_scores = []
for y_vals, col in zip(y_funcs, cols):
x_t = np.reshape(x, (-1,1))
mi_score = mutual_info_regression(x_t, y_vals)
mi_scores.append(mi_score[0])
mi_scores = np.array(mi_scores)
# Plot the results
x_vals = np.arange(1, 11, 1)
plt.figure(figsize=(15,4))
plt.tight_layout()
ax = plt.subplot(111)
ax.bar(x=x_vals-.15, height=abs(corr_matrix[1:]), color="firebrick", width=.3, label="Correlations")
ax.bar(x=x_vals+.15, height=mi_scores/5, color="dodgerblue", width=.3, label="MI scores / 5")
plt.xticks(ticks=x_vals, labels=cols)
for location in ["top", "right"]:
ax.spines[location].set_visible(False)
plt.legend()
plt.show()
As we can see on this plot, correlation coefficient does better on several ocassions, while MI score is better when we have a quadratic function, for example (see Y5 and Y6).
Therefore, my question would be: when doing a ML project (so the relations are not so clear, and features can even interact), how should we decide to use each of these metrics to remove a feature (i.e., one that seems to not be correlated with target feature Y)? Even more, should we do any removal at all, instead of letting the ML model decide better?
Happy coding!