Skip to content

Commit e4a3226

Browse files
committed
ENH: violins to overlap top to bottom from the origin
1 parent 9d03945 commit e4a3226

File tree

4 files changed

+36
-7
lines changed

4 files changed

+36
-7
lines changed

doc/changelog.qmd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ title: Changelog
8080
[](:attr:`~plotnine.data.midwest`) no longer have any of their columns as categoricals.
8181
This matches the respective datasets in R. {{< issue 913 >}}
8282

83+
- When the `width` of the violins in [](:class:`~plotnine.geom_violin`) is wider than their
84+
normal area, they now overlap with those closest to the origin on top. This makes it possible
85+
to create overlapping ridge plots using half violins.
86+
8387
### Bug Fixes
8488

8589
- Fixed bug in with the `legend_key_height` themeable where it wasn't applied.

plotnine/geoms/geom_violin.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
import typing
3+
from typing import TYPE_CHECKING, cast
44

55
import numpy as np
66
import pandas as pd
@@ -11,7 +11,7 @@
1111
from .geom_path import geom_path
1212
from .geom_polygon import geom_polygon
1313

14-
if typing.TYPE_CHECKING:
14+
if TYPE_CHECKING:
1515
from typing import Any
1616

1717
from matplotlib.axes import Axes
@@ -115,10 +115,17 @@ def draw_panel(
115115
ax: Axes,
116116
**params: Any,
117117
):
118-
quantiles = params["draw_quantiles"]
119-
style = params["style"]
118+
quantiles = params.pop("draw_quantiles")
119+
style = params.pop("style")
120+
zorder = params.pop("zorder")
121+
122+
for i, (group, df) in enumerate(data.groupby("group")):
123+
# Place the violins with the smalleer group number on top
124+
# of those with larger numbers. The group_zorder values should be
125+
# in the range [zorder, zorder + 1) to stay within the layer.
126+
group = cast("int", group)
127+
group_zorder = zorder + 0.9 / group
120128

121-
for i, (_, df) in enumerate(data.groupby("group")):
122129
# Find the points for the line to go all the way around
123130
df["xminv"] = df["x"] - df["violinwidth"] * (df["x"] - df["xmin"])
124131
df["xmaxv"] = df["x"] + df["violinwidth"] * (df["xmax"] - df["x"])
@@ -156,7 +163,12 @@ def draw_panel(
156163

157164
# plot violin polygon
158165
geom_polygon.draw_group(
159-
polygon_df, panel_params, coord, ax, **params
166+
polygon_df,
167+
panel_params,
168+
coord,
169+
ax,
170+
zorder=group_zorder,
171+
**params,
160172
)
161173

162174
if quantiles is not None:
@@ -174,7 +186,12 @@ def draw_panel(
174186

175187
# plot quantile segments
176188
geom_path.draw_group(
177-
segment_df, panel_params, coord, ax, **params
189+
segment_df,
190+
panel_params,
191+
coord,
192+
ax,
193+
zorder=group_zorder,
194+
**params,
178195
)
179196

180197

Loading

tests/test_geom_violin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,11 @@ def test_scales_free():
132132
+ facet_grid("am", "'column'", scales="free")
133133
)
134134
assert p == "scales_free"
135+
136+
137+
def test_overlap():
138+
p = ggplot(data, aes("x")) + geom_violin(
139+
aes(y="y", fill="x"), position="identity", width=2
140+
)
141+
142+
assert p == "overlap"

0 commit comments

Comments
 (0)