Skip to content
22 changes: 22 additions & 0 deletions doc/changelog.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ title: Changelog
theme(plot_footer_line=element_line(color="black"))
```

### API Changes

- Removed `geom.to_layer()`, `stat.to_layer()`, `annotate.to_layer()`,
`layer.from_geom()` and `layer.from_stat()`. Use
`layer(geom=...)` and `layer(stat=...)` directly.

The [](:class:`~plotnine.layer.layer`) constructor now handles both geom-first
and stat-first creation. When only a `stat` is provided, the geom is
automatically derived from the stat's default.

```python
# Before
layer.from_geom(geom_point())
layer.from_stat(stat_bin())
geom_point().to_layer()

# After
layer(geom=geom_point())
layer(stat=stat_bin())
layer(geom=geom_point())
```

### Bug Fixes

- Fixed [](:class:`~plotnine.geom_smooth`) / [](:class:`~plotnine.stat_smooth`) when using a linear model via "lm" with weights for the model to do a weighted regression. This bug did not affect the formula API of the linear model. ({{< issue 1005 >}})
Expand Down
16 changes: 3 additions & 13 deletions plotnine/geoms/annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from typing import Any

from plotnine import ggplot
from plotnine.layer import layer


class annotate:
Expand Down Expand Up @@ -135,16 +134,7 @@ def __radd__(self, other: ggplot) -> ggplot:
"""
Add to ggplot
"""
other += self.to_layer() # Add layer
return other

def to_layer(self) -> layer:
"""
Make a layer that represents this annotation
from ..layer import layer

Returns
-------
out : layer
Layer
"""
return self._annotation_geom.to_layer()
other += layer(geom=self._annotation_geom)
return other
88 changes: 4 additions & 84 deletions plotnine/geoms/geom.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
data_mapping_as_kwargs,
remove_missing,
)
from .._utils.registry import Register, Registry
from .._utils.registry import Register
from ..exceptions import PlotnineError
from ..layer import layer
from ..mapping.aes import rename_aesthetics
from ..mapping.evaluation import evaluate
from ..positions.position import position
from ..stats.stat import stat

if typing.TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -81,7 +79,7 @@ def __init__(
):
kwargs = rename_aesthetics(kwargs)
kwargs = data_mapping_as_kwargs((data, mapping), kwargs)
self._kwargs = kwargs # Will be used to create stat & layer
self._raw_kwargs = kwargs # Will be used to create stat & layer

# separate aesthetics and parameters
self.aes_params = {
Expand All @@ -92,47 +90,6 @@ def __init__(
}
self.mapping = kwargs["mapping"]
self.data = kwargs["data"]
self._stat = stat.from_geom(self)
self._position = position.from_geom(self)
self._verify_arguments(kwargs) # geom, stat, layer

@staticmethod
def from_stat(stat: stat) -> geom:
"""
Return an instantiated geom object

geoms should not override this method.

Parameters
----------
stat :
`stat`

Returns
-------
:
A geom object

Raises
------
PlotnineError
If unable to create a `geom`.
"""
name = stat.params["geom"]

if isinstance(name, geom):
return name

if isinstance(name, type) and issubclass(name, geom):
klass = name
elif isinstance(name, str):
if not name.startswith("geom_"):
name = f"geom_{name}"
klass = Registry[name]
else:
raise PlotnineError(f"Unknown geom of type {type(name)}")

return klass(stat=stat, **stat._kwargs)

@classmethod
def aesthetics(cls: type[geom]) -> set[str]:
Expand Down Expand Up @@ -163,7 +120,7 @@ def __deepcopy__(self, memo: dict[Any, Any]) -> geom:
new = result.__dict__

# don't make a deepcopy of data, or environment
shallow = {"data", "_kwargs", "environment"}
shallow = {"data", "_raw_kwargs", "environment"}
for key, item in old.items():
if key in shallow:
new[key] = item # pyright: ignore[reportIndexIssue]
Expand Down Expand Up @@ -473,46 +430,9 @@ def __radd__(self, other: ggplot) -> ggplot:
:
ggplot object with added layer.
"""
other += self.to_layer() # Add layer
other += layer(geom=self)
return other

def to_layer(self) -> layer:
"""
Make a layer that represents this geom

Returns
-------
:
Layer
"""
return layer.from_geom(self)

def _verify_arguments(self, kwargs: dict[str, Any]):
"""
Verify arguments passed to the geom
"""
geom_stat_args = kwargs.keys() | self._stat._kwargs.keys()
unknown = (
geom_stat_args
- self.aesthetics()
- self.DEFAULT_PARAMS.keys() # geom aesthetics
- self._stat.aesthetics() # geom parameters
- self._stat.DEFAULT_PARAMS.keys() # stat aesthetics
- { # stat parameters
"data",
"mapping",
"show_legend", # layer parameters
"inherit_aes",
"raster",
}
) # layer parameters
if unknown:
msg = (
"Parameters {}, are not understood by "
"either the geom, stat or layer."
)
raise PlotnineError(msg.format(unknown))

def handle_na(self, data: pd.DataFrame) -> pd.DataFrame:
"""
Remove rows with NaN values
Expand Down
11 changes: 5 additions & 6 deletions plotnine/geoms/geom_dotplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class geom_dotplot(geom):

def setup_data(self, data: pd.DataFrame) -> pd.DataFrame:
gp = self.params
sp = self._stat.params
sp = self.params["stat_params"]

# Issue warnings when parameters don't make sense
if gp["position"] == "stack":
Expand Down Expand Up @@ -207,16 +207,15 @@ def draw_group(
size = data["binwidth"].iloc[0] * params["dotsize"]
offsets = data["stackpos"] * params["stackratio"]

if params["binaxis"] == "x":
binaxis = params["stat_params"]["binaxis"]
if binaxis == "x":
width, height = size, size * factor
xpos, ypos = data["x"], data["y"] + height * offsets
elif params["binaxis"] == "y":
elif binaxis == "y":
width, height = size / factor, size
xpos, ypos = data["x"] + width * offsets, data["y"]
else:
raise ValueError(
f"Invalid valid value binaxis={params['binaxis']}"
)
raise ValueError(f"Invalid valid value binaxis={binaxis}")

circles = []
for xy in zip(xpos, ypos):
Expand Down
Loading