Skip to content

Commit 352b419

Browse files
authored
Merge pull request matplotlib#30387 from timhoffm/violin-kde
MNT: Refactor default violin KDE estimator
2 parents 5f8f94c + 6574ed5 commit 352b419

File tree

3 files changed

+38
-19
lines changed

3 files changed

+38
-19
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8878,18 +8878,8 @@ def violinplot(self, dataset, positions=None, vert=None,
88788878
.Axes.violin : Draw a violin from pre-computed statistics.
88798879
boxplot : Draw a box and whisker plot.
88808880
"""
8881-
8882-
def _kde_method(X, coords):
8883-
# Unpack in case of e.g. Pandas or xarray object
8884-
X = cbook._unpack_to_numpy(X)
8885-
# fallback gracefully if the vector contains only one value
8886-
if np.all(X[0] == X):
8887-
return (X[0] == coords).astype(float)
8888-
kde = mlab.GaussianKDE(X, bw_method)
8889-
return kde.evaluate(coords)
8890-
8891-
vpstats = cbook.violin_stats(dataset, _kde_method, points=points,
8892-
quantiles=quantiles)
8881+
vpstats = cbook.violin_stats(dataset, ("GaussianKDE", bw_method),
8882+
points=points, quantiles=quantiles)
88938883
return self.violin(vpstats, positions=positions, vert=vert,
88948884
orientation=orientation, widths=widths,
88958885
showmeans=showmeans, showextrema=showextrema,

lib/matplotlib/cbook.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from numpy import VisibleDeprecationWarning
3030

3131
import matplotlib
32-
from matplotlib import _api, _c_internal_utils
32+
from matplotlib import _api, _c_internal_utils, mlab
3333

3434

3535
class _ExceptionInfo:
@@ -1434,7 +1434,7 @@ def _reshape_2D(X, name):
14341434
return result
14351435

14361436

1437-
def violin_stats(X, method, points=100, quantiles=None):
1437+
def violin_stats(X, method=("GaussianKDE", "scott"), points=100, quantiles=None):
14381438
"""
14391439
Return a list of dictionaries of data which can be used to draw a series
14401440
of violin plots.
@@ -1453,11 +1453,23 @@ def violin_stats(X, method, points=100, quantiles=None):
14531453
Sample data that will be used to produce the gaussian kernel density
14541454
estimates. Must have 2 or fewer dimensions.
14551455
1456-
method : callable
1456+
method : (name, bw_method) or callable,
14571457
The method used to calculate the kernel density estimate for each
1458-
column of data. When called via ``method(v, coords)``, it should
1459-
return a vector of the values of the KDE evaluated at the values
1460-
specified in coords.
1458+
column of data. Valid values:
1459+
1460+
- a tuple of the form ``(name, bw_method)`` where *name* currently must
1461+
always be ``"GaussianKDE"`` and *bw_method* is the method used to
1462+
calculate the estimator bandwidth. Supported values are 'scott',
1463+
'silverman' or a float or a callable. If a float, this will be used
1464+
directly as `!kde.factor`. If a callable, it should take a
1465+
`matplotlib.mlab.GaussianKDE` instance as its only parameter and
1466+
return a float.
1467+
1468+
- a callable with the signature ::
1469+
1470+
def method(data: ndarray, coords: ndarray) -> ndarray
1471+
1472+
It should return the KDE of *data* evaluated at *coords*.
14611473
14621474
points : int, default: 100
14631475
Defines the number of points to evaluate each of the gaussian kernel
@@ -1485,6 +1497,20 @@ def violin_stats(X, method, points=100, quantiles=None):
14851497
- max: The maximum value for this column of data.
14861498
- quantiles: The quantile values for this column of data.
14871499
"""
1500+
if isinstance(method, tuple):
1501+
name, bw_method = method
1502+
if name != "GaussianKDE":
1503+
raise ValueError(f"Unknown KDE method name {name!r}. The only supported "
1504+
'named method is "GaussianKDE"')
1505+
1506+
def _kde_method(x, coords):
1507+
# fallback gracefully if the vector contains only one value
1508+
if np.all(x[0] == x):
1509+
return (x[0] == coords).astype(float)
1510+
kde = mlab.GaussianKDE(x, bw_method)
1511+
return kde.evaluate(coords)
1512+
1513+
method = _kde_method
14881514

14891515
# List of dictionaries describing each of the violins.
14901516
vpstats = []

lib/matplotlib/cbook.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ ls_mapper_r: dict[str, str]
133133
def contiguous_regions(mask: ArrayLike) -> list[np.ndarray]: ...
134134
def is_math_text(s: str) -> bool: ...
135135
def violin_stats(
136-
X: ArrayLike, method: Callable, points: int = ..., quantiles: ArrayLike | None = ...
136+
X: ArrayLike,
137+
method: tuple[Literal["GaussianKDE"], Literal["scott", "silverman"] | float | Callable] | Callable = ...,
138+
points: int = ...,
139+
quantiles: ArrayLike | None = ...
137140
) -> list[dict[str, Any]]: ...
138141
def pts_to_prestep(x: ArrayLike, *args: ArrayLike) -> np.ndarray: ...
139142
def pts_to_poststep(x: ArrayLike, *args: ArrayLike) -> np.ndarray: ...

0 commit comments

Comments
 (0)