Skip to content

Commit 22b4d66

Browse files
committed
Add style parameter to geom_sina
1 parent 2111050 commit 22b4d66

File tree

4 files changed

+46
-0
lines changed

4 files changed

+46
-0
lines changed

doc/changelog.qmd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ title: Changelog
7171
- Geoms [](:class:`~plotnine.geom_bar`) and [](:class:`~plotnine.geom_col`) have
7272
gained new parameter `just` that controls how the bars align with the axis break point.
7373

74+
- [](:class:`~plotnine.geom_sina`) has gained new parameter `style` just like
75+
[](:class:`~plotnine.geom_violin`), which make one-sided (half) sina plots.
76+
7477
### Enhancements
7578

7679
- Included datasets [](:attr:`~plotnine.data.mpg`), [](:attr:`~plotnine.data.msleep`) and

plotnine/stats/stat_sina.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ class stat_sina(stat):
5757
- `area` - Scale by the largest density/bin among the different sinas
5858
- `count` - areas are scaled proportionally to the number of points
5959
- `width` - Only scale according to the maxwidth parameter.
60+
style :
61+
Type of sina plot to draw. The options are
62+
```python
63+
'full' # Regular (2 sided)
64+
'left' # Left-sided half
65+
'right' # Right-sided half
66+
'left-right' # Alternate (left first) half by the group
67+
'right-left' # Alternate (right first) half by the group
68+
```
6069
6170
See Also
6271
--------
@@ -91,6 +100,7 @@ class stat_sina(stat):
91100
"bin_limit": 1,
92101
"random_state": None,
93102
"scale": "area",
103+
"style": "full",
94104
}
95105
CREATES = {"scaled"}
96106

@@ -245,6 +255,29 @@ def compute_group(cls, data, scales, **params):
245255

246256
def finish_layer(self, data, params):
247257
# Rescale x in case positions have been adjusted
258+
style = params["style"]
259+
x_mean = data["x"].to_numpy()
248260
x_mod = (data["xmax"] - data["xmin"]) / data["width"]
249261
data["x"] = data["x"] + data["x_diff"] * x_mod
262+
x = data["x"].to_numpy()
263+
even = data["group"].to_numpy() % 2 == 0
264+
265+
def mirror_x(bool_idx):
266+
"""
267+
Mirror x locations along the mean value
268+
"""
269+
data.loc[bool_idx, "x"] = (
270+
2 * x_mean[bool_idx] - data.loc[bool_idx, "x"]
271+
)
272+
273+
match style:
274+
case "left":
275+
mirror_x(x_mean < x)
276+
case "right":
277+
mirror_x(x < x_mean)
278+
case "left-right":
279+
mirror_x(even & (x < x_mean) | ~even & (x_mean < x))
280+
case "right-left":
281+
mirror_x(even & (x_mean < x) | ~even & (x < x_mean))
282+
250283
return data
30.2 KB
Loading

tests/test_geom_sina.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,13 @@ def test_method_counts():
6868
)
6969

7070
assert p == "method_counts"
71+
72+
73+
def test_style():
74+
p = (
75+
ggplot(data, aes("dist", "value"))
76+
+ geom_violin(style="left-right")
77+
+ geom_sina(style="left-right", random_state=123)
78+
)
79+
80+
assert p == "style"

0 commit comments

Comments
 (0)