Skip to content

Commit ce9793b

Browse files
committed
Inline geom.from_stat, stat.from_geom, and position.from_geom into layer resolvers
These factory methods are now inlined into the layer.py resolver functions (_resolve_stat, _resolve_position, layer.from_stat), centralising all component resolution in one place. Tests updated to exercise resolution through the layer API.
1 parent 218ca0b commit ce9793b

6 files changed

Lines changed: 33 additions & 143 deletions

File tree

plotnine/geoms/geom.py

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
data_mapping_as_kwargs,
1313
remove_missing,
1414
)
15-
from .._utils.registry import Register, Registry
15+
from .._utils.registry import Register
1616
from ..exceptions import PlotnineError
1717
from ..layer import layer
1818
from ..mapping.aes import rename_aesthetics
@@ -30,7 +30,6 @@
3030
from plotnine.facets.layout import Layout
3131
from plotnine.iapi import panel_view
3232
from plotnine.mapping import Environment
33-
from plotnine.stats.stat import stat
3433
from plotnine.typing import DataLike
3534

3635

@@ -92,44 +91,6 @@ def __init__(
9291
self.mapping = kwargs["mapping"]
9392
self.data = kwargs["data"]
9493

95-
@staticmethod
96-
def from_stat(stat: stat) -> geom:
97-
"""
98-
Return an instantiated geom object
99-
100-
geoms should not override this method.
101-
102-
Parameters
103-
----------
104-
stat :
105-
`stat`
106-
107-
Returns
108-
-------
109-
:
110-
A geom object
111-
112-
Raises
113-
------
114-
PlotnineError
115-
If unable to create a `geom`.
116-
"""
117-
name = stat.params.get("geom", "blank")
118-
119-
if isinstance(name, geom):
120-
return name
121-
122-
if isinstance(name, type) and issubclass(name, geom):
123-
klass = name
124-
elif isinstance(name, str):
125-
if not name.startswith("geom_"):
126-
name = f"geom_{name}"
127-
klass = Registry[name]
128-
else:
129-
raise PlotnineError(f"Unknown geom of type {type(name)}")
130-
131-
return klass(stat=stat, **stat._raw_kwargs)
132-
13394
@classmethod
13495
def aesthetics(cls: type[geom]) -> set[str]:
13596
"""

plotnine/layer.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,21 @@ def from_stat(stat: stat) -> layer:
155155
"""
156156
from .geoms.geom import geom as geom_cls
157157

158-
_geom = geom_cls.from_stat(stat)
159-
return layer(geom=_geom)
158+
name = stat.params.get("geom", "blank")
159+
160+
if isinstance(name, geom_cls):
161+
return layer(geom=name)
162+
163+
if isinstance(name, type) and issubclass(name, geom_cls):
164+
klass = name
165+
elif isinstance(name, str):
166+
if not name.startswith("geom_"):
167+
name = f"geom_{name}"
168+
klass = Registry[name]
169+
else:
170+
raise PlotnineError(f"Unknown geom of type {type(name)}")
171+
172+
return layer(geom=klass(stat=stat, **stat._raw_kwargs))
160173

161174
@staticmethod
162175
def _verify_arguments(geom: geom, stat: stat) -> None:
@@ -688,7 +701,7 @@ def _resolve_stat(
688701
from .stats.stat import stat as stat_cls
689702

690703
if stat_spec is None:
691-
return stat_cls.from_geom(geom_obj)
704+
stat_spec = geom_obj.params["stat"]
692705

693706
# Duck-type guard for module reloads
694707
if not isinstance(stat_spec, type) and hasattr(stat_spec, "compute_layer"):
@@ -734,7 +747,7 @@ def _resolve_position(
734747
from .positions.position import position as position_cls
735748

736749
if position_spec is None:
737-
return position_cls.from_geom(geom_obj)
750+
position_spec = geom_obj.params["position"]
738751

739752
if isinstance(position_spec, position_cls):
740753
return position_spec

plotnine/positions/position.py

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy as np
99

1010
from .._utils import check_required_aesthetics, groupby_apply
11-
from .._utils.registry import Register, Registry
11+
from .._utils.registry import Register
1212
from ..exceptions import PlotnineError, PlotnineWarning
1313
from ..mapping.aes import X_AESTHETICS, Y_AESTHETICS
1414

@@ -18,7 +18,6 @@
1818
import pandas as pd
1919

2020
from plotnine.facets.layout import Layout
21-
from plotnine.geoms.geom import geom
2221
from plotnine.iapi import pos_scales
2322
from plotnine.typing import TransformCol
2423

@@ -132,41 +131,6 @@ def transform_position(
132131

133132
return data
134133

135-
@staticmethod
136-
def from_geom(geom: geom) -> position:
137-
"""
138-
Create and return a position object for the geom
139-
140-
Parameters
141-
----------
142-
geom : geom
143-
An instantiated geom object.
144-
145-
Returns
146-
-------
147-
out : position
148-
A position object
149-
150-
Raises
151-
------
152-
PlotnineError
153-
If unable to create a `position`.
154-
"""
155-
name = geom.params["position"]
156-
if issubclass(type(name), position):
157-
return name
158-
159-
if isinstance(name, type) and issubclass(name, position):
160-
klass = name
161-
elif isinstance(name, str):
162-
if not name.startswith("position_"):
163-
name = f"position_{name}"
164-
klass = Registry[name]
165-
else:
166-
raise PlotnineError(f"Unknown position of type {type(name)}")
167-
168-
return klass()
169-
170134
@staticmethod
171135
def strategy(data: pd.DataFrame, params: dict[str, Any]) -> pd.DataFrame:
172136
"""

plotnine/stats/stat.py

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
remove_missing,
1313
uniquecols,
1414
)
15-
from .._utils.registry import Register, Registry
16-
from ..exceptions import PlotnineError
15+
from .._utils.registry import Register
1716
from ..layer import layer
1817
from ..mapping import aes
1918

@@ -22,7 +21,6 @@
2221

2322
from plotnine import ggplot
2423
from plotnine.facets.layout import Layout
25-
from plotnine.geoms.geom import geom
2624
from plotnine.iapi import pos_scales
2725
from plotnine.mapping import Environment
2826
from plotnine.typing import DataLike
@@ -82,52 +80,6 @@ def __init__(
8280
ae: kwargs[ae] for ae in self.aesthetics() & set(kwargs)
8381
}
8482

85-
@staticmethod
86-
def from_geom(geom: geom) -> stat:
87-
"""
88-
Return an instantiated stat object
89-
90-
stats should not override this method.
91-
92-
Parameters
93-
----------
94-
geom :
95-
A geom object
96-
97-
Returns
98-
-------
99-
stat
100-
A stat object
101-
102-
Raises
103-
------
104-
[](`~plotnine.exceptions.PlotnineError`) if unable to create a `stat`.
105-
"""
106-
name = geom.params["stat"]
107-
kwargs = geom._raw_kwargs
108-
# More stable when reloading modules than
109-
# using issubclass
110-
if not isinstance(name, type) and hasattr(name, "compute_layer"):
111-
return name
112-
113-
if isinstance(name, stat):
114-
return name
115-
elif isinstance(name, type) and issubclass(name, stat):
116-
klass = name
117-
elif isinstance(name, str):
118-
if not name.startswith("stat_"):
119-
name = f"stat_{name}"
120-
klass = Registry[name]
121-
else:
122-
raise PlotnineError(f"Unknown stat of type {type(name)}")
123-
124-
valid_kwargs = (
125-
klass.aesthetics() | klass.DEFAULT_PARAMS.keys()
126-
) & kwargs.keys()
127-
128-
params = {k: kwargs[k] for k in valid_kwargs}
129-
return klass(geom=geom, **params)
130-
13183
def __deepcopy__(self, memo: dict[Any, Any]) -> stat:
13284
"""
13385
Deep copy without copying the self.data dataframe

tests/test_geom.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from plotnine import aes, geom_point, ggplot, stat_identity
55
from plotnine.exceptions import PlotnineError
66
from plotnine.geoms.geom import geom
7+
from plotnine.layer import layer
78

89
data = pd.DataFrame({"col1": [1, 2, 3, 4], "col2": 2, "col3": list("abcd")})
910

@@ -51,13 +52,13 @@ class geom_abc(geom):
5152

5253
def test_geom_from_stat():
5354
stat = stat_identity(geom="point")
54-
assert isinstance(geom.from_stat(stat), geom_point)
55+
assert isinstance(layer.from_stat(stat).geom, geom_point)
5556

5657
stat = stat_identity(geom="geom_point")
57-
assert isinstance(geom.from_stat(stat), geom_point)
58+
assert isinstance(layer.from_stat(stat).geom, geom_point)
5859

5960
stat = stat_identity(geom=geom_point())
60-
assert isinstance(geom.from_stat(stat), geom_point)
61+
assert isinstance(layer.from_stat(stat).geom, geom_point)
6162

6263
stat = stat_identity(geom=geom_point)
63-
assert isinstance(geom.from_stat(stat), geom_point)
64+
assert isinstance(layer.from_stat(stat).geom, geom_point)

tests/test_position.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
stage,
2727
)
2828
from plotnine.exceptions import PlotnineError
29-
from plotnine.positions.position import position
3029

3130
n = 6
3231
m = 10
@@ -241,17 +240,17 @@ def test_jitterdodge():
241240

242241

243242
def test_position_from_geom():
244-
geom = geom_point(position="jitter")
245-
assert isinstance(position.from_geom(geom), position_jitter)
243+
lyr = geom_point(position="jitter").to_layer()
244+
assert isinstance(lyr.position, position_jitter)
246245

247-
geom = geom_point(position="position_jitter")
248-
assert isinstance(position.from_geom(geom), position_jitter)
246+
lyr = geom_point(position="position_jitter").to_layer()
247+
assert isinstance(lyr.position, position_jitter)
249248

250-
geom = geom_point(position=position_jitter())
251-
assert isinstance(position.from_geom(geom), position_jitter)
249+
lyr = geom_point(position=position_jitter()).to_layer()
250+
assert isinstance(lyr.position, position_jitter)
252251

253-
geom = geom_point(position=position_jitter)
254-
assert isinstance(position.from_geom(geom), position_jitter)
252+
lyr = geom_point(position=position_jitter).to_layer()
253+
assert isinstance(lyr.position, position_jitter)
255254

256255

257256
def test_dodge_empty_data():

0 commit comments

Comments
 (0)