train_df, test_df = train_test_split(df, test_size=0.30, random_state=RANDOM_STATE)
# Gambar 3.3: min-max vs robust scaling pada fitur yang berekor kanan.
values = train_df[[shape_feature]].to_numpy()
minmax_values = MinMaxScaler().fit_transform(values).ravel()
robust_values = RobustScaler().fit_transform(values).ravel()
rng = np.random.default_rng(RANDOM_STATE)
sample_idx = rng.choice(len(values), size=1600, replace=False)
fig, axes = plt.subplots(3, 1, figsize=(8, 4.8), sharey=True)
for ax, arr, title in [
(axes[0], values.ravel(), f'Mentah: {shape_feature}'),
(axes[1], minmax_values, 'Min-max scaling'),
(axes[2], robust_values, 'Robust scaling (median/IQR)'),
]:
y_jitter = rng.normal(0, 0.025, size=len(sample_idx))
ax.scatter(arr[sample_idx], y_jitter, s=10, alpha=0.35, color='0.25')
ax.axvline(np.median(arr), color='tab:blue', lw=1.2, label='median')
ax.set_title(title)
ax.set_yticks([])
axes[0].legend(loc='upper right')
fig.suptitle('Gambar 3.3 - Nilai ekstrem tetap ada, tetapi pusat/skala berubah')
plt.tight_layout()
# Gambar 3.4: transformasi bentuk distribusi pada fitur prediktif real.
feature_train = train_df[[shape_feature]]
shape_views = {
'Mentah': feature_train[shape_feature].to_numpy(),
'log1p': np.log1p(feature_train[shape_feature].to_numpy()),
'Yeo-Johnson': PowerTransformer(method='yeo-johnson').fit_transform(feature_train).ravel(),
'Quantile -> normal': QuantileTransformer(
n_quantiles=min(1000, len(feature_train)),
output_distribution='normal',
random_state=RANDOM_STATE,
).fit_transform(feature_train).ravel(),
}
fig, axes = plt.subplots(2, 2, figsize=(8, 5.2))
for ax, (name, arr) in zip(axes.ravel(), shape_views.items()):
ax.hist(arr, bins=40, color='0.35', edgecolor='white')
ax.set_title(name)
ax.set_ylabel('Frekuensi')
fig.suptitle(f'Gambar 3.4 - Transformasi bentuk distribusi {shape_feature}')
plt.tight_layout()
# Fixed ruler: model sama, representasi berubah.
X_train, X_test = train_df[feature_cols], test_df[feature_cols]
y_train, y_test = np.log1p(train_df['shares']), np.log1p(test_df['shares'])
def rmse(model):
model.fit(X_train, y_train)
pred = model.predict(X_test)
return np.sqrt(mean_squared_error(y_test, pred))
results = pd.DataFrame([
{'model': 'k-NN', 'representasi': 'raw', 'RMSE_log1p_shares': rmse(KNeighborsRegressor(n_neighbors=25))},
{'model': 'k-NN', 'representasi': 'StandardScaler', 'RMSE_log1p_shares': rmse(Pipeline([('scaler', StandardScaler()), ('model', KNeighborsRegressor(n_neighbors=25))]))},
{'model': 'HistGradientBoosting', 'representasi': 'raw', 'RMSE_log1p_shares': rmse(HistGradientBoostingRegressor(max_iter=120, learning_rate=0.06, random_state=RANDOM_STATE))},
{'model': 'HistGradientBoosting', 'representasi': 'StandardScaler', 'RMSE_log1p_shares': rmse(Pipeline([('scaler', StandardScaler()), ('model', HistGradientBoostingRegressor(max_iter=120, learning_rate=0.06, random_state=RANDOM_STATE))]))},
])
print(results.round(4).to_string(index=False))