ENH: Add MedianAbsoluteDeviationScaler (MADScaler) to sklearn.preprocessing

### Describe the workflow you want to enable

Today, if a user wants to centre features by the median and scale them by the median absolute deviation (MAD) they must hand-roll code like:
mad = 1.4826 * np.median(np.abs(X - np.median(X, axis=0)), axis=0)
X_scaled = (X - np.median(X, axis=0)) / mad

A built-in MedianAbsoluteDeviationScaler (or a statistic="mad" option on RobustScaler) would let them write a single, self-documenting line:
from sklearn.preprocessing import MedianAbsoluteDeviationScaler
X_scaled = MedianAbsoluteDeviationScaler().fit_transform(X)

That makes robust MAD scaling first-class, composable in pipelines, and reversible via inverse_transform().

### Describe your proposed solution

Add a new transformer:
class MedianAbsoluteDeviationScaler(BaseEstimator, TransformerMixin):
    with_centering: bool = True
    with_scaling:   bool = True
    copy:           bool = True
    unit_variance:  bool = False

    # learned in fit
    center_: ndarray
    scale_: ndarray

Fit logic
1. center_ = np.median(X, axis=0) (if with_centering)

2. mad = np.median(np.abs(X - center_), axis=0) * 1.4826

3. Guard against zeros with float_eps, store in scale_.

transform() and inverse_transform() reuse the pattern from RobustScaler.

Docs / tests

- Unit tests for shape preservation, inverse-transform round-trip, and robustness to outliers.

- A short subsection in preprocessing.rst and a gallery example comparing Standard, Robust (IQR) and MAD scalers.

- Changelog bullet in whats_new/v1.5.rst.

I am happy to implement this within ~2 weeks.

### Describe alternatives you've considered, if relevant

- Keep user-land recipes – fragments the ecosystem and lacks inverse_transform().

- Extend RobustScaler with statistic={"iqr","mad"} (default "iqr"). This also works, but changes a long-standing API and may require a deprecation cycle.

### Additional context

- MAD is a well-known σ-consistent robust scale estimator, more efficient than IQR for symmetric heavy-tailed or Laplace-like data.

- Estimated code diff ≈ 20 LOC plus tests/docs – labelled “good first issue” size.

/cc @jnothman @glemaitre for initial design feedback — thanks!

COMMENT:
Thanks for running the plots, @glevv! A few thoughts:

What’s new here?

- Unlike RobustScaler, which divides by the IQR (75 %–25 % range), this MADScaler uses the (optionally σ-corrected) median absolute deviation.

- In practice that gives a slightly tighter central spread at the cost of still letting true outliers sit beyond ±3 MAD.

- You can already see it on your “MADScaler” row: the bulk of points are marginally less compressed than with IQR-based scaling, especially on the heavy‐tailed MedInc axis.

Why would users pick MAD over IQR?

- For symmetric heavy‐tailed data (e.g. Cauchy, Laplace), MAD is a more efficient estimator of scale than IQR.

- It also aligns with a one‐line recipe many practitioners already hand-roll.

Next steps?

- Should I add a tiny inset showing the numeric ratio of scale estimates (MAD / IQR) for each feature?

- Or maybe overlay histograms of the scaled features’ absolute values to highlight the central “tightness”?

- Also, do you think we ought to drop the 1.4826 σ-correction by default (i.e. stay raw-MAD parallel to RobustScaler), or leave it on under unit_variance=True?

Happy to iterate on whichever visualization or API tweak would make the usefulness of a separate MedianAbsoluteDeviationScaler crystal-clear before turning this into a full PR. Let me know!

Labels: New Feature, module:preprocessing, Needs Investigation